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