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