xref: /haiku/src/kits/interface/Button.cpp (revision 7749d0bb0c358a3279b1b9cc76d8376e900130a5)
1 /*
2  *	Copyright 2001-2008, Haiku Inc. All rights reserved.
3  *  Distributed under the terms of the MIT License.
4  *
5  *	Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Mike Wilber
8  *		Stefano Ceccherini (burton666@libero.it)
9  *		Ivan Tonizza
10  *		Stephan Aßmus <superstippi@gmx.de>
11  */
12 
13 
14 #include <Button.h>
15 
16 #include <new>
17 
18 #include <ControlLook.h>
19 #include <Font.h>
20 #include <LayoutUtils.h>
21 #include <String.h>
22 #include <Window.h>
23 
24 #include <binary_compatibility/Interface.h>
25 
26 
27 BButton::BButton(BRect frame, const char* name, const char* label,
28 		BMessage* message, uint32 resizingMode, uint32 flags)
29 	: BControl(frame, name, label, message, resizingMode,
30 			flags | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
31 	fPreferredSize(-1, -1),
32 	fDrawAsDefault(false)
33 {
34 	// Resize to minimum height if needed
35 	font_height fh;
36 	GetFontHeight(&fh);
37 	float minHeight = 12.0f + (float)ceil(fh.ascent + fh.descent);
38 	if (Bounds().Height() < minHeight)
39 		ResizeTo(Bounds().Width(), minHeight);
40 }
41 
42 
43 BButton::BButton(const char* name, const char* label, BMessage* message,
44 		uint32 flags)
45 	: BControl(name, label, message,
46 			flags | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
47 	fPreferredSize(-1, -1),
48 	fDrawAsDefault(false)
49 {
50 }
51 
52 
53 BButton::BButton(const char* label, BMessage* message)
54 	: BControl(NULL, label, message,
55 			B_WILL_DRAW | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE),
56 	fPreferredSize(-1, -1),
57 	fDrawAsDefault(false)
58 {
59 }
60 
61 
62 BButton::~BButton()
63 {
64 }
65 
66 
67 BButton::BButton(BMessage* archive)
68 	: BControl(archive),
69 	fPreferredSize(-1, -1)
70 {
71 	if (archive->FindBool("_default", &fDrawAsDefault) != B_OK)
72 		fDrawAsDefault = false;
73 	// NOTE: Default button state will be synchronized with the window
74 	// in AttachedToWindow().
75 }
76 
77 
78 BArchivable*
79 BButton::Instantiate(BMessage* archive)
80 {
81 	if (validate_instantiation(archive, "BButton"))
82 		return new(std::nothrow) BButton(archive);
83 
84 	return NULL;
85 }
86 
87 
88 status_t
89 BButton::Archive(BMessage* archive, bool deep) const
90 {
91 	status_t err = BControl::Archive(archive, deep);
92 
93 	if (err != B_OK)
94 		return err;
95 
96 	if (IsDefault())
97 		err = archive->AddBool("_default", true);
98 
99 	return err;
100 }
101 
102 
103 void
104 BButton::Draw(BRect updateRect)
105 {
106 	if (be_control_look != NULL) {
107 		BRect rect(Bounds());
108 		rgb_color background = LowColor();
109 		rgb_color base = background;
110 		uint32 flags = be_control_look->Flags(this);
111 		if (IsDefault())
112 			flags |= BControlLook::B_DEFAULT_BUTTON;
113 		be_control_look->DrawButtonFrame(this, rect, updateRect,
114 			base, background, flags);
115 		be_control_look->DrawButtonBackground(this, rect, updateRect,
116 			base, flags);
117 
118 		// always leave some room around the label
119 		rect.InsetBy(3.0, 3.0);
120 		be_control_look->DrawLabel(this, Label(), rect, updateRect,
121 			base, flags, BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE));
122 		return;
123 	}
124 
125 	font_height fh;
126 	GetFontHeight(&fh);
127 
128 	const BRect bounds = Bounds();
129 	BRect rect = bounds;
130 
131 	const bool enabled = IsEnabled();
132 	const bool pushed = Value() == B_CONTROL_ON;
133 	// Default indicator
134 	if (IsDefault())
135 		rect = _DrawDefault(rect, enabled);
136 
137 	BRect fillArea = rect;
138 	fillArea.InsetBy(3.0, 3.0);
139 
140 	BString text = Label();
141 
142 #if 1
143 	// Label truncation
144 	BFont font;
145 	GetFont(&font);
146 	font.TruncateString(&text, B_TRUNCATE_END, fillArea.Width() - 4);
147 #endif
148 
149 	// Label position
150 	const float stringWidth = StringWidth(text.String());
151 	const float x = (rect.right - stringWidth) / 2.0;
152 	const float labelY = bounds.top
153 		+ ((bounds.Height() - fh.ascent - fh.descent) / 2.0)
154 		+ fh.ascent + 1.0;
155 	const float focusLineY = labelY + fh.descent;
156 
157 	/* speed trick:
158 	   if the focus changes but the button is not pressed then we can
159 	   redraw only the focus line,
160 	   if the focus changes and the button is pressed invert the internal rect
161 	   this block takes care of all the focus changes
162 	*/
163 	if (IsFocusChanging()) {
164 		if (pushed) {
165 			rect.InsetBy(2.0, 2.0);
166 			InvertRect(rect);
167 		} else {
168 			_DrawFocusLine(x, focusLineY, stringWidth, IsFocus()
169 				&& Window()->IsActive());
170 		}
171 
172 		return;
173 	}
174 
175 	// colors
176 	rgb_color panelBgColor = ui_color(B_PANEL_BACKGROUND_COLOR);
177 	rgb_color buttonBgColor = tint_color(panelBgColor, B_LIGHTEN_1_TINT);
178 	rgb_color lightColor;
179 	rgb_color maxLightColor;
180 	rgb_color maxShadowColor = tint_color(panelBgColor, B_DARKEN_MAX_TINT);
181 
182 	rgb_color dark1BorderColor;
183 	rgb_color dark2BorderColor;
184 
185 	rgb_color bevelColor1;
186 	rgb_color bevelColor2;
187 	rgb_color bevelColorRBCorner;
188 
189 	rgb_color borderBevelShadow;
190 	rgb_color borderBevelLight;
191 
192 	if (enabled) {
193 		lightColor = tint_color(panelBgColor, B_LIGHTEN_2_TINT);
194 		maxLightColor = tint_color(panelBgColor, B_LIGHTEN_MAX_TINT);
195 
196 		dark1BorderColor = tint_color(panelBgColor, B_DARKEN_3_TINT);
197 		dark2BorderColor = tint_color(panelBgColor, B_DARKEN_4_TINT);
198 
199 		bevelColor1 = tint_color(panelBgColor, B_DARKEN_2_TINT);
200 		bevelColor2 = panelBgColor;
201 
202 		if (IsDefault()) {
203 			borderBevelShadow = tint_color(dark1BorderColor,
204 				(B_NO_TINT + B_DARKEN_1_TINT) / 2);
205 			borderBevelLight = tint_color(dark1BorderColor, B_LIGHTEN_1_TINT);
206 
207 			borderBevelLight.red = (borderBevelLight.red + panelBgColor.red)
208 				/ 2;
209 			borderBevelLight.green = (borderBevelLight.green
210 				+ panelBgColor.green) / 2;
211 			borderBevelLight.blue = (borderBevelLight.blue
212 				+ panelBgColor.blue) / 2;
213 
214 			dark1BorderColor = tint_color(dark1BorderColor, B_DARKEN_3_TINT);
215 			dark2BorderColor = tint_color(dark1BorderColor, B_DARKEN_4_TINT);
216 
217 			bevelColorRBCorner = borderBevelShadow;
218 		} else {
219 			borderBevelShadow = tint_color(panelBgColor,
220 				(B_NO_TINT + B_DARKEN_1_TINT) / 2);
221 			borderBevelLight = buttonBgColor;
222 
223 			bevelColorRBCorner = dark1BorderColor;
224 		}
225 	} else {
226 		lightColor = tint_color(panelBgColor, B_LIGHTEN_2_TINT);
227 		maxLightColor = tint_color(panelBgColor, B_LIGHTEN_1_TINT);
228 
229 		dark1BorderColor = tint_color(panelBgColor, B_DARKEN_1_TINT);
230 		dark2BorderColor = tint_color(panelBgColor, B_DARKEN_2_TINT);
231 
232 		bevelColor1 = panelBgColor;
233 		bevelColor2 = buttonBgColor;
234 
235 		if (IsDefault()) {
236 			borderBevelShadow = dark1BorderColor;
237 			borderBevelLight = panelBgColor;
238 			dark1BorderColor = tint_color(dark1BorderColor, B_DARKEN_1_TINT);
239 			dark2BorderColor = tint_color(dark1BorderColor, 1.16);
240 
241 		} else {
242 			borderBevelShadow = panelBgColor;
243 			borderBevelLight = panelBgColor;
244 		}
245 
246 		bevelColorRBCorner = tint_color(panelBgColor, 1.08);;
247 	}
248 
249 	// fill the button area
250 	SetHighColor(buttonBgColor);
251 	FillRect(fillArea);
252 
253 	BeginLineArray(22);
254 	// bevel around external border
255 	AddLine(BPoint(rect.left, rect.bottom),
256 			BPoint(rect.left, rect.top), borderBevelShadow);
257 	AddLine(BPoint(rect.left + 1, rect.top),
258 			BPoint(rect.right, rect.top), borderBevelShadow);
259 
260 	AddLine(BPoint(rect.right, rect.top + 1),
261 			BPoint(rect.right, rect.bottom), borderBevelLight);
262 	AddLine(BPoint(rect.left + 1, rect.bottom),
263 			BPoint(rect.right - 1, rect.bottom), borderBevelLight);
264 
265 	rect.InsetBy(1.0, 1.0);
266 
267 	// external border
268 	AddLine(BPoint(rect.left, rect.bottom),
269 			BPoint(rect.left, rect.top), dark1BorderColor);
270 	AddLine(BPoint(rect.left + 1, rect.top),
271 			BPoint(rect.right, rect.top), dark1BorderColor);
272 	AddLine(BPoint(rect.right, rect.top + 1),
273 			BPoint(rect.right, rect.bottom), dark2BorderColor);
274 	AddLine(BPoint(rect.right - 1, rect.bottom),
275 			BPoint(rect.left + 1, rect.bottom), dark2BorderColor);
276 
277 	rect.InsetBy(1.0, 1.0);
278 
279 	// Light
280 	AddLine(BPoint(rect.left, rect.top),
281 			BPoint(rect.left, rect.top), buttonBgColor);
282 	AddLine(BPoint(rect.left, rect.top + 1),
283 			BPoint(rect.left, rect.bottom - 1), lightColor);
284 	AddLine(BPoint(rect.left, rect.bottom),
285 			BPoint(rect.left, rect.bottom), bevelColor2);
286 	AddLine(BPoint(rect.left + 1, rect.top),
287 			BPoint(rect.right - 1, rect.top), lightColor);
288 	AddLine(BPoint(rect.right, rect.top),
289 			BPoint(rect.right, rect.top), bevelColor2);
290 	// Shadow
291 	AddLine(BPoint(rect.left + 1, rect.bottom),
292 			BPoint(rect.right - 1, rect.bottom), bevelColor1);
293 	AddLine(BPoint(rect.right, rect.bottom),
294 			BPoint(rect.right, rect.bottom), bevelColorRBCorner);
295 	AddLine(BPoint(rect.right, rect.bottom - 1),
296 			BPoint(rect.right, rect.top + 1), bevelColor1);
297 
298 	rect.InsetBy(1.0, 1.0);
299 
300 	// Light
301 	AddLine(BPoint(rect.left, rect.top),
302 			BPoint(rect.left, rect.bottom - 1), maxLightColor);
303 	AddLine(BPoint(rect.left, rect.bottom),
304 			BPoint(rect.left, rect.bottom), buttonBgColor);
305 	AddLine(BPoint(rect.left + 1, rect.top),
306 			BPoint(rect.right - 1, rect.top), maxLightColor);
307 	AddLine(BPoint(rect.right, rect.top),
308 			BPoint(rect.right, rect.top), buttonBgColor);
309 	// Shadow
310 	AddLine(BPoint(rect.left + 1, rect.bottom),
311 			BPoint(rect.right, rect.bottom), bevelColor2);
312 	AddLine(BPoint(rect.right, rect.bottom - 1),
313 			BPoint(rect.right, rect.top + 1), bevelColor2);
314 
315 	rect.InsetBy(1.0,1.0);
316 
317 	EndLineArray();
318 
319 	// Invert if clicked
320 	if (enabled && pushed) {
321 		rect.InsetBy(-2.0, -2.0);
322 		InvertRect(rect);
323 	}
324 
325 	// Label color
326 	if (enabled) {
327 		if (pushed) {
328 			SetHighColor(maxLightColor);
329 			SetLowColor(255 - buttonBgColor.red,
330 						255 - buttonBgColor.green,
331 						255 - buttonBgColor.blue);
332 		} else {
333 			SetHighColor(ui_color(B_CONTROL_TEXT_COLOR));
334 			SetLowColor(buttonBgColor);
335 		}
336 	} else {
337 		SetHighColor(tint_color(panelBgColor, B_DISABLED_LABEL_TINT));
338 		SetLowColor(buttonBgColor);
339 	}
340 
341 	// Draw the label
342 	DrawString(text.String(), BPoint(x, labelY));
343 
344 	// Focus line
345 	if (enabled && IsFocus() && Window()->IsActive() && !pushed)
346 		_DrawFocusLine(x, focusLineY, stringWidth, true);
347 }
348 
349 
350 void
351 BButton::MouseDown(BPoint point)
352 {
353 	if (!IsEnabled())
354 		return;
355 
356 	SetValue(B_CONTROL_ON);
357 
358 	if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) {
359  		SetTracking(true);
360  		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
361  	} else {
362 		BRect bounds = Bounds();
363 		uint32 buttons;
364 
365 		do {
366 			Window()->UpdateIfNeeded();
367 			snooze(40000);
368 
369 			GetMouse(&point, &buttons, true);
370 
371  			bool inside = bounds.Contains(point);
372 
373 			if ((Value() == B_CONTROL_ON) != inside)
374 				SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
375 		} while (buttons != 0);
376 
377 		if (Value() == B_CONTROL_ON)
378 			Invoke();
379 	}
380 }
381 
382 
383 void
384 BButton::AttachedToWindow()
385 {
386 	BControl::AttachedToWindow();
387 		// low color will now be the parents view color
388 
389 	if (IsDefault())
390 		Window()->SetDefaultButton(this);
391 
392 	SetViewColor(B_TRANSPARENT_COLOR);
393 }
394 
395 
396 void
397 BButton::KeyDown(const char *bytes, int32 numBytes)
398 {
399 	if (*bytes == B_ENTER || *bytes == B_SPACE) {
400 		if (!IsEnabled())
401 			return;
402 
403 		SetValue(B_CONTROL_ON);
404 
405 		// make sure the user saw that
406 		Window()->UpdateIfNeeded();
407 		snooze(25000);
408 
409 		Invoke();
410 
411 	} else
412 		BControl::KeyDown(bytes, numBytes);
413 }
414 
415 
416 void
417 BButton::MakeDefault(bool flag)
418 {
419 	BButton *oldDefault = NULL;
420 	BWindow *window = Window();
421 
422 	if (window)
423 		oldDefault = window->DefaultButton();
424 
425 	if (flag) {
426 		if (fDrawAsDefault && oldDefault == this)
427 			return;
428 
429 		if (!fDrawAsDefault) {
430 			fDrawAsDefault = true;
431 
432 			if ((Flags() & B_SUPPORTS_LAYOUT) != 0)
433 				InvalidateLayout();
434 			else {
435 				ResizeBy(6.0f, 6.0f);
436 				MoveBy(-3.0f, -3.0f);
437 			}
438 		}
439 
440 		if (window && oldDefault != this)
441 			window->SetDefaultButton(this);
442 	} else {
443 		if (!fDrawAsDefault)
444 			return;
445 
446 		fDrawAsDefault = false;
447 
448 		if ((Flags() & B_SUPPORTS_LAYOUT) != 0)
449 			InvalidateLayout();
450 		else {
451 			ResizeBy(-6.0f, -6.0f);
452 			MoveBy(3.0f, 3.0f);
453 		}
454 
455 		if (window && oldDefault == this)
456 			window->SetDefaultButton(NULL);
457 	}
458 }
459 
460 
461 void
462 BButton::SetLabel(const char *string)
463 {
464 	BControl::SetLabel(string);
465 }
466 
467 
468 bool
469 BButton::IsDefault() const
470 {
471 	return fDrawAsDefault;
472 }
473 
474 
475 void
476 BButton::MessageReceived(BMessage *message)
477 {
478 	BControl::MessageReceived(message);
479 }
480 
481 
482 void
483 BButton::WindowActivated(bool active)
484 {
485 	BControl::WindowActivated(active);
486 }
487 
488 
489 void
490 BButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
491 {
492 	if (!IsTracking())
493 		return;
494 
495 	bool inside = Bounds().Contains(point);
496 
497 	if ((Value() == B_CONTROL_ON) != inside)
498 		SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
499 }
500 
501 
502 void
503 BButton::MouseUp(BPoint point)
504 {
505 	if (!IsTracking())
506 		return;
507 
508 	if (Bounds().Contains(point))
509 		Invoke();
510 
511 	SetTracking(false);
512 }
513 
514 
515 void
516 BButton::DetachedFromWindow()
517 {
518 	BControl::DetachedFromWindow();
519 }
520 
521 
522 void
523 BButton::SetValue(int32 value)
524 {
525 	if (value != Value())
526 		BControl::SetValue(value);
527 }
528 
529 
530 void
531 BButton::GetPreferredSize(float *_width, float *_height)
532 {
533 	_ValidatePreferredSize();
534 
535 	if (_width)
536 		*_width = fPreferredSize.width;
537 
538 	if (_height)
539 		*_height = fPreferredSize.height;
540 }
541 
542 
543 void
544 BButton::ResizeToPreferred()
545 {
546 	 BControl::ResizeToPreferred();
547 }
548 
549 
550 status_t
551 BButton::Invoke(BMessage *message)
552 {
553 	Sync();
554 	snooze(50000);
555 
556 	status_t err = BControl::Invoke(message);
557 
558 	SetValue(B_CONTROL_OFF);
559 
560 	return err;
561 }
562 
563 
564 void
565 BButton::FrameMoved(BPoint newLocation)
566 {
567 	BControl::FrameMoved(newLocation);
568 }
569 
570 
571 void
572 BButton::FrameResized(float width, float height)
573 {
574 	BControl::FrameResized(width, height);
575 }
576 
577 
578 void
579 BButton::MakeFocus(bool focused)
580 {
581 	BControl::MakeFocus(focused);
582 }
583 
584 
585 void
586 BButton::AllAttached()
587 {
588 	BControl::AllAttached();
589 }
590 
591 
592 void
593 BButton::AllDetached()
594 {
595 	BControl::AllDetached();
596 }
597 
598 
599 BHandler *
600 BButton::ResolveSpecifier(BMessage *message, int32 index,
601 									BMessage *specifier, int32 what,
602 									const char *property)
603 {
604 	return BControl::ResolveSpecifier(message, index, specifier, what, property);
605 }
606 
607 
608 status_t
609 BButton::GetSupportedSuites(BMessage *message)
610 {
611 	return BControl::GetSupportedSuites(message);
612 }
613 
614 
615 status_t
616 BButton::Perform(perform_code code, void* _data)
617 {
618 	switch (code) {
619 		case PERFORM_CODE_MIN_SIZE:
620 			((perform_data_min_size*)_data)->return_value
621 				= BButton::MinSize();
622 			return B_OK;
623 		case PERFORM_CODE_MAX_SIZE:
624 			((perform_data_max_size*)_data)->return_value
625 				= BButton::MaxSize();
626 			return B_OK;
627 		case PERFORM_CODE_PREFERRED_SIZE:
628 			((perform_data_preferred_size*)_data)->return_value
629 				= BButton::PreferredSize();
630 			return B_OK;
631 		case PERFORM_CODE_LAYOUT_ALIGNMENT:
632 			((perform_data_layout_alignment*)_data)->return_value
633 				= BButton::LayoutAlignment();
634 			return B_OK;
635 		case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
636 			((perform_data_has_height_for_width*)_data)->return_value
637 				= BButton::HasHeightForWidth();
638 			return B_OK;
639 		case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
640 		{
641 			perform_data_get_height_for_width* data
642 				= (perform_data_get_height_for_width*)_data;
643 			BButton::GetHeightForWidth(data->width, &data->min, &data->max,
644 				&data->preferred);
645 			return B_OK;
646 		}
647 		case PERFORM_CODE_SET_LAYOUT:
648 		{
649 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
650 			BButton::SetLayout(data->layout);
651 			return B_OK;
652 		}
653 		case PERFORM_CODE_INVALIDATE_LAYOUT:
654 		{
655 			perform_data_invalidate_layout* data
656 				= (perform_data_invalidate_layout*)_data;
657 			BButton::InvalidateLayout(data->descendants);
658 			return B_OK;
659 		}
660 		case PERFORM_CODE_DO_LAYOUT:
661 		{
662 			BButton::DoLayout();
663 			return B_OK;
664 		}
665 	}
666 
667 	return BControl::Perform(code, _data);
668 }
669 
670 
671 void
672 BButton::InvalidateLayout(bool descendants)
673 {
674 	// invalidate cached preferred size
675 	fPreferredSize.Set(-1, -1);
676 
677 	BControl::InvalidateLayout(descendants);
678 }
679 
680 
681 BSize
682 BButton::MinSize()
683 {
684 	return BLayoutUtils::ComposeSize(ExplicitMinSize(),
685 		_ValidatePreferredSize());
686 }
687 
688 
689 BSize
690 BButton::MaxSize()
691 {
692 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
693 		_ValidatePreferredSize());
694 }
695 
696 
697 BSize
698 BButton::PreferredSize()
699 {
700 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
701 		_ValidatePreferredSize());
702 }
703 
704 
705 void BButton::_ReservedButton1() {}
706 void BButton::_ReservedButton2() {}
707 void BButton::_ReservedButton3() {}
708 
709 
710 BButton &
711 BButton::operator=(const BButton &)
712 {
713 	return *this;
714 }
715 
716 
717 BSize
718 BButton::_ValidatePreferredSize()
719 {
720 	if (fPreferredSize.width < 0) {
721 		// width
722 		float width = 20.0f + (float)ceil(StringWidth(Label()));
723 		if (width < 75.0f)
724 			width = 75.0f;
725 
726 		if (fDrawAsDefault)
727 			width += 6.0f;
728 
729 		fPreferredSize.width = width;
730 
731 		// height
732 		font_height fontHeight;
733 		GetFontHeight(&fontHeight);
734 
735 		fPreferredSize.height
736 			= ceilf((fontHeight.ascent + fontHeight.descent) * 1.8)
737 				+ (fDrawAsDefault ? 6.0f : 0);
738 
739 		ResetLayoutInvalidation();
740 	}
741 
742 	return fPreferredSize;
743 }
744 
745 
746 BRect
747 BButton::_DrawDefault(BRect bounds, bool enabled)
748 {
749 	rgb_color low = LowColor();
750 	rgb_color focusColor = tint_color(low, enabled ?
751 		(B_DARKEN_1_TINT + B_DARKEN_2_TINT) / 2
752 		: (B_NO_TINT + B_DARKEN_1_TINT) / 2);
753 
754 	SetHighColor(focusColor);
755 
756 	StrokeRect(bounds, B_SOLID_LOW);
757 	bounds.InsetBy(1.0, 1.0);
758 	StrokeRect(bounds);
759 	bounds.InsetBy(1.0, 1.0);
760 	StrokeRect(bounds);
761 	bounds.InsetBy(1.0, 1.0);
762 
763 	return bounds;
764 }
765 
766 
767 void
768 BButton::_DrawFocusLine(float x, float y, float width, bool visible)
769 {
770 	if (visible)
771 		SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
772 	else {
773 		SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
774 			B_LIGHTEN_1_TINT));
775 	}
776 
777 	// Blue Line
778 	StrokeLine(BPoint(x, y), BPoint(x + width, y));
779 
780 	if (visible)
781 		SetHighColor(255, 255, 255);
782 	// White Line
783 	StrokeLine(BPoint(x, y + 1.0f), BPoint(x + width, y + 1.0f));
784 }
785 
786 
787