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