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 // draw the button 383 be_control_look->DrawButtonFrame(this, rect, updateRect, 384 tint_color(bgColor, frameTint), bgColor, 0, borders); 385 be_control_look->DrawButtonBackground(this, rect, updateRect, 386 tint_color(bgColor, bgTint), 0, borders); 387 388 switch (fParent->ButtonStyle()) { 389 case SPINNER_BUTTON_HORIZONTAL_ARROWS: 390 { 391 int32 arrowDirection = fSpinnerDirection == SPINNER_INCREMENT 392 ? be_control_look->B_RIGHT_ARROW 393 : be_control_look->B_LEFT_ARROW; 394 395 rect.InsetBy(0.0f, 1.0f); 396 be_control_look->DrawArrowShape(this, rect, updateRect, bgColor, 397 arrowDirection, 0, fgTint); 398 break; 399 } 400 401 case SPINNER_BUTTON_VERTICAL_ARROWS: 402 { 403 int32 arrowDirection = fSpinnerDirection == SPINNER_INCREMENT 404 ? be_control_look->B_UP_ARROW 405 : be_control_look->B_DOWN_ARROW; 406 407 rect.InsetBy(0.0f, 1.0f); 408 be_control_look->DrawArrowShape(this, rect, updateRect, bgColor, 409 arrowDirection, 0, fgTint); 410 break; 411 } 412 413 default: 414 case SPINNER_BUTTON_PLUS_MINUS: 415 { 416 BFont font; 417 fParent->GetFont(&font); 418 float inset = floorf(font.Size() / 4); 419 rect.InsetBy(inset, inset); 420 421 if (rect.IntegerWidth() % 2 != 0) 422 rect.right -= 1; 423 424 if (rect.IntegerHeight() % 2 != 0) 425 rect.bottom -= 1; 426 427 SetHighColor(tint_color(bgColor, fgTint)); 428 429 // draw the +/- 430 float halfHeight = floorf(rect.Height() / 2); 431 StrokeLine(BPoint(rect.left, rect.top + halfHeight), 432 BPoint(rect.right, rect.top + halfHeight)); 433 if (fSpinnerDirection == SPINNER_INCREMENT) { 434 float halfWidth = floorf(rect.Width() / 2); 435 StrokeLine(BPoint(rect.left + halfWidth, rect.top), 436 BPoint(rect.left + halfWidth, rect.bottom)); 437 } 438 } 439 } 440 } 441 442 443 void 444 SpinnerButton::MouseDown(BPoint where) 445 { 446 if (fIsEnabled) { 447 fIsMouseDown = true; 448 Invalidate(); 449 fRepeatDelay = 100000; 450 MouseDownThread<SpinnerButton>::TrackMouse(this, 451 &SpinnerButton::_DoneTracking, &SpinnerButton::_Track); 452 } 453 454 BView::MouseDown(where); 455 } 456 457 458 void 459 SpinnerButton::MouseMoved(BPoint where, uint32 transit, 460 const BMessage* message) 461 { 462 switch (transit) { 463 case B_ENTERED_VIEW: 464 case B_INSIDE_VIEW: 465 { 466 BPoint where; 467 uint32 buttons; 468 GetMouse(&where, &buttons); 469 fIsMouseOver = Bounds().Contains(where) && buttons == 0; 470 if (!fIsMouseDown) 471 Invalidate(); 472 473 break; 474 } 475 476 case B_EXITED_VIEW: 477 case B_OUTSIDE_VIEW: 478 fIsMouseOver = false; 479 MouseUp(Bounds().LeftTop()); 480 break; 481 } 482 483 BView::MouseMoved(where, transit, message); 484 } 485 486 487 void 488 SpinnerButton::MouseUp(BPoint where) 489 { 490 fIsMouseDown = false; 491 Invalidate(); 492 493 BView::MouseUp(where); 494 } 495 496 497 // #pragma mark - SpinnerButton private methods 498 499 500 void 501 SpinnerButton::_DoneTracking(BPoint where) 502 { 503 if (fIsMouseDown || !Bounds().Contains(where)) 504 fIsMouseDown = false; 505 } 506 507 508 void 509 SpinnerButton::_Track(BPoint where, uint32) 510 { 511 if (fParent == NULL || !Bounds().Contains(where)) { 512 fIsMouseDown = false; 513 return; 514 } 515 fIsMouseDown = true; 516 517 fSpinnerDirection == SPINNER_INCREMENT 518 ? fParent->Increment() 519 : fParent->Decrement(); 520 521 snooze(fRepeatDelay); 522 fRepeatDelay = 10000; 523 } 524 525 526 // #pragma mark - SpinnerTextView 527 528 529 SpinnerTextView::SpinnerTextView(BRect rect, BRect textRect) 530 : 531 BTextView(rect, "textview", textRect, B_FOLLOW_ALL, 532 B_WILL_DRAW | B_NAVIGABLE), 533 fParent(NULL) 534 { 535 } 536 537 538 SpinnerTextView::~SpinnerTextView() 539 { 540 } 541 542 543 void 544 SpinnerTextView::AttachedToWindow() 545 { 546 fParent = static_cast<BAbstractSpinner*>(Parent()); 547 548 BTextView::AttachedToWindow(); 549 } 550 551 552 void 553 SpinnerTextView::DetachedFromWindow() 554 { 555 fParent = NULL; 556 557 BTextView::DetachedFromWindow(); 558 } 559 560 561 void 562 SpinnerTextView::KeyDown(const char* bytes, int32 numBytes) 563 { 564 if (fParent == NULL) { 565 BTextView::KeyDown(bytes, numBytes); 566 return; 567 } 568 569 switch (bytes[0]) { 570 case B_ENTER: 571 case B_SPACE: 572 fParent->SetValueFromText(); 573 break; 574 575 case B_TAB: 576 fParent->KeyDown(bytes, numBytes); 577 break; 578 579 case B_LEFT_ARROW: 580 if (fParent->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS 581 && (modifiers() & B_CONTROL_KEY) != 0) { 582 // need to hold down control, otherwise can't move cursor 583 fParent->Decrement(); 584 } else 585 BTextView::KeyDown(bytes, numBytes); 586 break; 587 588 case B_UP_ARROW: 589 if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS) 590 fParent->Increment(); 591 else 592 BTextView::KeyDown(bytes, numBytes); 593 break; 594 595 case B_RIGHT_ARROW: 596 if (fParent->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS 597 && (modifiers() & B_CONTROL_KEY) != 0) { 598 // need to hold down control, otherwise can't move cursor 599 fParent->Increment(); 600 } else 601 BTextView::KeyDown(bytes, numBytes); 602 break; 603 604 case B_DOWN_ARROW: 605 if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS) 606 fParent->Decrement(); 607 else 608 BTextView::KeyDown(bytes, numBytes); 609 break; 610 611 default: 612 BTextView::KeyDown(bytes, numBytes); 613 break; 614 } 615 } 616 617 618 void 619 SpinnerTextView::MakeFocus(bool focus) 620 { 621 BTextView::MakeFocus(focus); 622 623 if (fParent == NULL) 624 return; 625 626 if (focus) 627 SelectAll(); 628 else 629 fParent->SetValueFromText(); 630 631 fParent->_DrawTextView(fParent->Bounds()); 632 } 633 634 635 // #pragma mark - BAbstractSpinner::LabelLayoutItem 636 637 638 BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BAbstractSpinner* parent) 639 : 640 fParent(parent), 641 fFrame() 642 { 643 } 644 645 646 BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BMessage* from) 647 : 648 BAbstractLayoutItem(from), 649 fParent(NULL), 650 fFrame() 651 { 652 from->FindRect(kFrameField, &fFrame); 653 } 654 655 656 bool 657 BAbstractSpinner::LabelLayoutItem::IsVisible() 658 { 659 return !fParent->IsHidden(fParent); 660 } 661 662 663 void 664 BAbstractSpinner::LabelLayoutItem::SetVisible(bool visible) 665 { 666 } 667 668 669 BRect 670 BAbstractSpinner::LabelLayoutItem::Frame() 671 { 672 return fFrame; 673 } 674 675 676 void 677 BAbstractSpinner::LabelLayoutItem::SetFrame(BRect frame) 678 { 679 fFrame = frame; 680 fParent->_UpdateFrame(); 681 } 682 683 684 void 685 BAbstractSpinner::LabelLayoutItem::SetParent(BAbstractSpinner* parent) 686 { 687 fParent = parent; 688 } 689 690 691 BView* 692 BAbstractSpinner::LabelLayoutItem::View() 693 { 694 return fParent; 695 } 696 697 698 BSize 699 BAbstractSpinner::LabelLayoutItem::BaseMinSize() 700 { 701 fParent->_ValidateLayoutData(); 702 703 if (fParent->Label() == NULL) 704 return BSize(-1.0f, -1.0f); 705 706 return BSize(fParent->fLayoutData->label_width 707 + be_control_look->DefaultLabelSpacing(), 708 fParent->fLayoutData->label_height); 709 } 710 711 712 BSize 713 BAbstractSpinner::LabelLayoutItem::BaseMaxSize() 714 { 715 return BaseMinSize(); 716 } 717 718 719 BSize 720 BAbstractSpinner::LabelLayoutItem::BasePreferredSize() 721 { 722 return BaseMinSize(); 723 } 724 725 726 BAlignment 727 BAbstractSpinner::LabelLayoutItem::BaseAlignment() 728 { 729 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 730 } 731 732 733 BRect 734 BAbstractSpinner::LabelLayoutItem::FrameInParent() const 735 { 736 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top); 737 } 738 739 740 status_t 741 BAbstractSpinner::LabelLayoutItem::Archive(BMessage* into, bool deep) const 742 { 743 BArchiver archiver(into); 744 status_t result = BAbstractLayoutItem::Archive(into, deep); 745 746 if (result == B_OK) 747 result = into->AddRect(kFrameField, fFrame); 748 749 return archiver.Finish(result); 750 } 751 752 753 BArchivable* 754 BAbstractSpinner::LabelLayoutItem::Instantiate(BMessage* from) 755 { 756 if (validate_instantiation(from, "BAbstractSpinner::LabelLayoutItem")) 757 return new LabelLayoutItem(from); 758 759 return NULL; 760 } 761 762 763 // #pragma mark - BAbstractSpinner::TextViewLayoutItem 764 765 766 BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BAbstractSpinner* parent) 767 : 768 fParent(parent), 769 fFrame() 770 { 771 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 772 } 773 774 775 BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BMessage* from) 776 : 777 BAbstractLayoutItem(from), 778 fParent(NULL), 779 fFrame() 780 { 781 from->FindRect(kFrameField, &fFrame); 782 } 783 784 785 bool 786 BAbstractSpinner::TextViewLayoutItem::IsVisible() 787 { 788 return !fParent->IsHidden(fParent); 789 } 790 791 792 void 793 BAbstractSpinner::TextViewLayoutItem::SetVisible(bool visible) 794 { 795 // not allowed 796 } 797 798 799 BRect 800 BAbstractSpinner::TextViewLayoutItem::Frame() 801 { 802 return fFrame; 803 } 804 805 806 void 807 BAbstractSpinner::TextViewLayoutItem::SetFrame(BRect frame) 808 { 809 fFrame = frame; 810 fParent->_UpdateFrame(); 811 } 812 813 814 void 815 BAbstractSpinner::TextViewLayoutItem::SetParent(BAbstractSpinner* parent) 816 { 817 fParent = parent; 818 } 819 820 821 BView* 822 BAbstractSpinner::TextViewLayoutItem::View() 823 { 824 return fParent; 825 } 826 827 828 BSize 829 BAbstractSpinner::TextViewLayoutItem::BaseMinSize() 830 { 831 fParent->_ValidateLayoutData(); 832 833 BSize size(fParent->fLayoutData->text_view_width, 834 fParent->fLayoutData->text_view_height); 835 836 return size; 837 } 838 839 840 BSize 841 BAbstractSpinner::TextViewLayoutItem::BaseMaxSize() 842 { 843 return BaseMinSize(); 844 } 845 846 847 BSize 848 BAbstractSpinner::TextViewLayoutItem::BasePreferredSize() 849 { 850 return BaseMinSize(); 851 } 852 853 854 BAlignment 855 BAbstractSpinner::TextViewLayoutItem::BaseAlignment() 856 { 857 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 858 } 859 860 861 BRect 862 BAbstractSpinner::TextViewLayoutItem::FrameInParent() const 863 { 864 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top); 865 } 866 867 868 status_t 869 BAbstractSpinner::TextViewLayoutItem::Archive(BMessage* into, bool deep) const 870 { 871 BArchiver archiver(into); 872 status_t result = BAbstractLayoutItem::Archive(into, deep); 873 874 if (result == B_OK) 875 result = into->AddRect(kFrameField, fFrame); 876 877 return archiver.Finish(result); 878 } 879 880 881 BArchivable* 882 BAbstractSpinner::TextViewLayoutItem::Instantiate(BMessage* from) 883 { 884 if (validate_instantiation(from, "BAbstractSpinner::TextViewLayoutItem")) 885 return new LabelLayoutItem(from); 886 887 return NULL; 888 } 889 890 891 // #pragma mark - BAbstractSpinner 892 893 894 BAbstractSpinner::BAbstractSpinner(BRect frame, const char* name, const char* label, 895 BMessage* message, uint32 resizingMode, uint32 flags) 896 : 897 BControl(frame, name, label, message, resizingMode, 898 flags | B_WILL_DRAW | B_FRAME_EVENTS) 899 { 900 _InitObject(); 901 } 902 903 904 BAbstractSpinner::BAbstractSpinner(const char* name, const char* label, BMessage* message, 905 uint32 flags) 906 : 907 BControl(name, label, message, flags | B_WILL_DRAW | B_FRAME_EVENTS) 908 { 909 _InitObject(); 910 } 911 912 913 BAbstractSpinner::BAbstractSpinner(BMessage* data) 914 : 915 BControl(data), 916 fButtonStyle(SPINNER_BUTTON_PLUS_MINUS) 917 { 918 _InitObject(); 919 920 if (data->FindInt32("_align") != B_OK) 921 fAlignment = B_ALIGN_LEFT; 922 923 if (data->FindInt32("_button_style") != B_OK) 924 fButtonStyle = SPINNER_BUTTON_PLUS_MINUS; 925 926 if (data->FindInt32("_divider") != B_OK) 927 fDivider = 0.0f; 928 } 929 930 931 BAbstractSpinner::~BAbstractSpinner() 932 { 933 delete fLayoutData; 934 fLayoutData = NULL; 935 } 936 937 938 BArchivable* 939 BAbstractSpinner::Instantiate(BMessage* data) 940 { 941 // cannot instantiate an abstract spinner 942 return NULL; 943 } 944 945 946 status_t 947 BAbstractSpinner::Archive(BMessage* data, bool deep) const 948 { 949 status_t status = BControl::Archive(data, deep); 950 data->AddString("class", "Spinner"); 951 952 if (status == B_OK) 953 status = data->AddInt32("_align", fAlignment); 954 955 if (status == B_OK) 956 data->AddInt32("_button_style", fButtonStyle); 957 958 if (status == B_OK) 959 status = data->AddFloat("_divider", fDivider); 960 961 return status; 962 } 963 964 965 status_t 966 BAbstractSpinner::GetSupportedSuites(BMessage* message) 967 { 968 message->AddString("suites", "suite/vnd.Haiku-spinner"); 969 970 BPropertyInfo prop_info(sProperties); 971 message->AddFlat("messages", &prop_info); 972 973 return BView::GetSupportedSuites(message); 974 } 975 976 977 BHandler* 978 BAbstractSpinner::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 979 int32 form, const char* property) 980 { 981 return BView::ResolveSpecifier(message, index, specifier, form, 982 property); 983 } 984 985 986 void 987 BAbstractSpinner::AttachedToWindow() 988 { 989 if (!Messenger().IsValid()) 990 SetTarget(Window()); 991 992 BControl::SetValue(Value()); 993 // sets the text and enables or disables the arrows 994 995 _UpdateTextViewColors(IsEnabled()); 996 fTextView->MakeEditable(IsEnabled()); 997 998 BView::AttachedToWindow(); 999 } 1000 1001 1002 void 1003 BAbstractSpinner::Draw(BRect updateRect) 1004 { 1005 _DrawLabel(updateRect); 1006 _DrawTextView(updateRect); 1007 fIncrement->Invalidate(); 1008 fDecrement->Invalidate(); 1009 } 1010 1011 1012 void 1013 BAbstractSpinner::FrameResized(float width, float height) 1014 { 1015 BView::FrameResized(width, height); 1016 1017 // TODO: this causes flickering still... 1018 1019 // changes in width 1020 1021 BRect bounds = Bounds(); 1022 1023 if (bounds.Width() > fLayoutData->previous_width) { 1024 // invalidate the region between the old and the new right border 1025 BRect rect = bounds; 1026 rect.left += fLayoutData->previous_width - kFrameMargin; 1027 rect.right--; 1028 Invalidate(rect); 1029 } else if (bounds.Width() < fLayoutData->previous_width) { 1030 // invalidate the region of the new right border 1031 BRect rect = bounds; 1032 rect.left = rect.right - kFrameMargin; 1033 Invalidate(rect); 1034 } 1035 1036 // changes in height 1037 1038 if (bounds.Height() > fLayoutData->previous_height) { 1039 // invalidate the region between the old and the new bottom border 1040 BRect rect = bounds; 1041 rect.top += fLayoutData->previous_height - kFrameMargin; 1042 rect.bottom--; 1043 Invalidate(rect); 1044 // invalidate label area 1045 rect = bounds; 1046 rect.right = fDivider; 1047 Invalidate(rect); 1048 } else if (bounds.Height() < fLayoutData->previous_height) { 1049 // invalidate the region of the new bottom border 1050 BRect rect = bounds; 1051 rect.top = rect.bottom - kFrameMargin; 1052 Invalidate(rect); 1053 // invalidate label area 1054 rect = bounds; 1055 rect.right = fDivider; 1056 Invalidate(rect); 1057 } 1058 1059 fLayoutData->previous_width = bounds.Width(); 1060 fLayoutData->previous_height = bounds.Height(); 1061 } 1062 1063 1064 void 1065 BAbstractSpinner::ValueChanged() 1066 { 1067 // hook method - does nothing 1068 } 1069 1070 1071 void 1072 BAbstractSpinner::MessageReceived(BMessage* message) 1073 { 1074 if (!IsEnabled() && message->what == B_COLORS_UPDATED) 1075 _UpdateTextViewColors(false); 1076 1077 BControl::MessageReceived(message); 1078 } 1079 1080 1081 void 1082 BAbstractSpinner::MakeFocus(bool focus) 1083 { 1084 fTextView->MakeFocus(focus); 1085 } 1086 1087 1088 void 1089 BAbstractSpinner::ResizeToPreferred() 1090 { 1091 BView::ResizeToPreferred(); 1092 1093 const char* label = Label(); 1094 if (label != NULL) { 1095 fDivider = ceilf(StringWidth(label)) 1096 + be_control_look->DefaultLabelSpacing(); 1097 } else 1098 fDivider = 0.0f; 1099 1100 _LayoutTextView(); 1101 } 1102 1103 1104 void 1105 BAbstractSpinner::SetFlags(uint32 flags) 1106 { 1107 // If the textview is navigable, set it to not navigable if needed, 1108 // else if it is not navigable, set it to navigable if needed 1109 if (fTextView->Flags() & B_NAVIGABLE) { 1110 if (!(flags & B_NAVIGABLE)) 1111 fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE); 1112 } else { 1113 if (flags & B_NAVIGABLE) 1114 fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE); 1115 } 1116 1117 // Don't make this one navigable 1118 flags &= ~B_NAVIGABLE; 1119 1120 BView::SetFlags(flags); 1121 } 1122 1123 1124 void 1125 BAbstractSpinner::WindowActivated(bool active) 1126 { 1127 _DrawTextView(fTextView->Frame()); 1128 } 1129 1130 1131 void 1132 BAbstractSpinner::SetAlignment(alignment align) 1133 { 1134 fAlignment = align; 1135 } 1136 1137 1138 void 1139 BAbstractSpinner::SetButtonStyle(spinner_button_style buttonStyle) 1140 { 1141 fButtonStyle = buttonStyle; 1142 } 1143 1144 1145 void 1146 BAbstractSpinner::SetDivider(float position) 1147 { 1148 position = roundf(position); 1149 1150 float delta = fDivider - position; 1151 if (delta == 0.0f) 1152 return; 1153 1154 fDivider = position; 1155 1156 if ((Flags() & B_SUPPORTS_LAYOUT) != 0) { 1157 // We should never get here, since layout support means, we also 1158 // layout the divider, and don't use this method at all. 1159 Relayout(); 1160 } else { 1161 _LayoutTextView(); 1162 Invalidate(); 1163 } 1164 } 1165 1166 1167 void 1168 BAbstractSpinner::SetEnabled(bool enable) 1169 { 1170 if (IsEnabled() == enable) 1171 return; 1172 1173 BControl::SetEnabled(enable); 1174 1175 fTextView->MakeEditable(enable); 1176 if (enable) 1177 fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE); 1178 else 1179 fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE); 1180 1181 _UpdateTextViewColors(enable); 1182 fTextView->Invalidate(); 1183 1184 _LayoutTextView(); 1185 Invalidate(); 1186 if (Window() != NULL) 1187 Window()->UpdateIfNeeded(); 1188 } 1189 1190 1191 void 1192 BAbstractSpinner::SetLabel(const char* label) 1193 { 1194 BControl::SetLabel(label); 1195 1196 if (Window() != NULL) 1197 Window()->UpdateIfNeeded(); 1198 } 1199 1200 1201 bool 1202 BAbstractSpinner::IsDecrementEnabled() const 1203 { 1204 return fDecrement->IsEnabled(); 1205 } 1206 1207 1208 void 1209 BAbstractSpinner::SetDecrementEnabled(bool enable) 1210 { 1211 if (IsDecrementEnabled() == enable) 1212 return; 1213 1214 fDecrement->SetEnabled(enable); 1215 fDecrement->Invalidate(); 1216 } 1217 1218 1219 bool 1220 BAbstractSpinner::IsIncrementEnabled() const 1221 { 1222 return fIncrement->IsEnabled(); 1223 } 1224 1225 1226 void 1227 BAbstractSpinner::SetIncrementEnabled(bool enable) 1228 { 1229 if (IsIncrementEnabled() == enable) 1230 return; 1231 1232 fIncrement->SetEnabled(enable); 1233 fIncrement->Invalidate(); 1234 } 1235 1236 1237 BSize 1238 BAbstractSpinner::MinSize() 1239 { 1240 _ValidateLayoutData(); 1241 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min); 1242 } 1243 1244 1245 BSize 1246 BAbstractSpinner::MaxSize() 1247 { 1248 _ValidateLayoutData(); 1249 1250 BSize max = fLayoutData->min; 1251 max.width = B_SIZE_UNLIMITED; 1252 1253 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max); 1254 } 1255 1256 1257 BSize 1258 BAbstractSpinner::PreferredSize() 1259 { 1260 _ValidateLayoutData(); 1261 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), 1262 fLayoutData->min); 1263 } 1264 1265 1266 BAlignment 1267 BAbstractSpinner::LayoutAlignment() 1268 { 1269 _ValidateLayoutData(); 1270 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), 1271 BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER)); 1272 } 1273 1274 1275 BLayoutItem* 1276 BAbstractSpinner::CreateLabelLayoutItem() 1277 { 1278 if (fLayoutData->label_layout_item == NULL) 1279 fLayoutData->label_layout_item = new LabelLayoutItem(this); 1280 1281 return fLayoutData->label_layout_item; 1282 } 1283 1284 1285 BLayoutItem* 1286 BAbstractSpinner::CreateTextViewLayoutItem() 1287 { 1288 if (fLayoutData->text_view_layout_item == NULL) 1289 fLayoutData->text_view_layout_item = new TextViewLayoutItem(this); 1290 1291 return fLayoutData->text_view_layout_item; 1292 } 1293 1294 1295 BTextView* 1296 BAbstractSpinner::TextView() const 1297 { 1298 return dynamic_cast<BTextView*>(fTextView); 1299 } 1300 1301 1302 // #pragma mark - BAbstractSpinner protected methods 1303 1304 1305 status_t 1306 BAbstractSpinner::AllArchived(BMessage* into) const 1307 { 1308 status_t result; 1309 if ((result = BControl::AllArchived(into)) != B_OK) 1310 return result; 1311 1312 BArchiver archiver(into); 1313 1314 BArchivable* textViewItem = fLayoutData->text_view_layout_item; 1315 if (archiver.IsArchived(textViewItem)) 1316 result = archiver.AddArchivable(kTextViewItemField, textViewItem); 1317 1318 if (result != B_OK) 1319 return result; 1320 1321 BArchivable* labelBarItem = fLayoutData->label_layout_item; 1322 if (archiver.IsArchived(labelBarItem)) 1323 result = archiver.AddArchivable(kLabelItemField, labelBarItem); 1324 1325 return result; 1326 } 1327 1328 1329 status_t 1330 BAbstractSpinner::AllUnarchived(const BMessage* from) 1331 { 1332 BUnarchiver unarchiver(from); 1333 1334 status_t result = B_OK; 1335 if ((result = BControl::AllUnarchived(from)) != B_OK) 1336 return result; 1337 1338 if (unarchiver.IsInstantiated(kTextViewItemField)) { 1339 TextViewLayoutItem*& textViewItem 1340 = fLayoutData->text_view_layout_item; 1341 result = unarchiver.FindObject(kTextViewItemField, 1342 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, textViewItem); 1343 1344 if (result == B_OK) 1345 textViewItem->SetParent(this); 1346 else 1347 return result; 1348 } 1349 1350 if (unarchiver.IsInstantiated(kLabelItemField)) { 1351 LabelLayoutItem*& labelItem = fLayoutData->label_layout_item; 1352 result = unarchiver.FindObject(kLabelItemField, 1353 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, labelItem); 1354 1355 if (result == B_OK) 1356 labelItem->SetParent(this); 1357 } 1358 1359 return result; 1360 } 1361 1362 1363 void 1364 BAbstractSpinner::DoLayout() 1365 { 1366 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) 1367 return; 1368 1369 if (GetLayout()) { 1370 BControl::DoLayout(); 1371 return; 1372 } 1373 1374 _ValidateLayoutData(); 1375 1376 BSize size(Bounds().Size()); 1377 if (size.width < fLayoutData->min.width) 1378 size.width = fLayoutData->min.width; 1379 1380 if (size.height < fLayoutData->min.height) 1381 size.height = fLayoutData->min.height; 1382 1383 float divider = 0; 1384 if (fLayoutData->label_layout_item != NULL 1385 && fLayoutData->text_view_layout_item != NULL 1386 && fLayoutData->label_layout_item->Frame().IsValid() 1387 && fLayoutData->text_view_layout_item->Frame().IsValid()) { 1388 divider = fLayoutData->text_view_layout_item->Frame().left 1389 - fLayoutData->label_layout_item->Frame().left; 1390 } else if (fLayoutData->label_width > 0) { 1391 divider = fLayoutData->label_width 1392 + be_control_look->DefaultLabelSpacing(); 1393 } 1394 fDivider = divider; 1395 1396 BRect dirty(fTextView->Frame()); 1397 _LayoutTextView(); 1398 1399 // invalidate dirty region 1400 dirty = dirty | fTextView->Frame(); 1401 dirty = dirty | fIncrement->Frame(); 1402 dirty = dirty | fDecrement->Frame(); 1403 1404 Invalidate(dirty); 1405 } 1406 1407 1408 void 1409 BAbstractSpinner::LayoutInvalidated(bool descendants) 1410 { 1411 if (fLayoutData != NULL) 1412 fLayoutData->valid = false; 1413 } 1414 1415 1416 // #pragma mark - BAbstractSpinner private methods 1417 1418 1419 void 1420 BAbstractSpinner::_DrawLabel(BRect updateRect) 1421 { 1422 BRect rect(Bounds()); 1423 rect.right = fDivider; 1424 if (!rect.IsValid() || !rect.Intersects(updateRect)) 1425 return; 1426 1427 _ValidateLayoutData(); 1428 1429 const char* label = Label(); 1430 if (label == NULL) 1431 return; 1432 1433 // horizontal position 1434 float x; 1435 switch (fAlignment) { 1436 case B_ALIGN_RIGHT: 1437 x = fDivider - fLayoutData->label_width - 3.0f; 1438 break; 1439 1440 case B_ALIGN_CENTER: 1441 x = fDivider - roundf(fLayoutData->label_width / 2.0f); 1442 break; 1443 1444 default: 1445 x = 0.0f; 1446 break; 1447 } 1448 1449 // vertical position 1450 font_height& fontHeight = fLayoutData->font_info; 1451 float y = rect.top 1452 + roundf((rect.Height() + 1.0f - fontHeight.ascent 1453 - fontHeight.descent) / 2.0f) 1454 + fontHeight.ascent + kFrameMargin * 2; 1455 1456 uint32 flags = 0; 1457 if (!IsEnabled()) 1458 flags |= BControlLook::B_DISABLED; 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