xref: /haiku/src/kits/interface/Button.cpp (revision d5cd5d63ff0ad395989db6cf4841a64d5b545d1d)
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),
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 / 2 - 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 / 2 - 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 / 2 - 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 		BRect bounds = Bounds();
282 		uint32 buttons;
283 
284 		do
285 		{
286 			snooze(40000);
287 
288 			GetMouse(&point, &buttons, true);
289 
290 			bool inside = bounds.Contains(point);
291 
292 			if ((Value() == B_CONTROL_ON) != inside)
293 				SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
294 		} while (buttons != 0);
295 
296 		if (Value() == B_CONTROL_ON)
297 			Invoke();
298 	}
299 	else
300 	{
301 		SetTracking(true);
302 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
303 	}
304 }
305 //------------------------------------------------------------------------------
306 void BButton::AttachedToWindow()
307 {
308 	BControl::AttachedToWindow();
309 
310 	if (IsDefault())
311 		Window()->SetDefaultButton(this);
312 }
313 //------------------------------------------------------------------------------
314 void BButton::KeyDown(const char *bytes, int32 numBytes)
315 {
316 	if (*bytes == B_ENTER || *bytes == B_SPACE)
317 	{
318 		if (!IsEnabled())
319 			return;
320 
321 		SetValue(B_CONTROL_ON);
322 		Invoke();
323 	}
324 	else
325 		BControl::KeyDown(bytes, numBytes);
326 }
327 //------------------------------------------------------------------------------
328 void BButton::MakeDefault(bool flag)
329 {
330 	BButton *oldDefault = NULL;
331 	BWindow *window = Window();
332 
333 	if (window)
334 		oldDefault = window->DefaultButton();
335 
336 	if (flag)
337 	{
338 		if (fDrawAsDefault && oldDefault == this)
339 			return;
340 
341 		fDrawAsDefault = true;
342 
343 		ResizeBy(6.0f, 6.0f);
344 		MoveBy(-3.0f, -3.0f);
345 
346 		if (window && oldDefault != this)
347 			window->SetDefaultButton(this);
348 	}
349 	else
350 	{
351 		if (!fDrawAsDefault)
352 			return;
353 
354 		fDrawAsDefault = false;
355 
356 		ResizeBy(-6.0f, -6.0f);
357 		MoveBy(3.0f, 3.0f);
358 
359 		if (window && oldDefault == this)
360 			window->SetDefaultButton(NULL);
361 	}
362 }
363 //------------------------------------------------------------------------------
364 void BButton::SetLabel(const char *string)
365 {
366 	BControl::SetLabel(string);
367 }
368 //------------------------------------------------------------------------------
369 bool BButton::IsDefault() const
370 {
371 	return fDrawAsDefault;
372 }
373 //------------------------------------------------------------------------------
374 void BButton::MessageReceived(BMessage *message)
375 {
376 	BControl::MessageReceived(message);
377 }
378 //------------------------------------------------------------------------------
379 void BButton::WindowActivated(bool active)
380 {
381 	BControl::WindowActivated(active);
382 }
383 //------------------------------------------------------------------------------
384 void BButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
385 {
386 	if (!IsTracking())
387 		return;
388 
389 	bool inside = Bounds().Contains(point);
390 
391 	if ((Value() == B_CONTROL_ON) != inside)
392 		SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
393 }
394 //------------------------------------------------------------------------------
395 void BButton::MouseUp(BPoint point)
396 {
397 	if (!IsTracking())
398 		return;
399 
400 	if (Bounds().Contains(point))
401 		Invoke();
402 
403 	SetTracking(false);
404 }
405 //------------------------------------------------------------------------------
406 void BButton::DetachedFromWindow()
407 {
408 	BControl::DetachedFromWindow();
409 }
410 //------------------------------------------------------------------------------
411 void BButton::SetValue(int32 value)
412 {
413 	BControl::SetValue(value);
414 }
415 //------------------------------------------------------------------------------
416 void BButton::GetPreferredSize(float *width, float *height)
417 {
418 	font_height fh;
419 	GetFontHeight(&fh);
420 
421 	*height = 12.0f + (float)ceil(fh.ascent + fh.descent);
422 	*width = 20.0f + (float)ceil(StringWidth(Label()));
423 
424 	if (*width < 75.0f)
425 		*width = 75.0f;
426 
427 	if (fDrawAsDefault)
428 	{
429 		*width += 6.0f;
430 		*height += 6.0f;
431 	}
432 }
433 //------------------------------------------------------------------------------
434 void BButton::ResizeToPreferred()
435 {
436 	 BControl::ResizeToPreferred();
437 }
438 //------------------------------------------------------------------------------
439 status_t BButton::Invoke(BMessage *message)
440 {
441 	Sync();
442 	snooze(50000);
443 
444 	status_t err = BControl::Invoke(message);
445 
446 	SetValue(B_CONTROL_OFF);
447 
448 	return err;
449 }
450 //------------------------------------------------------------------------------
451 void BButton::FrameMoved(BPoint newLocation)
452 {
453 	BControl::FrameMoved(newLocation);
454 }
455 //------------------------------------------------------------------------------
456 void BButton::FrameResized(float width, float height)
457 {
458 	BControl::FrameResized(width, height);
459 }
460 //------------------------------------------------------------------------------
461 void BButton::MakeFocus(bool focused)
462 {
463 	BControl::MakeFocus(focused);
464 }
465 //------------------------------------------------------------------------------
466 void BButton::AllAttached()
467 {
468 	BControl::AllAttached();
469 }
470 //------------------------------------------------------------------------------
471 void BButton::AllDetached()
472 {
473 	BControl::AllDetached();
474 }
475 //------------------------------------------------------------------------------
476 BHandler *BButton::ResolveSpecifier(BMessage *message, int32 index,
477 									BMessage *specifier, int32 what,
478 									const char *property)
479 {
480 	return BControl::ResolveSpecifier(message, index, specifier, what, property);
481 }
482 //------------------------------------------------------------------------------
483 status_t BButton::GetSupportedSuites(BMessage *message)
484 {
485 	return BControl::GetSupportedSuites(message);
486 }
487 //------------------------------------------------------------------------------
488 status_t BButton::Perform(perform_code d, void *arg)
489 {
490 	return BControl::Perform(d, arg);
491 }
492 //------------------------------------------------------------------------------
493 void BButton::_ReservedButton1() {}
494 void BButton::_ReservedButton2() {}
495 void BButton::_ReservedButton3() {}
496 //------------------------------------------------------------------------------
497 BButton &BButton::operator=(const BButton &)
498 {
499 	return *this;
500 }
501 //------------------------------------------------------------------------------
502 BRect BButton::DrawDefault(BRect bounds, bool enabled)
503 {
504 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR),
505 	lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT),
506 	darken1 = tint_color(no_tint, B_DARKEN_1_TINT),
507 	darken4 = tint_color(no_tint, B_DARKEN_4_TINT);
508 
509 	if (enabled)
510 	{
511 		// Dark border
512 		BeginLineArray(4);
513 		AddLine(BPoint(bounds.left, bounds.bottom - 1.0f),
514 			BPoint(bounds.left, bounds.top + 1.0f), darken4);
515 		AddLine(BPoint(bounds.left + 1.0f, bounds.top),
516 			BPoint(bounds.right - 1.0f, bounds.top), darken4);
517 		AddLine(BPoint(bounds.right, bounds.top + 1.0f),
518 			BPoint(bounds.right, bounds.bottom - 1.0f), darken4);
519 		AddLine(BPoint(bounds.left + 1.0f, bounds.bottom),
520 			BPoint(bounds.right - 1.0f, bounds.bottom), darken4);
521 		EndLineArray();
522 
523 		bounds.InsetBy(1.0f, 1.0f);
524 
525 		// Bevel
526 		SetHighColor(darken1);
527 		StrokeRect(bounds);
528 
529 		bounds.InsetBy(1.0f, 1.0f);
530 
531 		// Filling
532 		SetHighColor(lighten1);
533 		FillRect(bounds);
534 
535 		bounds.InsetBy(2.0f, 2.0f);
536 	}
537 	else
538 	{
539 		// Dark border
540 		BeginLineArray(4);
541 		AddLine(BPoint(bounds.left, bounds.bottom - 1.0f),
542 			BPoint(bounds.left, bounds.top + 1.0f), darken1);
543 		AddLine(BPoint(bounds.left + 1.0f, bounds.top),
544 			BPoint(bounds.right - 1.0f, bounds.top), darken1);
545 		AddLine(BPoint(bounds.right, bounds.top + 1.0f),
546 			BPoint(bounds.right, bounds.bottom - 1.0f), darken1);
547 		AddLine(BPoint(bounds.left + 1.0f, bounds.bottom),
548 			BPoint(bounds.right - 1.0f, bounds.bottom), darken1);
549 		EndLineArray();
550 
551 		bounds.InsetBy(1.0f, 1.0f);
552 
553 		// Filling
554 		SetHighColor(lighten1);
555 		FillRect(bounds);
556 
557 		bounds.InsetBy(3.0f, 3.0f);
558 	}
559 
560 	return bounds;
561 }
562 //------------------------------------------------------------------------------
563 status_t BButton::Execute()
564 {
565 	if (!IsEnabled())
566 		return B_ERROR;
567 
568 	SetValue(B_CONTROL_ON);
569 	return Invoke();
570 }
571 //------------------------------------------------------------------------------
572 
573 /*
574  * $Log $
575  *
576  * $Id  $
577  *
578  */
579