btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / kits / interface / AbstractSpinner.cpp
blob02709ca19730c94bc9ed6ef539e329f185b1ca10
1 /*
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
11 * Authors:
12 * DarkWyrm, darkwyrm@earthlink.net
13 * FeemanLou
14 * John Scipione, jscipione@gmail.com
18 #include <AbstractSpinner.h>
20 #include <algorithm>
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>
37 #include "Thread.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[] = {
49 "Align",
50 { B_GET_PROPERTY, 0 },
51 { B_DIRECT_SPECIFIER, 0 },
52 "Returns the alignment of the spinner label.",
54 { B_INT32_TYPE }
57 "Align",
58 { B_SET_PROPERTY, 0 },
59 { B_DIRECT_SPECIFIER, 0},
60 "Sets the alignment of the spinner label.",
62 { B_INT32_TYPE }
66 "ButtonStyle",
67 { B_GET_PROPERTY, 0 },
68 { B_DIRECT_SPECIFIER, 0 },
69 "Returns the style of the spinner buttons.",
71 { B_INT32_TYPE }
74 "ButtonStyle",
75 { B_SET_PROPERTY, 0 },
76 { B_DIRECT_SPECIFIER, 0},
77 "Sets the style of the spinner buttons.",
79 { B_INT32_TYPE }
83 "Divider",
84 { B_GET_PROPERTY, 0 },
85 { B_DIRECT_SPECIFIER, 0 },
86 "Returns the divider position of the spinner.",
88 { B_FLOAT_TYPE }
91 "Divider",
92 { B_SET_PROPERTY, 0 },
93 { B_DIRECT_SPECIFIER, 0},
94 "Sets the divider position of the spinner.",
96 { B_FLOAT_TYPE }
100 "Enabled",
101 { B_GET_PROPERTY, 0 },
102 { B_DIRECT_SPECIFIER, 0 },
103 "Returns whether or not the spinner is enabled.",
105 { B_BOOL_TYPE }
108 "Enabled",
109 { B_SET_PROPERTY, 0 },
110 { B_DIRECT_SPECIFIER, 0},
111 "Sets whether or not the spinner is enabled.",
113 { B_BOOL_TYPE }
117 "Label",
118 { B_GET_PROPERTY, 0 },
119 { B_DIRECT_SPECIFIER, 0 },
120 "Returns the spinner label.",
122 { B_STRING_TYPE }
125 "Label",
126 { B_SET_PROPERTY, 0 },
127 { B_DIRECT_SPECIFIER, 0},
128 "Sets the spinner label.",
130 { B_STRING_TYPE }
134 "Message",
135 { B_GET_PROPERTY, 0 },
136 { B_DIRECT_SPECIFIER, 0 },
137 "Returns the spinner invocation message.",
139 { B_MESSAGE_TYPE }
142 "Message",
143 { B_SET_PROPERTY, 0 },
144 { B_DIRECT_SPECIFIER, 0},
145 "Sets the spinner invocation message.",
147 { B_MESSAGE_TYPE }
150 { 0 }
154 typedef enum {
155 SPINNER_INCREMENT,
156 SPINNER_DECREMENT
157 } spinner_direction;
160 class SpinnerButton : public BView {
161 public:
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; };
177 private:
178 void _DoneTracking(BPoint where);
179 void _Track(BPoint where, uint32);
181 spinner_direction fSpinnerDirection;
182 BAbstractSpinner* fParent;
183 bool fIsEnabled;
184 bool fIsMouseDown;
185 bool fIsMouseOver;
186 bigtime_t fRepeatDelay;
190 class SpinnerTextView : public BTextView {
191 public:
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);
200 private:
201 BAbstractSpinner* fParent;
205 class BAbstractSpinner::LabelLayoutItem : public BAbstractLayoutItem {
206 public:
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);
229 private:
230 BAbstractSpinner* fParent;
231 BRect fFrame;
235 class BAbstractSpinner::TextViewLayoutItem : public BAbstractLayoutItem {
236 public:
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);
259 private:
260 BAbstractSpinner* fParent;
261 BRect fFrame;
265 struct BAbstractSpinner::LayoutData {
266 LayoutData(float width, float height)
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)
280 LabelLayoutItem* label_layout_item;
281 TextViewLayoutItem* text_view_layout_item;
283 font_height font_info;
285 float label_width;
286 float label_height;
287 float text_view_width;
288 float text_view_height;
290 float previous_width;
291 float previous_height;
293 BSize min;
294 BAlignment alignment;
296 bool valid;
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),
308 fParent(NULL),
309 fIsEnabled(true),
310 fIsMouseDown(false),
311 fIsMouseOver(false),
312 fRepeatDelay(100000)
317 SpinnerButton::~SpinnerButton()
322 void
323 SpinnerButton::AttachedToWindow()
325 fParent = static_cast<BAbstractSpinner*>(Parent());
327 AdoptParentColors();
328 BView::AttachedToWindow();
332 void
333 SpinnerButton::DetachedFromWindow()
335 fParent = NULL;
337 BView::DetachedFromWindow();
341 void
342 SpinnerButton::Draw(BRect updateRect)
344 BRect rect(Bounds());
345 if (!rect.IsValid() || !rect.Intersects(updateRect))
346 return;
348 BView::Draw(updateRect);
350 float frameTint = B_DARKEN_1_TINT;
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)
360 float bgTint;
361 if (fIsEnabled && fIsMouseOver)
362 bgTint = B_DARKEN_1_TINT;
363 else
364 bgTint = B_NO_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;
379 else
380 borders |= be_control_look->B_LEFT_BORDER;
382 uint32 flags = fIsMouseDown ? BControlLook::B_ACTIVATED : 0;
384 // draw the button
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);
400 break;
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);
412 break;
415 default:
416 case SPINNER_BUTTON_PLUS_MINUS:
418 BFont font;
419 fParent->GetFont(&font);
420 float inset = floorf(font.Size() / 4);
421 rect.InsetBy(inset, inset);
423 if (rect.IntegerWidth() % 2 != 0)
424 rect.right -= 1;
426 if (rect.IntegerHeight() % 2 != 0)
427 rect.bottom -= 1;
429 SetHighColor(tint_color(bgColor, fgTint));
431 // draw the +/-
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));
445 void
446 SpinnerButton::MouseDown(BPoint where)
448 if (fIsEnabled) {
449 fIsMouseDown = true;
450 Invalidate();
451 fRepeatDelay = 100000;
452 MouseDownThread<SpinnerButton>::TrackMouse(this,
453 &SpinnerButton::_DoneTracking, &SpinnerButton::_Track);
456 BView::MouseDown(where);
460 void
461 SpinnerButton::MouseMoved(BPoint where, uint32 transit,
462 const BMessage* message)
464 switch (transit) {
465 case B_ENTERED_VIEW:
466 case B_INSIDE_VIEW:
468 BPoint where;
469 uint32 buttons;
470 GetMouse(&where, &buttons);
471 fIsMouseOver = Bounds().Contains(where) && buttons == 0;
472 if (!fIsMouseDown)
473 Invalidate();
475 break;
478 case B_EXITED_VIEW:
479 case B_OUTSIDE_VIEW:
480 fIsMouseOver = false;
481 MouseUp(Bounds().LeftTop());
482 break;
485 BView::MouseMoved(where, transit, message);
489 void
490 SpinnerButton::MouseUp(BPoint where)
492 fIsMouseDown = false;
493 Invalidate();
495 BView::MouseUp(where);
499 // #pragma mark - SpinnerButton private methods
502 void
503 SpinnerButton::_DoneTracking(BPoint where)
505 if (fIsMouseDown || !Bounds().Contains(where))
506 fIsMouseDown = false;
510 void
511 SpinnerButton::_Track(BPoint where, uint32)
513 if (fParent == NULL || !Bounds().Contains(where)) {
514 fIsMouseDown = false;
515 return;
517 fIsMouseDown = true;
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),
535 fParent(NULL)
537 MakeResizable(true);
541 SpinnerTextView::~SpinnerTextView()
546 void
547 SpinnerTextView::AttachedToWindow()
549 fParent = static_cast<BAbstractSpinner*>(Parent());
551 BTextView::AttachedToWindow();
555 void
556 SpinnerTextView::DetachedFromWindow()
558 fParent = NULL;
560 BTextView::DetachedFromWindow();
564 void
565 SpinnerTextView::KeyDown(const char* bytes, int32 numBytes)
567 if (fParent == NULL) {
568 BTextView::KeyDown(bytes, numBytes);
569 return;
572 switch (bytes[0]) {
573 case B_ENTER:
574 case B_SPACE:
575 fParent->SetValueFromText();
576 break;
578 case B_TAB:
579 fParent->KeyDown(bytes, numBytes);
580 break;
582 case B_LEFT_ARROW:
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();
587 } else
588 BTextView::KeyDown(bytes, numBytes);
589 break;
591 case B_UP_ARROW:
592 if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS)
593 fParent->Increment();
594 else
595 BTextView::KeyDown(bytes, numBytes);
596 break;
598 case B_RIGHT_ARROW:
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();
603 } else
604 BTextView::KeyDown(bytes, numBytes);
605 break;
607 case B_DOWN_ARROW:
608 if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS)
609 fParent->Decrement();
610 else
611 BTextView::KeyDown(bytes, numBytes);
612 break;
614 default:
615 BTextView::KeyDown(bytes, numBytes);
616 break;
621 void
622 SpinnerTextView::MakeFocus(bool focus)
624 BTextView::MakeFocus(focus);
626 if (fParent == NULL)
627 return;
629 if (focus)
630 SelectAll();
631 else
632 fParent->SetValueFromText();
634 fParent->_DrawTextView(fParent->Bounds());
638 // #pragma mark - BAbstractSpinner::LabelLayoutItem
641 BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BAbstractSpinner* parent)
643 fParent(parent),
644 fFrame()
649 BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BMessage* from)
651 BAbstractLayoutItem(from),
652 fParent(NULL),
653 fFrame()
655 from->FindRect(kFrameField, &fFrame);
659 bool
660 BAbstractSpinner::LabelLayoutItem::IsVisible()
662 return !fParent->IsHidden(fParent);
666 void
667 BAbstractSpinner::LabelLayoutItem::SetVisible(bool visible)
672 BRect
673 BAbstractSpinner::LabelLayoutItem::Frame()
675 return fFrame;
679 void
680 BAbstractSpinner::LabelLayoutItem::SetFrame(BRect frame)
682 fFrame = frame;
683 fParent->_UpdateFrame();
687 void
688 BAbstractSpinner::LabelLayoutItem::SetParent(BAbstractSpinner* parent)
690 fParent = parent;
694 BView*
695 BAbstractSpinner::LabelLayoutItem::View()
697 return fParent;
701 BSize
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);
715 BSize
716 BAbstractSpinner::LabelLayoutItem::BaseMaxSize()
718 return BaseMinSize();
722 BSize
723 BAbstractSpinner::LabelLayoutItem::BasePreferredSize()
725 return BaseMinSize();
729 BAlignment
730 BAbstractSpinner::LabelLayoutItem::BaseAlignment()
732 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
736 BRect
737 BAbstractSpinner::LabelLayoutItem::FrameInParent() const
739 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top);
743 status_t
744 BAbstractSpinner::LabelLayoutItem::Archive(BMessage* into, bool deep) const
746 BArchiver archiver(into);
747 status_t result = BAbstractLayoutItem::Archive(into, deep);
749 if (result == B_OK)
750 result = into->AddRect(kFrameField, fFrame);
752 return archiver.Finish(result);
756 BArchivable*
757 BAbstractSpinner::LabelLayoutItem::Instantiate(BMessage* from)
759 if (validate_instantiation(from, "BAbstractSpinner::LabelLayoutItem"))
760 return new LabelLayoutItem(from);
762 return NULL;
766 // #pragma mark - BAbstractSpinner::TextViewLayoutItem
769 BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BAbstractSpinner* parent)
771 fParent(parent),
772 fFrame()
774 SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
778 BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BMessage* from)
780 BAbstractLayoutItem(from),
781 fParent(NULL),
782 fFrame()
784 from->FindRect(kFrameField, &fFrame);
788 bool
789 BAbstractSpinner::TextViewLayoutItem::IsVisible()
791 return !fParent->IsHidden(fParent);
795 void
796 BAbstractSpinner::TextViewLayoutItem::SetVisible(bool visible)
798 // not allowed
802 BRect
803 BAbstractSpinner::TextViewLayoutItem::Frame()
805 return fFrame;
809 void
810 BAbstractSpinner::TextViewLayoutItem::SetFrame(BRect frame)
812 fFrame = frame;
813 fParent->_UpdateFrame();
817 void
818 BAbstractSpinner::TextViewLayoutItem::SetParent(BAbstractSpinner* parent)
820 fParent = parent;
824 BView*
825 BAbstractSpinner::TextViewLayoutItem::View()
827 return fParent;
831 BSize
832 BAbstractSpinner::TextViewLayoutItem::BaseMinSize()
834 fParent->_ValidateLayoutData();
836 BSize size(fParent->fLayoutData->text_view_width,
837 fParent->fLayoutData->text_view_height);
839 return size;
843 BSize
844 BAbstractSpinner::TextViewLayoutItem::BaseMaxSize()
846 return BaseMinSize();
850 BSize
851 BAbstractSpinner::TextViewLayoutItem::BasePreferredSize()
853 return BaseMinSize();
857 BAlignment
858 BAbstractSpinner::TextViewLayoutItem::BaseAlignment()
860 return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
864 BRect
865 BAbstractSpinner::TextViewLayoutItem::FrameInParent() const
867 return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top);
871 status_t
872 BAbstractSpinner::TextViewLayoutItem::Archive(BMessage* into, bool deep) const
874 BArchiver archiver(into);
875 status_t result = BAbstractLayoutItem::Archive(into, deep);
877 if (result == B_OK)
878 result = into->AddRect(kFrameField, fFrame);
880 return archiver.Finish(result);
884 BArchivable*
885 BAbstractSpinner::TextViewLayoutItem::Instantiate(BMessage* from)
887 if (validate_instantiation(from, "BAbstractSpinner::TextViewLayoutItem"))
888 return new LabelLayoutItem(from);
890 return NULL;
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)
903 _InitObject();
907 BAbstractSpinner::BAbstractSpinner(const char* name, const char* label, BMessage* message,
908 uint32 flags)
910 BControl(name, label, message, flags | B_WILL_DRAW | B_FRAME_EVENTS)
912 _InitObject();
916 BAbstractSpinner::BAbstractSpinner(BMessage* data)
918 BControl(data),
919 fButtonStyle(SPINNER_BUTTON_PLUS_MINUS)
921 _InitObject();
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)
930 fDivider = 0.0f;
934 BAbstractSpinner::~BAbstractSpinner()
936 delete fLayoutData;
937 fLayoutData = NULL;
941 BArchivable*
942 BAbstractSpinner::Instantiate(BMessage* data)
944 // cannot instantiate an abstract spinner
945 return NULL;
949 status_t
950 BAbstractSpinner::Archive(BMessage* data, bool deep) const
952 status_t status = BControl::Archive(data, deep);
953 data->AddString("class", "Spinner");
955 if (status == B_OK)
956 status = data->AddInt32("_align", fAlignment);
958 if (status == B_OK)
959 data->AddInt32("_button_style", fButtonStyle);
961 if (status == B_OK)
962 status = data->AddFloat("_divider", fDivider);
964 return status;
968 status_t
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);
980 BHandler*
981 BAbstractSpinner::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
982 int32 form, const char* property)
984 return BView::ResolveSpecifier(message, index, specifier, form,
985 property);
989 void
990 BAbstractSpinner::AttachedToWindow()
992 if (!Messenger().IsValid())
993 SetTarget(Window());
995 BControl::SetValue(Value());
996 // sets the text and enables or disables the arrows
998 _UpdateTextViewColors(IsEnabled());
999 fTextView->MakeEditable(IsEnabled());
1001 BView::AttachedToWindow();
1005 void
1006 BAbstractSpinner::Draw(BRect updateRect)
1008 _DrawLabel(updateRect);
1009 _DrawTextView(updateRect);
1010 fIncrement->Invalidate();
1011 fDecrement->Invalidate();
1015 void
1016 BAbstractSpinner::FrameResized(float width, float height)
1018 BView::FrameResized(width, height);
1020 // TODO: this causes flickering still...
1022 // changes in width
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;
1030 rect.right--;
1031 Invalidate(rect);
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;
1036 Invalidate(rect);
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;
1045 rect.bottom--;
1046 Invalidate(rect);
1047 // invalidate label area
1048 rect = bounds;
1049 rect.right = fDivider;
1050 Invalidate(rect);
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;
1055 Invalidate(rect);
1056 // invalidate label area
1057 rect = bounds;
1058 rect.right = fDivider;
1059 Invalidate(rect);
1062 fLayoutData->previous_width = bounds.Width();
1063 fLayoutData->previous_height = bounds.Height();
1067 void
1068 BAbstractSpinner::ValueChanged()
1070 // hook method - does nothing
1074 void
1075 BAbstractSpinner::MessageReceived(BMessage* message)
1077 if (!IsEnabled() && message->what == B_COLORS_UPDATED)
1078 _UpdateTextViewColors(false);
1080 BControl::MessageReceived(message);
1084 void
1085 BAbstractSpinner::MakeFocus(bool focus)
1087 fTextView->MakeFocus(focus);
1091 void
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();
1100 } else
1101 fDivider = 0.0f;
1103 _LayoutTextView();
1107 void
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);
1115 } else {
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);
1127 void
1128 BAbstractSpinner::WindowActivated(bool active)
1130 _DrawTextView(fTextView->Frame());
1134 void
1135 BAbstractSpinner::SetAlignment(alignment align)
1137 fAlignment = align;
1141 void
1142 BAbstractSpinner::SetButtonStyle(spinner_button_style buttonStyle)
1144 fButtonStyle = buttonStyle;
1148 void
1149 BAbstractSpinner::SetDivider(float position)
1151 position = roundf(position);
1153 float delta = fDivider - position;
1154 if (delta == 0.0f)
1155 return;
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.
1162 Relayout();
1163 } else {
1164 _LayoutTextView();
1165 Invalidate();
1170 void
1171 BAbstractSpinner::SetEnabled(bool enable)
1173 if (IsEnabled() == enable)
1174 return;
1176 BControl::SetEnabled(enable);
1178 fTextView->MakeEditable(enable);
1179 if (enable)
1180 fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE);
1181 else
1182 fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE);
1184 _UpdateTextViewColors(enable);
1185 fTextView->Invalidate();
1187 _LayoutTextView();
1188 Invalidate();
1189 if (Window() != NULL)
1190 Window()->UpdateIfNeeded();
1194 void
1195 BAbstractSpinner::SetLabel(const char* label)
1197 BControl::SetLabel(label);
1199 if (Window() != NULL)
1200 Window()->UpdateIfNeeded();
1204 bool
1205 BAbstractSpinner::IsDecrementEnabled() const
1207 return fDecrement->IsEnabled();
1211 void
1212 BAbstractSpinner::SetDecrementEnabled(bool enable)
1214 if (IsDecrementEnabled() == enable)
1215 return;
1217 fDecrement->SetEnabled(enable);
1218 fDecrement->Invalidate();
1222 bool
1223 BAbstractSpinner::IsIncrementEnabled() const
1225 return fIncrement->IsEnabled();
1229 void
1230 BAbstractSpinner::SetIncrementEnabled(bool enable)
1232 if (IsIncrementEnabled() == enable)
1233 return;
1235 fIncrement->SetEnabled(enable);
1236 fIncrement->Invalidate();
1240 BSize
1241 BAbstractSpinner::MinSize()
1243 _ValidateLayoutData();
1244 return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min);
1248 BSize
1249 BAbstractSpinner::MaxSize()
1251 _ValidateLayoutData();
1253 BSize max = fLayoutData->min;
1254 max.width = B_SIZE_UNLIMITED;
1256 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max);
1260 BSize
1261 BAbstractSpinner::PreferredSize()
1263 _ValidateLayoutData();
1264 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
1265 fLayoutData->min);
1269 BAlignment
1270 BAbstractSpinner::LayoutAlignment()
1272 _ValidateLayoutData();
1273 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(),
1274 BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER));
1278 BLayoutItem*
1279 BAbstractSpinner::CreateLabelLayoutItem()
1281 if (fLayoutData->label_layout_item == NULL)
1282 fLayoutData->label_layout_item = new LabelLayoutItem(this);
1284 return fLayoutData->label_layout_item;
1288 BLayoutItem*
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;
1298 BTextView*
1299 BAbstractSpinner::TextView() const
1301 return dynamic_cast<BTextView*>(fTextView);
1305 // #pragma mark - BAbstractSpinner protected methods
1308 status_t
1309 BAbstractSpinner::AllArchived(BMessage* into) const
1311 status_t result;
1312 if ((result = BControl::AllArchived(into)) != B_OK)
1313 return result;
1315 BArchiver archiver(into);
1317 BArchivable* textViewItem = fLayoutData->text_view_layout_item;
1318 if (archiver.IsArchived(textViewItem))
1319 result = archiver.AddArchivable(kTextViewItemField, textViewItem);
1321 if (result != B_OK)
1322 return result;
1324 BArchivable* labelBarItem = fLayoutData->label_layout_item;
1325 if (archiver.IsArchived(labelBarItem))
1326 result = archiver.AddArchivable(kLabelItemField, labelBarItem);
1328 return result;
1332 status_t
1333 BAbstractSpinner::AllUnarchived(const BMessage* from)
1335 BUnarchiver unarchiver(from);
1337 status_t result = B_OK;
1338 if ((result = BControl::AllUnarchived(from)) != B_OK)
1339 return result;
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);
1347 if (result == B_OK)
1348 textViewItem->SetParent(this);
1349 else
1350 return result;
1353 if (unarchiver.IsInstantiated(kLabelItemField)) {
1354 LabelLayoutItem*& labelItem = fLayoutData->label_layout_item;
1355 result = unarchiver.FindObject(kLabelItemField,
1356 BUnarchiver::B_DONT_ASSUME_OWNERSHIP, labelItem);
1358 if (result == B_OK)
1359 labelItem->SetParent(this);
1362 return result;
1366 void
1367 BAbstractSpinner::DoLayout()
1369 if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
1370 return;
1372 if (GetLayout()) {
1373 BControl::DoLayout();
1374 return;
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;
1386 float divider = 0;
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();
1397 fDivider = divider;
1399 BRect dirty(fTextView->Frame());
1400 _LayoutTextView();
1402 // invalidate dirty region
1403 dirty = dirty | fTextView->Frame();
1404 dirty = dirty | fIncrement->Frame();
1405 dirty = dirty | fDecrement->Frame();
1407 Invalidate(dirty);
1411 void
1412 BAbstractSpinner::LayoutInvalidated(bool descendants)
1414 if (fLayoutData != NULL)
1415 fLayoutData->valid = false;
1419 // #pragma mark - BAbstractSpinner private methods
1422 void
1423 BAbstractSpinner::_DrawLabel(BRect updateRect)
1425 BRect rect(Bounds());
1426 rect.right = fDivider;
1427 if (!rect.IsValid() || !rect.Intersects(updateRect))
1428 return;
1430 _ValidateLayoutData();
1432 const char* label = Label();
1433 if (label == NULL)
1434 return;
1436 // horizontal position
1437 float x;
1438 switch (fAlignment) {
1439 case B_ALIGN_RIGHT:
1440 x = fDivider - fLayoutData->label_width - 3.0f;
1441 break;
1443 case B_ALIGN_CENTER:
1444 x = fDivider - roundf(fLayoutData->label_width / 2.0f);
1445 break;
1447 default:
1448 x = 0.0f;
1449 break;
1452 // vertical position
1453 font_height& fontHeight = fLayoutData->font_info;
1454 float y = rect.top
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));
1469 void
1470 BAbstractSpinner::_DrawTextView(BRect updateRect)
1472 BRect rect = fTextView->Frame();
1473 rect.InsetBy(-kFrameMargin, -kFrameMargin);
1474 if (!rect.IsValid() || !rect.Intersects(updateRect))
1475 return;
1477 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
1478 uint32 flags = 0;
1479 if (!IsEnabled())
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,
1486 flags);
1490 void
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();
1499 } else
1500 fDivider = 0.0f;
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);
1536 void
1537 BAbstractSpinner::_LayoutTextView()
1539 BRect rect;
1540 if (fLayoutData->text_view_layout_item != NULL) {
1541 rect = fLayoutData->text_view_layout_item->FrameInParent();
1542 } else {
1543 rect = Bounds();
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());
1569 void
1570 BAbstractSpinner::_UpdateFrame()
1572 if (fLayoutData->label_layout_item == NULL
1573 || fLayoutData->text_view_layout_item == NULL) {
1574 return;
1577 BRect labelFrame = fLayoutData->label_layout_item->Frame();
1578 BRect textViewFrame = fLayoutData->text_view_layout_item->Frame();
1580 if (!labelFrame.IsValid() || !textViewFrame.IsValid())
1581 return;
1583 // update divider
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)
1595 Relayout();
1599 void
1600 BAbstractSpinner::_UpdateTextViewColors(bool enable)
1602 // Mimick BTextControl's appearance.
1603 rgb_color textColor = ui_color(B_DOCUMENT_TEXT_COLOR);
1605 if (enable) {
1606 fTextView->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
1607 fTextView->SetLowUIColor(ViewUIColor());
1608 } else {
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);
1617 BFont font;
1618 fTextView->GetFontAndColor(0, &font);
1619 fTextView->SetFontAndColor(&font, B_FONT_ALL, &textColor);
1623 void
1624 BAbstractSpinner::_ValidateLayoutData()
1626 if (fLayoutData->valid)
1627 return;
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);
1636 } else {
1637 fLayoutData->label_width = 0;
1638 fLayoutData->label_height = 0;
1641 float divider = 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);
1659 if (divider > 0.0f)
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();
1672 // FBC padding
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() {}