xref: /haiku/src/kits/interface/CheckBox.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
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(2, 2);
158 
159 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
160 			SetPenSize(2);
161 			SetDrawingMode(B_OP_OVER);
162 				// needed because of anti-aliasing
163 			StrokeLine(BPoint(rect.left, rect.top),
164 					   BPoint(rect.right, rect.bottom));
165 			StrokeLine(BPoint(rect.left, rect.bottom),
166 					   BPoint(rect.right, rect.top));
167 			SetPenSize(1);
168 			SetDrawingMode(B_OP_COPY);
169 		}
170 
171 		// Label
172 		SetHighColor(darkenmax);
173 		DrawString(Label(), BPoint((float)ceil(10.0f + fontHeight.ascent),
174 			3.0f + (float)ceil(fontHeight.ascent)));
175 
176 		// Focus
177 		if (IsFocus()) {
178 			float x = (float)ceil(10.0f + fontHeight.ascent);
179 			float y = 5.0f + (float)ceil(fontHeight.ascent);
180 
181 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
182 			StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y));
183 		}
184 	} else {
185 		// Filling
186 		SetHighColor(lighten1);
187 		FillRect(rect);
188 
189 		// Box
190 		BeginLineArray(6);
191 
192 		AddLine(BPoint(rect.left, rect.bottom),
193 				BPoint(rect.left, rect.top), noTint);
194 		AddLine(BPoint(rect.left, rect.top),
195 				BPoint(rect.right, rect.top), noTint);
196 		rect.InsetBy(1, 1);
197 		AddLine(BPoint(rect.left, rect.bottom),
198 				BPoint(rect.left, rect.top), darken2);
199 		AddLine(BPoint(rect.left, rect.top),
200 				BPoint(rect.right, rect.top), darken2);
201 		AddLine(BPoint(rect.left + 1.0f, rect.bottom),
202 				BPoint(rect.right, rect.bottom), darken1);
203 		AddLine(BPoint(rect.right, rect.bottom),
204 				BPoint(rect.right, rect.top + 1.0f), darken1);
205 
206 		EndLineArray();
207 
208 		// Checkmark
209 		if (Value() == B_CONTROL_ON) {
210 			rect.InsetBy(2, 2);
211 
212 			SetHighColor(tint_color(ui_color(B_KEYBOARD_NAVIGATION_COLOR),
213 				B_DISABLED_MARK_TINT));
214 			SetPenSize(2);
215 			SetDrawingMode(B_OP_OVER);
216 				// needed because of anti-aliasing
217 			StrokeLine(BPoint(rect.left, rect.top),
218 					   BPoint(rect.right, rect.bottom));
219 			StrokeLine(BPoint(rect.left, rect.bottom),
220 					   BPoint(rect.right, rect.top));
221 			SetPenSize(1);
222 			SetDrawingMode(B_OP_COPY);
223 		}
224 
225 		// Label
226 		SetHighColor(tint_color(noTint, B_DISABLED_LABEL_TINT));
227 		DrawString(Label(), BPoint((float)ceil(10.0f + fontHeight.ascent),
228 			3.0f + (float)ceil(fontHeight.ascent)));
229 	}
230 }
231 
232 
233 void
234 BCheckBox::AttachedToWindow()
235 {
236 	BControl::AttachedToWindow();
237 }
238 
239 
240 void
241 BCheckBox::MouseDown(BPoint point)
242 {
243 	if (!IsEnabled())
244 		return;
245 
246 	fOutlined = true;
247 
248 	if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
249 		Invalidate();
250 		SetTracking(true);
251 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
252 	} else {
253 		BRect bounds = Bounds();
254 		uint32 buttons;
255 
256 		Invalidate();
257 		Window()->UpdateIfNeeded();
258 
259 		do {
260 			snooze(40000);
261 
262 			GetMouse(&point, &buttons, true);
263 
264 			bool inside = bounds.Contains(point);
265 			if (fOutlined != inside) {
266 				fOutlined = inside;
267 				Invalidate();
268 				Window()->UpdateIfNeeded();
269 			}
270 		} while (buttons != 0);
271 
272 		if (fOutlined) {
273 			fOutlined = false;
274 			SetValue(!Value());
275 			Invoke();
276 		} else {
277 			Invalidate();
278 			Window()->UpdateIfNeeded();
279 		}
280 	}
281 }
282 
283 
284 void
285 BCheckBox::MessageReceived(BMessage *message)
286 {
287 	BControl::MessageReceived(message);
288 }
289 
290 
291 void
292 BCheckBox::WindowActivated(bool active)
293 {
294 	BControl::WindowActivated(active);
295 }
296 
297 
298 void
299 BCheckBox::KeyDown(const char *bytes, int32 numBytes)
300 {
301 	BControl::KeyDown(bytes, numBytes);
302 }
303 
304 
305 void
306 BCheckBox::MouseUp(BPoint point)
307 {
308 	if (!IsTracking())
309 		return;
310 
311 	bool inside = Bounds().Contains(point);
312 
313 	if (fOutlined != inside) {
314 		fOutlined = inside;
315 		Invalidate();
316 	}
317 
318 	if (fOutlined) {
319 		fOutlined = false;
320 		SetValue(!Value());
321 		Invoke();
322 	} else {
323 		Invalidate();
324 	}
325 
326 	SetTracking(false);
327 }
328 
329 
330 void
331 BCheckBox::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
332 {
333 	if (!IsTracking())
334 		return;
335 
336 	bool inside = Bounds().Contains(point);
337 
338 	if (fOutlined != inside) {
339 		fOutlined = inside;
340 		Invalidate();
341 	}
342 }
343 
344 
345 void
346 BCheckBox::DetachedFromWindow()
347 {
348 	BControl::DetachedFromWindow();
349 }
350 
351 
352 void
353 BCheckBox::SetValue(int32 value)
354 {
355 	value = value ? B_CONTROL_ON : B_CONTROL_OFF;
356 		// we only accept boolean values
357 
358 	if (value != Value()) {
359 		BControl::SetValueNoUpdate(value);
360 		Invalidate(_CheckBoxFrame());
361 	}
362 }
363 
364 
365 void
366 BCheckBox::GetPreferredSize(float* _width, float* _height)
367 {
368 	font_height fontHeight;
369 	GetFontHeight(&fontHeight);
370 
371 	if (_width) {
372 		float width = 12.0f + fontHeight.ascent;
373 
374 		if (Label())
375 			width += StringWidth(Label());
376 
377 		*_width = (float)ceil(width);
378 	}
379 
380 	if (_height)
381 		*_height = (float)ceil(6.0f + fontHeight.ascent + fontHeight.descent);
382 }
383 
384 
385 void
386 BCheckBox::ResizeToPreferred()
387 {
388 	BControl::ResizeToPreferred();
389 }
390 
391 
392 status_t
393 BCheckBox::Invoke(BMessage *message)
394 {
395 	return BControl::Invoke(message);
396 }
397 
398 
399 void
400 BCheckBox::FrameMoved(BPoint newLocation)
401 {
402 	BControl::FrameMoved(newLocation);
403 }
404 
405 
406 void
407 BCheckBox::FrameResized(float width, float height)
408 {
409 	BControl::FrameResized(width, height);
410 }
411 
412 
413 BHandler *
414 BCheckBox::ResolveSpecifier(BMessage *message, int32 index,
415 	BMessage *specifier, int32 what, const char *property)
416 {
417 	return BControl::ResolveSpecifier(message, index, specifier, what,
418 		property);
419 }
420 
421 
422 status_t
423 BCheckBox::GetSupportedSuites(BMessage *message)
424 {
425 	return BControl::GetSupportedSuites(message);
426 }
427 
428 
429 void
430 BCheckBox::MakeFocus(bool focused)
431 {
432 	BControl::MakeFocus(focused);
433 }
434 
435 
436 void
437 BCheckBox::AllAttached()
438 {
439 	BControl::AllAttached();
440 }
441 
442 
443 void
444 BCheckBox::AllDetached()
445 {
446 	BControl::AllDetached();
447 }
448 
449 
450 status_t
451 BCheckBox::Perform(perform_code d, void *arg)
452 {
453 	return BControl::Perform(d, arg);
454 }
455 
456 
457 void
458 BCheckBox::InvalidateLayout(bool descendants)
459 {
460 	// invalidate cached preferred size
461 	fPreferredSize.Set(B_SIZE_UNSET, B_SIZE_UNSET);
462 
463 	BControl::InvalidateLayout(descendants);
464 }
465 
466 
467 BSize
468 BCheckBox::MinSize()
469 {
470 	return BLayoutUtils::ComposeSize(ExplicitMinSize(),
471 		_ValidatePreferredSize());
472 }
473 
474 
475 BSize
476 BCheckBox::MaxSize()
477 {
478 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
479 		BSize(B_SIZE_UNLIMITED, _ValidatePreferredSize().height));
480 }
481 
482 
483 BSize
484 BCheckBox::PreferredSize()
485 {
486 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
487 		_ValidatePreferredSize());
488 }
489 
490 
491 void BCheckBox::_ReservedCheckBox1() {}
492 void BCheckBox::_ReservedCheckBox2() {}
493 void BCheckBox::_ReservedCheckBox3() {}
494 
495 
496 BCheckBox &
497 BCheckBox::operator=(const BCheckBox &)
498 {
499 	return *this;
500 }
501 
502 
503 BRect
504 BCheckBox::_CheckBoxFrame() const
505 {
506 	font_height fontHeight;
507 	GetFontHeight(&fontHeight);
508 
509 	return BRect(1.0f, 3.0f, ceilf(3.0f + fontHeight.ascent),
510 		ceilf(5.0f + fontHeight.ascent));
511 }
512 
513 
514 BSize
515 BCheckBox::_ValidatePreferredSize()
516 {
517 	if (!fPreferredSize.IsWidthSet()) {
518 		font_height fontHeight;
519 		GetFontHeight(&fontHeight);
520 
521 		float width = 12.0f + fontHeight.ascent;
522 
523 		if (Label())
524 			width += StringWidth(Label());
525 
526 		fPreferredSize.width = (float)ceil(width);
527 
528 		fPreferredSize.height = (float)ceil(6.0f + fontHeight.ascent
529 			+ fontHeight.descent);
530 	}
531 
532 	return fPreferredSize;
533 }
534