2 * Copyright 2004 DarkWyrm <darkwyrm@earthlink.net>
3 * Copyright 2013 FeemanLou
4 * Copyright 2014-2015 Haiku, Inc. All rights reserved.
6 * Distributed under the terms of the MIT license.
8 * Originally written by DarkWyrm <darkwyrm@earthlink.net>
9 * Updated by FreemanLou as part of Google GCI 2013
12 * DarkWyrm, darkwyrm@earthlink.net
14 * John Scipione, jscipione@gmail.com
18 #include <AbstractSpinner.h>
22 #include <AbstractLayoutItem.h>
23 #include <Alignment.h>
24 #include <ControlLook.h>
26 #include <GradientLinear.h>
27 #include <LayoutItem.h>
28 #include <LayoutUtils.h>
30 #include <MessageFilter.h>
32 #include <PropertyInfo.h>
40 static const float kFrameMargin
= 2.0f
;
42 const char* const kFrameField
= "BAbstractSpinner:layoutItem:frame";
43 const char* const kLabelItemField
= "BAbstractSpinner:labelItem";
44 const char* const kTextViewItemField
= "BAbstractSpinner:textViewItem";
47 static property_info sProperties
[] = {
50 { B_GET_PROPERTY
, 0 },
51 { B_DIRECT_SPECIFIER
, 0 },
52 "Returns the alignment of the spinner label.",
58 { B_SET_PROPERTY
, 0 },
59 { B_DIRECT_SPECIFIER
, 0},
60 "Sets the alignment of the spinner label.",
67 { B_GET_PROPERTY
, 0 },
68 { B_DIRECT_SPECIFIER
, 0 },
69 "Returns the style of the spinner buttons.",
75 { B_SET_PROPERTY
, 0 },
76 { B_DIRECT_SPECIFIER
, 0},
77 "Sets the style of the spinner buttons.",
84 { B_GET_PROPERTY
, 0 },
85 { B_DIRECT_SPECIFIER
, 0 },
86 "Returns the divider position of the spinner.",
92 { B_SET_PROPERTY
, 0 },
93 { B_DIRECT_SPECIFIER
, 0},
94 "Sets the divider position of the spinner.",
101 { B_GET_PROPERTY
, 0 },
102 { B_DIRECT_SPECIFIER
, 0 },
103 "Returns whether or not the spinner is enabled.",
109 { B_SET_PROPERTY
, 0 },
110 { B_DIRECT_SPECIFIER
, 0},
111 "Sets whether or not the spinner is enabled.",
118 { B_GET_PROPERTY
, 0 },
119 { B_DIRECT_SPECIFIER
, 0 },
120 "Returns the spinner label.",
126 { B_SET_PROPERTY
, 0 },
127 { B_DIRECT_SPECIFIER
, 0},
128 "Sets the spinner label.",
135 { B_GET_PROPERTY
, 0 },
136 { B_DIRECT_SPECIFIER
, 0 },
137 "Returns the spinner invocation message.",
143 { B_SET_PROPERTY
, 0 },
144 { B_DIRECT_SPECIFIER
, 0},
145 "Sets the spinner invocation message.",
160 class SpinnerButton
: public BView
{
162 SpinnerButton(BRect frame
, const char* name
,
163 spinner_direction direction
);
164 virtual ~SpinnerButton();
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
);
174 bool IsEnabled() const { return fIsEnabled
; }
175 virtual void SetEnabled(bool enable
) { fIsEnabled
= enable
; };
178 void _DoneTracking(BPoint where
);
179 void _Track(BPoint where
, uint32
);
181 spinner_direction fSpinnerDirection
;
182 BAbstractSpinner
* fParent
;
186 bigtime_t fRepeatDelay
;
190 class SpinnerTextView
: public BTextView
{
192 SpinnerTextView(BRect rect
, BRect textRect
);
193 virtual ~SpinnerTextView();
195 virtual void AttachedToWindow();
196 virtual void DetachedFromWindow();
197 virtual void KeyDown(const char* bytes
, int32 numBytes
);
198 virtual void MakeFocus(bool focus
);
201 BAbstractSpinner
* fParent
;
205 class BAbstractSpinner::LabelLayoutItem
: public BAbstractLayoutItem
{
207 LabelLayoutItem(BAbstractSpinner
* parent
);
208 LabelLayoutItem(BMessage
* archive
);
210 virtual bool IsVisible();
211 virtual void SetVisible(bool visible
);
213 virtual BRect
Frame();
214 virtual void SetFrame(BRect frame
);
216 void SetParent(BAbstractSpinner
* parent
);
217 virtual BView
* View();
219 virtual BSize
BaseMinSize();
220 virtual BSize
BaseMaxSize();
221 virtual BSize
BasePreferredSize();
222 virtual BAlignment
BaseAlignment();
224 BRect
FrameInParent() const;
226 virtual status_t
Archive(BMessage
* into
, bool deep
= true) const;
227 static BArchivable
* Instantiate(BMessage
* from
);
230 BAbstractSpinner
* fParent
;
235 class BAbstractSpinner::TextViewLayoutItem
: public BAbstractLayoutItem
{
237 TextViewLayoutItem(BAbstractSpinner
* parent
);
238 TextViewLayoutItem(BMessage
* archive
);
240 virtual bool IsVisible();
241 virtual void SetVisible(bool visible
);
243 virtual BRect
Frame();
244 virtual void SetFrame(BRect frame
);
246 void SetParent(BAbstractSpinner
* parent
);
247 virtual BView
* View();
249 virtual BSize
BaseMinSize();
250 virtual BSize
BaseMaxSize();
251 virtual BSize
BasePreferredSize();
252 virtual BAlignment
BaseAlignment();
254 BRect
FrameInParent() const;
256 virtual status_t
Archive(BMessage
* into
, bool deep
= true) const;
257 static BArchivable
* Instantiate(BMessage
* from
);
260 BAbstractSpinner
* fParent
;
265 struct BAbstractSpinner::LayoutData
{
266 LayoutData(float width
, float height
)
268 label_layout_item(NULL
),
269 text_view_layout_item(NULL
),
274 previous_width(width
),
275 previous_height(height
),
280 LabelLayoutItem
* label_layout_item
;
281 TextViewLayoutItem
* text_view_layout_item
;
283 font_height font_info
;
287 float text_view_width
;
288 float text_view_height
;
290 float previous_width
;
291 float previous_height
;
294 BAlignment alignment
;
300 // #pragma mark - SpinnerButton
303 SpinnerButton::SpinnerButton(BRect frame
, const char* name
,
304 spinner_direction direction
)
306 BView(frame
, name
, B_FOLLOW_RIGHT
| B_FOLLOW_TOP
, B_WILL_DRAW
),
307 fSpinnerDirection(direction
),
317 SpinnerButton::~SpinnerButton()
323 SpinnerButton::AttachedToWindow()
325 fParent
= static_cast<BAbstractSpinner
*>(Parent());
328 BView::AttachedToWindow();
333 SpinnerButton::DetachedFromWindow()
337 BView::DetachedFromWindow();
342 SpinnerButton::Draw(BRect updateRect
)
344 BRect
rect(Bounds());
345 if (!rect
.IsValid() || !rect
.Intersects(updateRect
))
348 BView::Draw(updateRect
);
350 float frameTint
= B_DARKEN_1_TINT
;
354 fgTint
= B_DARKEN_1_TINT
;
355 else if (fIsMouseDown
)
356 fgTint
= B_DARKEN_MAX_TINT
;
358 fgTint
= 1.777f
; // 216 --> 48.2 (48)
361 if (fIsEnabled
&& fIsMouseOver
)
362 bgTint
= B_DARKEN_1_TINT
;
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
;
374 uint32 borders
= be_control_look
->B_TOP_BORDER
375 | be_control_look
->B_BOTTOM_BORDER
;
377 if (fSpinnerDirection
== SPINNER_INCREMENT
)
378 borders
|= be_control_look
->B_RIGHT_BORDER
;
380 borders
|= be_control_look
->B_LEFT_BORDER
;
382 uint32 flags
= fIsMouseDown
? BControlLook::B_ACTIVATED
: 0;
385 be_control_look
->DrawButtonFrame(this, rect
, updateRect
,
386 tint_color(bgColor
, frameTint
), bgColor
, flags
, borders
);
387 be_control_look
->DrawButtonBackground(this, rect
, updateRect
,
388 tint_color(bgColor
, bgTint
), flags
, borders
);
390 switch (fParent
->ButtonStyle()) {
391 case SPINNER_BUTTON_HORIZONTAL_ARROWS
:
393 int32 arrowDirection
= fSpinnerDirection
== SPINNER_INCREMENT
394 ? be_control_look
->B_RIGHT_ARROW
395 : be_control_look
->B_LEFT_ARROW
;
397 rect
.InsetBy(0.0f
, 1.0f
);
398 be_control_look
->DrawArrowShape(this, rect
, updateRect
, bgColor
,
399 arrowDirection
, 0, fgTint
);
403 case SPINNER_BUTTON_VERTICAL_ARROWS
:
405 int32 arrowDirection
= fSpinnerDirection
== SPINNER_INCREMENT
406 ? be_control_look
->B_UP_ARROW
407 : be_control_look
->B_DOWN_ARROW
;
409 rect
.InsetBy(0.0f
, 1.0f
);
410 be_control_look
->DrawArrowShape(this, rect
, updateRect
, bgColor
,
411 arrowDirection
, 0, fgTint
);
416 case SPINNER_BUTTON_PLUS_MINUS
:
419 fParent
->GetFont(&font
);
420 float inset
= floorf(font
.Size() / 4);
421 rect
.InsetBy(inset
, inset
);
423 if (rect
.IntegerWidth() % 2 != 0)
426 if (rect
.IntegerHeight() % 2 != 0)
429 SetHighColor(tint_color(bgColor
, fgTint
));
432 float halfHeight
= floorf(rect
.Height() / 2);
433 StrokeLine(BPoint(rect
.left
, rect
.top
+ halfHeight
),
434 BPoint(rect
.right
, rect
.top
+ halfHeight
));
435 if (fSpinnerDirection
== SPINNER_INCREMENT
) {
436 float halfWidth
= floorf(rect
.Width() / 2);
437 StrokeLine(BPoint(rect
.left
+ halfWidth
, rect
.top
+ 1),
438 BPoint(rect
.left
+ halfWidth
, rect
.bottom
- 1));
446 SpinnerButton::MouseDown(BPoint where
)
451 fRepeatDelay
= 100000;
452 MouseDownThread
<SpinnerButton
>::TrackMouse(this,
453 &SpinnerButton::_DoneTracking
, &SpinnerButton::_Track
);
456 BView::MouseDown(where
);
461 SpinnerButton::MouseMoved(BPoint where
, uint32 transit
,
462 const BMessage
* message
)
470 GetMouse(&where
, &buttons
);
471 fIsMouseOver
= Bounds().Contains(where
) && buttons
== 0;
480 fIsMouseOver
= false;
481 MouseUp(Bounds().LeftTop());
485 BView::MouseMoved(where
, transit
, message
);
490 SpinnerButton::MouseUp(BPoint where
)
492 fIsMouseDown
= false;
495 BView::MouseUp(where
);
499 // #pragma mark - SpinnerButton private methods
503 SpinnerButton::_DoneTracking(BPoint where
)
505 if (fIsMouseDown
|| !Bounds().Contains(where
))
506 fIsMouseDown
= false;
511 SpinnerButton::_Track(BPoint where
, uint32
)
513 if (fParent
== NULL
|| !Bounds().Contains(where
)) {
514 fIsMouseDown
= false;
519 fSpinnerDirection
== SPINNER_INCREMENT
520 ? fParent
->Increment()
521 : fParent
->Decrement();
523 snooze(fRepeatDelay
);
524 fRepeatDelay
= 10000;
528 // #pragma mark - SpinnerTextView
531 SpinnerTextView::SpinnerTextView(BRect rect
, BRect textRect
)
533 BTextView(rect
, "textview", textRect
, B_FOLLOW_ALL
,
534 B_WILL_DRAW
| B_NAVIGABLE
),
541 SpinnerTextView::~SpinnerTextView()
547 SpinnerTextView::AttachedToWindow()
549 fParent
= static_cast<BAbstractSpinner
*>(Parent());
551 BTextView::AttachedToWindow();
556 SpinnerTextView::DetachedFromWindow()
560 BTextView::DetachedFromWindow();
565 SpinnerTextView::KeyDown(const char* bytes
, int32 numBytes
)
567 if (fParent
== NULL
) {
568 BTextView::KeyDown(bytes
, numBytes
);
575 fParent
->SetValueFromText();
579 fParent
->KeyDown(bytes
, numBytes
);
583 if (fParent
->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS
584 && (modifiers() & B_CONTROL_KEY
) != 0) {
585 // need to hold down control, otherwise can't move cursor
586 fParent
->Decrement();
588 BTextView::KeyDown(bytes
, numBytes
);
592 if (fParent
->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS
)
593 fParent
->Increment();
595 BTextView::KeyDown(bytes
, numBytes
);
599 if (fParent
->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS
600 && (modifiers() & B_CONTROL_KEY
) != 0) {
601 // need to hold down control, otherwise can't move cursor
602 fParent
->Increment();
604 BTextView::KeyDown(bytes
, numBytes
);
608 if (fParent
->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS
)
609 fParent
->Decrement();
611 BTextView::KeyDown(bytes
, numBytes
);
615 BTextView::KeyDown(bytes
, numBytes
);
622 SpinnerTextView::MakeFocus(bool focus
)
624 BTextView::MakeFocus(focus
);
632 fParent
->SetValueFromText();
634 fParent
->_DrawTextView(fParent
->Bounds());
638 // #pragma mark - BAbstractSpinner::LabelLayoutItem
641 BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BAbstractSpinner
* parent
)
649 BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BMessage
* from
)
651 BAbstractLayoutItem(from
),
655 from
->FindRect(kFrameField
, &fFrame
);
660 BAbstractSpinner::LabelLayoutItem::IsVisible()
662 return !fParent
->IsHidden(fParent
);
667 BAbstractSpinner::LabelLayoutItem::SetVisible(bool visible
)
673 BAbstractSpinner::LabelLayoutItem::Frame()
680 BAbstractSpinner::LabelLayoutItem::SetFrame(BRect frame
)
683 fParent
->_UpdateFrame();
688 BAbstractSpinner::LabelLayoutItem::SetParent(BAbstractSpinner
* parent
)
695 BAbstractSpinner::LabelLayoutItem::View()
702 BAbstractSpinner::LabelLayoutItem::BaseMinSize()
704 fParent
->_ValidateLayoutData();
706 if (fParent
->Label() == NULL
)
707 return BSize(-1.0f
, -1.0f
);
709 return BSize(fParent
->fLayoutData
->label_width
710 + be_control_look
->DefaultLabelSpacing(),
711 fParent
->fLayoutData
->label_height
);
716 BAbstractSpinner::LabelLayoutItem::BaseMaxSize()
718 return BaseMinSize();
723 BAbstractSpinner::LabelLayoutItem::BasePreferredSize()
725 return BaseMinSize();
730 BAbstractSpinner::LabelLayoutItem::BaseAlignment()
732 return BAlignment(B_ALIGN_USE_FULL_WIDTH
, B_ALIGN_USE_FULL_HEIGHT
);
737 BAbstractSpinner::LabelLayoutItem::FrameInParent() const
739 return fFrame
.OffsetByCopy(-fParent
->Frame().left
, -fParent
->Frame().top
);
744 BAbstractSpinner::LabelLayoutItem::Archive(BMessage
* into
, bool deep
) const
746 BArchiver
archiver(into
);
747 status_t result
= BAbstractLayoutItem::Archive(into
, deep
);
750 result
= into
->AddRect(kFrameField
, fFrame
);
752 return archiver
.Finish(result
);
757 BAbstractSpinner::LabelLayoutItem::Instantiate(BMessage
* from
)
759 if (validate_instantiation(from
, "BAbstractSpinner::LabelLayoutItem"))
760 return new LabelLayoutItem(from
);
766 // #pragma mark - BAbstractSpinner::TextViewLayoutItem
769 BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BAbstractSpinner
* parent
)
774 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED
, B_SIZE_UNSET
));
778 BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BMessage
* from
)
780 BAbstractLayoutItem(from
),
784 from
->FindRect(kFrameField
, &fFrame
);
789 BAbstractSpinner::TextViewLayoutItem::IsVisible()
791 return !fParent
->IsHidden(fParent
);
796 BAbstractSpinner::TextViewLayoutItem::SetVisible(bool visible
)
803 BAbstractSpinner::TextViewLayoutItem::Frame()
810 BAbstractSpinner::TextViewLayoutItem::SetFrame(BRect frame
)
813 fParent
->_UpdateFrame();
818 BAbstractSpinner::TextViewLayoutItem::SetParent(BAbstractSpinner
* parent
)
825 BAbstractSpinner::TextViewLayoutItem::View()
832 BAbstractSpinner::TextViewLayoutItem::BaseMinSize()
834 fParent
->_ValidateLayoutData();
836 BSize
size(fParent
->fLayoutData
->text_view_width
,
837 fParent
->fLayoutData
->text_view_height
);
844 BAbstractSpinner::TextViewLayoutItem::BaseMaxSize()
846 return BaseMinSize();
851 BAbstractSpinner::TextViewLayoutItem::BasePreferredSize()
853 return BaseMinSize();
858 BAbstractSpinner::TextViewLayoutItem::BaseAlignment()
860 return BAlignment(B_ALIGN_USE_FULL_WIDTH
, B_ALIGN_USE_FULL_HEIGHT
);
865 BAbstractSpinner::TextViewLayoutItem::FrameInParent() const
867 return fFrame
.OffsetByCopy(-fParent
->Frame().left
, -fParent
->Frame().top
);
872 BAbstractSpinner::TextViewLayoutItem::Archive(BMessage
* into
, bool deep
) const
874 BArchiver
archiver(into
);
875 status_t result
= BAbstractLayoutItem::Archive(into
, deep
);
878 result
= into
->AddRect(kFrameField
, fFrame
);
880 return archiver
.Finish(result
);
885 BAbstractSpinner::TextViewLayoutItem::Instantiate(BMessage
* from
)
887 if (validate_instantiation(from
, "BAbstractSpinner::TextViewLayoutItem"))
888 return new LabelLayoutItem(from
);
894 // #pragma mark - BAbstractSpinner
897 BAbstractSpinner::BAbstractSpinner(BRect frame
, const char* name
, const char* label
,
898 BMessage
* message
, uint32 resizingMode
, uint32 flags
)
900 BControl(frame
, name
, label
, message
, resizingMode
,
901 flags
| B_WILL_DRAW
| B_FRAME_EVENTS
)
907 BAbstractSpinner::BAbstractSpinner(const char* name
, const char* label
, BMessage
* message
,
910 BControl(name
, label
, message
, flags
| B_WILL_DRAW
| B_FRAME_EVENTS
)
916 BAbstractSpinner::BAbstractSpinner(BMessage
* data
)
919 fButtonStyle(SPINNER_BUTTON_PLUS_MINUS
)
923 if (data
->FindInt32("_align") != B_OK
)
924 fAlignment
= B_ALIGN_LEFT
;
926 if (data
->FindInt32("_button_style") != B_OK
)
927 fButtonStyle
= SPINNER_BUTTON_PLUS_MINUS
;
929 if (data
->FindInt32("_divider") != B_OK
)
934 BAbstractSpinner::~BAbstractSpinner()
942 BAbstractSpinner::Instantiate(BMessage
* data
)
944 // cannot instantiate an abstract spinner
950 BAbstractSpinner::Archive(BMessage
* data
, bool deep
) const
952 status_t status
= BControl::Archive(data
, deep
);
953 data
->AddString("class", "Spinner");
956 status
= data
->AddInt32("_align", fAlignment
);
959 data
->AddInt32("_button_style", fButtonStyle
);
962 status
= data
->AddFloat("_divider", fDivider
);
969 BAbstractSpinner::GetSupportedSuites(BMessage
* message
)
971 message
->AddString("suites", "suite/vnd.Haiku-spinner");
973 BPropertyInfo
prop_info(sProperties
);
974 message
->AddFlat("messages", &prop_info
);
976 return BView::GetSupportedSuites(message
);
981 BAbstractSpinner::ResolveSpecifier(BMessage
* message
, int32 index
, BMessage
* specifier
,
982 int32 form
, const char* property
)
984 return BView::ResolveSpecifier(message
, index
, specifier
, form
,
990 BAbstractSpinner::AttachedToWindow()
992 if (!Messenger().IsValid())
995 BControl::SetValue(Value());
996 // sets the text and enables or disables the arrows
998 _UpdateTextViewColors(IsEnabled());
999 fTextView
->MakeEditable(IsEnabled());
1001 BView::AttachedToWindow();
1006 BAbstractSpinner::Draw(BRect updateRect
)
1008 _DrawLabel(updateRect
);
1009 _DrawTextView(updateRect
);
1010 fIncrement
->Invalidate();
1011 fDecrement
->Invalidate();
1016 BAbstractSpinner::FrameResized(float width
, float height
)
1018 BView::FrameResized(width
, height
);
1020 // TODO: this causes flickering still...
1024 BRect bounds
= Bounds();
1026 if (bounds
.Width() > fLayoutData
->previous_width
) {
1027 // invalidate the region between the old and the new right border
1028 BRect rect
= bounds
;
1029 rect
.left
+= fLayoutData
->previous_width
- kFrameMargin
;
1032 } else if (bounds
.Width() < fLayoutData
->previous_width
) {
1033 // invalidate the region of the new right border
1034 BRect rect
= bounds
;
1035 rect
.left
= rect
.right
- kFrameMargin
;
1039 // changes in height
1041 if (bounds
.Height() > fLayoutData
->previous_height
) {
1042 // invalidate the region between the old and the new bottom border
1043 BRect rect
= bounds
;
1044 rect
.top
+= fLayoutData
->previous_height
- kFrameMargin
;
1047 // invalidate label area
1049 rect
.right
= fDivider
;
1051 } else if (bounds
.Height() < fLayoutData
->previous_height
) {
1052 // invalidate the region of the new bottom border
1053 BRect rect
= bounds
;
1054 rect
.top
= rect
.bottom
- kFrameMargin
;
1056 // invalidate label area
1058 rect
.right
= fDivider
;
1062 fLayoutData
->previous_width
= bounds
.Width();
1063 fLayoutData
->previous_height
= bounds
.Height();
1068 BAbstractSpinner::ValueChanged()
1070 // hook method - does nothing
1075 BAbstractSpinner::MessageReceived(BMessage
* message
)
1077 if (!IsEnabled() && message
->what
== B_COLORS_UPDATED
)
1078 _UpdateTextViewColors(false);
1080 BControl::MessageReceived(message
);
1085 BAbstractSpinner::MakeFocus(bool focus
)
1087 fTextView
->MakeFocus(focus
);
1092 BAbstractSpinner::ResizeToPreferred()
1094 BView::ResizeToPreferred();
1096 const char* label
= Label();
1097 if (label
!= NULL
) {
1098 fDivider
= ceilf(StringWidth(label
))
1099 + be_control_look
->DefaultLabelSpacing();
1108 BAbstractSpinner::SetFlags(uint32 flags
)
1110 // If the textview is navigable, set it to not navigable if needed,
1111 // else if it is not navigable, set it to navigable if needed
1112 if (fTextView
->Flags() & B_NAVIGABLE
) {
1113 if (!(flags
& B_NAVIGABLE
))
1114 fTextView
->SetFlags(fTextView
->Flags() & ~B_NAVIGABLE
);
1116 if (flags
& B_NAVIGABLE
)
1117 fTextView
->SetFlags(fTextView
->Flags() | B_NAVIGABLE
);
1120 // Don't make this one navigable
1121 flags
&= ~B_NAVIGABLE
;
1123 BView::SetFlags(flags
);
1128 BAbstractSpinner::WindowActivated(bool active
)
1130 _DrawTextView(fTextView
->Frame());
1135 BAbstractSpinner::SetAlignment(alignment align
)
1142 BAbstractSpinner::SetButtonStyle(spinner_button_style buttonStyle
)
1144 fButtonStyle
= buttonStyle
;
1149 BAbstractSpinner::SetDivider(float position
)
1151 position
= roundf(position
);
1153 float delta
= fDivider
- position
;
1157 fDivider
= position
;
1159 if ((Flags() & B_SUPPORTS_LAYOUT
) != 0) {
1160 // We should never get here, since layout support means, we also
1161 // layout the divider, and don't use this method at all.
1171 BAbstractSpinner::SetEnabled(bool enable
)
1173 if (IsEnabled() == enable
)
1176 BControl::SetEnabled(enable
);
1178 fTextView
->MakeEditable(enable
);
1180 fTextView
->SetFlags(fTextView
->Flags() | B_NAVIGABLE
);
1182 fTextView
->SetFlags(fTextView
->Flags() & ~B_NAVIGABLE
);
1184 _UpdateTextViewColors(enable
);
1185 fTextView
->Invalidate();
1189 if (Window() != NULL
)
1190 Window()->UpdateIfNeeded();
1195 BAbstractSpinner::SetLabel(const char* label
)
1197 BControl::SetLabel(label
);
1199 if (Window() != NULL
)
1200 Window()->UpdateIfNeeded();
1205 BAbstractSpinner::IsDecrementEnabled() const
1207 return fDecrement
->IsEnabled();
1212 BAbstractSpinner::SetDecrementEnabled(bool enable
)
1214 if (IsDecrementEnabled() == enable
)
1217 fDecrement
->SetEnabled(enable
);
1218 fDecrement
->Invalidate();
1223 BAbstractSpinner::IsIncrementEnabled() const
1225 return fIncrement
->IsEnabled();
1230 BAbstractSpinner::SetIncrementEnabled(bool enable
)
1232 if (IsIncrementEnabled() == enable
)
1235 fIncrement
->SetEnabled(enable
);
1236 fIncrement
->Invalidate();
1241 BAbstractSpinner::MinSize()
1243 _ValidateLayoutData();
1244 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData
->min
);
1249 BAbstractSpinner::MaxSize()
1251 _ValidateLayoutData();
1253 BSize max
= fLayoutData
->min
;
1254 max
.width
= B_SIZE_UNLIMITED
;
1256 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max
);
1261 BAbstractSpinner::PreferredSize()
1263 _ValidateLayoutData();
1264 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
1270 BAbstractSpinner::LayoutAlignment()
1272 _ValidateLayoutData();
1273 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(),
1274 BAlignment(B_ALIGN_LEFT
, B_ALIGN_VERTICAL_CENTER
));
1279 BAbstractSpinner::CreateLabelLayoutItem()
1281 if (fLayoutData
->label_layout_item
== NULL
)
1282 fLayoutData
->label_layout_item
= new LabelLayoutItem(this);
1284 return fLayoutData
->label_layout_item
;
1289 BAbstractSpinner::CreateTextViewLayoutItem()
1291 if (fLayoutData
->text_view_layout_item
== NULL
)
1292 fLayoutData
->text_view_layout_item
= new TextViewLayoutItem(this);
1294 return fLayoutData
->text_view_layout_item
;
1299 BAbstractSpinner::TextView() const
1301 return dynamic_cast<BTextView
*>(fTextView
);
1305 // #pragma mark - BAbstractSpinner protected methods
1309 BAbstractSpinner::AllArchived(BMessage
* into
) const
1312 if ((result
= BControl::AllArchived(into
)) != B_OK
)
1315 BArchiver
archiver(into
);
1317 BArchivable
* textViewItem
= fLayoutData
->text_view_layout_item
;
1318 if (archiver
.IsArchived(textViewItem
))
1319 result
= archiver
.AddArchivable(kTextViewItemField
, textViewItem
);
1324 BArchivable
* labelBarItem
= fLayoutData
->label_layout_item
;
1325 if (archiver
.IsArchived(labelBarItem
))
1326 result
= archiver
.AddArchivable(kLabelItemField
, labelBarItem
);
1333 BAbstractSpinner::AllUnarchived(const BMessage
* from
)
1335 BUnarchiver
unarchiver(from
);
1337 status_t result
= B_OK
;
1338 if ((result
= BControl::AllUnarchived(from
)) != B_OK
)
1341 if (unarchiver
.IsInstantiated(kTextViewItemField
)) {
1342 TextViewLayoutItem
*& textViewItem
1343 = fLayoutData
->text_view_layout_item
;
1344 result
= unarchiver
.FindObject(kTextViewItemField
,
1345 BUnarchiver::B_DONT_ASSUME_OWNERSHIP
, textViewItem
);
1348 textViewItem
->SetParent(this);
1353 if (unarchiver
.IsInstantiated(kLabelItemField
)) {
1354 LabelLayoutItem
*& labelItem
= fLayoutData
->label_layout_item
;
1355 result
= unarchiver
.FindObject(kLabelItemField
,
1356 BUnarchiver::B_DONT_ASSUME_OWNERSHIP
, labelItem
);
1359 labelItem
->SetParent(this);
1367 BAbstractSpinner::DoLayout()
1369 if ((Flags() & B_SUPPORTS_LAYOUT
) == 0)
1373 BControl::DoLayout();
1377 _ValidateLayoutData();
1379 BSize
size(Bounds().Size());
1380 if (size
.width
< fLayoutData
->min
.width
)
1381 size
.width
= fLayoutData
->min
.width
;
1383 if (size
.height
< fLayoutData
->min
.height
)
1384 size
.height
= fLayoutData
->min
.height
;
1387 if (fLayoutData
->label_layout_item
!= NULL
1388 && fLayoutData
->text_view_layout_item
!= NULL
1389 && fLayoutData
->label_layout_item
->Frame().IsValid()
1390 && fLayoutData
->text_view_layout_item
->Frame().IsValid()) {
1391 divider
= fLayoutData
->text_view_layout_item
->Frame().left
1392 - fLayoutData
->label_layout_item
->Frame().left
;
1393 } else if (fLayoutData
->label_width
> 0) {
1394 divider
= fLayoutData
->label_width
1395 + be_control_look
->DefaultLabelSpacing();
1399 BRect
dirty(fTextView
->Frame());
1402 // invalidate dirty region
1403 dirty
= dirty
| fTextView
->Frame();
1404 dirty
= dirty
| fIncrement
->Frame();
1405 dirty
= dirty
| fDecrement
->Frame();
1412 BAbstractSpinner::LayoutInvalidated(bool descendants
)
1414 if (fLayoutData
!= NULL
)
1415 fLayoutData
->valid
= false;
1419 // #pragma mark - BAbstractSpinner private methods
1423 BAbstractSpinner::_DrawLabel(BRect updateRect
)
1425 BRect
rect(Bounds());
1426 rect
.right
= fDivider
;
1427 if (!rect
.IsValid() || !rect
.Intersects(updateRect
))
1430 _ValidateLayoutData();
1432 const char* label
= Label();
1436 // horizontal position
1438 switch (fAlignment
) {
1440 x
= fDivider
- fLayoutData
->label_width
- 3.0f
;
1443 case B_ALIGN_CENTER
:
1444 x
= fDivider
- roundf(fLayoutData
->label_width
/ 2.0f
);
1452 // vertical position
1453 font_height
& fontHeight
= fLayoutData
->font_info
;
1455 + roundf((rect
.Height() + 1.0f
- fontHeight
.ascent
1456 - fontHeight
.descent
) / 2.0f
)
1457 + fontHeight
.ascent
;
1459 uint32 flags
= be_control_look
->Flags(this);
1461 // erase the is control flag before drawing the label so that the label
1462 // will get drawn using B_PANEL_TEXT_COLOR.
1463 flags
&= ~BControlLook::B_IS_CONTROL
;
1465 be_control_look
->DrawLabel(this, label
, LowColor(), flags
, BPoint(x
, y
));
1470 BAbstractSpinner::_DrawTextView(BRect updateRect
)
1472 BRect rect
= fTextView
->Frame();
1473 rect
.InsetBy(-kFrameMargin
, -kFrameMargin
);
1474 if (!rect
.IsValid() || !rect
.Intersects(updateRect
))
1477 rgb_color base
= ui_color(B_PANEL_BACKGROUND_COLOR
);
1480 flags
|= BControlLook::B_DISABLED
;
1482 if (fTextView
->IsFocus() && Window()->IsActive())
1483 flags
|= BControlLook::B_FOCUSED
;
1485 be_control_look
->DrawTextControlBorder(this, rect
, updateRect
, base
,
1491 BAbstractSpinner::_InitObject()
1493 fAlignment
= B_ALIGN_LEFT
;
1494 fButtonStyle
= SPINNER_BUTTON_PLUS_MINUS
;
1496 if (Label() != NULL
) {
1497 fDivider
= StringWidth(Label())
1498 + be_control_look
->DefaultLabelSpacing();
1502 BControl::SetEnabled(true);
1503 BControl::SetValue(0);
1505 BRect
rect(Bounds());
1506 fLayoutData
= new LayoutData(rect
.Width(), rect
.Height());
1508 rect
.left
= fDivider
;
1509 rect
.InsetBy(kFrameMargin
, kFrameMargin
);
1510 rect
.right
-= rect
.Height() * 2 + kFrameMargin
* 2 + 1.0f
;
1511 BRect
textRect(rect
.OffsetToCopy(B_ORIGIN
));
1513 fTextView
= new SpinnerTextView(rect
, textRect
);
1514 AddChild(fTextView
);
1516 rect
.InsetBy(0.0f
, -kFrameMargin
);
1518 rect
.left
= rect
.right
+ kFrameMargin
* 2;
1519 rect
.right
= rect
.left
+ rect
.Height() - kFrameMargin
* 2;
1521 fDecrement
= new SpinnerButton(rect
, "decrement", SPINNER_DECREMENT
);
1522 AddChild(fDecrement
);
1524 rect
.left
= rect
.right
+ 1.0f
;
1525 rect
.right
= rect
.left
+ rect
.Height() - kFrameMargin
* 2;
1527 fIncrement
= new SpinnerButton(rect
, "increment", SPINNER_INCREMENT
);
1528 AddChild(fIncrement
);
1530 uint32 navigableFlags
= Flags() & B_NAVIGABLE
;
1531 if (navigableFlags
!= 0)
1532 BControl::SetFlags(Flags() & ~B_NAVIGABLE
);
1537 BAbstractSpinner::_LayoutTextView()
1540 if (fLayoutData
->text_view_layout_item
!= NULL
) {
1541 rect
= fLayoutData
->text_view_layout_item
->FrameInParent();
1544 rect
.left
= fDivider
;
1546 rect
.InsetBy(kFrameMargin
, kFrameMargin
);
1547 rect
.right
-= rect
.Height() * 2 + kFrameMargin
* 2 + 1.0f
;
1549 fTextView
->MoveTo(rect
.left
, rect
.top
);
1550 fTextView
->ResizeTo(rect
.Width(), rect
.Height());
1551 fTextView
->SetTextRect(rect
.OffsetToCopy(B_ORIGIN
));
1553 rect
.InsetBy(0.0f
, -kFrameMargin
);
1555 rect
.left
= rect
.right
+ kFrameMargin
* 2;
1556 rect
.right
= rect
.left
+ rect
.Height() - kFrameMargin
* 2;
1558 fDecrement
->ResizeTo(rect
.Width(), rect
.Height());
1559 fDecrement
->MoveTo(rect
.LeftTop());
1561 rect
.left
= rect
.right
+ 1.0f
;
1562 rect
.right
= rect
.left
+ rect
.Height() - kFrameMargin
* 2;
1564 fIncrement
->ResizeTo(rect
.Width(), rect
.Height());
1565 fIncrement
->MoveTo(rect
.LeftTop());
1570 BAbstractSpinner::_UpdateFrame()
1572 if (fLayoutData
->label_layout_item
== NULL
1573 || fLayoutData
->text_view_layout_item
== NULL
) {
1577 BRect labelFrame
= fLayoutData
->label_layout_item
->Frame();
1578 BRect textViewFrame
= fLayoutData
->text_view_layout_item
->Frame();
1580 if (!labelFrame
.IsValid() || !textViewFrame
.IsValid())
1584 fDivider
= textViewFrame
.left
- labelFrame
.left
;
1586 BRect frame
= textViewFrame
| labelFrame
;
1587 MoveTo(frame
.left
, frame
.top
);
1588 BSize oldSize
= Bounds().Size();
1589 ResizeTo(frame
.Width(), frame
.Height());
1590 BSize newSize
= Bounds().Size();
1592 // If the size changes, ResizeTo() will trigger a relayout, otherwise
1593 // we need to do that explicitly.
1594 if (newSize
!= oldSize
)
1600 BAbstractSpinner::_UpdateTextViewColors(bool enable
)
1602 // Mimick BTextControl's appearance.
1603 rgb_color textColor
= ui_color(B_DOCUMENT_TEXT_COLOR
);
1606 fTextView
->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR
);
1607 fTextView
->SetLowUIColor(ViewUIColor());
1609 rgb_color color
= ui_color(B_DOCUMENT_BACKGROUND_COLOR
);
1610 color
= disable_color(ViewColor(), color
);
1611 textColor
= disable_color(textColor
, ViewColor());
1613 fTextView
->SetViewColor(color
);
1614 fTextView
->SetLowColor(color
);
1618 fTextView
->GetFontAndColor(0, &font
);
1619 fTextView
->SetFontAndColor(&font
, B_FONT_ALL
, &textColor
);
1624 BAbstractSpinner::_ValidateLayoutData()
1626 if (fLayoutData
->valid
)
1629 font_height
& fontHeight
= fLayoutData
->font_info
;
1630 GetFontHeight(&fontHeight
);
1632 if (Label() != NULL
) {
1633 fLayoutData
->label_width
= StringWidth(Label());
1634 fLayoutData
->label_height
= ceilf(fontHeight
.ascent
1635 + fontHeight
.descent
+ fontHeight
.leading
);
1637 fLayoutData
->label_width
= 0;
1638 fLayoutData
->label_height
= 0;
1642 if (fLayoutData
->label_width
> 0) {
1643 divider
= ceilf(fLayoutData
->label_width
1644 + be_control_look
->DefaultLabelSpacing());
1647 if ((Flags() & B_SUPPORTS_LAYOUT
) == 0)
1648 divider
= std::max(divider
, fDivider
);
1650 float minTextWidth
= fTextView
->StringWidth("99999");
1652 float textViewHeight
= fTextView
->LineHeight(0) + kFrameMargin
* 2;
1653 float textViewWidth
= minTextWidth
+ textViewHeight
* 2;
1655 fLayoutData
->text_view_width
= textViewWidth
;
1656 fLayoutData
->text_view_height
= textViewHeight
;
1658 BSize
min(textViewWidth
, textViewHeight
);
1660 min
.width
+= divider
;
1662 if (fLayoutData
->label_height
> min
.height
)
1663 min
.height
= fLayoutData
->label_height
;
1665 fLayoutData
->min
= min
;
1666 fLayoutData
->valid
= true;
1668 ResetLayoutInvalidation();
1674 void BAbstractSpinner::_ReservedAbstractSpinner20() {}
1675 void BAbstractSpinner::_ReservedAbstractSpinner19() {}
1676 void BAbstractSpinner::_ReservedAbstractSpinner18() {}
1677 void BAbstractSpinner::_ReservedAbstractSpinner17() {}
1678 void BAbstractSpinner::_ReservedAbstractSpinner16() {}
1679 void BAbstractSpinner::_ReservedAbstractSpinner15() {}
1680 void BAbstractSpinner::_ReservedAbstractSpinner14() {}
1681 void BAbstractSpinner::_ReservedAbstractSpinner13() {}
1682 void BAbstractSpinner::_ReservedAbstractSpinner12() {}
1683 void BAbstractSpinner::_ReservedAbstractSpinner11() {}
1684 void BAbstractSpinner::_ReservedAbstractSpinner10() {}
1685 void BAbstractSpinner::_ReservedAbstractSpinner9() {}
1686 void BAbstractSpinner::_ReservedAbstractSpinner8() {}
1687 void BAbstractSpinner::_ReservedAbstractSpinner7() {}
1688 void BAbstractSpinner::_ReservedAbstractSpinner6() {}
1689 void BAbstractSpinner::_ReservedAbstractSpinner5() {}
1690 void BAbstractSpinner::_ReservedAbstractSpinner4() {}
1691 void BAbstractSpinner::_ReservedAbstractSpinner3() {}
1692 void BAbstractSpinner::_ReservedAbstractSpinner2() {}
1693 void BAbstractSpinner::_ReservedAbstractSpinner1() {}