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