xref: /haiku/src/kits/interface/CheckBox.cpp (revision 58481f0f6ef1a61ba07283f012cafbc2ed874ead)
1 /*
2  * Copyright 2001-2007, Haiku Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  */
8 
9 /*!	BCheckBox displays an on/off control. */
10 
11 
12 #include <CheckBox.h>
13 
14 #include <ControlLook.h>
15 #include <LayoutUtils.h>
16 #include <Window.h>
17 
18 #include <binary_compatibility/Interface.h>
19 
20 
21 BCheckBox::BCheckBox(BRect frame, const char *name, const char *label,
22 		BMessage *message, uint32 resizingMode, uint32 flags)
23 	: BControl(frame, name, label, message, resizingMode, flags),
24 	  fPreferredSize(),
25 	  fOutlined(false)
26 {
27 	// Resize to minimum height if needed
28 	font_height fontHeight;
29 	GetFontHeight(&fontHeight);
30 	float minHeight = (float)ceil(6.0f + fontHeight.ascent
31 		+ fontHeight.descent);
32 	if (Bounds().Height() < minHeight)
33 		ResizeTo(Bounds().Width(), minHeight);
34 }
35 
36 
37 BCheckBox::BCheckBox(const char *name, const char *label, BMessage *message,
38 	uint32 flags)
39 	: BControl(name, label, message, flags | B_WILL_DRAW | B_NAVIGABLE),
40 	  fPreferredSize(),
41 	  fOutlined(false)
42 {
43 }
44 
45 
46 BCheckBox::BCheckBox(const char *label, BMessage *message)
47 	: BControl(NULL, label, message, B_WILL_DRAW | B_NAVIGABLE),
48 	  fPreferredSize(),
49 	  fOutlined(false)
50 {
51 }
52 
53 
54 BCheckBox::~BCheckBox()
55 {
56 }
57 
58 
59 BCheckBox::BCheckBox(BMessage *archive)
60 	: BControl(archive),
61 	fOutlined(false)
62 {
63 }
64 
65 
66 BArchivable *
67 BCheckBox::Instantiate(BMessage *archive)
68 {
69 	if (validate_instantiation(archive, "BCheckBox"))
70 		return new BCheckBox(archive);
71 
72 	return NULL;
73 }
74 
75 
76 status_t
77 BCheckBox::Archive(BMessage *archive, bool deep) const
78 {
79 	return BControl::Archive(archive, deep);
80 }
81 
82 
83 void
84 BCheckBox::Draw(BRect updateRect)
85 {
86 	if (be_control_look) {
87 		rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
88 
89 		uint32 flags = be_control_look->Flags(this);
90 		if (fOutlined)
91 			flags |= BControlLook::B_CLICKED;
92 
93 		BRect checkBoxRect(_CheckBoxFrame());
94 		BRect rect(checkBoxRect);
95 		be_control_look->DrawCheckBox(this, rect, updateRect,base, flags);
96 
97 		BRect labelRect(Bounds());
98 		labelRect.left = checkBoxRect.right
99 			+ be_control_look->DefaultLabelSpacing();
100 
101 		be_control_look->DrawLabel(this, Label(), labelRect, updateRect,
102 			base, flags);
103 		return;
104 	}
105 
106 	font_height fontHeight;
107 	GetFontHeight(&fontHeight);
108 
109 	// If the focus is changing, just redraw the focus indicator
110 	if (IsFocusChanging()) {
111 		float x = (float)ceil(10.0f + fontHeight.ascent);
112 		float y = 5.0f + (float)ceil(fontHeight.ascent);
113 
114 		if (IsFocus())
115 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
116 		else
117 			SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
118 
119 		StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y));
120 		return;
121 	}
122 
123 	rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR);
124 	rgb_color lighten1 = tint_color(noTint, B_LIGHTEN_1_TINT);
125 	rgb_color lightenMax = tint_color(noTint, B_LIGHTEN_MAX_TINT);
126 	rgb_color darken1 = tint_color(noTint, B_DARKEN_1_TINT);
127 	rgb_color darken2 = tint_color(noTint, B_DARKEN_2_TINT);
128 	rgb_color darken3 = tint_color(noTint, B_DARKEN_3_TINT);
129 	rgb_color darken4 = tint_color(noTint, B_DARKEN_4_TINT);
130 	rgb_color darkenmax = tint_color(noTint, B_DARKEN_MAX_TINT);
131 
132 	BRect rect = _CheckBoxFrame();
133 
134 	if (IsEnabled()) {
135 		// Filling
136 		SetHighColor(lightenMax);
137 		FillRect(rect);
138 
139 		// Box
140 		if (fOutlined) {
141 			SetHighColor(darken3);
142 			StrokeRect(rect);
143 
144 			rect.InsetBy(1, 1);
145 
146 			BeginLineArray(6);
147 
148 			AddLine(BPoint(rect.left, rect.bottom),
149 					BPoint(rect.left, rect.top), darken2);
150 			AddLine(BPoint(rect.left, rect.top),
151 					BPoint(rect.right, rect.top), darken2);
152 			AddLine(BPoint(rect.left, rect.bottom),
153 					BPoint(rect.right, rect.bottom), darken4);
154 			AddLine(BPoint(rect.right, rect.bottom),
155 					BPoint(rect.right, rect.top), darken4);
156 
157 			EndLineArray();
158 		} else {
159 			BeginLineArray(6);
160 
161 			AddLine(BPoint(rect.left, rect.bottom),
162 					BPoint(rect.left, rect.top), darken1);
163 			AddLine(BPoint(rect.left, rect.top),
164 					BPoint(rect.right, rect.top), darken1);
165 			rect.InsetBy(1, 1);
166 			AddLine(BPoint(rect.left, rect.bottom),
167 					BPoint(rect.left, rect.top), darken4);
168 			AddLine(BPoint(rect.left, rect.top),
169 					BPoint(rect.right, rect.top), darken4);
170 			AddLine(BPoint(rect.left + 1.0f, rect.bottom),
171 					BPoint(rect.right, rect.bottom), noTint);
172 			AddLine(BPoint(rect.right, rect.bottom),
173 					BPoint(rect.right, rect.top + 1.0f), noTint);
174 
175 			EndLineArray();
176 		}
177 
178 		// Checkmark
179 		if (Value() == B_CONTROL_ON) {
180 			rect.InsetBy(3, 3);
181 
182 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
183 			SetPenSize(2);
184 			StrokeLine(BPoint(rect.left, rect.top),
185 					   BPoint(rect.right, rect.bottom));
186 			StrokeLine(BPoint(rect.left, rect.bottom),
187 					   BPoint(rect.right, rect.top));
188 			SetPenSize(1);
189 		}
190 
191 		// Label
192 		SetHighColor(darkenmax);
193 		DrawString(Label(), BPoint((float)ceil(10.0f + fontHeight.ascent),
194 			3.0f + (float)ceil(fontHeight.ascent)));
195 
196 		// Focus
197 		if (IsFocus()) {
198 			float x = (float)ceil(10.0f + fontHeight.ascent);
199 			float y = 5.0f + (float)ceil(fontHeight.ascent);
200 
201 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
202 			StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y));
203 		}
204 	} else {
205 		// Filling
206 		SetHighColor(lighten1);
207 		FillRect(rect);
208 
209 		// Box
210 		BeginLineArray(6);
211 
212 		AddLine(BPoint(rect.left, rect.bottom),
213 				BPoint(rect.left, rect.top), noTint);
214 		AddLine(BPoint(rect.left, rect.top),
215 				BPoint(rect.right, rect.top), noTint);
216 		rect.InsetBy(1, 1);
217 		AddLine(BPoint(rect.left, rect.bottom),
218 				BPoint(rect.left, rect.top), darken2);
219 		AddLine(BPoint(rect.left, rect.top),
220 				BPoint(rect.right, rect.top), darken2);
221 		AddLine(BPoint(rect.left + 1.0f, rect.bottom),
222 				BPoint(rect.right, rect.bottom), darken1);
223 		AddLine(BPoint(rect.right, rect.bottom),
224 				BPoint(rect.right, rect.top + 1.0f), darken1);
225 
226 		EndLineArray();
227 
228 		// Checkmark
229 		if (Value() == B_CONTROL_ON) {
230 			rect.InsetBy(3, 3);
231 
232 			SetHighColor(tint_color(ui_color(B_KEYBOARD_NAVIGATION_COLOR),
233 				B_DISABLED_MARK_TINT));
234 			SetPenSize(2);
235 			StrokeLine(BPoint(rect.left, rect.top),
236 					   BPoint(rect.right, rect.bottom));
237 			StrokeLine(BPoint(rect.left, rect.bottom),
238 					   BPoint(rect.right, rect.top));
239 			SetPenSize(1);
240 		}
241 
242 		// Label
243 		SetHighColor(tint_color(noTint, B_DISABLED_LABEL_TINT));
244 		DrawString(Label(), BPoint((float)ceil(10.0f + fontHeight.ascent),
245 			3.0f + (float)ceil(fontHeight.ascent)));
246 	}
247 }
248 
249 
250 void
251 BCheckBox::AttachedToWindow()
252 {
253 	BControl::AttachedToWindow();
254 }
255 
256 
257 void
258 BCheckBox::MouseDown(BPoint point)
259 {
260 	if (!IsEnabled())
261 		return;
262 
263 	fOutlined = true;
264 
265 	if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
266 		Invalidate();
267 		SetTracking(true);
268 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
269 	} else {
270 		BRect bounds = Bounds();
271 		uint32 buttons;
272 
273 		Invalidate();
274 		Window()->UpdateIfNeeded();
275 
276 		do {
277 			snooze(40000);
278 
279 			GetMouse(&point, &buttons, true);
280 
281 			bool inside = bounds.Contains(point);
282 			if (fOutlined != inside) {
283 				fOutlined = inside;
284 				Invalidate();
285 				Window()->UpdateIfNeeded();
286 			}
287 		} while (buttons != 0);
288 
289 		if (fOutlined) {
290 			fOutlined = false;
291 			SetValue(!Value());
292 			Invoke();
293 		} else {
294 			Invalidate();
295 			Window()->UpdateIfNeeded();
296 		}
297 	}
298 }
299 
300 
301 void
302 BCheckBox::MessageReceived(BMessage *message)
303 {
304 	BControl::MessageReceived(message);
305 }
306 
307 
308 void
309 BCheckBox::WindowActivated(bool active)
310 {
311 	BControl::WindowActivated(active);
312 }
313 
314 
315 void
316 BCheckBox::KeyDown(const char *bytes, int32 numBytes)
317 {
318 	BControl::KeyDown(bytes, numBytes);
319 }
320 
321 
322 void
323 BCheckBox::MouseUp(BPoint point)
324 {
325 	if (!IsTracking())
326 		return;
327 
328 	bool inside = Bounds().Contains(point);
329 
330 	if (fOutlined != inside) {
331 		fOutlined = inside;
332 		Invalidate();
333 	}
334 
335 	if (fOutlined) {
336 		fOutlined = false;
337 		SetValue(!Value());
338 		Invoke();
339 	} else {
340 		Invalidate();
341 	}
342 
343 	SetTracking(false);
344 }
345 
346 
347 void
348 BCheckBox::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
349 {
350 	if (!IsTracking())
351 		return;
352 
353 	bool inside = Bounds().Contains(point);
354 
355 	if (fOutlined != inside) {
356 		fOutlined = inside;
357 		Invalidate();
358 	}
359 }
360 
361 
362 void
363 BCheckBox::DetachedFromWindow()
364 {
365 	BControl::DetachedFromWindow();
366 }
367 
368 
369 void
370 BCheckBox::SetValue(int32 value)
371 {
372 	value = value ? B_CONTROL_ON : B_CONTROL_OFF;
373 		// we only accept boolean values
374 
375 	if (value != Value()) {
376 		BControl::SetValueNoUpdate(value);
377 		Invalidate(_CheckBoxFrame());
378 	}
379 }
380 
381 
382 void
383 BCheckBox::GetPreferredSize(float* _width, float* _height)
384 {
385 	font_height fontHeight;
386 	GetFontHeight(&fontHeight);
387 
388 	if (_width) {
389 		float width = 12.0f + fontHeight.ascent;
390 
391 		if (Label())
392 			width += StringWidth(Label());
393 
394 		*_width = (float)ceil(width);
395 	}
396 
397 	if (_height)
398 		*_height = (float)ceil(6.0f + fontHeight.ascent + fontHeight.descent);
399 }
400 
401 
402 void
403 BCheckBox::ResizeToPreferred()
404 {
405 	BControl::ResizeToPreferred();
406 }
407 
408 
409 status_t
410 BCheckBox::Invoke(BMessage *message)
411 {
412 	return BControl::Invoke(message);
413 }
414 
415 
416 void
417 BCheckBox::FrameMoved(BPoint newLocation)
418 {
419 	BControl::FrameMoved(newLocation);
420 }
421 
422 
423 void
424 BCheckBox::FrameResized(float width, float height)
425 {
426 	BControl::FrameResized(width, height);
427 }
428 
429 
430 BHandler *
431 BCheckBox::ResolveSpecifier(BMessage *message, int32 index,
432 	BMessage *specifier, int32 what, const char *property)
433 {
434 	return BControl::ResolveSpecifier(message, index, specifier, what,
435 		property);
436 }
437 
438 
439 status_t
440 BCheckBox::GetSupportedSuites(BMessage *message)
441 {
442 	return BControl::GetSupportedSuites(message);
443 }
444 
445 
446 void
447 BCheckBox::MakeFocus(bool focused)
448 {
449 	BControl::MakeFocus(focused);
450 }
451 
452 
453 void
454 BCheckBox::AllAttached()
455 {
456 	BControl::AllAttached();
457 }
458 
459 
460 void
461 BCheckBox::AllDetached()
462 {
463 	BControl::AllDetached();
464 }
465 
466 
467 status_t
468 BCheckBox::Perform(perform_code code, void* _data)
469 {
470 	switch (code) {
471 		case PERFORM_CODE_MIN_SIZE:
472 			((perform_data_min_size*)_data)->return_value
473 				= BCheckBox::MinSize();
474 			return B_OK;
475 		case PERFORM_CODE_MAX_SIZE:
476 			((perform_data_max_size*)_data)->return_value
477 				= BCheckBox::MaxSize();
478 			return B_OK;
479 		case PERFORM_CODE_PREFERRED_SIZE:
480 			((perform_data_preferred_size*)_data)->return_value
481 				= BCheckBox::PreferredSize();
482 			return B_OK;
483 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
484 			((perform_data_layout_alignment*)_data)->return_value
485 				= BCheckBox::LayoutAlignment();
486 			return B_OK;
487 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
488 			((perform_data_has_height_for_width*)_data)->return_value
489 				= BCheckBox::HasHeightForWidth();
490 			return B_OK;
491 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
492 		{
493 			perform_data_get_height_for_width* data
494 				= (perform_data_get_height_for_width*)_data;
495 			BCheckBox::GetHeightForWidth(data->width, &data->min, &data->max,
496 				&data->preferred);
497 			return B_OK;
498 }
499 		case PERFORM_CODE_SET_LAYOUT:
500 		{
501 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
502 			BCheckBox::SetLayout(data->layout);
503 			return B_OK;
504 		}
505 		case PERFORM_CODE_INVALIDATE_LAYOUT:
506 		{
507 			perform_data_invalidate_layout* data
508 				= (perform_data_invalidate_layout*)_data;
509 			BCheckBox::InvalidateLayout(data->descendants);
510 			return B_OK;
511 		}
512 		case PERFORM_CODE_DO_LAYOUT:
513 		{
514 			BCheckBox::DoLayout();
515 			return B_OK;
516 		}
517 	}
518 
519 	return BControl::Perform(code, _data);
520 }
521 
522 
523 void
524 BCheckBox::InvalidateLayout(bool descendants)
525 {
526 	// invalidate cached preferred size
527 	fPreferredSize.Set(B_SIZE_UNSET, B_SIZE_UNSET);
528 
529 	BControl::InvalidateLayout(descendants);
530 }
531 
532 
533 BSize
534 BCheckBox::MinSize()
535 {
536 	return BLayoutUtils::ComposeSize(ExplicitMinSize(),
537 		_ValidatePreferredSize());
538 }
539 
540 
541 BSize
542 BCheckBox::MaxSize()
543 {
544 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
545 		BSize(B_SIZE_UNLIMITED, _ValidatePreferredSize().height));
546 }
547 
548 
549 BSize
550 BCheckBox::PreferredSize()
551 {
552 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
553 		_ValidatePreferredSize());
554 }
555 
556 
557 void BCheckBox::_ReservedCheckBox1() {}
558 void BCheckBox::_ReservedCheckBox2() {}
559 void BCheckBox::_ReservedCheckBox3() {}
560 
561 
562 BCheckBox &
563 BCheckBox::operator=(const BCheckBox &)
564 {
565 	return *this;
566 }
567 
568 
569 BRect
570 BCheckBox::_CheckBoxFrame() const
571 {
572 	font_height fontHeight;
573 	GetFontHeight(&fontHeight);
574 
575 	return BRect(0.0f, 2.0f, ceilf(3.0f + fontHeight.ascent),
576 		ceilf(5.0f + fontHeight.ascent));
577 }
578 
579 
580 BSize
581 BCheckBox::_ValidatePreferredSize()
582 {
583 	if (!fPreferredSize.IsWidthSet()) {
584 		font_height fontHeight;
585 		GetFontHeight(&fontHeight);
586 
587 		float width = 12.0f + fontHeight.ascent;
588 
589 		if (Label())
590 			width += StringWidth(Label());
591 
592 		fPreferredSize.width = (float)ceil(width);
593 
594 		fPreferredSize.height = (float)ceil(6.0f + fontHeight.ascent
595 			+ fontHeight.descent);
596 	}
597 
598 	return fPreferredSize;
599 }
600