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