1 /* 2 * Copyright 2004 DarkWyrm <darkwyrm@earthlink.net> 3 * Copyright 2013 FeemanLou 4 * Copyright 2014-2015 Haiku, Inc. All rights reserved. 5 * 6 * Distributed under the terms of the MIT license. 7 * 8 * Originally written by DarkWyrm <darkwyrm@earthlink.net> 9 * Updated by FreemanLou as part of Google GCI 2013 10 * 11 * Authors: 12 * DarkWyrm, darkwyrm@earthlink.net 13 * FeemanLou 14 * John Scipione, jscipione@gmail.com 15 */ 16 17 18 #include <AbstractSpinner.h> 19 20 #include <algorithm> 21 22 #include <AbstractLayoutItem.h> 23 #include <Alignment.h> 24 #include <ControlLook.h> 25 #include <Font.h> 26 #include <GradientLinear.h> 27 #include <LayoutItem.h> 28 #include <LayoutUtils.h> 29 #include <Message.h> 30 #include <MessageFilter.h> 31 #include <MessageRunner.h> 32 #include <Point.h> 33 #include <PropertyInfo.h> 34 #include <TextView.h> 35 #include <View.h> 36 #include <Window.h> 37 38 39 static const float kFrameMargin = 2.0f; 40 41 const char* const kFrameField = "BAbstractSpinner:layoutItem:frame"; 42 const char* const kLabelItemField = "BAbstractSpinner:labelItem"; 43 const char* const kTextViewItemField = "BAbstractSpinner:textViewItem"; 44 45 46 static property_info sProperties[] = { 47 { 48 "Align", 49 { B_GET_PROPERTY, 0 }, 50 { B_DIRECT_SPECIFIER, 0 }, 51 "Returns the alignment of the spinner label.", 52 0, 53 { B_INT32_TYPE } 54 }, 55 { 56 "Align", 57 { B_SET_PROPERTY, 0 }, 58 { B_DIRECT_SPECIFIER, 0}, 59 "Sets the alignment of the spinner label.", 60 0, 61 { B_INT32_TYPE } 62 }, 63 64 { 65 "ButtonStyle", 66 { B_GET_PROPERTY, 0 }, 67 { B_DIRECT_SPECIFIER, 0 }, 68 "Returns the style of the spinner buttons.", 69 0, 70 { B_INT32_TYPE } 71 }, 72 { 73 "ButtonStyle", 74 { B_SET_PROPERTY, 0 }, 75 { B_DIRECT_SPECIFIER, 0}, 76 "Sets the style of the spinner buttons.", 77 0, 78 { B_INT32_TYPE } 79 }, 80 81 { 82 "Divider", 83 { B_GET_PROPERTY, 0 }, 84 { B_DIRECT_SPECIFIER, 0 }, 85 "Returns the divider position of the spinner.", 86 0, 87 { B_FLOAT_TYPE } 88 }, 89 { 90 "Divider", 91 { B_SET_PROPERTY, 0 }, 92 { B_DIRECT_SPECIFIER, 0}, 93 "Sets the divider position of the spinner.", 94 0, 95 { B_FLOAT_TYPE } 96 }, 97 98 { 99 "Enabled", 100 { B_GET_PROPERTY, 0 }, 101 { B_DIRECT_SPECIFIER, 0 }, 102 "Returns whether or not the spinner is enabled.", 103 0, 104 { B_BOOL_TYPE } 105 }, 106 { 107 "Enabled", 108 { B_SET_PROPERTY, 0 }, 109 { B_DIRECT_SPECIFIER, 0}, 110 "Sets whether or not the spinner is enabled.", 111 0, 112 { B_BOOL_TYPE } 113 }, 114 115 { 116 "Label", 117 { B_GET_PROPERTY, 0 }, 118 { B_DIRECT_SPECIFIER, 0 }, 119 "Returns the spinner label.", 120 0, 121 { B_STRING_TYPE } 122 }, 123 { 124 "Label", 125 { B_SET_PROPERTY, 0 }, 126 { B_DIRECT_SPECIFIER, 0}, 127 "Sets the spinner label.", 128 0, 129 { B_STRING_TYPE } 130 }, 131 132 { 133 "Message", 134 { B_GET_PROPERTY, 0 }, 135 { B_DIRECT_SPECIFIER, 0 }, 136 "Returns the spinner invocation message.", 137 0, 138 { B_MESSAGE_TYPE } 139 }, 140 { 141 "Message", 142 { B_SET_PROPERTY, 0 }, 143 { B_DIRECT_SPECIFIER, 0}, 144 "Sets the spinner invocation message.", 145 0, 146 { B_MESSAGE_TYPE } 147 }, 148 149 { 0 } 150 }; 151 152 153 typedef enum { 154 SPINNER_INCREMENT, 155 SPINNER_DECREMENT 156 } spinner_direction; 157 158 159 class SpinnerButton : public BView { 160 public: 161 SpinnerButton(BRect frame, const char* name, 162 spinner_direction direction); 163 virtual ~SpinnerButton(); 164 165 virtual void AttachedToWindow(); 166 virtual void DetachedFromWindow(); 167 virtual void Draw(BRect updateRect); 168 virtual void MouseDown(BPoint where); 169 virtual void MouseUp(BPoint where); 170 virtual void MouseMoved(BPoint where, uint32 transit, 171 const BMessage* message); 172 virtual void MessageReceived(BMessage* message); 173 174 bool IsEnabled() const { return fIsEnabled; } 175 virtual void SetEnabled(bool enable) { fIsEnabled = enable; }; 176 177 private: 178 spinner_direction fSpinnerDirection; 179 BAbstractSpinner* fParent; 180 bool fIsEnabled; 181 bool fIsMouseDown; 182 bool fIsMouseOver; 183 BMessageRunner* fRepeater; 184 }; 185 186 187 class SpinnerTextView : public BTextView { 188 public: 189 SpinnerTextView(BRect rect, BRect textRect); 190 virtual ~SpinnerTextView(); 191 192 virtual void AttachedToWindow(); 193 virtual void DetachedFromWindow(); 194 virtual void KeyDown(const char* bytes, int32 numBytes); 195 virtual void MakeFocus(bool focus); 196 197 private: 198 BAbstractSpinner* fParent; 199 }; 200 201 202 class BAbstractSpinner::LabelLayoutItem : public BAbstractLayoutItem { 203 public: 204 LabelLayoutItem(BAbstractSpinner* parent); 205 LabelLayoutItem(BMessage* archive); 206 207 virtual bool IsVisible(); 208 virtual void SetVisible(bool visible); 209 210 virtual BRect Frame(); 211 virtual void SetFrame(BRect frame); 212 213 void SetParent(BAbstractSpinner* parent); 214 virtual BView* View(); 215 216 virtual BSize BaseMinSize(); 217 virtual BSize BaseMaxSize(); 218 virtual BSize BasePreferredSize(); 219 virtual BAlignment BaseAlignment(); 220 221 BRect FrameInParent() const; 222 223 virtual status_t Archive(BMessage* into, bool deep = true) const; 224 static BArchivable* Instantiate(BMessage* from); 225 226 private: 227 BAbstractSpinner* fParent; 228 BRect fFrame; 229 }; 230 231 232 class BAbstractSpinner::TextViewLayoutItem : public BAbstractLayoutItem { 233 public: 234 TextViewLayoutItem(BAbstractSpinner* parent); 235 TextViewLayoutItem(BMessage* archive); 236 237 virtual bool IsVisible(); 238 virtual void SetVisible(bool visible); 239 240 virtual BRect Frame(); 241 virtual void SetFrame(BRect frame); 242 243 void SetParent(BAbstractSpinner* parent); 244 virtual BView* View(); 245 246 virtual BSize BaseMinSize(); 247 virtual BSize BaseMaxSize(); 248 virtual BSize BasePreferredSize(); 249 virtual BAlignment BaseAlignment(); 250 251 BRect FrameInParent() const; 252 253 virtual status_t Archive(BMessage* into, bool deep = true) const; 254 static BArchivable* Instantiate(BMessage* from); 255 256 private: 257 BAbstractSpinner* fParent; 258 BRect fFrame; 259 }; 260 261 262 struct BAbstractSpinner::LayoutData { 263 LayoutData(float width, float height) 264 : 265 label_layout_item(NULL), 266 text_view_layout_item(NULL), 267 label_width(0), 268 label_height(0), 269 text_view_width(0), 270 text_view_height(0), 271 previous_width(width), 272 previous_height(height), 273 valid(false) 274 { 275 } 276 277 LabelLayoutItem* label_layout_item; 278 TextViewLayoutItem* text_view_layout_item; 279 280 font_height font_info; 281 282 float label_width; 283 float label_height; 284 float text_view_width; 285 float text_view_height; 286 287 float previous_width; 288 float previous_height; 289 290 BSize min; 291 BAlignment alignment; 292 293 bool valid; 294 }; 295 296 297 // #pragma mark - SpinnerButton 298 299 300 SpinnerButton::SpinnerButton(BRect frame, const char* name, 301 spinner_direction direction) 302 : 303 BView(frame, name, B_FOLLOW_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW), 304 fSpinnerDirection(direction), 305 fParent(NULL), 306 fIsEnabled(true), 307 fIsMouseDown(false), 308 fIsMouseOver(false), 309 fRepeater(NULL) 310 { 311 } 312 313 314 SpinnerButton::~SpinnerButton() 315 { 316 delete fRepeater; 317 } 318 319 320 void 321 SpinnerButton::AttachedToWindow() 322 { 323 fParent = static_cast<BAbstractSpinner*>(Parent()); 324 325 AdoptParentColors(); 326 BView::AttachedToWindow(); 327 } 328 329 330 void 331 SpinnerButton::DetachedFromWindow() 332 { 333 fParent = NULL; 334 335 BView::DetachedFromWindow(); 336 } 337 338 339 void 340 SpinnerButton::Draw(BRect updateRect) 341 { 342 BRect rect(Bounds()); 343 if (!rect.IsValid() || !rect.Intersects(updateRect)) 344 return; 345 346 BView::Draw(updateRect); 347 348 float frameTint = fIsEnabled ? B_DARKEN_1_TINT : B_NO_TINT; 349 350 float fgTint; 351 if (!fIsEnabled) 352 fgTint = B_DARKEN_1_TINT; 353 else if (fIsMouseDown) 354 fgTint = B_DARKEN_MAX_TINT; 355 else 356 fgTint = 1.777f; // 216 --> 48.2 (48) 357 358 float bgTint; 359 if (fIsEnabled && fIsMouseOver) 360 bgTint = B_DARKEN_1_TINT; 361 else 362 bgTint = B_NO_TINT; 363 364 rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR); 365 if (bgColor.red + bgColor.green + bgColor.blue <= 128 * 3) { 366 // if dark background make the tint lighter 367 frameTint = 2.0f - frameTint; 368 fgTint = 2.0f - fgTint; 369 bgTint = 2.0f - bgTint; 370 } 371 372 uint32 borders = be_control_look->B_TOP_BORDER 373 | be_control_look->B_BOTTOM_BORDER; 374 375 if (fSpinnerDirection == SPINNER_INCREMENT) 376 borders |= be_control_look->B_RIGHT_BORDER; 377 else 378 borders |= be_control_look->B_LEFT_BORDER; 379 380 uint32 flags = fIsMouseDown ? BControlLook::B_ACTIVATED : 0; 381 flags |= !fIsEnabled ? BControlLook::B_DISABLED : 0; 382 383 // draw the button 384 be_control_look->DrawButtonFrame(this, rect, updateRect, 385 tint_color(bgColor, frameTint), bgColor, flags, borders); 386 be_control_look->DrawButtonBackground(this, rect, updateRect, 387 tint_color(bgColor, bgTint), flags, borders); 388 389 switch (fParent->ButtonStyle()) { 390 case SPINNER_BUTTON_HORIZONTAL_ARROWS: 391 { 392 int32 arrowDirection = fSpinnerDirection == SPINNER_INCREMENT 393 ? be_control_look->B_RIGHT_ARROW 394 : be_control_look->B_LEFT_ARROW; 395 396 rect.InsetBy(0.0f, 1.0f); 397 be_control_look->DrawArrowShape(this, rect, updateRect, bgColor, 398 arrowDirection, 0, fgTint); 399 break; 400 } 401 402 case SPINNER_BUTTON_VERTICAL_ARROWS: 403 { 404 int32 arrowDirection = fSpinnerDirection == SPINNER_INCREMENT 405 ? be_control_look->B_UP_ARROW 406 : be_control_look->B_DOWN_ARROW; 407 408 rect.InsetBy(0.0f, 1.0f); 409 be_control_look->DrawArrowShape(this, rect, updateRect, bgColor, 410 arrowDirection, 0, fgTint); 411 break; 412 } 413 414 default: 415 case SPINNER_BUTTON_PLUS_MINUS: 416 { 417 BFont font; 418 fParent->GetFont(&font); 419 float inset = floorf(font.Size() / 4); 420 rect.InsetBy(inset, inset); 421 422 if (rect.IntegerWidth() % 2 != 0) 423 rect.right -= 1; 424 425 if (rect.IntegerHeight() % 2 != 0) 426 rect.bottom -= 1; 427 428 SetHighColor(tint_color(bgColor, fgTint)); 429 430 // draw the +/- 431 float halfHeight = floorf(rect.Height() / 2); 432 StrokeLine(BPoint(rect.left, rect.top + halfHeight), 433 BPoint(rect.right, rect.top + halfHeight)); 434 if (fSpinnerDirection == SPINNER_INCREMENT) { 435 float halfWidth = floorf(rect.Width() / 2); 436 StrokeLine(BPoint(rect.left + halfWidth, rect.top + 1), 437 BPoint(rect.left + halfWidth, rect.bottom - 1)); 438 } 439 } 440 } 441 } 442 443 444 void 445 SpinnerButton::MouseDown(BPoint where) 446 { 447 if (fIsEnabled) { 448 fIsMouseDown = true; 449 fSpinnerDirection == SPINNER_INCREMENT 450 ? fParent->Increment() 451 : fParent->Decrement(); 452 Invalidate(); 453 BMessage repeatMessage('rept'); 454 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 455 fRepeater = new BMessageRunner(BMessenger(this), repeatMessage, 456 200000); 457 } 458 459 BView::MouseDown(where); 460 } 461 462 463 void 464 SpinnerButton::MouseMoved(BPoint where, uint32 transit, 465 const BMessage* message) 466 { 467 switch (transit) { 468 case B_ENTERED_VIEW: 469 case B_INSIDE_VIEW: 470 { 471 BPoint where; 472 uint32 buttons; 473 GetMouse(&where, &buttons); 474 fIsMouseOver = Bounds().Contains(where) && buttons == 0; 475 476 break; 477 } 478 479 case B_EXITED_VIEW: 480 case B_OUTSIDE_VIEW: 481 fIsMouseOver = false; 482 MouseUp(Bounds().LeftTop()); 483 break; 484 } 485 486 BView::MouseMoved(where, transit, message); 487 } 488 489 490 void 491 SpinnerButton::MouseUp(BPoint where) 492 { 493 fIsMouseDown = false; 494 delete fRepeater; 495 fRepeater = NULL; 496 Invalidate(); 497 498 BView::MouseUp(where); 499 } 500 501 502 void 503 SpinnerButton::MessageReceived(BMessage* message) 504 { 505 switch (message->what) { 506 case 'rept': 507 { 508 if (fIsMouseDown && fRepeater != NULL) { 509 fSpinnerDirection == SPINNER_INCREMENT 510 ? fParent->Increment() 511 : fParent->Decrement(); 512 } 513 514 break; 515 } 516 517 default: 518 BView::MessageReceived(message); 519 } 520 } 521 522 523 // #pragma mark - SpinnerTextView 524 525 526 SpinnerTextView::SpinnerTextView(BRect rect, BRect textRect) 527 : 528 BTextView(rect, "textview", textRect, B_FOLLOW_ALL, 529 B_WILL_DRAW | B_NAVIGABLE), 530 fParent(NULL) 531 { 532 MakeResizable(true); 533 } 534 535 536 SpinnerTextView::~SpinnerTextView() 537 { 538 } 539 540 541 void 542 SpinnerTextView::AttachedToWindow() 543 { 544 fParent = static_cast<BAbstractSpinner*>(Parent()); 545 546 BTextView::AttachedToWindow(); 547 } 548 549 550 void 551 SpinnerTextView::DetachedFromWindow() 552 { 553 fParent = NULL; 554 555 BTextView::DetachedFromWindow(); 556 } 557 558 559 void 560 SpinnerTextView::KeyDown(const char* bytes, int32 numBytes) 561 { 562 if (fParent == NULL) { 563 BTextView::KeyDown(bytes, numBytes); 564 return; 565 } 566 567 switch (bytes[0]) { 568 case B_ENTER: 569 case B_SPACE: 570 fParent->SetValueFromText(); 571 break; 572 573 case B_TAB: 574 fParent->KeyDown(bytes, numBytes); 575 break; 576 577 case B_LEFT_ARROW: 578 if (fParent->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS 579 && (modifiers() & B_CONTROL_KEY) != 0) { 580 // need to hold down control, otherwise can't move cursor 581 fParent->Decrement(); 582 } else 583 BTextView::KeyDown(bytes, numBytes); 584 break; 585 586 case B_UP_ARROW: 587 if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS) 588 fParent->Increment(); 589 else 590 BTextView::KeyDown(bytes, numBytes); 591 break; 592 593 case B_RIGHT_ARROW: 594 if (fParent->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS 595 && (modifiers() & B_CONTROL_KEY) != 0) { 596 // need to hold down control, otherwise can't move cursor 597 fParent->Increment(); 598 } else 599 BTextView::KeyDown(bytes, numBytes); 600 break; 601 602 case B_DOWN_ARROW: 603 if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS) 604 fParent->Decrement(); 605 else 606 BTextView::KeyDown(bytes, numBytes); 607 break; 608 609 default: 610 BTextView::KeyDown(bytes, numBytes); 611 break; 612 } 613 } 614 615 616 void 617 SpinnerTextView::MakeFocus(bool focus) 618 { 619 BTextView::MakeFocus(focus); 620 621 if (fParent == NULL) 622 return; 623 624 if (focus) 625 SelectAll(); 626 else 627 fParent->SetValueFromText(); 628 629 fParent->_DrawTextView(fParent->Bounds()); 630 } 631 632 633 // #pragma mark - BAbstractSpinner::LabelLayoutItem 634 635 636 BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BAbstractSpinner* parent) 637 : 638 fParent(parent), 639 fFrame() 640 { 641 } 642 643 644 BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BMessage* from) 645 : 646 BAbstractLayoutItem(from), 647 fParent(NULL), 648 fFrame() 649 { 650 from->FindRect(kFrameField, &fFrame); 651 } 652 653 654 bool 655 BAbstractSpinner::LabelLayoutItem::IsVisible() 656 { 657 return !fParent->IsHidden(fParent); 658 } 659 660 661 void 662 BAbstractSpinner::LabelLayoutItem::SetVisible(bool visible) 663 { 664 } 665 666 667 BRect 668 BAbstractSpinner::LabelLayoutItem::Frame() 669 { 670 return fFrame; 671 } 672 673 674 void 675 BAbstractSpinner::LabelLayoutItem::SetFrame(BRect frame) 676 { 677 fFrame = frame; 678 fParent->_UpdateFrame(); 679 } 680 681 682 void 683 BAbstractSpinner::LabelLayoutItem::SetParent(BAbstractSpinner* parent) 684 { 685 fParent = parent; 686 } 687 688 689 BView* 690 BAbstractSpinner::LabelLayoutItem::View() 691 { 692 return fParent; 693 } 694 695 696 BSize 697 BAbstractSpinner::LabelLayoutItem::BaseMinSize() 698 { 699 fParent->_ValidateLayoutData(); 700 701 if (fParent->Label() == NULL) 702 return BSize(-1.0f, -1.0f); 703 704 return BSize(fParent->fLayoutData->label_width 705 + be_control_look->DefaultLabelSpacing(), 706 fParent->fLayoutData->label_height); 707 } 708 709 710 BSize 711 BAbstractSpinner::LabelLayoutItem::BaseMaxSize() 712 { 713 return BaseMinSize(); 714 } 715 716 717 BSize 718 BAbstractSpinner::LabelLayoutItem::BasePreferredSize() 719 { 720 return BaseMinSize(); 721 } 722 723 724 BAlignment 725 BAbstractSpinner::LabelLayoutItem::BaseAlignment() 726 { 727 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 728 } 729 730 731 BRect 732 BAbstractSpinner::LabelLayoutItem::FrameInParent() const 733 { 734 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top); 735 } 736 737 738 status_t 739 BAbstractSpinner::LabelLayoutItem::Archive(BMessage* into, bool deep) const 740 { 741 BArchiver archiver(into); 742 status_t result = BAbstractLayoutItem::Archive(into, deep); 743 744 if (result == B_OK) 745 result = into->AddRect(kFrameField, fFrame); 746 747 return archiver.Finish(result); 748 } 749 750 751 BArchivable* 752 BAbstractSpinner::LabelLayoutItem::Instantiate(BMessage* from) 753 { 754 if (validate_instantiation(from, "BAbstractSpinner::LabelLayoutItem")) 755 return new LabelLayoutItem(from); 756 757 return NULL; 758 } 759 760 761 // #pragma mark - BAbstractSpinner::TextViewLayoutItem 762 763 764 BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BAbstractSpinner* parent) 765 : 766 fParent(parent), 767 fFrame() 768 { 769 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 770 } 771 772 773 BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BMessage* from) 774 : 775 BAbstractLayoutItem(from), 776 fParent(NULL), 777 fFrame() 778 { 779 from->FindRect(kFrameField, &fFrame); 780 } 781 782 783 bool 784 BAbstractSpinner::TextViewLayoutItem::IsVisible() 785 { 786 return !fParent->IsHidden(fParent); 787 } 788 789 790 void 791 BAbstractSpinner::TextViewLayoutItem::SetVisible(bool visible) 792 { 793 // not allowed 794 } 795 796 797 BRect 798 BAbstractSpinner::TextViewLayoutItem::Frame() 799 { 800 return fFrame; 801 } 802 803 804 void 805 BAbstractSpinner::TextViewLayoutItem::SetFrame(BRect frame) 806 { 807 fFrame = frame; 808 fParent->_UpdateFrame(); 809 } 810 811 812 void 813 BAbstractSpinner::TextViewLayoutItem::SetParent(BAbstractSpinner* parent) 814 { 815 fParent = parent; 816 } 817 818 819 BView* 820 BAbstractSpinner::TextViewLayoutItem::View() 821 { 822 return fParent; 823 } 824 825 826 BSize 827 BAbstractSpinner::TextViewLayoutItem::BaseMinSize() 828 { 829 fParent->_ValidateLayoutData(); 830 831 BSize size(fParent->fLayoutData->text_view_width, 832 fParent->fLayoutData->text_view_height); 833 834 return size; 835 } 836 837 838 BSize 839 BAbstractSpinner::TextViewLayoutItem::BaseMaxSize() 840 { 841 return BaseMinSize(); 842 } 843 844 845 BSize 846 BAbstractSpinner::TextViewLayoutItem::BasePreferredSize() 847 { 848 return BaseMinSize(); 849 } 850 851 852 BAlignment 853 BAbstractSpinner::TextViewLayoutItem::BaseAlignment() 854 { 855 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 856 } 857 858 859 BRect 860 BAbstractSpinner::TextViewLayoutItem::FrameInParent() const 861 { 862 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top); 863 } 864 865 866 status_t 867 BAbstractSpinner::TextViewLayoutItem::Archive(BMessage* into, bool deep) const 868 { 869 BArchiver archiver(into); 870 status_t result = BAbstractLayoutItem::Archive(into, deep); 871 872 if (result == B_OK) 873 result = into->AddRect(kFrameField, fFrame); 874 875 return archiver.Finish(result); 876 } 877 878 879 BArchivable* 880 BAbstractSpinner::TextViewLayoutItem::Instantiate(BMessage* from) 881 { 882 if (validate_instantiation(from, "BAbstractSpinner::TextViewLayoutItem")) 883 return new LabelLayoutItem(from); 884 885 return NULL; 886 } 887 888 889 // #pragma mark - BAbstractSpinner 890 891 892 BAbstractSpinner::BAbstractSpinner(BRect frame, const char* name, const char* label, 893 BMessage* message, uint32 resizingMode, uint32 flags) 894 : 895 BControl(frame, name, label, message, resizingMode, 896 flags | B_WILL_DRAW | B_FRAME_EVENTS) 897 { 898 _InitObject(); 899 } 900 901 902 BAbstractSpinner::BAbstractSpinner(const char* name, const char* label, BMessage* message, 903 uint32 flags) 904 : 905 BControl(name, label, message, flags | B_WILL_DRAW | B_FRAME_EVENTS) 906 { 907 _InitObject(); 908 } 909 910 911 BAbstractSpinner::BAbstractSpinner(BMessage* data) 912 : 913 BControl(data), 914 fButtonStyle(SPINNER_BUTTON_PLUS_MINUS) 915 { 916 _InitObject(); 917 918 if (data->FindInt32("_align") != B_OK) 919 fAlignment = B_ALIGN_LEFT; 920 921 if (data->FindInt32("_button_style") != B_OK) 922 fButtonStyle = SPINNER_BUTTON_PLUS_MINUS; 923 924 if (data->FindInt32("_divider") != B_OK) 925 fDivider = 0.0f; 926 } 927 928 929 BAbstractSpinner::~BAbstractSpinner() 930 { 931 delete fLayoutData; 932 fLayoutData = NULL; 933 } 934 935 936 BArchivable* 937 BAbstractSpinner::Instantiate(BMessage* data) 938 { 939 // cannot instantiate an abstract spinner 940 return NULL; 941 } 942 943 944 status_t 945 BAbstractSpinner::Archive(BMessage* data, bool deep) const 946 { 947 status_t status = BControl::Archive(data, deep); 948 data->AddString("class", "Spinner"); 949 950 if (status == B_OK) 951 status = data->AddInt32("_align", fAlignment); 952 953 if (status == B_OK) 954 data->AddInt32("_button_style", fButtonStyle); 955 956 if (status == B_OK) 957 status = data->AddFloat("_divider", fDivider); 958 959 return status; 960 } 961 962 963 status_t 964 BAbstractSpinner::GetSupportedSuites(BMessage* message) 965 { 966 message->AddString("suites", "suite/vnd.Haiku-spinner"); 967 968 BPropertyInfo prop_info(sProperties); 969 message->AddFlat("messages", &prop_info); 970 971 return BControl::GetSupportedSuites(message); 972 } 973 974 975 BHandler* 976 BAbstractSpinner::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 977 int32 form, const char* property) 978 { 979 return BControl::ResolveSpecifier(message, index, specifier, form, 980 property); 981 } 982 983 984 void 985 BAbstractSpinner::AttachedToWindow() 986 { 987 if (!Messenger().IsValid()) 988 SetTarget(Window()); 989 990 BControl::SetValue(Value()); 991 // sets the text and enables or disables the arrows 992 993 _UpdateTextViewColors(IsEnabled()); 994 fTextView->MakeEditable(IsEnabled()); 995 996 BControl::AttachedToWindow(); 997 } 998 999 1000 void 1001 BAbstractSpinner::Draw(BRect updateRect) 1002 { 1003 _DrawLabel(updateRect); 1004 _DrawTextView(updateRect); 1005 fIncrement->Invalidate(); 1006 fDecrement->Invalidate(); 1007 } 1008 1009 1010 void 1011 BAbstractSpinner::FrameResized(float width, float height) 1012 { 1013 BControl::FrameResized(width, height); 1014 1015 // TODO: this causes flickering still... 1016 1017 // changes in width 1018 1019 BRect bounds = Bounds(); 1020 1021 if (bounds.Width() > fLayoutData->previous_width) { 1022 // invalidate the region between the old and the new right border 1023 BRect rect = bounds; 1024 rect.left += fLayoutData->previous_width - kFrameMargin; 1025 rect.right--; 1026 Invalidate(rect); 1027 } else if (bounds.Width() < fLayoutData->previous_width) { 1028 // invalidate the region of the new right border 1029 BRect rect = bounds; 1030 rect.left = rect.right - kFrameMargin; 1031 Invalidate(rect); 1032 } 1033 1034 // changes in height 1035 1036 if (bounds.Height() > fLayoutData->previous_height) { 1037 // invalidate the region between the old and the new bottom border 1038 BRect rect = bounds; 1039 rect.top += fLayoutData->previous_height - kFrameMargin; 1040 rect.bottom--; 1041 Invalidate(rect); 1042 // invalidate label area 1043 rect = bounds; 1044 rect.right = fDivider; 1045 Invalidate(rect); 1046 } else if (bounds.Height() < fLayoutData->previous_height) { 1047 // invalidate the region of the new bottom border 1048 BRect rect = bounds; 1049 rect.top = rect.bottom - kFrameMargin; 1050 Invalidate(rect); 1051 // invalidate label area 1052 rect = bounds; 1053 rect.right = fDivider; 1054 Invalidate(rect); 1055 } 1056 1057 fLayoutData->previous_width = bounds.Width(); 1058 fLayoutData->previous_height = bounds.Height(); 1059 } 1060 1061 1062 void 1063 BAbstractSpinner::ValueChanged() 1064 { 1065 // hook method - does nothing 1066 } 1067 1068 1069 void 1070 BAbstractSpinner::MessageReceived(BMessage* message) 1071 { 1072 if (!IsEnabled() && message->what == B_COLORS_UPDATED) 1073 _UpdateTextViewColors(false); 1074 1075 BControl::MessageReceived(message); 1076 } 1077 1078 1079 void 1080 BAbstractSpinner::MakeFocus(bool focus) 1081 { 1082 fTextView->MakeFocus(focus); 1083 } 1084 1085 1086 void 1087 BAbstractSpinner::ResizeToPreferred() 1088 { 1089 BControl::ResizeToPreferred(); 1090 1091 const char* label = Label(); 1092 if (label != NULL) { 1093 fDivider = ceilf(StringWidth(label)) 1094 + be_control_look->DefaultLabelSpacing(); 1095 } else 1096 fDivider = 0.0f; 1097 1098 _LayoutTextView(); 1099 } 1100 1101 1102 void 1103 BAbstractSpinner::SetFlags(uint32 flags) 1104 { 1105 // If the textview is navigable, set it to not navigable if needed, 1106 // else if it is not navigable, set it to navigable if needed 1107 if (fTextView->Flags() & B_NAVIGABLE) { 1108 if (!(flags & B_NAVIGABLE)) 1109 fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE); 1110 } else { 1111 if (flags & B_NAVIGABLE) 1112 fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE); 1113 } 1114 1115 // Don't make this one navigable 1116 flags &= ~B_NAVIGABLE; 1117 1118 BControl::SetFlags(flags); 1119 } 1120 1121 1122 void 1123 BAbstractSpinner::WindowActivated(bool active) 1124 { 1125 _DrawTextView(fTextView->Frame()); 1126 } 1127 1128 1129 void 1130 BAbstractSpinner::SetAlignment(alignment align) 1131 { 1132 fAlignment = align; 1133 } 1134 1135 1136 void 1137 BAbstractSpinner::SetButtonStyle(spinner_button_style buttonStyle) 1138 { 1139 fButtonStyle = buttonStyle; 1140 } 1141 1142 1143 void 1144 BAbstractSpinner::SetDivider(float position) 1145 { 1146 position = roundf(position); 1147 1148 float delta = fDivider - position; 1149 if (delta == 0.0f) 1150 return; 1151 1152 fDivider = position; 1153 1154 if ((Flags() & B_SUPPORTS_LAYOUT) != 0) { 1155 // We should never get here, since layout support means, we also 1156 // layout the divider, and don't use this method at all. 1157 Relayout(); 1158 } else { 1159 _LayoutTextView(); 1160 Invalidate(); 1161 } 1162 } 1163 1164 1165 void 1166 BAbstractSpinner::SetEnabled(bool enable) 1167 { 1168 if (IsEnabled() == enable) 1169 return; 1170 1171 BControl::SetEnabled(enable); 1172 1173 fTextView->MakeEditable(enable); 1174 if (enable) 1175 fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE); 1176 else 1177 fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE); 1178 1179 _UpdateTextViewColors(enable); 1180 fTextView->Invalidate(); 1181 1182 _LayoutTextView(); 1183 Invalidate(); 1184 if (Window() != NULL) 1185 Window()->UpdateIfNeeded(); 1186 } 1187 1188 1189 void 1190 BAbstractSpinner::SetLabel(const char* label) 1191 { 1192 BControl::SetLabel(label); 1193 1194 if (Window() != NULL) 1195 Window()->UpdateIfNeeded(); 1196 } 1197 1198 1199 bool 1200 BAbstractSpinner::IsDecrementEnabled() const 1201 { 1202 return fDecrement->IsEnabled(); 1203 } 1204 1205 1206 void 1207 BAbstractSpinner::SetDecrementEnabled(bool enable) 1208 { 1209 if (IsDecrementEnabled() == enable) 1210 return; 1211 1212 fDecrement->SetEnabled(enable); 1213 fDecrement->Invalidate(); 1214 } 1215 1216 1217 bool 1218 BAbstractSpinner::IsIncrementEnabled() const 1219 { 1220 return fIncrement->IsEnabled(); 1221 } 1222 1223 1224 void 1225 BAbstractSpinner::SetIncrementEnabled(bool enable) 1226 { 1227 if (IsIncrementEnabled() == enable) 1228 return; 1229 1230 fIncrement->SetEnabled(enable); 1231 fIncrement->Invalidate(); 1232 } 1233 1234 1235 BSize 1236 BAbstractSpinner::MinSize() 1237 { 1238 _ValidateLayoutData(); 1239 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min); 1240 } 1241 1242 1243 BSize 1244 BAbstractSpinner::MaxSize() 1245 { 1246 _ValidateLayoutData(); 1247 1248 BSize max = fLayoutData->min; 1249 max.width = B_SIZE_UNLIMITED; 1250 1251 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max); 1252 } 1253 1254 1255 BSize 1256 BAbstractSpinner::PreferredSize() 1257 { 1258 _ValidateLayoutData(); 1259 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), 1260 fLayoutData->min); 1261 } 1262 1263 1264 BAlignment 1265 BAbstractSpinner::LayoutAlignment() 1266 { 1267 _ValidateLayoutData(); 1268 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), 1269 BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER)); 1270 } 1271 1272 1273 BLayoutItem* 1274 BAbstractSpinner::CreateLabelLayoutItem() 1275 { 1276 if (fLayoutData->label_layout_item == NULL) 1277 fLayoutData->label_layout_item = new LabelLayoutItem(this); 1278 1279 return fLayoutData->label_layout_item; 1280 } 1281 1282 1283 BLayoutItem* 1284 BAbstractSpinner::CreateTextViewLayoutItem() 1285 { 1286 if (fLayoutData->text_view_layout_item == NULL) 1287 fLayoutData->text_view_layout_item = new TextViewLayoutItem(this); 1288 1289 return fLayoutData->text_view_layout_item; 1290 } 1291 1292 1293 BTextView* 1294 BAbstractSpinner::TextView() const 1295 { 1296 return dynamic_cast<BTextView*>(fTextView); 1297 } 1298 1299 1300 // #pragma mark - BAbstractSpinner protected methods 1301 1302 1303 status_t 1304 BAbstractSpinner::AllArchived(BMessage* into) const 1305 { 1306 status_t result; 1307 if ((result = BControl::AllArchived(into)) != B_OK) 1308 return result; 1309 1310 BArchiver archiver(into); 1311 1312 BArchivable* textViewItem = fLayoutData->text_view_layout_item; 1313 if (archiver.IsArchived(textViewItem)) 1314 result = archiver.AddArchivable(kTextViewItemField, textViewItem); 1315 1316 if (result != B_OK) 1317 return result; 1318 1319 BArchivable* labelBarItem = fLayoutData->label_layout_item; 1320 if (archiver.IsArchived(labelBarItem)) 1321 result = archiver.AddArchivable(kLabelItemField, labelBarItem); 1322 1323 return result; 1324 } 1325 1326 1327 status_t 1328 BAbstractSpinner::AllUnarchived(const BMessage* from) 1329 { 1330 BUnarchiver unarchiver(from); 1331 1332 status_t result = B_OK; 1333 if ((result = BControl::AllUnarchived(from)) != B_OK) 1334 return result; 1335 1336 if (unarchiver.IsInstantiated(kTextViewItemField)) { 1337 TextViewLayoutItem*& textViewItem 1338 = fLayoutData->text_view_layout_item; 1339 result = unarchiver.FindObject(kTextViewItemField, 1340 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, textViewItem); 1341 1342 if (result == B_OK) 1343 textViewItem->SetParent(this); 1344 else 1345 return result; 1346 } 1347 1348 if (unarchiver.IsInstantiated(kLabelItemField)) { 1349 LabelLayoutItem*& labelItem = fLayoutData->label_layout_item; 1350 result = unarchiver.FindObject(kLabelItemField, 1351 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, labelItem); 1352 1353 if (result == B_OK) 1354 labelItem->SetParent(this); 1355 } 1356 1357 return result; 1358 } 1359 1360 1361 void 1362 BAbstractSpinner::DoLayout() 1363 { 1364 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) 1365 return; 1366 1367 if (GetLayout()) { 1368 BControl::DoLayout(); 1369 return; 1370 } 1371 1372 _ValidateLayoutData(); 1373 1374 BSize size(Bounds().Size()); 1375 if (size.width < fLayoutData->min.width) 1376 size.width = fLayoutData->min.width; 1377 1378 if (size.height < fLayoutData->min.height) 1379 size.height = fLayoutData->min.height; 1380 1381 float divider = 0; 1382 if (fLayoutData->label_layout_item != NULL 1383 && fLayoutData->text_view_layout_item != NULL 1384 && fLayoutData->label_layout_item->Frame().IsValid() 1385 && fLayoutData->text_view_layout_item->Frame().IsValid()) { 1386 divider = fLayoutData->text_view_layout_item->Frame().left 1387 - fLayoutData->label_layout_item->Frame().left; 1388 } else if (fLayoutData->label_width > 0) { 1389 divider = fLayoutData->label_width 1390 + be_control_look->DefaultLabelSpacing(); 1391 } 1392 fDivider = divider; 1393 1394 BRect dirty(fTextView->Frame()); 1395 _LayoutTextView(); 1396 1397 // invalidate dirty region 1398 dirty = dirty | fTextView->Frame(); 1399 dirty = dirty | fIncrement->Frame(); 1400 dirty = dirty | fDecrement->Frame(); 1401 1402 Invalidate(dirty); 1403 } 1404 1405 1406 void 1407 BAbstractSpinner::LayoutInvalidated(bool descendants) 1408 { 1409 if (fLayoutData != NULL) 1410 fLayoutData->valid = false; 1411 } 1412 1413 1414 // #pragma mark - BAbstractSpinner private methods 1415 1416 1417 void 1418 BAbstractSpinner::_DrawLabel(BRect updateRect) 1419 { 1420 BRect rect(Bounds()); 1421 rect.right = fDivider; 1422 if (!rect.IsValid() || !rect.Intersects(updateRect)) 1423 return; 1424 1425 _ValidateLayoutData(); 1426 1427 const char* label = Label(); 1428 if (label == NULL) 1429 return; 1430 1431 // horizontal position 1432 float x; 1433 switch (fAlignment) { 1434 case B_ALIGN_RIGHT: 1435 x = fDivider - fLayoutData->label_width - 3.0f; 1436 break; 1437 1438 case B_ALIGN_CENTER: 1439 x = fDivider - roundf(fLayoutData->label_width / 2.0f); 1440 break; 1441 1442 default: 1443 x = 0.0f; 1444 break; 1445 } 1446 1447 // vertical position 1448 font_height& fontHeight = fLayoutData->font_info; 1449 float y = rect.top 1450 + roundf((rect.Height() + 1.0f - fontHeight.ascent 1451 - fontHeight.descent) / 2.0f) 1452 + fontHeight.ascent; 1453 1454 uint32 flags = be_control_look->Flags(this); 1455 1456 // erase the is control flag before drawing the label so that the label 1457 // will get drawn using B_PANEL_TEXT_COLOR. 1458 flags &= ~BControlLook::B_IS_CONTROL; 1459 1460 be_control_look->DrawLabel(this, label, LowColor(), flags, BPoint(x, y)); 1461 } 1462 1463 1464 void 1465 BAbstractSpinner::_DrawTextView(BRect updateRect) 1466 { 1467 BRect rect = fTextView->Frame(); 1468 rect.InsetBy(-kFrameMargin, -kFrameMargin); 1469 if (!rect.IsValid() || !rect.Intersects(updateRect)) 1470 return; 1471 1472 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1473 uint32 flags = 0; 1474 if (!IsEnabled()) 1475 flags |= BControlLook::B_DISABLED; 1476 1477 if (fTextView->IsFocus() && Window()->IsActive()) 1478 flags |= BControlLook::B_FOCUSED; 1479 1480 be_control_look->DrawTextControlBorder(this, rect, updateRect, base, 1481 flags); 1482 } 1483 1484 1485 void 1486 BAbstractSpinner::_InitObject() 1487 { 1488 fAlignment = B_ALIGN_LEFT; 1489 fButtonStyle = SPINNER_BUTTON_PLUS_MINUS; 1490 1491 if (Label() != NULL) { 1492 fDivider = StringWidth(Label()) 1493 + be_control_look->DefaultLabelSpacing(); 1494 } else 1495 fDivider = 0.0f; 1496 1497 BControl::SetEnabled(true); 1498 BControl::SetValue(0); 1499 1500 BRect rect(Bounds()); 1501 fLayoutData = new LayoutData(rect.Width(), rect.Height()); 1502 1503 rect.left = fDivider; 1504 rect.InsetBy(kFrameMargin, kFrameMargin); 1505 rect.right -= rect.Height() * 2 + kFrameMargin * 2 + 1.0f; 1506 BRect textRect(rect.OffsetToCopy(B_ORIGIN)); 1507 1508 fTextView = new SpinnerTextView(rect, textRect); 1509 AddChild(fTextView); 1510 1511 rect.InsetBy(0.0f, -kFrameMargin); 1512 1513 rect.left = rect.right + kFrameMargin * 2; 1514 rect.right = rect.left + rect.Height() - kFrameMargin * 2; 1515 1516 fDecrement = new SpinnerButton(rect, "decrement", SPINNER_DECREMENT); 1517 AddChild(fDecrement); 1518 1519 rect.left = rect.right + 1.0f; 1520 rect.right = rect.left + rect.Height() - kFrameMargin * 2; 1521 1522 fIncrement = new SpinnerButton(rect, "increment", SPINNER_INCREMENT); 1523 AddChild(fIncrement); 1524 1525 uint32 navigableFlags = Flags() & B_NAVIGABLE; 1526 if (navigableFlags != 0) 1527 BControl::SetFlags(Flags() & ~B_NAVIGABLE); 1528 } 1529 1530 1531 void 1532 BAbstractSpinner::_LayoutTextView() 1533 { 1534 BRect rect; 1535 if (fLayoutData->text_view_layout_item != NULL) { 1536 rect = fLayoutData->text_view_layout_item->FrameInParent(); 1537 } else { 1538 rect = Bounds(); 1539 rect.left = fDivider; 1540 } 1541 rect.InsetBy(kFrameMargin, kFrameMargin); 1542 rect.right -= rect.Height() * 2 + kFrameMargin * 2 + 1.0f; 1543 1544 fTextView->MoveTo(rect.left, rect.top); 1545 fTextView->ResizeTo(rect.Width(), rect.Height()); 1546 fTextView->SetTextRect(rect.OffsetToCopy(B_ORIGIN)); 1547 1548 rect.InsetBy(0.0f, -kFrameMargin); 1549 1550 rect.left = rect.right + kFrameMargin * 2; 1551 rect.right = rect.left + rect.Height() - kFrameMargin * 2; 1552 1553 fDecrement->ResizeTo(rect.Width(), rect.Height()); 1554 fDecrement->MoveTo(rect.LeftTop()); 1555 1556 rect.left = rect.right + 1.0f; 1557 rect.right = rect.left + rect.Height() - kFrameMargin * 2; 1558 1559 fIncrement->ResizeTo(rect.Width(), rect.Height()); 1560 fIncrement->MoveTo(rect.LeftTop()); 1561 } 1562 1563 1564 void 1565 BAbstractSpinner::_UpdateFrame() 1566 { 1567 if (fLayoutData->label_layout_item == NULL 1568 || fLayoutData->text_view_layout_item == NULL) { 1569 return; 1570 } 1571 1572 BRect labelFrame = fLayoutData->label_layout_item->Frame(); 1573 BRect textViewFrame = fLayoutData->text_view_layout_item->Frame(); 1574 1575 if (!labelFrame.IsValid() || !textViewFrame.IsValid()) 1576 return; 1577 1578 // update divider 1579 fDivider = textViewFrame.left - labelFrame.left; 1580 1581 BRect frame = textViewFrame | labelFrame; 1582 MoveTo(frame.left, frame.top); 1583 BSize oldSize = Bounds().Size(); 1584 ResizeTo(frame.Width(), frame.Height()); 1585 BSize newSize = Bounds().Size(); 1586 1587 // If the size changes, ResizeTo() will trigger a relayout, otherwise 1588 // we need to do that explicitly. 1589 if (newSize != oldSize) 1590 Relayout(); 1591 } 1592 1593 1594 void 1595 BAbstractSpinner::_UpdateTextViewColors(bool enable) 1596 { 1597 // Mimick BTextControl's appearance. 1598 rgb_color textColor = ui_color(B_DOCUMENT_TEXT_COLOR); 1599 1600 if (enable) { 1601 fTextView->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR); 1602 fTextView->SetLowUIColor(ViewUIColor()); 1603 } else { 1604 rgb_color color = ui_color(B_DOCUMENT_BACKGROUND_COLOR); 1605 color = disable_color(ViewColor(), color); 1606 textColor = disable_color(textColor, ViewColor()); 1607 1608 fTextView->SetViewColor(color); 1609 fTextView->SetLowColor(color); 1610 } 1611 1612 BFont font; 1613 fTextView->GetFontAndColor(0, &font); 1614 fTextView->SetFontAndColor(&font, B_FONT_ALL, &textColor); 1615 } 1616 1617 1618 void 1619 BAbstractSpinner::_ValidateLayoutData() 1620 { 1621 if (fLayoutData->valid) 1622 return; 1623 1624 font_height& fontHeight = fLayoutData->font_info; 1625 GetFontHeight(&fontHeight); 1626 1627 if (Label() != NULL) { 1628 fLayoutData->label_width = StringWidth(Label()); 1629 fLayoutData->label_height = ceilf(fontHeight.ascent 1630 + fontHeight.descent + fontHeight.leading); 1631 } else { 1632 fLayoutData->label_width = 0; 1633 fLayoutData->label_height = 0; 1634 } 1635 1636 float divider = 0; 1637 if (fLayoutData->label_width > 0) { 1638 divider = ceilf(fLayoutData->label_width 1639 + be_control_look->DefaultLabelSpacing()); 1640 } 1641 1642 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) 1643 divider = std::max(divider, fDivider); 1644 1645 float minTextWidth = fTextView->StringWidth("99999"); 1646 1647 float textViewHeight = fTextView->LineHeight(0) + kFrameMargin * 2; 1648 float textViewWidth = minTextWidth + textViewHeight * 2; 1649 1650 fLayoutData->text_view_width = textViewWidth; 1651 fLayoutData->text_view_height = textViewHeight; 1652 1653 BSize min(textViewWidth, textViewHeight); 1654 if (divider > 0.0f) 1655 min.width += divider; 1656 1657 if (fLayoutData->label_height > min.height) 1658 min.height = fLayoutData->label_height; 1659 1660 fLayoutData->min = min; 1661 fLayoutData->valid = true; 1662 1663 ResetLayoutInvalidation(); 1664 } 1665 1666 1667 // FBC padding 1668 1669 void BAbstractSpinner::_ReservedAbstractSpinner20() {} 1670 void BAbstractSpinner::_ReservedAbstractSpinner19() {} 1671 void BAbstractSpinner::_ReservedAbstractSpinner18() {} 1672 void BAbstractSpinner::_ReservedAbstractSpinner17() {} 1673 void BAbstractSpinner::_ReservedAbstractSpinner16() {} 1674 void BAbstractSpinner::_ReservedAbstractSpinner15() {} 1675 void BAbstractSpinner::_ReservedAbstractSpinner14() {} 1676 void BAbstractSpinner::_ReservedAbstractSpinner13() {} 1677 void BAbstractSpinner::_ReservedAbstractSpinner12() {} 1678 void BAbstractSpinner::_ReservedAbstractSpinner11() {} 1679 void BAbstractSpinner::_ReservedAbstractSpinner10() {} 1680 void BAbstractSpinner::_ReservedAbstractSpinner9() {} 1681 void BAbstractSpinner::_ReservedAbstractSpinner8() {} 1682 void BAbstractSpinner::_ReservedAbstractSpinner7() {} 1683 void BAbstractSpinner::_ReservedAbstractSpinner6() {} 1684 void BAbstractSpinner::_ReservedAbstractSpinner5() {} 1685 void BAbstractSpinner::_ReservedAbstractSpinner4() {} 1686 void BAbstractSpinner::_ReservedAbstractSpinner3() {} 1687 void BAbstractSpinner::_ReservedAbstractSpinner2() {} 1688 void BAbstractSpinner::_ReservedAbstractSpinner1() {} 1689