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 MakeResizable(true); 536 } 537 538 539 SpinnerTextView::~SpinnerTextView() 540 { 541 } 542 543 544 void 545 SpinnerTextView::AttachedToWindow() 546 { 547 fParent = static_cast<BAbstractSpinner*>(Parent()); 548 549 BTextView::AttachedToWindow(); 550 } 551 552 553 void 554 SpinnerTextView::DetachedFromWindow() 555 { 556 fParent = NULL; 557 558 BTextView::DetachedFromWindow(); 559 } 560 561 562 void 563 SpinnerTextView::KeyDown(const char* bytes, int32 numBytes) 564 { 565 if (fParent == NULL) { 566 BTextView::KeyDown(bytes, numBytes); 567 return; 568 } 569 570 switch (bytes[0]) { 571 case B_ENTER: 572 case B_SPACE: 573 fParent->SetValueFromText(); 574 break; 575 576 case B_TAB: 577 fParent->KeyDown(bytes, numBytes); 578 break; 579 580 case B_LEFT_ARROW: 581 if (fParent->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS 582 && (modifiers() & B_CONTROL_KEY) != 0) { 583 // need to hold down control, otherwise can't move cursor 584 fParent->Decrement(); 585 } else 586 BTextView::KeyDown(bytes, numBytes); 587 break; 588 589 case B_UP_ARROW: 590 if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS) 591 fParent->Increment(); 592 else 593 BTextView::KeyDown(bytes, numBytes); 594 break; 595 596 case B_RIGHT_ARROW: 597 if (fParent->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS 598 && (modifiers() & B_CONTROL_KEY) != 0) { 599 // need to hold down control, otherwise can't move cursor 600 fParent->Increment(); 601 } else 602 BTextView::KeyDown(bytes, numBytes); 603 break; 604 605 case B_DOWN_ARROW: 606 if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS) 607 fParent->Decrement(); 608 else 609 BTextView::KeyDown(bytes, numBytes); 610 break; 611 612 default: 613 BTextView::KeyDown(bytes, numBytes); 614 break; 615 } 616 } 617 618 619 void 620 SpinnerTextView::MakeFocus(bool focus) 621 { 622 BTextView::MakeFocus(focus); 623 624 if (fParent == NULL) 625 return; 626 627 if (focus) 628 SelectAll(); 629 else 630 fParent->SetValueFromText(); 631 632 fParent->_DrawTextView(fParent->Bounds()); 633 } 634 635 636 // #pragma mark - BAbstractSpinner::LabelLayoutItem 637 638 639 BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BAbstractSpinner* parent) 640 : 641 fParent(parent), 642 fFrame() 643 { 644 } 645 646 647 BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BMessage* from) 648 : 649 BAbstractLayoutItem(from), 650 fParent(NULL), 651 fFrame() 652 { 653 from->FindRect(kFrameField, &fFrame); 654 } 655 656 657 bool 658 BAbstractSpinner::LabelLayoutItem::IsVisible() 659 { 660 return !fParent->IsHidden(fParent); 661 } 662 663 664 void 665 BAbstractSpinner::LabelLayoutItem::SetVisible(bool visible) 666 { 667 } 668 669 670 BRect 671 BAbstractSpinner::LabelLayoutItem::Frame() 672 { 673 return fFrame; 674 } 675 676 677 void 678 BAbstractSpinner::LabelLayoutItem::SetFrame(BRect frame) 679 { 680 fFrame = frame; 681 fParent->_UpdateFrame(); 682 } 683 684 685 void 686 BAbstractSpinner::LabelLayoutItem::SetParent(BAbstractSpinner* parent) 687 { 688 fParent = parent; 689 } 690 691 692 BView* 693 BAbstractSpinner::LabelLayoutItem::View() 694 { 695 return fParent; 696 } 697 698 699 BSize 700 BAbstractSpinner::LabelLayoutItem::BaseMinSize() 701 { 702 fParent->_ValidateLayoutData(); 703 704 if (fParent->Label() == NULL) 705 return BSize(-1.0f, -1.0f); 706 707 return BSize(fParent->fLayoutData->label_width 708 + be_control_look->DefaultLabelSpacing(), 709 fParent->fLayoutData->label_height); 710 } 711 712 713 BSize 714 BAbstractSpinner::LabelLayoutItem::BaseMaxSize() 715 { 716 return BaseMinSize(); 717 } 718 719 720 BSize 721 BAbstractSpinner::LabelLayoutItem::BasePreferredSize() 722 { 723 return BaseMinSize(); 724 } 725 726 727 BAlignment 728 BAbstractSpinner::LabelLayoutItem::BaseAlignment() 729 { 730 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 731 } 732 733 734 BRect 735 BAbstractSpinner::LabelLayoutItem::FrameInParent() const 736 { 737 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top); 738 } 739 740 741 status_t 742 BAbstractSpinner::LabelLayoutItem::Archive(BMessage* into, bool deep) const 743 { 744 BArchiver archiver(into); 745 status_t result = BAbstractLayoutItem::Archive(into, deep); 746 747 if (result == B_OK) 748 result = into->AddRect(kFrameField, fFrame); 749 750 return archiver.Finish(result); 751 } 752 753 754 BArchivable* 755 BAbstractSpinner::LabelLayoutItem::Instantiate(BMessage* from) 756 { 757 if (validate_instantiation(from, "BAbstractSpinner::LabelLayoutItem")) 758 return new LabelLayoutItem(from); 759 760 return NULL; 761 } 762 763 764 // #pragma mark - BAbstractSpinner::TextViewLayoutItem 765 766 767 BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BAbstractSpinner* parent) 768 : 769 fParent(parent), 770 fFrame() 771 { 772 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 773 } 774 775 776 BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BMessage* from) 777 : 778 BAbstractLayoutItem(from), 779 fParent(NULL), 780 fFrame() 781 { 782 from->FindRect(kFrameField, &fFrame); 783 } 784 785 786 bool 787 BAbstractSpinner::TextViewLayoutItem::IsVisible() 788 { 789 return !fParent->IsHidden(fParent); 790 } 791 792 793 void 794 BAbstractSpinner::TextViewLayoutItem::SetVisible(bool visible) 795 { 796 // not allowed 797 } 798 799 800 BRect 801 BAbstractSpinner::TextViewLayoutItem::Frame() 802 { 803 return fFrame; 804 } 805 806 807 void 808 BAbstractSpinner::TextViewLayoutItem::SetFrame(BRect frame) 809 { 810 fFrame = frame; 811 fParent->_UpdateFrame(); 812 } 813 814 815 void 816 BAbstractSpinner::TextViewLayoutItem::SetParent(BAbstractSpinner* parent) 817 { 818 fParent = parent; 819 } 820 821 822 BView* 823 BAbstractSpinner::TextViewLayoutItem::View() 824 { 825 return fParent; 826 } 827 828 829 BSize 830 BAbstractSpinner::TextViewLayoutItem::BaseMinSize() 831 { 832 fParent->_ValidateLayoutData(); 833 834 BSize size(fParent->fLayoutData->text_view_width, 835 fParent->fLayoutData->text_view_height); 836 837 return size; 838 } 839 840 841 BSize 842 BAbstractSpinner::TextViewLayoutItem::BaseMaxSize() 843 { 844 return BaseMinSize(); 845 } 846 847 848 BSize 849 BAbstractSpinner::TextViewLayoutItem::BasePreferredSize() 850 { 851 return BaseMinSize(); 852 } 853 854 855 BAlignment 856 BAbstractSpinner::TextViewLayoutItem::BaseAlignment() 857 { 858 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 859 } 860 861 862 BRect 863 BAbstractSpinner::TextViewLayoutItem::FrameInParent() const 864 { 865 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top); 866 } 867 868 869 status_t 870 BAbstractSpinner::TextViewLayoutItem::Archive(BMessage* into, bool deep) const 871 { 872 BArchiver archiver(into); 873 status_t result = BAbstractLayoutItem::Archive(into, deep); 874 875 if (result == B_OK) 876 result = into->AddRect(kFrameField, fFrame); 877 878 return archiver.Finish(result); 879 } 880 881 882 BArchivable* 883 BAbstractSpinner::TextViewLayoutItem::Instantiate(BMessage* from) 884 { 885 if (validate_instantiation(from, "BAbstractSpinner::TextViewLayoutItem")) 886 return new LabelLayoutItem(from); 887 888 return NULL; 889 } 890 891 892 // #pragma mark - BAbstractSpinner 893 894 895 BAbstractSpinner::BAbstractSpinner(BRect frame, const char* name, const char* label, 896 BMessage* message, uint32 resizingMode, uint32 flags) 897 : 898 BControl(frame, name, label, message, resizingMode, 899 flags | B_WILL_DRAW | B_FRAME_EVENTS) 900 { 901 _InitObject(); 902 } 903 904 905 BAbstractSpinner::BAbstractSpinner(const char* name, const char* label, BMessage* message, 906 uint32 flags) 907 : 908 BControl(name, label, message, flags | B_WILL_DRAW | B_FRAME_EVENTS) 909 { 910 _InitObject(); 911 } 912 913 914 BAbstractSpinner::BAbstractSpinner(BMessage* data) 915 : 916 BControl(data), 917 fButtonStyle(SPINNER_BUTTON_PLUS_MINUS) 918 { 919 _InitObject(); 920 921 if (data->FindInt32("_align") != B_OK) 922 fAlignment = B_ALIGN_LEFT; 923 924 if (data->FindInt32("_button_style") != B_OK) 925 fButtonStyle = SPINNER_BUTTON_PLUS_MINUS; 926 927 if (data->FindInt32("_divider") != B_OK) 928 fDivider = 0.0f; 929 } 930 931 932 BAbstractSpinner::~BAbstractSpinner() 933 { 934 delete fLayoutData; 935 fLayoutData = NULL; 936 } 937 938 939 BArchivable* 940 BAbstractSpinner::Instantiate(BMessage* data) 941 { 942 // cannot instantiate an abstract spinner 943 return NULL; 944 } 945 946 947 status_t 948 BAbstractSpinner::Archive(BMessage* data, bool deep) const 949 { 950 status_t status = BControl::Archive(data, deep); 951 data->AddString("class", "Spinner"); 952 953 if (status == B_OK) 954 status = data->AddInt32("_align", fAlignment); 955 956 if (status == B_OK) 957 data->AddInt32("_button_style", fButtonStyle); 958 959 if (status == B_OK) 960 status = data->AddFloat("_divider", fDivider); 961 962 return status; 963 } 964 965 966 status_t 967 BAbstractSpinner::GetSupportedSuites(BMessage* message) 968 { 969 message->AddString("suites", "suite/vnd.Haiku-spinner"); 970 971 BPropertyInfo prop_info(sProperties); 972 message->AddFlat("messages", &prop_info); 973 974 return BView::GetSupportedSuites(message); 975 } 976 977 978 BHandler* 979 BAbstractSpinner::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 980 int32 form, const char* property) 981 { 982 return BView::ResolveSpecifier(message, index, specifier, form, 983 property); 984 } 985 986 987 void 988 BAbstractSpinner::AttachedToWindow() 989 { 990 if (!Messenger().IsValid()) 991 SetTarget(Window()); 992 993 BControl::SetValue(Value()); 994 // sets the text and enables or disables the arrows 995 996 _UpdateTextViewColors(IsEnabled()); 997 fTextView->MakeEditable(IsEnabled()); 998 999 BView::AttachedToWindow(); 1000 } 1001 1002 1003 void 1004 BAbstractSpinner::Draw(BRect updateRect) 1005 { 1006 _DrawLabel(updateRect); 1007 _DrawTextView(updateRect); 1008 fIncrement->Invalidate(); 1009 fDecrement->Invalidate(); 1010 } 1011 1012 1013 void 1014 BAbstractSpinner::FrameResized(float width, float height) 1015 { 1016 BView::FrameResized(width, height); 1017 1018 // TODO: this causes flickering still... 1019 1020 // changes in width 1021 1022 BRect bounds = Bounds(); 1023 1024 if (bounds.Width() > fLayoutData->previous_width) { 1025 // invalidate the region between the old and the new right border 1026 BRect rect = bounds; 1027 rect.left += fLayoutData->previous_width - kFrameMargin; 1028 rect.right--; 1029 Invalidate(rect); 1030 } else if (bounds.Width() < fLayoutData->previous_width) { 1031 // invalidate the region of the new right border 1032 BRect rect = bounds; 1033 rect.left = rect.right - kFrameMargin; 1034 Invalidate(rect); 1035 } 1036 1037 // changes in height 1038 1039 if (bounds.Height() > fLayoutData->previous_height) { 1040 // invalidate the region between the old and the new bottom border 1041 BRect rect = bounds; 1042 rect.top += fLayoutData->previous_height - kFrameMargin; 1043 rect.bottom--; 1044 Invalidate(rect); 1045 // invalidate label area 1046 rect = bounds; 1047 rect.right = fDivider; 1048 Invalidate(rect); 1049 } else if (bounds.Height() < fLayoutData->previous_height) { 1050 // invalidate the region of the new bottom border 1051 BRect rect = bounds; 1052 rect.top = rect.bottom - kFrameMargin; 1053 Invalidate(rect); 1054 // invalidate label area 1055 rect = bounds; 1056 rect.right = fDivider; 1057 Invalidate(rect); 1058 } 1059 1060 fLayoutData->previous_width = bounds.Width(); 1061 fLayoutData->previous_height = bounds.Height(); 1062 } 1063 1064 1065 void 1066 BAbstractSpinner::ValueChanged() 1067 { 1068 // hook method - does nothing 1069 } 1070 1071 1072 void 1073 BAbstractSpinner::MessageReceived(BMessage* message) 1074 { 1075 if (!IsEnabled() && message->what == B_COLORS_UPDATED) 1076 _UpdateTextViewColors(false); 1077 1078 BControl::MessageReceived(message); 1079 } 1080 1081 1082 void 1083 BAbstractSpinner::MakeFocus(bool focus) 1084 { 1085 fTextView->MakeFocus(focus); 1086 } 1087 1088 1089 void 1090 BAbstractSpinner::ResizeToPreferred() 1091 { 1092 BView::ResizeToPreferred(); 1093 1094 const char* label = Label(); 1095 if (label != NULL) { 1096 fDivider = ceilf(StringWidth(label)) 1097 + be_control_look->DefaultLabelSpacing(); 1098 } else 1099 fDivider = 0.0f; 1100 1101 _LayoutTextView(); 1102 } 1103 1104 1105 void 1106 BAbstractSpinner::SetFlags(uint32 flags) 1107 { 1108 // If the textview is navigable, set it to not navigable if needed, 1109 // else if it is not navigable, set it to navigable if needed 1110 if (fTextView->Flags() & B_NAVIGABLE) { 1111 if (!(flags & B_NAVIGABLE)) 1112 fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE); 1113 } else { 1114 if (flags & B_NAVIGABLE) 1115 fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE); 1116 } 1117 1118 // Don't make this one navigable 1119 flags &= ~B_NAVIGABLE; 1120 1121 BView::SetFlags(flags); 1122 } 1123 1124 1125 void 1126 BAbstractSpinner::WindowActivated(bool active) 1127 { 1128 _DrawTextView(fTextView->Frame()); 1129 } 1130 1131 1132 void 1133 BAbstractSpinner::SetAlignment(alignment align) 1134 { 1135 fAlignment = align; 1136 } 1137 1138 1139 void 1140 BAbstractSpinner::SetButtonStyle(spinner_button_style buttonStyle) 1141 { 1142 fButtonStyle = buttonStyle; 1143 } 1144 1145 1146 void 1147 BAbstractSpinner::SetDivider(float position) 1148 { 1149 position = roundf(position); 1150 1151 float delta = fDivider - position; 1152 if (delta == 0.0f) 1153 return; 1154 1155 fDivider = position; 1156 1157 if ((Flags() & B_SUPPORTS_LAYOUT) != 0) { 1158 // We should never get here, since layout support means, we also 1159 // layout the divider, and don't use this method at all. 1160 Relayout(); 1161 } else { 1162 _LayoutTextView(); 1163 Invalidate(); 1164 } 1165 } 1166 1167 1168 void 1169 BAbstractSpinner::SetEnabled(bool enable) 1170 { 1171 if (IsEnabled() == enable) 1172 return; 1173 1174 BControl::SetEnabled(enable); 1175 1176 fTextView->MakeEditable(enable); 1177 if (enable) 1178 fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE); 1179 else 1180 fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE); 1181 1182 _UpdateTextViewColors(enable); 1183 fTextView->Invalidate(); 1184 1185 _LayoutTextView(); 1186 Invalidate(); 1187 if (Window() != NULL) 1188 Window()->UpdateIfNeeded(); 1189 } 1190 1191 1192 void 1193 BAbstractSpinner::SetLabel(const char* label) 1194 { 1195 BControl::SetLabel(label); 1196 1197 if (Window() != NULL) 1198 Window()->UpdateIfNeeded(); 1199 } 1200 1201 1202 bool 1203 BAbstractSpinner::IsDecrementEnabled() const 1204 { 1205 return fDecrement->IsEnabled(); 1206 } 1207 1208 1209 void 1210 BAbstractSpinner::SetDecrementEnabled(bool enable) 1211 { 1212 if (IsDecrementEnabled() == enable) 1213 return; 1214 1215 fDecrement->SetEnabled(enable); 1216 fDecrement->Invalidate(); 1217 } 1218 1219 1220 bool 1221 BAbstractSpinner::IsIncrementEnabled() const 1222 { 1223 return fIncrement->IsEnabled(); 1224 } 1225 1226 1227 void 1228 BAbstractSpinner::SetIncrementEnabled(bool enable) 1229 { 1230 if (IsIncrementEnabled() == enable) 1231 return; 1232 1233 fIncrement->SetEnabled(enable); 1234 fIncrement->Invalidate(); 1235 } 1236 1237 1238 BSize 1239 BAbstractSpinner::MinSize() 1240 { 1241 _ValidateLayoutData(); 1242 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min); 1243 } 1244 1245 1246 BSize 1247 BAbstractSpinner::MaxSize() 1248 { 1249 _ValidateLayoutData(); 1250 1251 BSize max = fLayoutData->min; 1252 max.width = B_SIZE_UNLIMITED; 1253 1254 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max); 1255 } 1256 1257 1258 BSize 1259 BAbstractSpinner::PreferredSize() 1260 { 1261 _ValidateLayoutData(); 1262 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), 1263 fLayoutData->min); 1264 } 1265 1266 1267 BAlignment 1268 BAbstractSpinner::LayoutAlignment() 1269 { 1270 _ValidateLayoutData(); 1271 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), 1272 BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER)); 1273 } 1274 1275 1276 BLayoutItem* 1277 BAbstractSpinner::CreateLabelLayoutItem() 1278 { 1279 if (fLayoutData->label_layout_item == NULL) 1280 fLayoutData->label_layout_item = new LabelLayoutItem(this); 1281 1282 return fLayoutData->label_layout_item; 1283 } 1284 1285 1286 BLayoutItem* 1287 BAbstractSpinner::CreateTextViewLayoutItem() 1288 { 1289 if (fLayoutData->text_view_layout_item == NULL) 1290 fLayoutData->text_view_layout_item = new TextViewLayoutItem(this); 1291 1292 return fLayoutData->text_view_layout_item; 1293 } 1294 1295 1296 BTextView* 1297 BAbstractSpinner::TextView() const 1298 { 1299 return dynamic_cast<BTextView*>(fTextView); 1300 } 1301 1302 1303 // #pragma mark - BAbstractSpinner protected methods 1304 1305 1306 status_t 1307 BAbstractSpinner::AllArchived(BMessage* into) const 1308 { 1309 status_t result; 1310 if ((result = BControl::AllArchived(into)) != B_OK) 1311 return result; 1312 1313 BArchiver archiver(into); 1314 1315 BArchivable* textViewItem = fLayoutData->text_view_layout_item; 1316 if (archiver.IsArchived(textViewItem)) 1317 result = archiver.AddArchivable(kTextViewItemField, textViewItem); 1318 1319 if (result != B_OK) 1320 return result; 1321 1322 BArchivable* labelBarItem = fLayoutData->label_layout_item; 1323 if (archiver.IsArchived(labelBarItem)) 1324 result = archiver.AddArchivable(kLabelItemField, labelBarItem); 1325 1326 return result; 1327 } 1328 1329 1330 status_t 1331 BAbstractSpinner::AllUnarchived(const BMessage* from) 1332 { 1333 BUnarchiver unarchiver(from); 1334 1335 status_t result = B_OK; 1336 if ((result = BControl::AllUnarchived(from)) != B_OK) 1337 return result; 1338 1339 if (unarchiver.IsInstantiated(kTextViewItemField)) { 1340 TextViewLayoutItem*& textViewItem 1341 = fLayoutData->text_view_layout_item; 1342 result = unarchiver.FindObject(kTextViewItemField, 1343 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, textViewItem); 1344 1345 if (result == B_OK) 1346 textViewItem->SetParent(this); 1347 else 1348 return result; 1349 } 1350 1351 if (unarchiver.IsInstantiated(kLabelItemField)) { 1352 LabelLayoutItem*& labelItem = fLayoutData->label_layout_item; 1353 result = unarchiver.FindObject(kLabelItemField, 1354 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, labelItem); 1355 1356 if (result == B_OK) 1357 labelItem->SetParent(this); 1358 } 1359 1360 return result; 1361 } 1362 1363 1364 void 1365 BAbstractSpinner::DoLayout() 1366 { 1367 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) 1368 return; 1369 1370 if (GetLayout()) { 1371 BControl::DoLayout(); 1372 return; 1373 } 1374 1375 _ValidateLayoutData(); 1376 1377 BSize size(Bounds().Size()); 1378 if (size.width < fLayoutData->min.width) 1379 size.width = fLayoutData->min.width; 1380 1381 if (size.height < fLayoutData->min.height) 1382 size.height = fLayoutData->min.height; 1383 1384 float divider = 0; 1385 if (fLayoutData->label_layout_item != NULL 1386 && fLayoutData->text_view_layout_item != NULL 1387 && fLayoutData->label_layout_item->Frame().IsValid() 1388 && fLayoutData->text_view_layout_item->Frame().IsValid()) { 1389 divider = fLayoutData->text_view_layout_item->Frame().left 1390 - fLayoutData->label_layout_item->Frame().left; 1391 } else if (fLayoutData->label_width > 0) { 1392 divider = fLayoutData->label_width 1393 + be_control_look->DefaultLabelSpacing(); 1394 } 1395 fDivider = divider; 1396 1397 BRect dirty(fTextView->Frame()); 1398 _LayoutTextView(); 1399 1400 // invalidate dirty region 1401 dirty = dirty | fTextView->Frame(); 1402 dirty = dirty | fIncrement->Frame(); 1403 dirty = dirty | fDecrement->Frame(); 1404 1405 Invalidate(dirty); 1406 } 1407 1408 1409 void 1410 BAbstractSpinner::LayoutInvalidated(bool descendants) 1411 { 1412 if (fLayoutData != NULL) 1413 fLayoutData->valid = false; 1414 } 1415 1416 1417 // #pragma mark - BAbstractSpinner private methods 1418 1419 1420 void 1421 BAbstractSpinner::_DrawLabel(BRect updateRect) 1422 { 1423 BRect rect(Bounds()); 1424 rect.right = fDivider; 1425 if (!rect.IsValid() || !rect.Intersects(updateRect)) 1426 return; 1427 1428 _ValidateLayoutData(); 1429 1430 const char* label = Label(); 1431 if (label == NULL) 1432 return; 1433 1434 // horizontal position 1435 float x; 1436 switch (fAlignment) { 1437 case B_ALIGN_RIGHT: 1438 x = fDivider - fLayoutData->label_width - 3.0f; 1439 break; 1440 1441 case B_ALIGN_CENTER: 1442 x = fDivider - roundf(fLayoutData->label_width / 2.0f); 1443 break; 1444 1445 default: 1446 x = 0.0f; 1447 break; 1448 } 1449 1450 // vertical position 1451 font_height& fontHeight = fLayoutData->font_info; 1452 float y = rect.top 1453 + roundf((rect.Height() + 1.0f - fontHeight.ascent 1454 - fontHeight.descent) / 2.0f) 1455 + fontHeight.ascent; 1456 1457 uint32 flags = be_control_look->Flags(this); 1458 1459 // erase the is control flag before drawing the label so that the label 1460 // will get drawn using B_PANEL_TEXT_COLOR. 1461 flags &= ~BControlLook::B_IS_CONTROL; 1462 1463 be_control_look->DrawLabel(this, label, LowColor(), flags, BPoint(x, y)); 1464 } 1465 1466 1467 void 1468 BAbstractSpinner::_DrawTextView(BRect updateRect) 1469 { 1470 BRect rect = fTextView->Frame(); 1471 rect.InsetBy(-kFrameMargin, -kFrameMargin); 1472 if (!rect.IsValid() || !rect.Intersects(updateRect)) 1473 return; 1474 1475 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 1476 uint32 flags = 0; 1477 if (!IsEnabled()) 1478 flags |= BControlLook::B_DISABLED; 1479 1480 if (fTextView->IsFocus() && Window()->IsActive()) 1481 flags |= BControlLook::B_FOCUSED; 1482 1483 be_control_look->DrawTextControlBorder(this, rect, updateRect, base, 1484 flags); 1485 } 1486 1487 1488 void 1489 BAbstractSpinner::_InitObject() 1490 { 1491 fAlignment = B_ALIGN_LEFT; 1492 fButtonStyle = SPINNER_BUTTON_PLUS_MINUS; 1493 1494 if (Label() != NULL) { 1495 fDivider = StringWidth(Label()) 1496 + be_control_look->DefaultLabelSpacing(); 1497 } else 1498 fDivider = 0.0f; 1499 1500 BControl::SetEnabled(true); 1501 BControl::SetValue(0); 1502 1503 BRect rect(Bounds()); 1504 fLayoutData = new LayoutData(rect.Width(), rect.Height()); 1505 1506 rect.left = fDivider; 1507 rect.InsetBy(kFrameMargin, kFrameMargin); 1508 rect.right -= rect.Height() * 2 + kFrameMargin * 2 + 1.0f; 1509 BRect textRect(rect.OffsetToCopy(B_ORIGIN)); 1510 1511 fTextView = new SpinnerTextView(rect, textRect); 1512 AddChild(fTextView); 1513 1514 rect.InsetBy(0.0f, -kFrameMargin); 1515 1516 rect.left = rect.right + kFrameMargin * 2; 1517 rect.right = rect.left + rect.Height() - kFrameMargin * 2; 1518 1519 fDecrement = new SpinnerButton(rect, "decrement", SPINNER_DECREMENT); 1520 AddChild(fDecrement); 1521 1522 rect.left = rect.right + 1.0f; 1523 rect.right = rect.left + rect.Height() - kFrameMargin * 2; 1524 1525 fIncrement = new SpinnerButton(rect, "increment", SPINNER_INCREMENT); 1526 AddChild(fIncrement); 1527 1528 uint32 navigableFlags = Flags() & B_NAVIGABLE; 1529 if (navigableFlags != 0) 1530 BControl::SetFlags(Flags() & ~B_NAVIGABLE); 1531 } 1532 1533 1534 void 1535 BAbstractSpinner::_LayoutTextView() 1536 { 1537 BRect rect; 1538 if (fLayoutData->text_view_layout_item != NULL) { 1539 rect = fLayoutData->text_view_layout_item->FrameInParent(); 1540 } else { 1541 rect = Bounds(); 1542 rect.left = fDivider; 1543 } 1544 rect.InsetBy(kFrameMargin, kFrameMargin); 1545 rect.right -= rect.Height() * 2 + kFrameMargin * 2 + 1.0f; 1546 1547 fTextView->MoveTo(rect.left, rect.top); 1548 fTextView->ResizeTo(rect.Width(), rect.Height()); 1549 fTextView->SetTextRect(rect.OffsetToCopy(B_ORIGIN)); 1550 1551 rect.InsetBy(0.0f, -kFrameMargin); 1552 1553 rect.left = rect.right + kFrameMargin * 2; 1554 rect.right = rect.left + rect.Height() - kFrameMargin * 2; 1555 1556 fDecrement->ResizeTo(rect.Width(), rect.Height()); 1557 fDecrement->MoveTo(rect.LeftTop()); 1558 1559 rect.left = rect.right + 1.0f; 1560 rect.right = rect.left + rect.Height() - kFrameMargin * 2; 1561 1562 fIncrement->ResizeTo(rect.Width(), rect.Height()); 1563 fIncrement->MoveTo(rect.LeftTop()); 1564 } 1565 1566 1567 void 1568 BAbstractSpinner::_UpdateFrame() 1569 { 1570 if (fLayoutData->label_layout_item == NULL 1571 || fLayoutData->text_view_layout_item == NULL) { 1572 return; 1573 } 1574 1575 BRect labelFrame = fLayoutData->label_layout_item->Frame(); 1576 BRect textViewFrame = fLayoutData->text_view_layout_item->Frame(); 1577 1578 if (!labelFrame.IsValid() || !textViewFrame.IsValid()) 1579 return; 1580 1581 // update divider 1582 fDivider = textViewFrame.left - labelFrame.left; 1583 1584 BRect frame = textViewFrame | labelFrame; 1585 MoveTo(frame.left, frame.top); 1586 BSize oldSize = Bounds().Size(); 1587 ResizeTo(frame.Width(), frame.Height()); 1588 BSize newSize = Bounds().Size(); 1589 1590 // If the size changes, ResizeTo() will trigger a relayout, otherwise 1591 // we need to do that explicitly. 1592 if (newSize != oldSize) 1593 Relayout(); 1594 } 1595 1596 1597 void 1598 BAbstractSpinner::_UpdateTextViewColors(bool enable) 1599 { 1600 // Mimick BTextControl's appearance. 1601 rgb_color textColor = ui_color(B_DOCUMENT_TEXT_COLOR); 1602 1603 if (enable) { 1604 fTextView->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR); 1605 fTextView->SetLowUIColor(ViewUIColor()); 1606 } else { 1607 rgb_color color = ui_color(B_DOCUMENT_BACKGROUND_COLOR); 1608 color = disable_color(ViewColor(), color); 1609 textColor = disable_color(textColor, ViewColor()); 1610 1611 fTextView->SetViewColor(color); 1612 fTextView->SetLowColor(color); 1613 } 1614 1615 BFont font; 1616 fTextView->GetFontAndColor(0, &font); 1617 fTextView->SetFontAndColor(&font, B_FONT_ALL, &textColor); 1618 } 1619 1620 1621 void 1622 BAbstractSpinner::_ValidateLayoutData() 1623 { 1624 if (fLayoutData->valid) 1625 return; 1626 1627 font_height& fontHeight = fLayoutData->font_info; 1628 GetFontHeight(&fontHeight); 1629 1630 if (Label() != NULL) { 1631 fLayoutData->label_width = StringWidth(Label()); 1632 fLayoutData->label_height = ceilf(fontHeight.ascent 1633 + fontHeight.descent + fontHeight.leading); 1634 } else { 1635 fLayoutData->label_width = 0; 1636 fLayoutData->label_height = 0; 1637 } 1638 1639 float divider = 0; 1640 if (fLayoutData->label_width > 0) { 1641 divider = ceilf(fLayoutData->label_width 1642 + be_control_look->DefaultLabelSpacing()); 1643 } 1644 1645 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) 1646 divider = std::max(divider, fDivider); 1647 1648 float minTextWidth = fTextView->StringWidth("99999"); 1649 1650 float textViewHeight = fTextView->LineHeight(0) + kFrameMargin * 2; 1651 float textViewWidth = minTextWidth + textViewHeight * 2; 1652 1653 fLayoutData->text_view_width = textViewWidth; 1654 fLayoutData->text_view_height = textViewHeight; 1655 1656 BSize min(textViewWidth, textViewHeight); 1657 if (divider > 0.0f) 1658 min.width += divider; 1659 1660 if (fLayoutData->label_height > min.height) 1661 min.height = fLayoutData->label_height; 1662 1663 fLayoutData->min = min; 1664 fLayoutData->valid = true; 1665 1666 ResetLayoutInvalidation(); 1667 } 1668 1669 1670 // FBC padding 1671 1672 void BAbstractSpinner::_ReservedAbstractSpinner20() {} 1673 void BAbstractSpinner::_ReservedAbstractSpinner19() {} 1674 void BAbstractSpinner::_ReservedAbstractSpinner18() {} 1675 void BAbstractSpinner::_ReservedAbstractSpinner17() {} 1676 void BAbstractSpinner::_ReservedAbstractSpinner16() {} 1677 void BAbstractSpinner::_ReservedAbstractSpinner15() {} 1678 void BAbstractSpinner::_ReservedAbstractSpinner14() {} 1679 void BAbstractSpinner::_ReservedAbstractSpinner13() {} 1680 void BAbstractSpinner::_ReservedAbstractSpinner12() {} 1681 void BAbstractSpinner::_ReservedAbstractSpinner11() {} 1682 void BAbstractSpinner::_ReservedAbstractSpinner10() {} 1683 void BAbstractSpinner::_ReservedAbstractSpinner9() {} 1684 void BAbstractSpinner::_ReservedAbstractSpinner8() {} 1685 void BAbstractSpinner::_ReservedAbstractSpinner7() {} 1686 void BAbstractSpinner::_ReservedAbstractSpinner6() {} 1687 void BAbstractSpinner::_ReservedAbstractSpinner5() {} 1688 void BAbstractSpinner::_ReservedAbstractSpinner4() {} 1689 void BAbstractSpinner::_ReservedAbstractSpinner3() {} 1690 void BAbstractSpinner::_ReservedAbstractSpinner2() {} 1691 void BAbstractSpinner::_ReservedAbstractSpinner1() {} 1692