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