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