xref: /haiku/src/kits/interface/Button.cpp (revision db10640de90f7f9519ba2da9577b7c1af3c64f6b)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2001-2002, OpenBeOS
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		Button.cpp
23 //	Author:			Marc Flerackers (mflerackers@androme.be)
24 //	Description:	BButton displays and controls a button in a window.
25 //------------------------------------------------------------------------------
26 
27 // Standard Includes -----------------------------------------------------------
28 
29 // System Includes -------------------------------------------------------------
30 #include <Button.h>
31 #include <Window.h>
32 #include <Errors.h>
33 
34 // Project Includes ------------------------------------------------------------
35 
36 // Local Includes --------------------------------------------------------------
37 
38 // Local Defines ---------------------------------------------------------------
39 
40 // Globals ---------------------------------------------------------------------
41 
42 //------------------------------------------------------------------------------
43 BButton::BButton(BRect frame, const char *name, const char *label, BMessage *message,
44 				  uint32 resizingMode, uint32 flags)
45 	:	BControl(frame, name, label, message, resizingMode, flags |= B_WILL_DRAW),
46 		fDrawAsDefault(false)
47 {
48 	// Resize to minimum height if needed
49 	font_height fh;
50 	GetFontHeight(&fh);
51 	float minHeight = 12.0f + (float)ceil(fh.ascent + fh.descent);
52 	if (Bounds().Height() < minHeight)
53 		ResizeTo(Bounds().Width(), minHeight);
54 }
55 //------------------------------------------------------------------------------
56 BButton::~BButton()
57 {
58 }
59 //------------------------------------------------------------------------------
60 BButton::BButton(BMessage *archive)
61 	:	BControl (archive)
62 {
63 	if (archive->FindBool("_default", &fDrawAsDefault) != B_OK)
64 		fDrawAsDefault = false;
65 }
66 //------------------------------------------------------------------------------
67 BArchivable *BButton::Instantiate(BMessage *archive)
68 {
69 	if (validate_instantiation(archive, "BButton"))
70 		return new BButton(archive);
71 	else
72 		return NULL;
73 }
74 //------------------------------------------------------------------------------
75 status_t BButton::Archive(BMessage* archive, bool deep) const
76 {
77 	status_t err = BControl::Archive(archive, deep);
78 
79 	if (err != B_OK)
80 		return err;
81 
82 	if (IsDefault())
83 		err = archive->AddBool("_default", true);
84 
85 	return err;
86 }
87 //------------------------------------------------------------------------------
88 void BButton::Draw(BRect updateRect)
89 {
90 	font_height fh;
91 	GetFontHeight(&fh);
92 
93 	BRect bounds(Bounds());
94 
95 	// If the focus is changing, just redraw the focus indicator
96 	if (IsFocusChanging())
97 	{
98 		float x = (bounds.right - StringWidth(Label())) / 2.0f;
99 		float y = bounds.bottom - fh.descent - (IsDefault() ? 6.0f : 3.0f);
100 
101 		if (IsFocus())
102 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
103 		else
104 			SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
105 				B_LIGHTEN_1_TINT));
106 
107 		StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y));
108 
109 		return;
110 	}
111 
112 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR),
113 		lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT),
114 		lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT),
115 		lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT),
116 		darken2 = tint_color(no_tint, B_DARKEN_2_TINT),
117 		darken4 = tint_color(no_tint, B_DARKEN_4_TINT),
118 		darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT);
119 
120 	BRect rect(bounds);
121 
122 	if (IsDefault())
123 		rect = DrawDefault(rect, IsEnabled());
124 	else
125 		rect.InsetBy(1,1);
126 
127 	if (IsEnabled())
128 	{
129 		// This can be set outside draw
130 		SetHighColor(darken4);
131 
132 		// Dark border
133 		StrokeRect(rect);
134 
135 		BeginLineArray(8);
136 
137 		// Corners
138 		AddLine(rect.LeftBottom(), rect.LeftBottom(), no_tint);
139 		AddLine(rect.LeftTop(), rect.LeftTop(), no_tint);
140 		AddLine(rect.RightTop(), rect.RightTop(), no_tint);
141 		AddLine(rect.RightBottom(), rect.RightBottom(), no_tint);
142 
143 		rect.InsetBy(1, 1);
144 
145 		// First bevel
146 		AddLine(BPoint(rect.left + 1.0f, rect.bottom),
147 			BPoint(rect.right, rect.bottom), darken2);
148 		AddLine(BPoint(rect.right, rect.bottom - 1.0f),
149 			BPoint(rect.right, rect.top + 1.0f), darken2);
150 
151 		AddLine(BPoint(rect.left, rect.top),
152 			BPoint(rect.left, rect.bottom), lighten1);
153 		AddLine(BPoint(rect.left + 1.0f, rect.top),
154 			BPoint(rect.right, rect.top), lighten1);
155 
156 		EndLineArray();
157 
158 		rect.InsetBy(1, 1);
159 
160 		// Second bevel
161 		SetHighColor(lightenmax);
162 		FillRect(rect);
163 
164 		SetHighColor(no_tint);
165 		StrokeLine(BPoint(rect.right, rect.top + 1.0f),
166 			BPoint(rect.right, rect.bottom));
167 		StrokeLine(BPoint(rect.left + 1.0f, rect.bottom));
168 
169 		rect.InsetBy(1, 1);
170 
171 		// Filling
172 		rect.left += 1.0f;
173 		rect.top += 1.0f;
174 		SetHighColor(lighten1);
175 		FillRect(rect);
176 
177 		if (Value())
178 		{
179 			// Invert
180 			rect.left -= 3;
181 			rect.top -= 3;
182 			rect.right += 2;
183 			rect.bottom += 2;
184 			InvertRect(rect);
185 		}
186 
187 		// Label
188 		float x = (bounds.right - StringWidth(Label())) / 2.0f;
189 		float y = bounds.bottom - fh.descent - (IsDefault() ? 8.0f : 5.0f);
190 
191 		if (Value())
192 		{
193 			SetHighColor(lightenmax);
194 			SetLowColor(darkenmax);
195 		}
196 		else
197 		{
198 			SetHighColor(darkenmax);
199 			SetLowColor(lighten2);
200 		}
201 
202 		DrawString(Label(), BPoint(x, y));
203 
204 		// Focus
205 		if (IsFocus())
206 		{
207 			y += 2.0f;
208 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
209 			StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y));
210 		}
211 	}
212 	else
213 	{
214 		// This can be set outside draw
215 		SetHighColor(darken2);
216 
217 		// Dark border
218 		StrokeRect(rect);
219 
220 		BeginLineArray(8);
221 
222 		// Corners
223 		AddLine(rect.LeftBottom(), rect.LeftBottom(), no_tint);
224 		AddLine(rect.LeftTop(), rect.LeftTop(), no_tint);
225 		AddLine(rect.RightTop(), rect.RightTop(), no_tint);
226 		AddLine(rect.RightBottom(), rect.RightBottom(), no_tint);
227 
228 		rect.InsetBy(1, 1);
229 
230 		// First bevel
231 		AddLine(BPoint(rect.left + 1.0f, rect.bottom),
232 			BPoint(rect.right, rect.bottom), no_tint);
233 		AddLine(BPoint(rect.right, rect.bottom - 1.0f),
234 			BPoint(rect.right, rect.top + 1.0f), no_tint);
235 
236 		AddLine(BPoint(rect.left, rect.top),
237 			BPoint(rect.left, rect.bottom), lighten1);
238 		AddLine(BPoint(rect.left + 1.0f, rect.top),
239 			BPoint(rect.right, rect.top), lighten1);
240 
241 		EndLineArray();
242 
243 		rect.InsetBy(1, 1);
244 
245 		// Second bevel
246 		SetHighColor(lightenmax);
247 		FillRect(rect);
248 
249 		SetHighColor(no_tint);
250 		StrokeLine(BPoint(rect.right, rect.top + 1.0f),
251 			BPoint(rect.right, rect.bottom));
252 		StrokeLine(BPoint(rect.left + 1.0f, rect.bottom));
253 
254 		rect.InsetBy(1, 1);
255 
256 		// Filling
257 		rect.left += 1.0f;
258 		rect.top += 1.0f;
259 		SetHighColor(lighten1);
260 		FillRect(rect);
261 
262 		// Label
263 		float x = (bounds.right - StringWidth(Label())) / 2.0f;
264 		float y = bounds.bottom - fh.descent - 5.0f;
265 
266 		SetHighColor(tint_color(no_tint, B_DISABLED_LABEL_TINT));
267 		SetLowColor(lighten2);
268 		DrawString(Label(), BPoint(x, y));
269 	}
270 }
271 //------------------------------------------------------------------------------
272 void BButton::MouseDown(BPoint point)
273 {
274 	if (!IsEnabled())
275 		return;
276 
277 	SetValue(B_CONTROL_ON);
278 
279 	if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS)
280 	{
281  		SetTracking(true);
282  		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
283  	}
284  	else
285  	{
286 		BRect bounds = Bounds();
287 		uint32 buttons;
288 
289 		do
290 		{
291 			Window()->UpdateIfNeeded();
292 
293 			snooze(40000);
294 
295 			GetMouse(&point, &buttons, true);
296 
297  			bool inside = bounds.Contains(ConvertFromScreen(point));
298 
299 			if ((Value() == B_CONTROL_ON) != inside)
300 				SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
301 		} while (buttons != 0);
302 
303 		if (Value() == B_CONTROL_ON)
304 			Invoke();
305 	}
306 }
307 //------------------------------------------------------------------------------
308 void BButton::AttachedToWindow()
309 {
310 	BControl::AttachedToWindow();
311 
312 	if (IsDefault())
313 		Window()->SetDefaultButton(this);
314 }
315 //------------------------------------------------------------------------------
316 void BButton::KeyDown(const char *bytes, int32 numBytes)
317 {
318 	if (*bytes == B_ENTER || *bytes == B_SPACE)
319 	{
320 		if (!IsEnabled())
321 			return;
322 
323 		SetValue(B_CONTROL_ON);
324 		Invoke();
325 	}
326 	else
327 		BControl::KeyDown(bytes, numBytes);
328 }
329 //------------------------------------------------------------------------------
330 void BButton::MakeDefault(bool flag)
331 {
332 	BButton *oldDefault = NULL;
333 	BWindow *window = Window();
334 
335 	if (window)
336 		oldDefault = window->DefaultButton();
337 
338 	if (flag)
339 	{
340 		if (fDrawAsDefault && oldDefault == this)
341 			return;
342 
343 		fDrawAsDefault = true;
344 
345 		ResizeBy(6.0f, 6.0f);
346 		MoveBy(-3.0f, -3.0f);
347 
348 		if (window && oldDefault != this)
349 			window->SetDefaultButton(this);
350 	}
351 	else
352 	{
353 		if (!fDrawAsDefault)
354 			return;
355 
356 		fDrawAsDefault = false;
357 
358 		ResizeBy(-6.0f, -6.0f);
359 		MoveBy(3.0f, 3.0f);
360 
361 		if (window && oldDefault == this)
362 			window->SetDefaultButton(NULL);
363 	}
364 }
365 //------------------------------------------------------------------------------
366 void BButton::SetLabel(const char *string)
367 {
368 	BControl::SetLabel(string);
369 }
370 //------------------------------------------------------------------------------
371 bool BButton::IsDefault() const
372 {
373 	return fDrawAsDefault;
374 }
375 //------------------------------------------------------------------------------
376 void BButton::MessageReceived(BMessage *message)
377 {
378 	BControl::MessageReceived(message);
379 }
380 //------------------------------------------------------------------------------
381 void BButton::WindowActivated(bool active)
382 {
383 	BControl::WindowActivated(active);
384 }
385 //------------------------------------------------------------------------------
386 void BButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
387 {
388 	if (!IsTracking())
389 		return;
390 
391 	bool inside = Bounds().Contains(point);
392 
393 	if ((Value() == B_CONTROL_ON) != inside)
394 		SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
395 }
396 //------------------------------------------------------------------------------
397 void BButton::MouseUp(BPoint point)
398 {
399 	if (!IsTracking())
400 		return;
401 
402 	if (Bounds().Contains(point))
403 		Invoke();
404 
405 	SetTracking(false);
406 }
407 //------------------------------------------------------------------------------
408 void BButton::DetachedFromWindow()
409 {
410 	BControl::DetachedFromWindow();
411 }
412 //------------------------------------------------------------------------------
413 void BButton::SetValue(int32 value)
414 {
415 	BControl::SetValue(value);
416 }
417 //------------------------------------------------------------------------------
418 void BButton::GetPreferredSize(float *width, float *height)
419 {
420 	font_height fh;
421 	GetFontHeight(&fh);
422 
423 	*height = 12.0f + (float)ceil(fh.ascent + fh.descent);
424 	*width = 20.0f + (float)ceil(StringWidth(Label()));
425 
426 	if (*width < 75.0f)
427 		*width = 75.0f;
428 
429 	if (fDrawAsDefault)
430 	{
431 		*width += 6.0f;
432 		*height += 6.0f;
433 	}
434 }
435 //------------------------------------------------------------------------------
436 void BButton::ResizeToPreferred()
437 {
438 	 BControl::ResizeToPreferred();
439 }
440 //------------------------------------------------------------------------------
441 status_t BButton::Invoke(BMessage *message)
442 {
443 	Sync();
444 	snooze(50000);
445 
446 	status_t err = BControl::Invoke(message);
447 
448 	SetValue(B_CONTROL_OFF);
449 
450 	return err;
451 }
452 //------------------------------------------------------------------------------
453 void BButton::FrameMoved(BPoint newLocation)
454 {
455 	BControl::FrameMoved(newLocation);
456 }
457 //------------------------------------------------------------------------------
458 void BButton::FrameResized(float width, float height)
459 {
460 	BControl::FrameResized(width, height);
461 }
462 //------------------------------------------------------------------------------
463 void BButton::MakeFocus(bool focused)
464 {
465 	BControl::MakeFocus(focused);
466 }
467 //------------------------------------------------------------------------------
468 void BButton::AllAttached()
469 {
470 	BControl::AllAttached();
471 }
472 //------------------------------------------------------------------------------
473 void BButton::AllDetached()
474 {
475 	BControl::AllDetached();
476 }
477 //------------------------------------------------------------------------------
478 BHandler *BButton::ResolveSpecifier(BMessage *message, int32 index,
479 									BMessage *specifier, int32 what,
480 									const char *property)
481 {
482 	return BControl::ResolveSpecifier(message, index, specifier, what, property);
483 }
484 //------------------------------------------------------------------------------
485 status_t BButton::GetSupportedSuites(BMessage *message)
486 {
487 	return BControl::GetSupportedSuites(message);
488 }
489 //------------------------------------------------------------------------------
490 status_t BButton::Perform(perform_code d, void *arg)
491 {
492 	return BControl::Perform(d, arg);
493 }
494 //------------------------------------------------------------------------------
495 void BButton::_ReservedButton1() {}
496 void BButton::_ReservedButton2() {}
497 void BButton::_ReservedButton3() {}
498 //------------------------------------------------------------------------------
499 BButton &BButton::operator=(const BButton &)
500 {
501 	return *this;
502 }
503 //------------------------------------------------------------------------------
504 BRect BButton::DrawDefault(BRect bounds, bool enabled)
505 {
506 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR),
507 	lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT),
508 	darken1 = tint_color(no_tint, B_DARKEN_1_TINT),
509 	darken4 = tint_color(no_tint, B_DARKEN_4_TINT);
510 
511 	if (enabled)
512 	{
513 		// Dark border
514 		BeginLineArray(4);
515 		AddLine(BPoint(bounds.left, bounds.bottom - 1.0f),
516 			BPoint(bounds.left, bounds.top + 1.0f), darken4);
517 		AddLine(BPoint(bounds.left + 1.0f, bounds.top),
518 			BPoint(bounds.right - 1.0f, bounds.top), darken4);
519 		AddLine(BPoint(bounds.right, bounds.top + 1.0f),
520 			BPoint(bounds.right, bounds.bottom - 1.0f), darken4);
521 		AddLine(BPoint(bounds.left + 1.0f, bounds.bottom),
522 			BPoint(bounds.right - 1.0f, bounds.bottom), darken4);
523 		EndLineArray();
524 
525 		bounds.InsetBy(1.0f, 1.0f);
526 
527 		// Bevel
528 		SetHighColor(darken1);
529 		StrokeRect(bounds);
530 
531 		bounds.InsetBy(1.0f, 1.0f);
532 
533 		// Filling
534 		SetHighColor(lighten1);
535 		FillRect(bounds);
536 
537 		bounds.InsetBy(2.0f, 2.0f);
538 	}
539 	else
540 	{
541 		// Dark border
542 		BeginLineArray(4);
543 		AddLine(BPoint(bounds.left, bounds.bottom - 1.0f),
544 			BPoint(bounds.left, bounds.top + 1.0f), darken1);
545 		AddLine(BPoint(bounds.left + 1.0f, bounds.top),
546 			BPoint(bounds.right - 1.0f, bounds.top), darken1);
547 		AddLine(BPoint(bounds.right, bounds.top + 1.0f),
548 			BPoint(bounds.right, bounds.bottom - 1.0f), darken1);
549 		AddLine(BPoint(bounds.left + 1.0f, bounds.bottom),
550 			BPoint(bounds.right - 1.0f, bounds.bottom), darken1);
551 		EndLineArray();
552 
553 		bounds.InsetBy(1.0f, 1.0f);
554 
555 		// Filling
556 		SetHighColor(lighten1);
557 		FillRect(bounds);
558 
559 		bounds.InsetBy(3.0f, 3.0f);
560 	}
561 
562 	return bounds;
563 }
564 //------------------------------------------------------------------------------
565 status_t BButton::Execute()
566 {
567 	if (!IsEnabled())
568 		return B_ERROR;
569 
570 	SetValue(B_CONTROL_ON);
571 	return Invoke();
572 }
573 //------------------------------------------------------------------------------
574 
575 /*
576  * $Log $
577  *
578  * $Id  $
579  *
580  */
581