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