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