xref: /haiku/src/kits/interface/SeparatorView.cpp (revision e81a954787e50e56a7f06f72705b7859b6ab06d1)
1 /*
2  * Copyright 2001-2009, Stephan Aßmus <superstippi@gmx.de>
3  * Copyright 2001-2009, Ingo Weinhold <ingo_weinhold@gmx.de>
4  * All rights reserved. Distributed under the terms of the MIT license.
5  */
6 
7 
8 #include "SeparatorView.h"
9 
10 #include <new>
11 
12 #include <math.h>
13 #include <stdio.h>
14 
15 #include <ControlLook.h>
16 #include <LayoutUtils.h>
17 #include <Region.h>
18 
19 
20 static const float kMinBorderLength = 5.0f;
21 
22 
23 // TODO: Actually implement alignment support!
24 // TODO: More testing, especially archiving.
25 
26 
27 BSeparatorView::BSeparatorView(orientation orientation, border_style border)
28 	:
29 	BView(NULL, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
30 {
31 	_Init(NULL, NULL, orientation, BAlignment(B_ALIGN_HORIZONTAL_CENTER,
32 		B_ALIGN_VERTICAL_CENTER), border);
33 }
34 
35 
36 BSeparatorView::BSeparatorView(const char* name, const char* label,
37 	orientation orientation, border_style border, const BAlignment& alignment)
38 	:
39 	BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
40 {
41 	_Init(label, NULL, orientation, alignment, border);
42 }
43 
44 
45 BSeparatorView::BSeparatorView(const char* name, BView* labelView,
46 	orientation orientation, border_style border, const BAlignment& alignment)
47 	:
48 	BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
49 {
50 	_Init(NULL, labelView, orientation, alignment, border);
51 }
52 
53 
54 BSeparatorView::BSeparatorView(const char* label,
55 	orientation orientation, border_style border, const BAlignment& alignment)
56 	:
57 	BView("", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
58 {
59 	_Init(label, NULL, orientation, alignment, border);
60 }
61 
62 
63 BSeparatorView::BSeparatorView(BView* labelView,
64 	orientation orientation, border_style border, const BAlignment& alignment)
65 	:
66 	BView("", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
67 {
68 	_Init(NULL, labelView, orientation, alignment, border);
69 }
70 
71 
72 BSeparatorView::BSeparatorView(BMessage* archive)
73 	:
74 	BView(archive),
75 	fLabel(),
76 	fLabelView(NULL),
77 	fOrientation(B_HORIZONTAL),
78 	fAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER,
79 		B_ALIGN_VERTICAL_CENTER)),
80 	fBorder(B_FANCY_BORDER)
81 {
82 	// NULL archives will be caught by BView.
83 
84 	const char* label;
85 	if (archive->FindString("_labelview", &label) == B_OK) {
86 		fLabelView = FindView(label);
87 	} else if (archive->FindString("_label", &label) == B_OK) {
88 		fLabel.SetTo(label);
89 	}
90 
91 	int32 orientation;
92 	if (archive->FindInt32("_orientation", &orientation) == B_OK)
93 		fOrientation = (enum orientation)orientation;
94 
95 	int32 hAlignment;
96 	int32 vAlignment;
97 	if (archive->FindInt32("_halignment", &hAlignment) == B_OK
98 		&& archive->FindInt32("_valignment", &vAlignment) == B_OK) {
99 		fAlignment.horizontal = (alignment)hAlignment;
100 		fAlignment.vertical = (vertical_alignment)vAlignment;
101 	}
102 
103 	int32 borderStyle;
104 	if (archive->FindInt32("_border", &borderStyle) != B_OK)
105 		fBorder = (border_style)borderStyle;
106 }
107 
108 
109 BSeparatorView::~BSeparatorView()
110 {
111 }
112 
113 
114 // #pragma mark - Archiving
115 
116 
117 BArchivable*
118 BSeparatorView::Instantiate(BMessage* archive)
119 {
120 	if (validate_instantiation(archive, "BSeparatorView"))
121 		return new (std::nothrow) BSeparatorView(archive);
122 
123 	return NULL;
124 }
125 
126 
127 status_t
128 BSeparatorView::Archive(BMessage* into, bool deep) const
129 {
130 	// TODO: Test this.
131 	status_t result = BView::Archive(into, deep);
132 	if (result != B_OK)
133 		return result;
134 
135 	if (fLabelView != NULL)
136 		result = into->AddString("_labelview", fLabelView->Name());
137 	else
138 		result = into->AddString("_label", fLabel.String());
139 
140 	if (result == B_OK)
141 		result = into->AddInt32("_orientation", fOrientation);
142 
143 	if (result == B_OK)
144 		result = into->AddInt32("_halignment", fAlignment.horizontal);
145 
146 	if (result == B_OK)
147 		result = into->AddInt32("_valignment", fAlignment.vertical);
148 
149 	if (result == B_OK)
150 		result = into->AddInt32("_border", fBorder);
151 
152 	return result;
153 }
154 
155 
156 // #pragma mark -
157 
158 
159 void
160 BSeparatorView::Draw(BRect updateRect)
161 {
162 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
163 	if (BView* parent = Parent()) {
164 		if (parent->ViewColor() != B_TRANSPARENT_COLOR)
165 			base = parent->ViewColor();
166 	}
167 
168 	BRect labelBounds;
169 	if (fLabelView != NULL) {
170 		labelBounds = fLabelView->Frame();
171 	} else if (fLabel.CountChars() > 0) {
172 		labelBounds = _MaxLabelBounds();
173 		float labelWidth = StringWidth(fLabel.String());
174 		if (fOrientation == B_HORIZONTAL) {
175 			switch (fAlignment.horizontal) {
176 				case B_ALIGN_LEFT:
177 				default:
178 					labelBounds.right = labelBounds.left + labelWidth;
179 					break;
180 
181 				case B_ALIGN_RIGHT:
182 					labelBounds.left = labelBounds.right - labelWidth;
183 					break;
184 
185 				case B_ALIGN_CENTER:
186 					labelBounds.left = (labelBounds.left + labelBounds.right
187 						- labelWidth) / 2;
188 					labelBounds.right = labelBounds.left + labelWidth;
189 					break;
190 			}
191 		} else {
192 			switch (fAlignment.vertical) {
193 				case B_ALIGN_TOP:
194 				default:
195 					labelBounds.bottom = labelBounds.top + labelWidth;
196 					break;
197 
198 				case B_ALIGN_BOTTOM:
199 					labelBounds.top = labelBounds.bottom - labelWidth;
200 					break;
201 
202 				case B_ALIGN_MIDDLE:
203 					labelBounds.top = (labelBounds.top + labelBounds.bottom
204 						- labelWidth) / 2;
205 					labelBounds.bottom = labelBounds.top + labelWidth;
206 					break;
207 			}
208 		}
209 	}
210 
211 	BRect bounds = Bounds();
212 	BRegion region(bounds);
213 	if (labelBounds.IsValid()) {
214 		region.Exclude(labelBounds);
215 		ConstrainClippingRegion(&region);
216 	}
217 
218 	float borderSize = _BorderSize();
219 	if (borderSize > 0.0f) {
220 		if (fOrientation == B_HORIZONTAL) {
221 			bounds.top = floorf((bounds.top + bounds.bottom + 1 - borderSize)
222 				/ 2);
223 			bounds.bottom = bounds.top + borderSize - 1;
224 			region.Exclude(bounds);
225 			be_control_look->DrawBorder(this, bounds, updateRect, base,
226 				fBorder, 0, BControlLook::B_TOP_BORDER);
227 		} else {
228 			bounds.left = floorf((bounds.left + bounds.right + 1 - borderSize)
229 				/ 2);
230 			bounds.right = bounds.left + borderSize - 1;
231 			region.Exclude(bounds);
232 			be_control_look->DrawBorder(this, bounds, updateRect, base,
233 				fBorder, 0, BControlLook::B_LEFT_BORDER);
234 		}
235 		if (labelBounds.IsValid())
236 			region.Include(labelBounds);
237 
238 		ConstrainClippingRegion(&region);
239 	}
240 
241 	SetLowColor(base);
242 	FillRect(updateRect, B_SOLID_LOW);
243 
244 	if (fLabel.CountChars() > 0) {
245 		font_height fontHeight;
246 		GetFontHeight(&fontHeight);
247 
248 		SetHighColor(0, 0, 0, 255);
249 
250 		if (fOrientation == B_HORIZONTAL) {
251 			DrawString(fLabel.String(), BPoint(labelBounds.left,
252 				labelBounds.top + ceilf(fontHeight.ascent)));
253 		} else {
254 			DrawString(fLabel.String(), BPoint(labelBounds.left
255 				+ ceilf(fontHeight.ascent), labelBounds.bottom));
256 		}
257 	}
258 }
259 
260 
261 void
262 BSeparatorView::GetPreferredSize(float* _width, float* _height)
263 {
264 	float width = 0.0f;
265 	float height = 0.0f;
266 
267 	if (fLabelView != NULL) {
268 		fLabelView->GetPreferredSize(&width, &height);
269 	} else if (fLabel.CountChars() > 0) {
270 		width = StringWidth(fLabel.String());
271 		font_height fontHeight;
272 		GetFontHeight(&fontHeight);
273 		height = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
274 		if (fOrientation == B_VERTICAL) {
275 			// swap width and height
276 			float temp = height;
277 			height = width;
278 			width = temp;
279 		}
280 	}
281 
282 	float borderSize = _BorderSize();
283 
284 	// Add some room for the border
285 	if (fOrientation == B_HORIZONTAL) {
286 		width += kMinBorderLength * 2.0f;
287 		height = max_c(height, borderSize - 1.0f);
288 	} else {
289 		height += kMinBorderLength * 2.0f;
290 		width = max_c(width, borderSize - 1.0f);
291 	}
292 
293 	if (_width != NULL)
294 		*_width = width;
295 
296 	if (_height != NULL)
297 		*_height = height;
298 }
299 
300 
301 BSize
302 BSeparatorView::MinSize()
303 {
304 	BSize size;
305 	GetPreferredSize(&size.width, &size.height);
306 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), size);
307 }
308 
309 
310 BSize
311 BSeparatorView::MaxSize()
312 {
313 	BSize size(MinSize());
314 	if (fOrientation == B_HORIZONTAL)
315 		size.width = B_SIZE_UNLIMITED;
316 	else
317 		size.height = B_SIZE_UNLIMITED;
318 
319 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
320 }
321 
322 
323 BSize
324 BSeparatorView::PreferredSize()
325 {
326 	BSize size;
327 	GetPreferredSize(&size.width, &size.height);
328 
329 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
330 }
331 
332 
333 // #pragma mark -
334 
335 
336 void
337 BSeparatorView::SetOrientation(orientation orientation)
338 {
339 	if (orientation == fOrientation)
340 		return;
341 
342 	fOrientation = orientation;
343 
344 	BFont font;
345 	GetFont(&font);
346 	if (fOrientation == B_VERTICAL)
347 		font.SetRotation(90.0f);
348 
349 	SetFont(&font);
350 
351 	Invalidate();
352 	InvalidateLayout();
353 }
354 
355 
356 void
357 BSeparatorView::SetAlignment(const BAlignment& aligment)
358 {
359 	if (aligment == fAlignment)
360 		return;
361 
362 	fAlignment = aligment;
363 
364 	Invalidate();
365 	InvalidateLayout();
366 }
367 
368 
369 void
370 BSeparatorView::SetBorderStyle(border_style border)
371 {
372 	if (border == fBorder)
373 		return;
374 
375 	fBorder = border;
376 
377 	Invalidate();
378 }
379 
380 
381 void
382 BSeparatorView::SetLabel(const char* label)
383 {
384 	if (label == NULL)
385 		label = "";
386 
387 	if (fLabel == label)
388 		return;
389 
390 	fLabel.SetTo(label);
391 
392 	Invalidate();
393 	InvalidateLayout();
394 }
395 
396 
397 void
398 BSeparatorView::SetLabel(BView* view, bool deletePrevious)
399 {
400 	if (fLabelView == view)
401 		return;
402 
403 	if (fLabelView != NULL) {
404 		fLabelView->RemoveSelf();
405 		if (deletePrevious)
406 			delete fLabelView;
407 	}
408 
409 	fLabelView = view;
410 
411 	if (fLabelView != NULL)
412 		AddChild(view);
413 }
414 
415 
416 status_t
417 BSeparatorView::Perform(perform_code code, void* data)
418 {
419 	return BView::Perform(code, data);
420 }
421 
422 
423 // #pragma mark - protected/private
424 
425 
426 void
427 BSeparatorView::DoLayout()
428 {
429 	BView::DoLayout();
430 
431 	if (fLabelView == NULL)
432 		return;
433 
434 	BRect bounds = _MaxLabelBounds();
435 	BRect frame = BLayoutUtils::AlignInFrame(bounds, fLabelView->MaxSize(),
436 		fAlignment);
437 
438 	fLabelView->MoveTo(frame.LeftTop());
439 	fLabelView->ResizeTo(frame.Width(), frame.Height());
440 }
441 
442 
443 void
444 BSeparatorView::_Init(const char* label, BView* labelView,
445 	orientation orientation, BAlignment alignment, border_style border)
446 {
447 	fLabel = "";
448 	fLabelView = NULL;
449 
450 	fOrientation = B_HORIZONTAL;
451 	fAlignment = alignment;
452 	fBorder = border;
453 
454 	SetFont(be_bold_font);
455 	SetViewColor(B_TRANSPARENT_32_BIT);
456 
457 	SetLabel(label);
458 	SetLabel(labelView, true);
459 	SetOrientation(orientation);
460 }
461 
462 
463 float
464 BSeparatorView::_BorderSize() const
465 {
466 	// TODO: Get these values from the BControlLook class.
467 	switch (fBorder) {
468 		case B_PLAIN_BORDER:
469 			return 1.0f;
470 
471 		case B_FANCY_BORDER:
472 			return 2.0f;
473 
474 		case B_NO_BORDER:
475 		default:
476 			return 0.0f;
477 	}
478 }
479 
480 
481 BRect
482 BSeparatorView::_MaxLabelBounds() const
483 {
484 	BRect bounds = Bounds();
485 	if (fOrientation == B_HORIZONTAL)
486 		bounds.InsetBy(kMinBorderLength, 0.0f);
487 	else
488 		bounds.InsetBy(0.0f, kMinBorderLength);
489 
490 	return bounds;
491 }
492 
493 
494 // #pragma mark - FBC padding
495 
496 
497 void BSeparatorView::_ReservedSeparatorView1() {}
498 void BSeparatorView::_ReservedSeparatorView2() {}
499 void BSeparatorView::_ReservedSeparatorView3() {}
500 void BSeparatorView::_ReservedSeparatorView4() {}
501 void BSeparatorView::_ReservedSeparatorView5() {}
502 void BSeparatorView::_ReservedSeparatorView6() {}
503 void BSeparatorView::_ReservedSeparatorView7() {}
504 void BSeparatorView::_ReservedSeparatorView8() {}
505 void BSeparatorView::_ReservedSeparatorView9() {}
506 void BSeparatorView::_ReservedSeparatorView10() {}
507