xref: /haiku/src/kits/interface/Button.cpp (revision 38f832c7eb696038294df0341e08acceca4716b3)
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 (fDrawAsDefault)
83 		err = archive->AddBool("_default", fDrawAsDefault);
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 			SetDrawingMode(B_OP_INVERT);
185 			SetHighColor(0, 0, 0);
186 			FillRect(rect);
187 			SetDrawingMode(B_OP_COPY);
188 		}
189 
190 		// Label
191 		float x = bounds.right / 2 - StringWidth(Label()) / 2.0f;
192 		float y = bounds.bottom - fh.descent - (IsDefault() ? 8.0f : 5.0f);
193 
194 		if (Value())
195 		{
196 			SetHighColor(lightenmax);
197 			SetLowColor(darkenmax);
198 		}
199 		else
200 		{
201 			SetHighColor(darkenmax);
202 			SetLowColor(lighten2);
203 		}
204 
205 		DrawString(Label(), BPoint(x, y));
206 
207 		// Focus
208 		if (IsFocus())
209 		{
210 			y += 2.0f;
211 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
212 			StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y));
213 		}
214 	}
215 	else
216 	{
217 		// This can be set outside draw
218 		SetHighColor(darken2);
219 
220 		// Dark border
221 		StrokeRect(rect);
222 
223 		BeginLineArray(8);
224 
225 		// Corners
226 		AddLine(rect.LeftBottom(), rect.LeftBottom(), no_tint);
227 		AddLine(rect.LeftTop(), rect.LeftTop(), no_tint);
228 		AddLine(rect.RightTop(), rect.RightTop(), no_tint);
229 		AddLine(rect.RightBottom(), rect.RightBottom(), no_tint);
230 
231 		rect.InsetBy(1, 1);
232 
233 		// First bevel
234 		AddLine(BPoint(rect.left + 1.0f, rect.bottom),
235 			BPoint(rect.right, rect.bottom), no_tint);
236 		AddLine(BPoint(rect.right, rect.bottom - 1.0f),
237 			BPoint(rect.right, rect.top + 1.0f), no_tint);
238 
239 		AddLine(BPoint(rect.left, rect.top),
240 			BPoint(rect.left, rect.bottom), lighten1);
241 		AddLine(BPoint(rect.left + 1.0f, rect.top),
242 			BPoint(rect.right, rect.top), lighten1);
243 
244 		EndLineArray();
245 
246 		rect.InsetBy(1, 1);
247 
248 		// Second bevel
249 		SetHighColor(lightenmax);
250 		FillRect(rect);
251 
252 		SetHighColor(no_tint);
253 		StrokeLine(BPoint(rect.right, rect.top + 1.0f),
254 			BPoint(rect.right, rect.bottom));
255 		StrokeLine(BPoint(rect.left + 1.0f, rect.bottom));
256 
257 		rect.InsetBy(1, 1);
258 
259 		// Filling
260 		rect.left += 1.0f;
261 		rect.top += 1.0f;
262 		SetHighColor(lighten1);
263 		FillRect(rect);
264 
265 		// Label
266 		float x = bounds.right / 2 - StringWidth(Label()) / 2.0f;
267 		float y = bounds.bottom - fh.descent - 5.0f;
268 
269 		SetHighColor(tint_color(no_tint, B_DISABLED_LABEL_TINT));
270 		SetLowColor(lighten2);
271 		DrawString(Label(), BPoint(x, y));
272 	}
273 }
274 //------------------------------------------------------------------------------
275 void BButton::MouseDown(BPoint point)
276 {
277 	if (!IsEnabled())
278 	{
279 		BControl::MouseDown(point);
280 		return;
281 	}
282 
283 	SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY | B_SUSPEND_VIEW_FOCUS);
284 
285 	SetValue(B_CONTROL_ON);
286 	SetTracking(true);
287 }
288 //------------------------------------------------------------------------------
289 void BButton::AttachedToWindow()
290 {
291 	BControl::AttachedToWindow();
292 
293 	if (IsDefault())
294 		Window()->SetDefaultButton(this);
295 }
296 //------------------------------------------------------------------------------
297 void BButton::KeyDown ( const char *bytes, int32 numBytes )
298 {
299 	if (numBytes == 1)
300 	{
301 		switch (bytes[0])
302 		{
303 			case B_ENTER:
304 			case B_SPACE:
305 				SetValue(B_CONTROL_ON);
306 				snooze(50000);
307 				SetValue(B_CONTROL_OFF);
308 				Invoke();
309 				break;
310 
311 			default:
312 				BControl::KeyDown(bytes, numBytes);
313 		}
314 	}
315 	else
316 		BControl::KeyDown(bytes, numBytes);
317 }
318 //------------------------------------------------------------------------------
319 void BButton::MakeDefault(bool flag)
320 {
321 	if (flag == IsDefault())
322 		return;
323 
324 	fDrawAsDefault = flag;
325 	BWindow *window = Window();
326 
327 	if (flag)
328 	{
329 		ResizeBy(6.0f, 6.0f);
330 		MoveBy(-3.0f, -3.0f);
331 
332 		if (window)
333 			window->SetDefaultButton((BButton*)this);
334 	}
335 	else
336 	{
337 		ResizeBy(-6.0f, -6.0f);
338 		MoveBy(3.0f, 3.0f);
339 
340 		if (window)
341 			window->SetDefaultButton(NULL);
342 	}
343 }
344 //------------------------------------------------------------------------------
345 void BButton::SetLabel(const char *string)
346 {
347 	BControl::SetLabel(string);
348 }
349 //------------------------------------------------------------------------------
350 bool BButton::IsDefault() const
351 {
352 	return fDrawAsDefault;
353 }
354 //------------------------------------------------------------------------------
355 void BButton::MessageReceived(BMessage *message)
356 {
357 	BControl::MessageReceived(message);
358 }
359 //------------------------------------------------------------------------------
360 void BButton::WindowActivated(bool active)
361 {
362 	BControl::WindowActivated(active);
363 }
364 //------------------------------------------------------------------------------
365 void BButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
366 {
367 	if (IsEnabled() && IsTracking())
368 	{
369 		if (transit == B_EXITED_VIEW)
370 			SetValue(B_CONTROL_OFF);
371 		else if (transit == B_ENTERED_VIEW)
372 			SetValue(B_CONTROL_ON);
373 	}
374 	else
375 		BControl::MouseMoved(point, transit, message);
376 }
377 //------------------------------------------------------------------------------
378 void BButton::MouseUp(BPoint point)
379 {
380 	if (IsEnabled() && IsTracking())
381 	{
382 		if (Bounds().Contains(point))
383 		{
384 			if ( Value() == B_CONTROL_ON)
385 			{
386 				SetValue(B_CONTROL_OFF);
387 				Invoke();
388 			}
389 		}
390 
391 		SetTracking(false);
392 	}
393 }
394 //------------------------------------------------------------------------------
395 void BButton::DetachedFromWindow()
396 {
397 	BControl::DetachedFromWindow();
398 }
399 //------------------------------------------------------------------------------
400 void BButton::SetValue(int32 value)
401 {
402 	BControl::SetValue(value);
403 }
404 //------------------------------------------------------------------------------
405 void BButton::GetPreferredSize(float *width, float *height)
406 {
407 	font_height fh;
408 	GetFontHeight(&fh);
409 
410 	*height = 12.0f + (float)ceil(fh.ascent + fh.descent);
411 	*width = 20.0f + (float)ceil(StringWidth(Label()));
412 
413 	if (*width < 75.0f)
414 		*width = 75.0f;
415 
416 	if (fDrawAsDefault)
417 	{
418 		*width += 6.0f;
419 		*height += 6.0f;
420 	}
421 }
422 //------------------------------------------------------------------------------
423 void BButton::ResizeToPreferred()
424 {
425 	 BControl::ResizeToPreferred();
426 }
427 //------------------------------------------------------------------------------
428 status_t BButton::Invoke(BMessage *message)
429 {
430 	return BControl::Invoke(message);
431 }
432 //------------------------------------------------------------------------------
433 void BButton::FrameMoved(BPoint newLocation)
434 {
435 	BControl::FrameMoved(newLocation);
436 }
437 //------------------------------------------------------------------------------
438 void BButton::FrameResized(float width, float height)
439 {
440 	BControl::FrameResized(width, height);
441 }
442 //------------------------------------------------------------------------------
443 void BButton::MakeFocus(bool focused)
444 {
445 	BControl::MakeFocus(focused);
446 }
447 //------------------------------------------------------------------------------
448 void BButton::AllAttached()
449 {
450 	BControl::AllAttached();
451 }
452 //------------------------------------------------------------------------------
453 void BButton::AllDetached()
454 {
455 	BControl::AllDetached();
456 }
457 //------------------------------------------------------------------------------
458 BHandler *BButton::ResolveSpecifier(BMessage *message, int32 index,
459 									BMessage *specifier, int32 what,
460 									const char *property)
461 {
462 	return BControl::ResolveSpecifier(message, index, specifier, what, property);
463 }
464 //------------------------------------------------------------------------------
465 status_t BButton::GetSupportedSuites(BMessage *message)
466 {
467 	return BControl::GetSupportedSuites(message);
468 }
469 //------------------------------------------------------------------------------
470 status_t BButton::Perform(perform_code d, void *arg)
471 {
472 	return B_ERROR;
473 }
474 
475 //------------------------------------------------------------------------------
476 void BButton::_ReservedButton1() {}
477 void BButton::_ReservedButton2() {}
478 void BButton::_ReservedButton3() {}
479 //------------------------------------------------------------------------------
480 BButton &BButton::operator=(const BButton &)
481 {
482 	return *this;
483 }
484 //------------------------------------------------------------------------------
485 BRect BButton::DrawDefault(BRect bounds, bool enabled)
486 {
487 	rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR),
488 	lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT),
489 	darken1 = tint_color(no_tint, B_DARKEN_1_TINT),
490 	darken4 = tint_color(no_tint, B_DARKEN_4_TINT);
491 
492 	if (enabled)
493 	{
494 		// Dark border
495 		BeginLineArray(4);
496 		AddLine(BPoint(bounds.left, bounds.bottom - 1.0f),
497 			BPoint(bounds.left, bounds.top + 1.0f), darken4);
498 		AddLine(BPoint(bounds.left + 1.0f, bounds.top),
499 			BPoint(bounds.right - 1.0f, bounds.top), darken4);
500 		AddLine(BPoint(bounds.right, bounds.top + 1.0f),
501 			BPoint(bounds.right, bounds.bottom - 1.0f), darken4);
502 		AddLine(BPoint(bounds.left + 1.0f, bounds.bottom),
503 			BPoint(bounds.right - 1.0f, bounds.bottom), darken4);
504 		EndLineArray();
505 
506 		bounds.InsetBy(1.0f, 1.0f);
507 
508 		// Bevel
509 		SetHighColor(darken1);
510 		StrokeRect(bounds);
511 
512 		bounds.InsetBy(1.0f, 1.0f);
513 
514 		// Filling
515 		SetHighColor(lighten1);
516 		FillRect(bounds);
517 
518 		bounds.InsetBy(2.0f, 2.0f);
519 	}
520 	else
521 	{
522 		// Dark border
523 		BeginLineArray(4);
524 		AddLine(BPoint(bounds.left, bounds.bottom - 1.0f),
525 			BPoint(bounds.left, bounds.top + 1.0f), darken1);
526 		AddLine(BPoint(bounds.left + 1.0f, bounds.top),
527 			BPoint(bounds.right - 1.0f, bounds.top), darken1);
528 		AddLine(BPoint(bounds.right, bounds.top + 1.0f),
529 			BPoint(bounds.right, bounds.bottom - 1.0f), darken1);
530 		AddLine(BPoint(bounds.left + 1.0f, bounds.bottom),
531 			BPoint(bounds.right - 1.0f, bounds.bottom), darken1);
532 		EndLineArray();
533 
534 		bounds.InsetBy(1.0f, 1.0f);
535 
536 		// Filling
537 		SetHighColor(lighten1);
538 		FillRect(bounds);
539 
540 		bounds.InsetBy(3.0f, 3.0f);
541 	}
542 
543 	return bounds;
544 }
545 //------------------------------------------------------------------------------
546 status_t BButton::Execute()
547 {
548 	// TODO: Is there a use for this? Maybe visual feedback happens here?
549 	return Invoke();
550 }
551 //------------------------------------------------------------------------------
552 
553 /*
554  * $Log $
555  *
556  * $Id  $
557  *
558  */
559