1 /* 2 * Copyright 2001-2005, Haiku. 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 #include <Font.h> 16 #include <String.h> 17 #include <Window.h> 18 19 20 BButton::BButton(BRect frame, const char *name, const char *label, BMessage *message, 21 uint32 resizingMode, uint32 flags) 22 : BControl(frame, name, label, message, resizingMode, flags |= B_WILL_DRAW), 23 fDrawAsDefault(false) 24 { 25 // Resize to minimum height if needed 26 font_height fh; 27 GetFontHeight(&fh); 28 float minHeight = 12.0f + (float)ceil(fh.ascent + fh.descent); 29 if (Bounds().Height() < minHeight) 30 ResizeTo(Bounds().Width(), minHeight); 31 } 32 33 34 BButton::~BButton() 35 { 36 } 37 38 39 BButton::BButton(BMessage *archive) 40 : BControl (archive) 41 { 42 if (archive->FindBool("_default", &fDrawAsDefault) != B_OK) 43 fDrawAsDefault = false; 44 } 45 46 47 BArchivable * 48 BButton::Instantiate(BMessage *archive) 49 { 50 if (validate_instantiation(archive, "BButton")) 51 return new BButton(archive); 52 53 return NULL; 54 } 55 56 57 status_t 58 BButton::Archive(BMessage* archive, bool deep) const 59 { 60 status_t err = BControl::Archive(archive, deep); 61 62 if (err != B_OK) 63 return err; 64 65 if (IsDefault()) 66 err = archive->AddBool("_default", true); 67 68 return err; 69 } 70 71 72 void 73 BButton::Draw(BRect updateRect) 74 { 75 font_height fh; 76 GetFontHeight(&fh); 77 78 const BRect bounds = Bounds(); 79 BRect rect = bounds; 80 81 const bool enabled = IsEnabled(); 82 const bool pushed = Value() == B_CONTROL_ON; 83 #if 0 84 // Default indicator 85 if (IsDefault()) 86 rect = DrawDefault(rect, enabled); 87 else 88 rect.InsetBy(1.0f, 1.0f); 89 90 BRect fillArea = rect; 91 fillArea.InsetBy(3.0f, 3.0f); 92 93 BString text = Label(); 94 95 #if 1 96 // Label truncation 97 BFont font; 98 GetFont(&font); 99 font.TruncateString(&text, B_TRUNCATE_END, fillArea.Width()); 100 #endif 101 102 // Label position 103 const float stringWidth = StringWidth(text.String()); 104 const float x = (bounds.right - stringWidth) / 2.0f; 105 const float labelY = bounds.top 106 + ((bounds.Height() - fh.ascent - fh.descent) / 2.0f) 107 + fh.ascent + 1.0f; 108 const float focusLineY = labelY + fh.descent; 109 110 /* speed trick: 111 if the focus changes but the button is not pressed then we can 112 redraw only the focus line, 113 if the focus changes and the button is pressed invert the internal rect 114 this block takes care of all the focus changes 115 */ 116 if (IsFocusChanging()) { 117 if (pushed) { 118 rect.InsetBy(2.0, 2.0); 119 InvertRect(rect); 120 } else 121 DrawFocusLine(x, focusLineY, stringWidth, IsFocus() && Window()->IsActive()); 122 123 return; 124 } 125 126 // Colors 127 const rgb_color panelBgColor = ui_color(B_PANEL_BACKGROUND_COLOR); 128 const rgb_color buttonBgColor=tint_color(panelBgColor, B_LIGHTEN_1_TINT); 129 const rgb_color maxLightColor=tint_color(panelBgColor, B_LIGHTEN_MAX_TINT); 130 const rgb_color maxShadowColor=tint_color(panelBgColor, B_DARKEN_MAX_TINT); 131 const rgb_color darkBorderColor = tint_color(panelBgColor, 132 enabled ? B_DARKEN_4_TINT : B_DARKEN_2_TINT); 133 const rgb_color firstBevelColor = enabled ? tint_color(panelBgColor, B_DARKEN_2_TINT) 134 : panelBgColor; 135 const rgb_color cornerColor = IsDefault() ? firstBevelColor : panelBgColor; 136 137 // Fill the button area 138 SetHighColor(buttonBgColor); 139 FillRect(fillArea); 140 141 // external border 142 SetHighColor(darkBorderColor); 143 StrokeRect(rect); 144 145 BeginLineArray(14); 146 147 // Corners 148 AddLine(rect.LeftTop(), rect.LeftTop(), cornerColor); 149 AddLine(rect.LeftBottom(), rect.LeftBottom(), cornerColor); 150 AddLine(rect.RightTop(), rect.RightTop(), cornerColor); 151 AddLine(rect.RightBottom(), rect.RightBottom(), cornerColor); 152 153 rect.InsetBy(1.0f,1.0f); 154 155 // Shadow 156 AddLine(rect.LeftBottom(), rect.RightBottom(), firstBevelColor); 157 AddLine(rect.RightBottom(), rect.RightTop(), firstBevelColor); 158 // Light 159 AddLine(rect.LeftTop(), rect.LeftBottom(),buttonBgColor); 160 AddLine(rect.LeftTop(), rect.RightTop(), buttonBgColor); 161 162 rect.InsetBy(1.0f, 1.0f); 163 164 // Shadow 165 AddLine(rect.LeftBottom(), rect.RightBottom(), panelBgColor); 166 AddLine(rect.RightBottom(), rect.RightTop(), panelBgColor); 167 // Light 168 AddLine(rect.LeftTop(), rect.LeftBottom(),maxLightColor); 169 AddLine(rect.LeftTop(), rect.RightTop(), maxLightColor); 170 171 rect.InsetBy(1.0f,1.0f); 172 173 // Light 174 AddLine(rect.LeftTop(), rect.LeftBottom(),maxLightColor); 175 AddLine(rect.LeftTop(), rect.RightTop(), maxLightColor); 176 177 EndLineArray(); 178 179 // Invert if clicked 180 if (enabled && pushed) { 181 rect.InsetBy(-2.0f, -2.0f); 182 InvertRect(rect); 183 } 184 185 // Label color 186 if (enabled) { 187 if (pushed) { 188 SetHighColor(maxLightColor); 189 SetLowColor(maxShadowColor); 190 } else { 191 SetHighColor(maxShadowColor); 192 SetLowColor(tint_color(panelBgColor, B_LIGHTEN_2_TINT)); 193 } 194 } else { 195 SetHighColor(tint_color(panelBgColor, B_DISABLED_LABEL_TINT)); 196 SetLowColor(tint_color(panelBgColor, B_LIGHTEN_2_TINT)); 197 } 198 199 // Draw the label 200 DrawString(text.String(), BPoint(x, labelY)); 201 202 // Focus line 203 if (enabled && IsFocus() && Window()->IsActive() && !pushed) 204 DrawFocusLine(x,focusLineY,stringWidth,true); 205 #else 206 // Default indicator 207 if (IsDefault()) 208 rect = DrawDefault(rect, enabled); 209 210 BRect fillArea = rect; 211 fillArea.InsetBy(3.0, 3.0); 212 213 BString text = Label(); 214 215 #if 1 216 // Label truncation 217 BFont font; 218 GetFont(&font); 219 font.TruncateString(&text, B_TRUNCATE_END, fillArea.Width() - 4); 220 #endif 221 222 // Label position 223 const float stringWidth = StringWidth(text.String()); 224 const float x = (rect.right - stringWidth) / 2.0; 225 const float labelY = bounds.top 226 + ((bounds.Height() - fh.ascent - fh.descent) / 2.0) 227 + fh.ascent + 1.0; 228 const float focusLineY = labelY + fh.descent; 229 230 /* speed trick: 231 if the focus changes but the button is not pressed then we can 232 redraw only the focus line, 233 if the focus changes and the button is pressed invert the internal rect 234 this block takes care of all the focus changes 235 */ 236 if (IsFocusChanging()) { 237 if (pushed) { 238 rect.InsetBy(2.0, 2.0); 239 InvertRect(rect); 240 } else 241 DrawFocusLine(x, focusLineY, stringWidth, IsFocus() && Window()->IsActive()); 242 243 return; 244 } 245 246 // colors 247 const rgb_color panelBgColor = ui_color(B_PANEL_BACKGROUND_COLOR); 248 const rgb_color buttonBgColor = tint_color(panelBgColor, B_LIGHTEN_1_TINT); 249 const rgb_color lightColor = tint_color(panelBgColor, enabled ? B_LIGHTEN_2_TINT 250 : B_LIGHTEN_1_TINT); 251 const rgb_color maxLightColor = tint_color(panelBgColor, enabled ? B_LIGHTEN_MAX_TINT 252 : B_LIGHTEN_2_TINT); 253 const rgb_color maxShadowColor = tint_color(panelBgColor, B_DARKEN_MAX_TINT); 254 255 rgb_color dark1BorderColor = tint_color(panelBgColor, enabled ? B_DARKEN_3_TINT 256 : B_DARKEN_1_TINT); 257 rgb_color dark2BorderColor = tint_color(panelBgColor, enabled ? B_DARKEN_4_TINT 258 : B_DARKEN_2_TINT); 259 260 rgb_color bevelColor1 = enabled ? tint_color(panelBgColor, B_DARKEN_2_TINT) 261 : panelBgColor; 262 rgb_color bevelColor2 = enabled ? panelBgColor : buttonBgColor; 263 264 rgb_color borderBevelShadow; 265 rgb_color borderBevelLight; 266 if (IsDefault()) { 267 rgb_color focusColor = dark1BorderColor; 268 269 borderBevelShadow = enabled ? tint_color(focusColor, (B_NO_TINT + B_DARKEN_1_TINT) / 2) 270 : focusColor; 271 borderBevelLight = enabled ? tint_color(focusColor, B_LIGHTEN_1_TINT) : focusColor; 272 273 borderBevelLight.red = (borderBevelLight.red + panelBgColor.red) / 2; 274 borderBevelLight.green = (borderBevelLight.green + panelBgColor.green) / 2; 275 borderBevelLight.blue = (borderBevelLight.blue + panelBgColor.blue) / 2; 276 277 dark1BorderColor = tint_color(focusColor, enabled ? B_DARKEN_3_TINT 278 : B_DARKEN_1_TINT); 279 dark2BorderColor = tint_color(focusColor, enabled ? B_DARKEN_4_TINT 280 : B_DARKEN_2_TINT); 281 } else { 282 borderBevelShadow = enabled ? tint_color(panelBgColor, (B_NO_TINT + B_DARKEN_1_TINT) / 2) 283 : panelBgColor; 284 borderBevelLight = enabled ? buttonBgColor : panelBgColor; 285 } 286 287 // fill the button area 288 SetHighColor(buttonBgColor); 289 FillRect(fillArea); 290 291 BeginLineArray(16); 292 // bevel around external border 293 AddLine(BPoint(rect.left, rect.bottom), 294 BPoint(rect.left, rect.top), borderBevelShadow); 295 AddLine(BPoint(rect.left + 1, rect.top), 296 BPoint(rect.right, rect.top), borderBevelShadow); 297 298 AddLine(BPoint(rect.right, rect.top + 1), 299 BPoint(rect.right, rect.bottom), borderBevelLight); 300 AddLine(BPoint(rect.left + 1, rect.bottom), 301 BPoint(rect.right - 1, rect.bottom), borderBevelLight); 302 303 rect.InsetBy(1.0, 1.0); 304 305 // external border 306 AddLine(BPoint(rect.left, rect.bottom), 307 BPoint(rect.left, rect.top), dark1BorderColor); 308 AddLine(BPoint(rect.left + 1, rect.top), 309 BPoint(rect.right, rect.top), dark1BorderColor); 310 AddLine(BPoint(rect.right, rect.top + 1), 311 BPoint(rect.right, rect.bottom), dark2BorderColor); 312 AddLine(BPoint(rect.right - 1, rect.bottom), 313 BPoint(rect.left + 1, rect.bottom), dark2BorderColor); 314 315 rect.InsetBy(1.0, 1.0); 316 317 // Light 318 AddLine(BPoint(rect.left, rect.top), 319 BPoint(rect.left, rect.bottom), lightColor); 320 AddLine(BPoint(rect.left + 1, rect.top), 321 BPoint(rect.right, rect.top), lightColor); 322 // Shadow 323 AddLine(BPoint(rect.left + 1, rect.bottom), 324 BPoint(rect.right, rect.bottom), bevelColor1); 325 AddLine(BPoint(rect.right, rect.bottom - 1), 326 BPoint(rect.right, rect.top + 1), bevelColor1); 327 328 rect.InsetBy(1.0, 1.0); 329 330 // Light 331 AddLine(BPoint(rect.left, rect.top), 332 BPoint(rect.left, rect.bottom), maxLightColor); 333 AddLine(BPoint(rect.left + 1, rect.top), 334 BPoint(rect.right, rect.top), maxLightColor); 335 // Shadow 336 AddLine(BPoint(rect.left + 1, rect.bottom), 337 BPoint(rect.right, rect.bottom), bevelColor2); 338 AddLine(BPoint(rect.right, rect.bottom - 1), 339 BPoint(rect.right, rect.top + 1), bevelColor2); 340 341 rect.InsetBy(1.0,1.0); 342 343 EndLineArray(); 344 345 // Invert if clicked 346 if (enabled && pushed) { 347 rect.InsetBy(-2.0, -2.0); 348 InvertRect(rect); 349 } 350 351 // Label color 352 if (enabled) { 353 if (pushed) { 354 SetHighColor(maxLightColor); 355 SetLowColor(255 - buttonBgColor.red, 356 255 - buttonBgColor.green, 357 255 - buttonBgColor.blue); 358 } else { 359 SetHighColor(maxShadowColor); 360 SetLowColor(buttonBgColor); 361 } 362 } else { 363 SetHighColor(tint_color(panelBgColor, B_DISABLED_LABEL_TINT)); 364 SetLowColor(buttonBgColor); 365 } 366 367 // Draw the label 368 DrawString(text.String(), BPoint(x, labelY)); 369 370 // Focus line 371 if (enabled && IsFocus() && Window()->IsActive() && !pushed) 372 DrawFocusLine(x, focusLineY, stringWidth, true); 373 #endif 374 } 375 376 377 void 378 BButton::MouseDown(BPoint point) 379 { 380 if (!IsEnabled()) 381 return; 382 383 SetValue(B_CONTROL_ON); 384 385 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) { 386 SetTracking(true); 387 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); 388 } else { 389 BRect bounds = Bounds(); 390 uint32 buttons; 391 392 do { 393 Window()->UpdateIfNeeded(); 394 snooze(40000); 395 396 GetMouse(&point, &buttons, true); 397 398 bool inside = bounds.Contains(point); 399 400 if ((Value() == B_CONTROL_ON) != inside) 401 SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF); 402 } while (buttons != 0); 403 404 if (Value() == B_CONTROL_ON) 405 Invoke(); 406 } 407 } 408 409 410 void 411 BButton::AttachedToWindow() 412 { 413 BControl::AttachedToWindow(); 414 // low color will now be the parents view color 415 416 if (IsDefault()) 417 Window()->SetDefaultButton(this); 418 419 SetViewColor(B_TRANSPARENT_COLOR); 420 } 421 422 423 void 424 BButton::KeyDown(const char *bytes, int32 numBytes) 425 { 426 if (*bytes == B_ENTER || *bytes == B_SPACE) { 427 if (!IsEnabled()) 428 return; 429 430 SetValue(B_CONTROL_ON); 431 432 // make sure the user saw that 433 Window()->UpdateIfNeeded(); 434 snooze(25000); 435 436 Invoke(); 437 438 } else 439 BControl::KeyDown(bytes, numBytes); 440 } 441 442 443 void 444 BButton::MakeDefault(bool flag) 445 { 446 BButton *oldDefault = NULL; 447 BWindow *window = Window(); 448 449 if (window) 450 oldDefault = window->DefaultButton(); 451 452 if (flag) { 453 if (fDrawAsDefault && oldDefault == this) 454 return; 455 456 if (!fDrawAsDefault) { 457 fDrawAsDefault = true; 458 459 ResizeBy(6.0f, 6.0f); 460 MoveBy(-3.0f, -3.0f); 461 } 462 463 if (window && oldDefault != this) 464 window->SetDefaultButton(this); 465 } else { 466 if (!fDrawAsDefault) 467 return; 468 469 fDrawAsDefault = false; 470 471 ResizeBy(-6.0f, -6.0f); 472 MoveBy(3.0f, 3.0f); 473 474 if (window && oldDefault == this) 475 window->SetDefaultButton(NULL); 476 } 477 } 478 479 480 void 481 BButton::SetLabel(const char *string) 482 { 483 BControl::SetLabel(string); 484 } 485 486 487 bool 488 BButton::IsDefault() const 489 { 490 return fDrawAsDefault; 491 } 492 493 494 void 495 BButton::MessageReceived(BMessage *message) 496 { 497 BControl::MessageReceived(message); 498 } 499 500 501 void 502 BButton::WindowActivated(bool active) 503 { 504 BControl::WindowActivated(active); 505 } 506 507 508 void 509 BButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 510 { 511 if (!IsTracking()) 512 return; 513 514 bool inside = Bounds().Contains(point); 515 516 if ((Value() == B_CONTROL_ON) != inside) 517 SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF); 518 } 519 520 521 void 522 BButton::MouseUp(BPoint point) 523 { 524 if (!IsTracking()) 525 return; 526 527 if (Bounds().Contains(point)) 528 Invoke(); 529 530 SetTracking(false); 531 } 532 533 534 void 535 BButton::DetachedFromWindow() 536 { 537 BControl::DetachedFromWindow(); 538 } 539 540 541 void 542 BButton::SetValue(int32 value) 543 { 544 if (value != Value()) { 545 BControl::SetValue(value); 546 Invalidate(); 547 } 548 } 549 550 551 void 552 BButton::GetPreferredSize(float *_width, float *_height) 553 { 554 if (_height) { 555 font_height fontHeight; 556 GetFontHeight(&fontHeight); 557 558 *_height = 12.0f + (float)ceil(fontHeight.ascent + fontHeight.descent) 559 + (fDrawAsDefault ? 6.0f : 0); 560 } 561 562 if (_width) { 563 float width = 20.0f + (float)ceil(StringWidth(Label())); 564 if (width < 75.0f) 565 width = 75.0f; 566 567 if (fDrawAsDefault) 568 width += 6.0f; 569 570 *_width = width; 571 } 572 } 573 574 575 void 576 BButton::ResizeToPreferred() 577 { 578 BControl::ResizeToPreferred(); 579 } 580 581 582 status_t 583 BButton::Invoke(BMessage *message) 584 { 585 Sync(); 586 snooze(50000); 587 588 status_t err = BControl::Invoke(message); 589 590 SetValue(B_CONTROL_OFF); 591 592 return err; 593 } 594 595 596 void 597 BButton::FrameMoved(BPoint newLocation) 598 { 599 BControl::FrameMoved(newLocation); 600 } 601 602 603 void 604 BButton::FrameResized(float width, float height) 605 { 606 BControl::FrameResized(width, height); 607 } 608 609 610 void 611 BButton::MakeFocus(bool focused) 612 { 613 BControl::MakeFocus(focused); 614 } 615 616 617 void 618 BButton::AllAttached() 619 { 620 BControl::AllAttached(); 621 } 622 623 624 void 625 BButton::AllDetached() 626 { 627 BControl::AllDetached(); 628 } 629 630 631 BHandler * 632 BButton::ResolveSpecifier(BMessage *message, int32 index, 633 BMessage *specifier, int32 what, 634 const char *property) 635 { 636 return BControl::ResolveSpecifier(message, index, specifier, what, property); 637 } 638 639 640 status_t 641 BButton::GetSupportedSuites(BMessage *message) 642 { 643 return BControl::GetSupportedSuites(message); 644 } 645 646 647 status_t 648 BButton::Perform(perform_code d, void *arg) 649 { 650 return BControl::Perform(d, arg); 651 } 652 653 654 void BButton::_ReservedButton1() {} 655 void BButton::_ReservedButton2() {} 656 void BButton::_ReservedButton3() {} 657 658 659 BButton & 660 BButton::operator=(const BButton &) 661 { 662 return *this; 663 } 664 665 666 BRect 667 BButton::DrawDefault(BRect bounds, bool enabled) 668 { 669 #if 0 670 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR), 671 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT), 672 darken1 = tint_color(no_tint, B_DARKEN_1_TINT); 673 674 rgb_color borderColor; 675 if (enabled) 676 borderColor = tint_color(no_tint, B_DARKEN_4_TINT); 677 else 678 borderColor = darken1; 679 680 // Dark border 681 BeginLineArray(4); 682 AddLine(BPoint(bounds.left, bounds.bottom - 1.0f), 683 BPoint(bounds.left, bounds.top + 1.0f), borderColor); 684 AddLine(BPoint(bounds.left + 1.0f, bounds.top), 685 BPoint(bounds.right - 1.0f, bounds.top), borderColor); 686 AddLine(BPoint(bounds.right, bounds.top + 1.0f), 687 BPoint(bounds.right, bounds.bottom - 1.0f), borderColor); 688 AddLine(BPoint(bounds.left + 1.0f, bounds.bottom), 689 BPoint(bounds.right - 1.0f, bounds.bottom), borderColor); 690 EndLineArray(); 691 692 if (enabled) { 693 // Bevel 694 bounds.InsetBy(1.0f, 1.0f); 695 SetHighColor(darken1); 696 StrokeRect(bounds); 697 } 698 699 bounds.InsetBy(1.0f, 1.0f); 700 701 // Filling 702 float inset = enabled? 2.0f : 3.0f; 703 SetHighColor(lighten1); 704 705 FillRect(BRect(bounds.left, bounds.top, 706 bounds.right, bounds.top+inset-1.0f)); 707 FillRect(BRect(bounds.left, bounds.bottom-inset+1.0f, 708 bounds.right, bounds.bottom)); 709 FillRect(BRect(bounds.left, bounds.top+inset-1.0f, 710 bounds.left+inset-1.0f, bounds.bottom-inset+1.0f)); 711 FillRect(BRect(bounds.right-inset+1.0f, bounds.top+inset-1.0f, 712 bounds.right, bounds.bottom-inset+1.0f)); 713 714 bounds.InsetBy(inset,inset); 715 716 return bounds; 717 #else 718 rgb_color low = LowColor(); 719 rgb_color focusColor = tint_color(low, enabled ? (B_DARKEN_1_TINT + B_DARKEN_2_TINT) / 2 720 : (B_NO_TINT + B_DARKEN_1_TINT) / 2); 721 722 SetHighColor(focusColor); 723 724 StrokeRect(bounds, B_SOLID_LOW); 725 bounds.InsetBy(1.0, 1.0); 726 StrokeRect(bounds); 727 bounds.InsetBy(1.0, 1.0); 728 StrokeRect(bounds); 729 bounds.InsetBy(1.0, 1.0); 730 731 return bounds; 732 #endif 733 } 734 735 736 status_t 737 BButton::Execute() 738 { 739 if (!IsEnabled()) 740 return B_ERROR; 741 742 SetValue(B_CONTROL_ON); 743 return Invoke(); 744 } 745 746 747 void 748 BButton::DrawFocusLine(float x, float y, float width, bool visible) 749 { 750 if (visible) 751 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 752 else { 753 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 754 B_LIGHTEN_1_TINT)); 755 } 756 757 // Blue Line 758 StrokeLine(BPoint(x, y), BPoint(x + width, y)); 759 760 if (visible) 761 SetHighColor(255, 255, 255); 762 // White Line 763 StrokeLine(BPoint(x, y + 1.0f), BPoint(x + width, y + 1.0f)); 764 } 765