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