From a0ba79fbff0d2a8ce0f008362d5e8832c1d994c2 Mon Sep 17 00:00:00 2001 From: John Scipione Date: Wed, 11 Mar 2015 17:17:16 -0400 Subject: [PATCH] Split BSpinner into BAbstractSpinner and... 2 concrete classes which are currently implemented: * BSpinner (works on int32s) * BDecimalSpinner (works on doubles) In addition BAbstractSpinner now inherits from BControl instead of BView/BInvoker. This allowed for code simplification at the cost of needing to cast for the decimal version because SetValue(int32 value) comes from BControl. Also, add a spinner_button_style enum with 3 options: * SPINNER_BUTTON_HORIZONTAL_ARROWS * SPINNER_BUTTON_VERTICAL_ARROWS * SPINNER_BUTTON_PLUS_MINUS which sets the spinner arrows to either use horizontal arrows (left/right) vertical arrows, (up/down), or +/- symbols (the default). If the spinner button is using horizontal arrows you can decrement and increment the spinner value by pushing control+left/right, otherwise you can increment and decrement by pushing up or down. The reason for needing control is so that you can move the cursor in the textbox otherwise. Switch the 3 apps that are currently using BSpinners to use the integer variety in Deskbar preferences, WebPostive preferences, and Screen preferences. --- headers/private/interface/AbstractSpinner.h | 168 ++ headers/private/interface/DecimalSpinner.h | 96 ++ headers/private/interface/Spinner.h | 139 +- src/apps/deskbar/PreferencesWindow.cpp | 6 - src/apps/webpositive/SettingsWindow.cpp | 2 - src/kits/interface/AbstractSpinner.cpp | 1690 +++++++++++++++++++ src/kits/interface/DecimalSpinner.cpp | 372 ++++ src/kits/interface/Jamfile | 2 + src/kits/interface/Spinner.cpp | 1663 +----------------- src/preferences/screen/ScreenWindow.cpp | 2 - 10 files changed, 2401 insertions(+), 1739 deletions(-) create mode 100644 headers/private/interface/AbstractSpinner.h create mode 100644 headers/private/interface/DecimalSpinner.h create mode 100644 src/kits/interface/AbstractSpinner.cpp create mode 100644 src/kits/interface/DecimalSpinner.cpp diff --git a/headers/private/interface/AbstractSpinner.h b/headers/private/interface/AbstractSpinner.h new file mode 100644 index 0000000000..9d961b1260 --- /dev/null +++ b/headers/private/interface/AbstractSpinner.h @@ -0,0 +1,168 @@ +/* + * Copyright 2004 DarkWyrm + * Copyright 2013 FeemanLou + * Copyright 2014-2015 Haiku, Inc. All rights reserved. + * + * Distributed under the terms of the MIT license. + * + * Originally written by DarkWyrm + * Updated by FreemanLou as part of Google GCI 2013 + * + * Authors: + * DarkWyrm, darkwyrm@earthlink.net + * FeemanLou + * John Scipione, jscipione@gmail.com + */ +#ifndef _ABSTRACT_SPINNER_H +#define _ABSTRACT_SPINNER_H + + +#include + + +typedef enum { + SPINNER_BUTTON_HORIZONTAL_ARROWS, + SPINNER_BUTTON_VERTICAL_ARROWS, + SPINNER_BUTTON_PLUS_MINUS +} spinner_button_style; + + +class BTextView; +class SpinnerButton; +class SpinnerTextView; + + +/*! BAbstractSpinner provides an input whose value can be nudged up or down + by way of two small buttons on the right. +*/ +class BAbstractSpinner : public BControl { +public: + BAbstractSpinner(BRect frame, const char* name, + const char* label, BMessage* message, + uint32 resizingMode = B_FOLLOW_LEFT | B_FOLLOW_TOP, + uint32 flags = B_WILL_DRAW | B_NAVIGABLE); + BAbstractSpinner(const char* name, const char* label, + BMessage* message, + uint32 flags = B_WILL_DRAW | B_NAVIGABLE); + BAbstractSpinner(BMessage* data); + virtual ~BAbstractSpinner(); + + static BArchivable* Instantiate(BMessage* data); + virtual status_t Archive(BMessage* data, bool deep = true) const; + + virtual status_t GetSupportedSuites(BMessage* message); + virtual BHandler* ResolveSpecifier(BMessage* message, int32 index, + BMessage* specifier, int32 form, + const char* property); + + virtual void AttachedToWindow(); + virtual void Draw(BRect updateRect); + virtual void FrameResized(float width, float height); + virtual void ValueChanged(); + + virtual void Decrement() = 0; + virtual void Increment() = 0; + virtual void MakeFocus(bool focus = true); + virtual void ResizeToPreferred(); + virtual void SetFlags(uint32 flags); + virtual void WindowActivated(bool active); + + alignment Alignment() const { return fAlignment; }; + virtual void SetAlignment(alignment align); + + spinner_button_style ButtonStyle() const { return fButtonStyle; }; + virtual void SetButtonStyle(spinner_button_style buttonStyle); + + float Divider() const { return fDivider; }; + virtual void SetDivider(float position); + + virtual void SetEnabled(bool enable); + + virtual void SetLabel(const char* label); + + virtual void SetValueFromText() = 0; + + bool IsDecrementEnabled() const; + virtual void SetDecrementEnabled(bool enable); + + bool IsIncrementEnabled() const; + virtual void SetIncrementEnabled(bool enable); + + virtual BSize MinSize(); + virtual BSize MaxSize(); + virtual BSize PreferredSize(); + virtual BAlignment LayoutAlignment(); + + BLayoutItem* CreateLabelLayoutItem(); + BLayoutItem* CreateTextViewLayoutItem(); + + BTextView* TextView() const; + +private: + // FBC padding + virtual void _ReservedAbstractSpinner20(); + virtual void _ReservedAbstractSpinner19(); + virtual void _ReservedAbstractSpinner18(); + virtual void _ReservedAbstractSpinner17(); + virtual void _ReservedAbstractSpinner16(); + virtual void _ReservedAbstractSpinner15(); + virtual void _ReservedAbstractSpinner14(); + virtual void _ReservedAbstractSpinner13(); + virtual void _ReservedAbstractSpinner12(); + virtual void _ReservedAbstractSpinner11(); + virtual void _ReservedAbstractSpinner10(); + virtual void _ReservedAbstractSpinner9(); + virtual void _ReservedAbstractSpinner8(); + virtual void _ReservedAbstractSpinner7(); + virtual void _ReservedAbstractSpinner6(); + virtual void _ReservedAbstractSpinner5(); + virtual void _ReservedAbstractSpinner4(); + virtual void _ReservedAbstractSpinner3(); + virtual void _ReservedAbstractSpinner2(); + virtual void _ReservedAbstractSpinner1(); + +protected: + virtual status_t AllArchived(BMessage* into) const; + virtual status_t AllUnarchived(const BMessage* from); + + virtual void LayoutInvalidated(bool descendants); + virtual void DoLayout(); + +private: + class LabelLayoutItem; + class TextViewLayoutItem; + struct LayoutData; + + friend class SpinnerButton; + friend class SpinnerTextView; + + friend class LabelLayoutItem; + friend class TextViewLayoutItem; + friend struct LayoutData; + + void _DrawLabel(BRect updateRect); + void _DrawTextView(BRect updateRect); + void _InitObject(); + void _LayoutTextView(); + void _UpdateFrame(); + void _UpdateTextViewColors(bool enable); + void _ValidateLayoutData(); + + BAbstractSpinner& operator=(const BAbstractSpinner& other); + + alignment fAlignment; + spinner_button_style fButtonStyle; + float fDivider; + + LayoutData* fLayoutData; + + SpinnerTextView* fTextView; + SpinnerButton* fIncrement; + SpinnerButton* fDecrement; + + // FBC padding + uint32 _reserved[20]; +}; + + +#endif // _ABSTRACT_SPINNER_H diff --git a/headers/private/interface/DecimalSpinner.h b/headers/private/interface/DecimalSpinner.h new file mode 100644 index 0000000000..46161c70bd --- /dev/null +++ b/headers/private/interface/DecimalSpinner.h @@ -0,0 +1,96 @@ +/* + * Copyright 2015 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + * + * Authors: + * John Scipione, jscipione@gmail.com + */ +#ifndef _DECIMAL_SPINNER_H +#define _DECIMAL_SPINNER_H + + +#include + + +class BDecimalSpinner : public BAbstractSpinner { +public: + BDecimalSpinner(BRect frame, const char* name, + const char* label, BMessage* message, + uint32 resizingMode = B_FOLLOW_LEFT | B_FOLLOW_TOP, + uint32 flags = B_WILL_DRAW | B_NAVIGABLE); + BDecimalSpinner(const char* name, const char* label, + BMessage* message, + uint32 flags = B_WILL_DRAW | B_NAVIGABLE); + BDecimalSpinner(BMessage* data); + virtual ~BDecimalSpinner(); + + static BArchivable* Instantiate(BMessage* data); + virtual status_t Archive(BMessage* data, bool deep = true) const; + + virtual status_t GetSupportedSuites(BMessage* message); + + virtual void AttachedToWindow(); + + virtual void Increment(); + virtual void Decrement(); + + virtual void SetEnabled(bool enable); + + uint32 Precision() const { return fPrecision; }; + virtual void SetPrecision(uint32 precision) { fPrecision = precision; }; + + double MaxValue() const { return fMaxValue; } + virtual void SetMaxValue(double max); + + double MinValue() const { return fMinValue; } + virtual void SetMinValue(double min); + + void Range(double* min, double* max); + virtual void SetRange(double min, double max); + + double Step() const { return fStep; } + virtual void SetStep(double step) { fStep = step; }; + + double Value() const { return fValue; }; + virtual void SetValue(int32 value); + virtual void SetValue(double value); + virtual void SetValueFromText(); + +private: + // FBC padding + virtual void _ReservedDecimalSpinner20(); + virtual void _ReservedDecimalSpinner19(); + virtual void _ReservedDecimalSpinner18(); + virtual void _ReservedDecimalSpinner17(); + virtual void _ReservedDecimalSpinner16(); + virtual void _ReservedDecimalSpinner15(); + virtual void _ReservedDecimalSpinner14(); + virtual void _ReservedDecimalSpinner13(); + virtual void _ReservedDecimalSpinner12(); + virtual void _ReservedDecimalSpinner11(); + virtual void _ReservedDecimalSpinner10(); + virtual void _ReservedDecimalSpinner9(); + virtual void _ReservedDecimalSpinner8(); + virtual void _ReservedDecimalSpinner7(); + virtual void _ReservedDecimalSpinner6(); + virtual void _ReservedDecimalSpinner5(); + virtual void _ReservedDecimalSpinner4(); + virtual void _ReservedDecimalSpinner3(); + virtual void _ReservedDecimalSpinner2(); + virtual void _ReservedDecimalSpinner1(); + +private: + void _InitObject(); + + double fMinValue; + double fMaxValue; + double fStep; + double fValue; + uint32 fPrecision; + + // FBC padding + uint32 _reserved[20]; +}; + + +#endif // _DECIMAL_SPINNER_H diff --git a/headers/private/interface/Spinner.h b/headers/private/interface/Spinner.h index 50e01bc7f9..e106ded902 100644 --- a/headers/private/interface/Spinner.h +++ b/headers/private/interface/Spinner.h @@ -1,35 +1,18 @@ /* - * Copyright 2004 DarkWyrm - * Copyright 2013 FeemanLou - * Copyright 2014 Haiku, Inc. All rights reserved. - * + * Copyright 2015 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT license. * - * Originally written by DarkWyrm - * Updated by FreemanLou as part of Google GCI 2013 - * * Authors: - * DarkWyrm, darkwyrm@earthlink.net - * FeemanLou * John Scipione, jscipione@gmail.com */ -#ifndef SPINNER_H -#define SPINNER_H +#ifndef _SPINNER_H +#define _SPINNER_H -#include -#include +#include -class BTextView; -class SpinnerArrow; -class SpinnerTextView; - - -/*! BSpinner provides a numeric input whose value can be nudged up or down - by way of two small buttons on the right. -*/ -class BSpinner : public BView, public BInvoker { +class BSpinner : public BAbstractSpinner { public: BSpinner(BRect frame, const char* name, const char* label, BMessage* message, @@ -44,69 +27,27 @@ public: static BArchivable* Instantiate(BMessage* data); virtual status_t Archive(BMessage* data, bool deep = true) const; + virtual void Increment(); + virtual void Decrement(); + virtual status_t GetSupportedSuites(BMessage* message); - virtual BHandler* ResolveSpecifier(BMessage* message, int32 index, - BMessage* specifier, int32 form, - const char* property); virtual void AttachedToWindow(); - virtual void Draw(BRect updateRect); - virtual void FrameResized(float width, float height); - virtual void ValueChanged(); - virtual void Decrement(); - virtual void Increment(); - virtual void MakeFocus(bool focus = true); - virtual void ResizeToPreferred(); - virtual void SetFlags(uint32 flags); - virtual void SetValueFromText(); - virtual void WindowActivated(bool active); - - alignment Alignment() const { return fAlignment; }; - virtual void SetAlignment(alignment align); - - float Divider() const { return fDivider; }; - virtual void SetDivider(float position); - - bool IsEnabled() const { return fIsEnabled; }; virtual void SetEnabled(bool enable); - const char* Label() const { return fLabel; }; - virtual void SetLabel(const char* text); + int32 MaxValue() const { return fMaxValue; } + virtual void SetMaxValue(int32 max); - uint32 Precision() const { return fPrecision; }; - virtual void SetPrecision(uint32 precision) { fPrecision = precision; }; + int32 MinValue() const { return fMinValue; } + virtual void SetMinValue(int32 min); - double MaxValue() const { return fMaxValue; } - virtual void SetMaxValue(double max); + void Range(int32* min, int32* max); + virtual void SetRange(int32 min, int32 max); - double MinValue() const { return fMinValue; } - virtual void SetMinValue(double min); - - void Range(double* min, double* max); - virtual void SetRange(double min, double max); - - double Step() const { return fStep; } - virtual void SetStep(double step) { fStep = step; }; - - double Value() const { return fValue; }; - virtual void SetValue(double value); - - bool IsDecrementEnabled() const; - virtual void SetDecrementEnabled(bool enable); - - bool IsIncrementEnabled() const; - virtual void SetIncrementEnabled(bool enable); - - virtual BSize MinSize(); - virtual BSize MaxSize(); - virtual BSize PreferredSize(); - virtual BAlignment LayoutAlignment(); - - BLayoutItem* CreateLabelLayoutItem(); - BLayoutItem* CreateTextViewLayoutItem(); - - BTextView* TextView() const; + int32 Value() const { return fValue; }; + virtual void SetValue(int32 value); + virtual void SetValueFromText(); private: // FBC padding @@ -131,54 +72,16 @@ private: virtual void _ReservedSpinner2(); virtual void _ReservedSpinner1(); -protected: - virtual status_t AllArchived(BMessage* into) const; - virtual status_t AllUnarchived(const BMessage* from); - - virtual void LayoutInvalidated(bool descendants); - virtual void DoLayout(); - private: - class LabelLayoutItem; - class TextViewLayoutItem; - struct LayoutData; - - friend class SpinnerArrow; - friend class SpinnerTextView; - - friend class LabelLayoutItem; - friend class TextViewLayoutItem; - friend struct LayoutData; - - void _DrawLabel(BRect updateRect); - void _DrawTextView(BRect updateRect); void _InitObject(); - void _LayoutTextView(); - void _UpdateFrame(); - void _UpdateTextViewColors(bool enable); - void _ValidateLayoutData(); - BSpinner& operator=(const BSpinner& other); - - alignment fAlignment; - float fDivider; - bool fIsEnabled; - const char* fLabel; - double fMinValue; - double fMaxValue; - double fStep; - double fValue; - uint32 fPrecision; - - LayoutData* fLayoutData; - - SpinnerTextView* fTextView; - SpinnerArrow* fIncrement; - SpinnerArrow* fDecrement; + int32 fMinValue; + int32 fMaxValue; + int32 fValue; // FBC padding uint32 _reserved[20]; }; -#endif // SPINNER_H +#endif // _SPINNER_H diff --git a/src/apps/deskbar/PreferencesWindow.cpp b/src/apps/deskbar/PreferencesWindow.cpp index ea88ed5116..c3be5d630b 100644 --- a/src/apps/deskbar/PreferencesWindow.cpp +++ b/src/apps/deskbar/PreferencesWindow.cpp @@ -103,23 +103,17 @@ PreferencesWindow::PreferencesWindow(BRect frame) // Menu settings fMenuRecentDocuments->SetValue(fSettings.recentDocsEnabled); fMenuRecentDocumentCount->SetEnabled(fSettings.recentDocsEnabled); - fMenuRecentDocumentCount->SetPrecision(0); fMenuRecentDocumentCount->SetRange(0, 50); - fMenuRecentDocumentCount->SetStep(1); fMenuRecentDocumentCount->SetValue(fSettings.recentDocsCount); fMenuRecentApplications->SetValue(fSettings.recentAppsEnabled); fMenuRecentApplicationCount->SetEnabled(fSettings.recentAppsEnabled); - fMenuRecentApplicationCount->SetPrecision(0); fMenuRecentApplicationCount->SetRange(0, 50); - fMenuRecentApplicationCount->SetStep(1); fMenuRecentApplicationCount->SetValue(fSettings.recentAppsCount); fMenuRecentFolders->SetValue(fSettings.recentFoldersEnabled); fMenuRecentFolderCount->SetEnabled(fSettings.recentFoldersEnabled); - fMenuRecentFolderCount->SetPrecision(0); fMenuRecentFolderCount->SetRange(0, 50); - fMenuRecentFolderCount->SetStep(1); fMenuRecentFolderCount->SetValue(fSettings.recentFoldersCount); // Applications settings diff --git a/src/apps/webpositive/SettingsWindow.cpp b/src/apps/webpositive/SettingsWindow.cpp index 6c62d11bd8..ae71aee8f2 100644 --- a/src/apps/webpositive/SettingsWindow.cpp +++ b/src/apps/webpositive/SettingsWindow.cpp @@ -307,9 +307,7 @@ SettingsWindow::_CreateGeneralPage(float spacing) fDaysInHistory = new BSpinner("days in history", B_TRANSLATE("Number of days to keep links in History menu:"), new BMessage(MSG_HISTORY_MENU_DAYS_CHANGED)); - fDaysInHistory->SetPrecision(0); fDaysInHistory->SetRange(1, 35); - fDaysInHistory->SetStep(1); fDaysInHistory->SetValue( BrowsingHistory::DefaultInstance()->MaxHistoryItemAge()); diff --git a/src/kits/interface/AbstractSpinner.cpp b/src/kits/interface/AbstractSpinner.cpp new file mode 100644 index 0000000000..1a1c31bb85 --- /dev/null +++ b/src/kits/interface/AbstractSpinner.cpp @@ -0,0 +1,1690 @@ +/* + * Copyright 2004 DarkWyrm + * Copyright 2013 FeemanLou + * Copyright 2014-2015 Haiku, Inc. All rights reserved. + * + * Distributed under the terms of the MIT license. + * + * Originally written by DarkWyrm + * Updated by FreemanLou as part of Google GCI 2013 + * + * Authors: + * DarkWyrm, darkwyrm@earthlink.net + * FeemanLou + * John Scipione, jscipione@gmail.com + */ + + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Thread.h" + + +static const float kFrameMargin = 2.0f; + +const char* const kFrameField = "BAbstractSpinner:layoutItem:frame"; +const char* const kLabelItemField = "BAbstractSpinner:labelItem"; +const char* const kTextViewItemField = "BAbstractSpinner:textViewItem"; + + +static property_info sProperties[] = { + { + "Align", + { B_GET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0 }, + "Returns the alignment of the spinner label.", + 0, + { B_INT32_TYPE } + }, + { + "Align", + { B_SET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0}, + "Sets the alignment of the spinner label.", + 0, + { B_INT32_TYPE } + }, + + { + "ButtonStyle", + { B_GET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0 }, + "Returns the style of the spinner buttons.", + 0, + { B_INT32_TYPE } + }, + { + "ButtonStyle", + { B_SET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0}, + "Sets the style of the spinner buttons.", + 0, + { B_INT32_TYPE } + }, + + { + "Divider", + { B_GET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0 }, + "Returns the divider position of the spinner.", + 0, + { B_FLOAT_TYPE } + }, + { + "Divider", + { B_SET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0}, + "Sets the divider position of the spinner.", + 0, + { B_FLOAT_TYPE } + }, + + { + "Enabled", + { B_GET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0 }, + "Returns whether or not the spinner is enabled.", + 0, + { B_BOOL_TYPE } + }, + { + "Enabled", + { B_SET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0}, + "Sets whether or not the spinner is enabled.", + 0, + { B_BOOL_TYPE } + }, + + { + "Label", + { B_GET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0 }, + "Returns the spinner label.", + 0, + { B_STRING_TYPE } + }, + { + "Label", + { B_SET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0}, + "Sets the spinner label.", + 0, + { B_STRING_TYPE } + }, + + { + "Message", + { B_GET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0 }, + "Returns the spinner invocation message.", + 0, + { B_MESSAGE_TYPE } + }, + { + "Message", + { B_SET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0}, + "Sets the spinner invocation message.", + 0, + { B_MESSAGE_TYPE } + }, + + { 0 } +}; + + +typedef enum { + SPINNER_INCREMENT, + SPINNER_DECREMENT +} spinner_direction; + + +class SpinnerButton : public BView { +public: + SpinnerButton(BRect frame, const char* name, + spinner_direction direction); + virtual ~SpinnerButton(); + + virtual void AttachedToWindow(); + virtual void DetachedFromWindow(); + virtual void Draw(BRect updateRect); + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* message); + + bool IsEnabled() const { return fIsEnabled; } + virtual void SetEnabled(bool enable) { fIsEnabled = enable; }; + +private: + void _DoneTracking(BPoint where); + void _Track(BPoint where, uint32); + + spinner_direction fSpinnerDirection; + BAbstractSpinner* fParent; + bool fIsEnabled; + bool fIsMouseDown; + bool fIsMouseOver; + bigtime_t fRepeatDelay; +}; + + +class SpinnerTextView : public BTextView { +public: + SpinnerTextView(BRect rect, BRect textRect); + virtual ~SpinnerTextView(); + + virtual void AttachedToWindow(); + virtual void DetachedFromWindow(); + virtual void KeyDown(const char* bytes, int32 numBytes); + virtual void MakeFocus(bool focus); + +private: + BAbstractSpinner* fParent; +}; + + +class BAbstractSpinner::LabelLayoutItem : public BAbstractLayoutItem { +public: + LabelLayoutItem(BAbstractSpinner* parent); + LabelLayoutItem(BMessage* archive); + + virtual bool IsVisible(); + virtual void SetVisible(bool visible); + + virtual BRect Frame(); + virtual void SetFrame(BRect frame); + + void SetParent(BAbstractSpinner* parent); + virtual BView* View(); + + virtual BSize BaseMinSize(); + virtual BSize BaseMaxSize(); + virtual BSize BasePreferredSize(); + virtual BAlignment BaseAlignment(); + + BRect FrameInParent() const; + + virtual status_t Archive(BMessage* into, bool deep = true) const; + static BArchivable* Instantiate(BMessage* from); + +private: + BAbstractSpinner* fParent; + BRect fFrame; +}; + + +class BAbstractSpinner::TextViewLayoutItem : public BAbstractLayoutItem { +public: + TextViewLayoutItem(BAbstractSpinner* parent); + TextViewLayoutItem(BMessage* archive); + + virtual bool IsVisible(); + virtual void SetVisible(bool visible); + + virtual BRect Frame(); + virtual void SetFrame(BRect frame); + + void SetParent(BAbstractSpinner* parent); + virtual BView* View(); + + virtual BSize BaseMinSize(); + virtual BSize BaseMaxSize(); + virtual BSize BasePreferredSize(); + virtual BAlignment BaseAlignment(); + + BRect FrameInParent() const; + + virtual status_t Archive(BMessage* into, bool deep = true) const; + static BArchivable* Instantiate(BMessage* from); + +private: + BAbstractSpinner* fParent; + BRect fFrame; +}; + + +struct BAbstractSpinner::LayoutData { + LayoutData(float width, float height) + : + label_layout_item(NULL), + text_view_layout_item(NULL), + label_width(0), + label_height(0), + text_view_width(0), + text_view_height(0), + previous_width(width), + previous_height(height), + valid(false) + { + } + + LabelLayoutItem* label_layout_item; + TextViewLayoutItem* text_view_layout_item; + + font_height font_info; + + float label_width; + float label_height; + float text_view_width; + float text_view_height; + + float previous_width; + float previous_height; + + BSize min; + BAlignment alignment; + + bool valid; +}; + + +// #pragma mark - SpinnerButton + + +SpinnerButton::SpinnerButton(BRect frame, const char* name, + spinner_direction direction) + : + BView(frame, name, B_FOLLOW_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW), + fSpinnerDirection(direction), + fParent(NULL), + fIsEnabled(true), + fIsMouseDown(false), + fIsMouseOver(false), + fRepeatDelay(100000) +{ + rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR); + SetViewColor(bgColor); + SetLowColor(bgColor); +} + + +SpinnerButton::~SpinnerButton() +{ +} + + +void +SpinnerButton::AttachedToWindow() +{ + fParent = static_cast(Parent()); + + BView::AttachedToWindow(); +} + + +void +SpinnerButton::DetachedFromWindow() +{ + fParent = NULL; + + BView::DetachedFromWindow(); +} + + +void +SpinnerButton::Draw(BRect updateRect) +{ + BRect rect(Bounds()); + if (!rect.IsValid() || !rect.Intersects(updateRect)) + return; + + BView::Draw(updateRect); + + float fgTint; + if (!fIsEnabled) + fgTint = B_DARKEN_1_TINT; + else if (fIsMouseDown) + fgTint = B_DARKEN_MAX_TINT; + else + fgTint = B_DARKEN_3_TINT; + + float bgTint; + if (fIsEnabled && fIsMouseOver) + bgTint = B_DARKEN_1_TINT; + else + bgTint = B_NO_TINT; + + rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR); + if (bgColor.red + bgColor.green + bgColor.blue <= 128 * 3) { + // if dark background make the tint lighter + fgTint = 2.0f - fgTint; + bgTint = 2.0f - bgTint; + } + + uint32 borders = be_control_look->B_TOP_BORDER + | be_control_look->B_BOTTOM_BORDER; + + if (fSpinnerDirection == SPINNER_INCREMENT) + borders |= be_control_look->B_RIGHT_BORDER; + else + borders |= be_control_look->B_LEFT_BORDER; + + // draw the button + be_control_look->DrawButtonFrame(this, rect, updateRect, + tint_color(bgColor, fgTint), bgColor, 0, borders); + be_control_look->DrawButtonBackground(this, rect, updateRect, + tint_color(bgColor, bgTint), 0, borders); + + switch (fParent->ButtonStyle()) { + case SPINNER_BUTTON_HORIZONTAL_ARROWS: + { + int32 arrowDirection = fSpinnerDirection == SPINNER_INCREMENT + ? be_control_look->B_RIGHT_ARROW + : be_control_look->B_LEFT_ARROW; + + rect.InsetBy(0.0f, 1.0f); + be_control_look->DrawArrowShape(this, rect, updateRect, bgColor, + arrowDirection, 0, fgTint); + break; + } + + case SPINNER_BUTTON_VERTICAL_ARROWS: + { + int32 arrowDirection = fSpinnerDirection == SPINNER_INCREMENT + ? be_control_look->B_UP_ARROW + : be_control_look->B_DOWN_ARROW; + + rect.InsetBy(0.0f, 1.0f); + be_control_look->DrawArrowShape(this, rect, updateRect, bgColor, + arrowDirection, 0, fgTint); + break; + } + + default: + case SPINNER_BUTTON_PLUS_MINUS: + { + BFont font; + fParent->GetFont(&font); + float inset = floorf(font.Size() / 4); + rect.InsetBy(inset, inset); + + if (rect.IntegerWidth() % 2 != 0) + rect.right -= 1; + + if (rect.IntegerHeight() % 2 != 0) + rect.bottom -= 1; + + SetHighColor(tint_color(bgColor, fgTint)); + + // draw the +/- + float halfHeight = floorf(rect.Height() / 2); + StrokeLine(BPoint(rect.left, rect.top + halfHeight), + BPoint(rect.right, rect.top + halfHeight)); + if (fSpinnerDirection == SPINNER_INCREMENT) { + float halfWidth = floorf(rect.Width() / 2); + StrokeLine(BPoint(rect.left + halfWidth, rect.top), + BPoint(rect.left + halfWidth, rect.bottom)); + } + } + } +} + + +void +SpinnerButton::MouseDown(BPoint where) +{ + if (fIsEnabled) { + fIsMouseDown = true; + Invalidate(); + fRepeatDelay = 100000; + MouseDownThread::TrackMouse(this, + &SpinnerButton::_DoneTracking, &SpinnerButton::_Track); + } + + BView::MouseDown(where); +} + + +void +SpinnerButton::MouseMoved(BPoint where, uint32 transit, + const BMessage* message) +{ + switch (transit) { + case B_ENTERED_VIEW: + case B_INSIDE_VIEW: + { + BPoint where; + uint32 buttons; + GetMouse(&where, &buttons); + fIsMouseOver = Bounds().Contains(where) && buttons == 0; + if (!fIsMouseDown) + Invalidate(); + + break; + } + + case B_EXITED_VIEW: + case B_OUTSIDE_VIEW: + fIsMouseOver = false; + MouseUp(Bounds().LeftTop()); + break; + } + + BView::MouseMoved(where, transit, message); +} + + +void +SpinnerButton::MouseUp(BPoint where) +{ + fIsMouseDown = false; + Invalidate(); + + BView::MouseUp(where); +} + + +// #pragma mark - SpinnerButton private methods + + +void +SpinnerButton::_DoneTracking(BPoint where) +{ + if (fIsMouseDown || !Bounds().Contains(where)) + fIsMouseDown = false; +} + + +void +SpinnerButton::_Track(BPoint where, uint32) +{ + if (fParent == NULL || !Bounds().Contains(where)) { + fIsMouseDown = false; + return; + } + fIsMouseDown = true; + + fSpinnerDirection == SPINNER_INCREMENT + ? fParent->Increment() + : fParent->Decrement(); + + snooze(fRepeatDelay); + fRepeatDelay = 10000; +} + + +// #pragma mark - SpinnerTextView + + +SpinnerTextView::SpinnerTextView(BRect rect, BRect textRect) + : + BTextView(rect, "textview", textRect, B_FOLLOW_ALL, + B_WILL_DRAW | B_NAVIGABLE), + fParent(NULL) +{ + rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR); + SetViewColor(bgColor); + SetLowColor(bgColor); +} + + +SpinnerTextView::~SpinnerTextView() +{ +} + + +void +SpinnerTextView::AttachedToWindow() +{ + fParent = static_cast(Parent()); + + BTextView::AttachedToWindow(); +} + + +void +SpinnerTextView::DetachedFromWindow() +{ + fParent = NULL; + + BTextView::DetachedFromWindow(); +} + + +void +SpinnerTextView::KeyDown(const char* bytes, int32 numBytes) +{ + if (fParent == NULL) { + BTextView::KeyDown(bytes, numBytes); + return; + } + + switch (bytes[0]) { + case B_ENTER: + case B_SPACE: + fParent->SetValueFromText(); + break; + + case B_TAB: + fParent->KeyDown(bytes, numBytes); + break; + + case B_LEFT_ARROW: + if (fParent->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS + && (modifiers() & B_CONTROL_KEY) != 0) { + // need to hold down control, otherwise can't move cursor + fParent->Decrement(); + } else + BTextView::KeyDown(bytes, numBytes); + break; + + case B_UP_ARROW: + if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS) + fParent->Increment(); + else + BTextView::KeyDown(bytes, numBytes); + break; + + case B_RIGHT_ARROW: + if (fParent->ButtonStyle() == SPINNER_BUTTON_HORIZONTAL_ARROWS + && (modifiers() & B_CONTROL_KEY) != 0) { + // need to hold down control, otherwise can't move cursor + fParent->Increment(); + } else + BTextView::KeyDown(bytes, numBytes); + break; + + case B_DOWN_ARROW: + if (fParent->ButtonStyle() != SPINNER_BUTTON_HORIZONTAL_ARROWS) + fParent->Decrement(); + else + BTextView::KeyDown(bytes, numBytes); + break; + + default: + BTextView::KeyDown(bytes, numBytes); + break; + } +} + + +void +SpinnerTextView::MakeFocus(bool focus) +{ + BTextView::MakeFocus(focus); + + if (fParent == NULL) + return; + + if (focus) + SelectAll(); + else + fParent->SetValueFromText(); + + fParent->_DrawTextView(fParent->Bounds()); +} + + +// #pragma mark - BAbstractSpinner::LabelLayoutItem + + +BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BAbstractSpinner* parent) + : + fParent(parent), + fFrame() +{ +} + + +BAbstractSpinner::LabelLayoutItem::LabelLayoutItem(BMessage* from) + : + BAbstractLayoutItem(from), + fParent(NULL), + fFrame() +{ + from->FindRect(kFrameField, &fFrame); +} + + +bool +BAbstractSpinner::LabelLayoutItem::IsVisible() +{ + return !fParent->IsHidden(fParent); +} + + +void +BAbstractSpinner::LabelLayoutItem::SetVisible(bool visible) +{ +} + + +BRect +BAbstractSpinner::LabelLayoutItem::Frame() +{ + return fFrame; +} + + +void +BAbstractSpinner::LabelLayoutItem::SetFrame(BRect frame) +{ + fFrame = frame; + fParent->_UpdateFrame(); +} + + +void +BAbstractSpinner::LabelLayoutItem::SetParent(BAbstractSpinner* parent) +{ + fParent = parent; +} + + +BView* +BAbstractSpinner::LabelLayoutItem::View() +{ + return fParent; +} + + +BSize +BAbstractSpinner::LabelLayoutItem::BaseMinSize() +{ + fParent->_ValidateLayoutData(); + + if (fParent->Label() == NULL) + return BSize(-1.0f, -1.0f); + + return BSize(fParent->fLayoutData->label_width + + be_control_look->DefaultLabelSpacing(), + fParent->fLayoutData->label_height); +} + + +BSize +BAbstractSpinner::LabelLayoutItem::BaseMaxSize() +{ + return BaseMinSize(); +} + + +BSize +BAbstractSpinner::LabelLayoutItem::BasePreferredSize() +{ + return BaseMinSize(); +} + + +BAlignment +BAbstractSpinner::LabelLayoutItem::BaseAlignment() +{ + return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); +} + + +BRect +BAbstractSpinner::LabelLayoutItem::FrameInParent() const +{ + return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top); +} + + +status_t +BAbstractSpinner::LabelLayoutItem::Archive(BMessage* into, bool deep) const +{ + BArchiver archiver(into); + status_t result = BAbstractLayoutItem::Archive(into, deep); + + if (result == B_OK) + result = into->AddRect(kFrameField, fFrame); + + return archiver.Finish(result); +} + + +BArchivable* +BAbstractSpinner::LabelLayoutItem::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "BAbstractSpinner::LabelLayoutItem")) + return new LabelLayoutItem(from); + + return NULL; +} + + +// #pragma mark - BAbstractSpinner::TextViewLayoutItem + + +BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BAbstractSpinner* parent) + : + fParent(parent), + fFrame() +{ + SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); +} + + +BAbstractSpinner::TextViewLayoutItem::TextViewLayoutItem(BMessage* from) + : + BAbstractLayoutItem(from), + fParent(NULL), + fFrame() +{ + from->FindRect(kFrameField, &fFrame); +} + + +bool +BAbstractSpinner::TextViewLayoutItem::IsVisible() +{ + return !fParent->IsHidden(fParent); +} + + +void +BAbstractSpinner::TextViewLayoutItem::SetVisible(bool visible) +{ + // not allowed +} + + +BRect +BAbstractSpinner::TextViewLayoutItem::Frame() +{ + return fFrame; +} + + +void +BAbstractSpinner::TextViewLayoutItem::SetFrame(BRect frame) +{ + fFrame = frame; + fParent->_UpdateFrame(); +} + + +void +BAbstractSpinner::TextViewLayoutItem::SetParent(BAbstractSpinner* parent) +{ + fParent = parent; +} + + +BView* +BAbstractSpinner::TextViewLayoutItem::View() +{ + return fParent; +} + + +BSize +BAbstractSpinner::TextViewLayoutItem::BaseMinSize() +{ + fParent->_ValidateLayoutData(); + + BSize size(fParent->fLayoutData->text_view_width, + fParent->fLayoutData->text_view_height); + + return size; +} + + +BSize +BAbstractSpinner::TextViewLayoutItem::BaseMaxSize() +{ + return BaseMinSize(); +} + + +BSize +BAbstractSpinner::TextViewLayoutItem::BasePreferredSize() +{ + return BaseMinSize(); +} + + +BAlignment +BAbstractSpinner::TextViewLayoutItem::BaseAlignment() +{ + return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); +} + + +BRect +BAbstractSpinner::TextViewLayoutItem::FrameInParent() const +{ + return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top); +} + + +status_t +BAbstractSpinner::TextViewLayoutItem::Archive(BMessage* into, bool deep) const +{ + BArchiver archiver(into); + status_t result = BAbstractLayoutItem::Archive(into, deep); + + if (result == B_OK) + result = into->AddRect(kFrameField, fFrame); + + return archiver.Finish(result); +} + + +BArchivable* +BAbstractSpinner::TextViewLayoutItem::Instantiate(BMessage* from) +{ + if (validate_instantiation(from, "BAbstractSpinner::TextViewLayoutItem")) + return new LabelLayoutItem(from); + + return NULL; +} + + +// #pragma mark - BAbstractSpinner + + +BAbstractSpinner::BAbstractSpinner(BRect frame, const char* name, const char* label, + BMessage* message, uint32 resizingMode, uint32 flags) + : + BControl(frame, name, label, message, resizingMode, + flags | B_WILL_DRAW | B_FRAME_EVENTS) +{ + _InitObject(); +} + + +BAbstractSpinner::BAbstractSpinner(const char* name, const char* label, BMessage* message, + uint32 flags) + : + BControl(name, label, message, flags | B_WILL_DRAW | B_FRAME_EVENTS) +{ + _InitObject(); +} + + +BAbstractSpinner::BAbstractSpinner(BMessage* data) + : + BControl(data), + fButtonStyle(SPINNER_BUTTON_PLUS_MINUS) +{ + _InitObject(); + + if (data->FindInt32("_align") != B_OK) + fAlignment = B_ALIGN_LEFT; + + if (data->FindInt32("_button_style") != B_OK) + fButtonStyle = SPINNER_BUTTON_PLUS_MINUS; + + if (data->FindInt32("_divider") != B_OK) + fDivider = 0.0f; +} + + +BAbstractSpinner::~BAbstractSpinner() +{ + delete fLayoutData; + fLayoutData = NULL; +} + + +BArchivable* +BAbstractSpinner::Instantiate(BMessage* data) +{ + // cannot instantiate an abstract spinner + return NULL; +} + + +status_t +BAbstractSpinner::Archive(BMessage* data, bool deep) const +{ + status_t status = BControl::Archive(data, deep); + data->AddString("class", "Spinner"); + + if (status == B_OK) + status = data->AddInt32("_align", fAlignment); + + if (status == B_OK) + data->AddInt32("_button_style", fButtonStyle); + + if (status == B_OK) + status = data->AddFloat("_divider", fDivider); + + return status; +} + + +status_t +BAbstractSpinner::GetSupportedSuites(BMessage* message) +{ + message->AddString("suites", "suite/vnd.Haiku-spinner"); + + BPropertyInfo prop_info(sProperties); + message->AddFlat("messages", &prop_info); + + return BView::GetSupportedSuites(message); +} + + +BHandler* +BAbstractSpinner::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, + int32 form, const char* property) +{ + return BView::ResolveSpecifier(message, index, specifier, form, + property); +} + + +void +BAbstractSpinner::AttachedToWindow() +{ + if (!Messenger().IsValid()) + SetTarget(Window()); + + BControl::SetValue(Value()); + // sets the text and enables or disables the arrows + + _UpdateTextViewColors(IsEnabled()); + fTextView->MakeEditable(IsEnabled()); + + BView::AttachedToWindow(); +} + + +void +BAbstractSpinner::Draw(BRect updateRect) +{ + _DrawLabel(updateRect); + _DrawTextView(updateRect); + fIncrement->Invalidate(); + fDecrement->Invalidate(); +} + + +void +BAbstractSpinner::FrameResized(float width, float height) +{ + BView::FrameResized(width, height); + + // TODO: this causes flickering still... + + // changes in width + + BRect bounds = Bounds(); + + if (bounds.Width() > fLayoutData->previous_width) { + // invalidate the region between the old and the new right border + BRect rect = bounds; + rect.left += fLayoutData->previous_width - kFrameMargin; + rect.right--; + Invalidate(rect); + } else if (bounds.Width() < fLayoutData->previous_width) { + // invalidate the region of the new right border + BRect rect = bounds; + rect.left = rect.right - kFrameMargin; + Invalidate(rect); + } + + // changes in height + + if (bounds.Height() > fLayoutData->previous_height) { + // invalidate the region between the old and the new bottom border + BRect rect = bounds; + rect.top += fLayoutData->previous_height - kFrameMargin; + rect.bottom--; + Invalidate(rect); + // invalidate label area + rect = bounds; + rect.right = fDivider; + Invalidate(rect); + } else if (bounds.Height() < fLayoutData->previous_height) { + // invalidate the region of the new bottom border + BRect rect = bounds; + rect.top = rect.bottom - kFrameMargin; + Invalidate(rect); + // invalidate label area + rect = bounds; + rect.right = fDivider; + Invalidate(rect); + } + + fLayoutData->previous_width = bounds.Width(); + fLayoutData->previous_height = bounds.Height(); +} + + +void +BAbstractSpinner::ValueChanged() +{ + // hook method - does nothing +} + + +void +BAbstractSpinner::MakeFocus(bool focus) +{ + fTextView->MakeFocus(focus); +} + + +void +BAbstractSpinner::ResizeToPreferred() +{ + BView::ResizeToPreferred(); + + const char* label = Label(); + if (label != NULL) { + fDivider = ceilf(StringWidth(label)) + + be_control_look->DefaultLabelSpacing(); + } else + fDivider = 0.0f; + + _LayoutTextView(); +} + + +void +BAbstractSpinner::SetFlags(uint32 flags) +{ + // If the textview is navigable, set it to not navigable if needed, + // else if it is not navigable, set it to navigable if needed + if (fTextView->Flags() & B_NAVIGABLE) { + if (!(flags & B_NAVIGABLE)) + fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE); + } else { + if (flags & B_NAVIGABLE) + fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE); + } + + // Don't make this one navigable + flags &= ~B_NAVIGABLE; + + BView::SetFlags(flags); +} + + +void +BAbstractSpinner::WindowActivated(bool active) +{ + _DrawTextView(fTextView->Frame()); +} + + +void +BAbstractSpinner::SetAlignment(alignment align) +{ + fAlignment = align; +} + + +void +BAbstractSpinner::SetButtonStyle(spinner_button_style buttonStyle) +{ + fButtonStyle = buttonStyle; +} + + +void +BAbstractSpinner::SetDivider(float position) +{ + position = roundf(position); + + float delta = fDivider - position; + if (delta == 0.0f) + return; + + fDivider = position; + + if ((Flags() & B_SUPPORTS_LAYOUT) != 0) { + // We should never get here, since layout support means, we also + // layout the divider, and don't use this method at all. + Relayout(); + } else { + _LayoutTextView(); + Invalidate(); + } +} + + +void +BAbstractSpinner::SetEnabled(bool enable) +{ + if (IsEnabled() == enable) + return; + + BControl::SetEnabled(enable); + + fTextView->MakeEditable(enable); + if (enable) + fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE); + else + fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE); + + _UpdateTextViewColors(enable); + fTextView->Invalidate(); + + _LayoutTextView(); + Invalidate(); + if (Window() != NULL) + Window()->UpdateIfNeeded(); +} + + +void +BAbstractSpinner::SetLabel(const char* label) +{ + BControl::SetLabel(label); + + if (Window() != NULL) + Window()->UpdateIfNeeded(); +} + + +bool +BAbstractSpinner::IsDecrementEnabled() const +{ + return fDecrement->IsEnabled(); +} + + +void +BAbstractSpinner::SetDecrementEnabled(bool enable) +{ + if (IsDecrementEnabled() == enable) + return; + + fDecrement->SetEnabled(enable); + fDecrement->Invalidate(); +} + + +bool +BAbstractSpinner::IsIncrementEnabled() const +{ + return fIncrement->IsEnabled(); +} + + +void +BAbstractSpinner::SetIncrementEnabled(bool enable) +{ + if (IsIncrementEnabled() == enable) + return; + + fIncrement->SetEnabled(enable); + fIncrement->Invalidate(); +} + + +BSize +BAbstractSpinner::MinSize() +{ + _ValidateLayoutData(); + return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min); +} + + +BSize +BAbstractSpinner::MaxSize() +{ + _ValidateLayoutData(); + + BSize max = fLayoutData->min; + max.width = B_SIZE_UNLIMITED; + + return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max); +} + + +BSize +BAbstractSpinner::PreferredSize() +{ + _ValidateLayoutData(); + return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), + fLayoutData->min); +} + + +BAlignment +BAbstractSpinner::LayoutAlignment() +{ + _ValidateLayoutData(); + return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), + BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER)); +} + + +BLayoutItem* +BAbstractSpinner::CreateLabelLayoutItem() +{ + if (fLayoutData->label_layout_item == NULL) + fLayoutData->label_layout_item = new LabelLayoutItem(this); + + return fLayoutData->label_layout_item; +} + + +BLayoutItem* +BAbstractSpinner::CreateTextViewLayoutItem() +{ + if (fLayoutData->text_view_layout_item == NULL) + fLayoutData->text_view_layout_item = new TextViewLayoutItem(this); + + return fLayoutData->text_view_layout_item; +} + + +BTextView* +BAbstractSpinner::TextView() const +{ + return dynamic_cast(fTextView); +} + + +// #pragma mark - BAbstractSpinner protected methods + + +status_t +BAbstractSpinner::AllArchived(BMessage* into) const +{ + status_t result; + if ((result = BControl::AllArchived(into)) != B_OK) + return result; + + BArchiver archiver(into); + + BArchivable* textViewItem = fLayoutData->text_view_layout_item; + if (archiver.IsArchived(textViewItem)) + result = archiver.AddArchivable(kTextViewItemField, textViewItem); + + if (result != B_OK) + return result; + + BArchivable* labelBarItem = fLayoutData->label_layout_item; + if (archiver.IsArchived(labelBarItem)) + result = archiver.AddArchivable(kLabelItemField, labelBarItem); + + return result; +} + + +status_t +BAbstractSpinner::AllUnarchived(const BMessage* from) +{ + BUnarchiver unarchiver(from); + + status_t result = B_OK; + if ((result = BControl::AllUnarchived(from)) != B_OK) + return result; + + if (unarchiver.IsInstantiated(kTextViewItemField)) { + TextViewLayoutItem*& textViewItem + = fLayoutData->text_view_layout_item; + result = unarchiver.FindObject(kTextViewItemField, + BUnarchiver::B_DONT_ASSUME_OWNERSHIP, textViewItem); + + if (result == B_OK) + textViewItem->SetParent(this); + else + return result; + } + + if (unarchiver.IsInstantiated(kLabelItemField)) { + LabelLayoutItem*& labelItem = fLayoutData->label_layout_item; + result = unarchiver.FindObject(kLabelItemField, + BUnarchiver::B_DONT_ASSUME_OWNERSHIP, labelItem); + + if (result == B_OK) + labelItem->SetParent(this); + } + + return result; +} + + +void +BAbstractSpinner::DoLayout() +{ + if ((Flags() & B_SUPPORTS_LAYOUT) == 0) + return; + + if (GetLayout()) { + BControl::DoLayout(); + return; + } + + _ValidateLayoutData(); + + BSize size(Bounds().Size()); + if (size.width < fLayoutData->min.width) + size.width = fLayoutData->min.width; + + if (size.height < fLayoutData->min.height) + size.height = fLayoutData->min.height; + + float divider = 0; + if (fLayoutData->label_layout_item != NULL + && fLayoutData->text_view_layout_item != NULL + && fLayoutData->label_layout_item->Frame().IsValid() + && fLayoutData->text_view_layout_item->Frame().IsValid()) { + divider = fLayoutData->text_view_layout_item->Frame().left + - fLayoutData->label_layout_item->Frame().left; + } else if (fLayoutData->label_width > 0) { + divider = fLayoutData->label_width + + be_control_look->DefaultLabelSpacing(); + } + fDivider = divider; + + BRect dirty(fTextView->Frame()); + _LayoutTextView(); + + // invalidate dirty region + dirty = dirty | fTextView->Frame(); + dirty = dirty | fIncrement->Frame(); + dirty = dirty | fDecrement->Frame(); + + Invalidate(dirty); +} + + +void +BAbstractSpinner::LayoutInvalidated(bool descendants) +{ + if (fLayoutData != NULL) + fLayoutData->valid = false; +} + + +// #pragma mark - BAbstractSpinner private methods + + +void +BAbstractSpinner::_DrawLabel(BRect updateRect) +{ + BRect rect(Bounds()); + rect.right = fDivider; + if (!rect.IsValid() || !rect.Intersects(updateRect)) + return; + + _ValidateLayoutData(); + + const char* label = Label(); + if (label == NULL) + return; + + // horizontal position + float x; + switch (fAlignment) { + case B_ALIGN_RIGHT: + x = fDivider - fLayoutData->label_width - 3.0f; + break; + + case B_ALIGN_CENTER: + x = fDivider - roundf(fLayoutData->label_width / 2.0f); + break; + + default: + x = 0.0f; + break; + } + + // vertical position + font_height& fontHeight = fLayoutData->font_info; + float y = rect.top + + roundf((rect.Height() + 1.0f - fontHeight.ascent + - fontHeight.descent) / 2.0f) + + fontHeight.ascent + kFrameMargin * 2; + + uint32 flags = 0; + if (!IsEnabled()) + flags |= BControlLook::B_DISABLED; + + be_control_look->DrawLabel(this, label, LowColor(), flags, BPoint(x, y)); +} + + +void +BAbstractSpinner::_DrawTextView(BRect updateRect) +{ + BRect rect = fTextView->Frame(); + rect.InsetBy(-kFrameMargin, -kFrameMargin); + if (!rect.IsValid() || !rect.Intersects(updateRect)) + return; + + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + uint32 flags = 0; + if (!IsEnabled()) + flags |= BControlLook::B_DISABLED; + + if (fTextView->IsFocus() && Window()->IsActive()) + flags |= BControlLook::B_FOCUSED; + + be_control_look->DrawTextControlBorder(this, rect, updateRect, base, + flags); +} + + +void +BAbstractSpinner::_InitObject() +{ + rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR); + SetViewColor(bgColor); + SetLowColor(bgColor); + + fAlignment = B_ALIGN_LEFT; + fButtonStyle = SPINNER_BUTTON_PLUS_MINUS; + + if (Label() != NULL) { + fDivider = StringWidth(Label()) + + be_control_look->DefaultLabelSpacing(); + } else + fDivider = 0.0f; + + BControl::SetEnabled(true); + BControl::SetValue(0); + + BRect rect(Bounds()); + fLayoutData = new LayoutData(rect.Width(), rect.Height()); + + rect.left = fDivider; + rect.InsetBy(kFrameMargin, kFrameMargin); + rect.right -= rect.Height() * 2 + kFrameMargin * 2 + 1.0f; + BRect textRect(rect.OffsetToCopy(B_ORIGIN)); + + fTextView = new SpinnerTextView(rect, textRect); + AddChild(fTextView); + + rect.InsetBy(0.0f, -kFrameMargin); + + rect.left = rect.right + kFrameMargin * 2; + rect.right = rect.left + rect.Height() - kFrameMargin * 2; + + fDecrement = new SpinnerButton(rect, "decrement", SPINNER_DECREMENT); + AddChild(fDecrement); + + rect.left = rect.right + 1.0f; + rect.right = rect.left + rect.Height() - kFrameMargin * 2; + + fIncrement = new SpinnerButton(rect, "increment", SPINNER_INCREMENT); + AddChild(fIncrement); + + uint32 navigableFlags = Flags() & B_NAVIGABLE; + if (navigableFlags != 0) + BControl::SetFlags(Flags() & ~B_NAVIGABLE); +} + + +void +BAbstractSpinner::_LayoutTextView() +{ + BRect rect; + if (fLayoutData->text_view_layout_item != NULL) { + rect = fLayoutData->text_view_layout_item->FrameInParent(); + } else { + rect = Bounds(); + rect.left = fDivider; + } + rect.InsetBy(kFrameMargin, kFrameMargin); + rect.right -= rect.Height() * 2 + kFrameMargin * 2 + 1.0f; + + fTextView->MoveTo(rect.left, rect.top); + fTextView->ResizeTo(rect.Width(), rect.Height()); + fTextView->SetTextRect(rect.OffsetToCopy(B_ORIGIN)); + + rect.InsetBy(0.0f, -kFrameMargin); + + rect.left = rect.right + kFrameMargin * 2; + rect.right = rect.left + rect.Height() - kFrameMargin * 2; + + fDecrement->ResizeTo(rect.Width(), rect.Height()); + fDecrement->MoveTo(rect.LeftTop()); + + rect.left = rect.right + 1.0f; + rect.right = rect.left + rect.Height() - kFrameMargin * 2; + + fIncrement->ResizeTo(rect.Width(), rect.Height()); + fIncrement->MoveTo(rect.LeftTop()); +} + + +void +BAbstractSpinner::_UpdateFrame() +{ + if (fLayoutData->label_layout_item == NULL + || fLayoutData->text_view_layout_item == NULL) { + return; + } + + BRect labelFrame = fLayoutData->label_layout_item->Frame(); + BRect textViewFrame = fLayoutData->text_view_layout_item->Frame(); + + if (!labelFrame.IsValid() || !textViewFrame.IsValid()) + return; + + // update divider + fDivider = textViewFrame.left - labelFrame.left; + + BRect frame = textViewFrame | labelFrame; + MoveTo(frame.left, frame.top); + BSize oldSize = Bounds().Size(); + ResizeTo(frame.Width(), frame.Height()); + BSize newSize = Bounds().Size(); + + // If the size changes, ResizeTo() will trigger a relayout, otherwise + // we need to do that explicitly. + if (newSize != oldSize) + Relayout(); +} + + +void +BAbstractSpinner::_UpdateTextViewColors(bool enable) +{ + rgb_color textColor; + rgb_color bgColor; + BFont font; + + fTextView->GetFontAndColor(0, &font); + + if (enable) + textColor = ui_color(B_DOCUMENT_TEXT_COLOR); + else { + textColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), + B_DISABLED_LABEL_TINT); + } + + fTextView->SetFontAndColor(&font, B_FONT_ALL, &textColor); + + if (enable) + bgColor = ui_color(B_DOCUMENT_BACKGROUND_COLOR); + else { + bgColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), + B_LIGHTEN_2_TINT); + } + + fTextView->SetViewColor(bgColor); + fTextView->SetLowColor(bgColor); +} + + +void +BAbstractSpinner::_ValidateLayoutData() +{ + if (fLayoutData->valid) + return; + + font_height fontHeight = fLayoutData->font_info; + GetFontHeight(&fontHeight); + + if (Label() != NULL) { + fLayoutData->label_width = StringWidth(Label()); + fLayoutData->label_height = ceilf(fontHeight.ascent + + fontHeight.descent + fontHeight.leading); + } else { + fLayoutData->label_width = 0; + fLayoutData->label_height = 0; + } + + float divider = 0; + if (fLayoutData->label_width > 0) { + divider = ceilf(fLayoutData->label_width + + be_control_look->DefaultLabelSpacing()); + } + + if ((Flags() & B_SUPPORTS_LAYOUT) == 0) + divider = std::max(divider, fDivider); + + float minTextWidth = fTextView->StringWidth("99999"); + + float textViewHeight = fTextView->LineHeight(0) + kFrameMargin * 2; + float textViewWidth = minTextWidth + textViewHeight * 2; + + fLayoutData->text_view_width = textViewWidth; + fLayoutData->text_view_height = textViewHeight; + + BSize min(textViewWidth, textViewHeight); + if (divider > 0.0f) + min.width += divider; + + if (fLayoutData->label_height > min.height) + min.height = fLayoutData->label_height; + + fLayoutData->min = min; + fLayoutData->valid = true; + + ResetLayoutInvalidation(); +} + + +// FBC padding + +void BAbstractSpinner::_ReservedAbstractSpinner20() {} +void BAbstractSpinner::_ReservedAbstractSpinner19() {} +void BAbstractSpinner::_ReservedAbstractSpinner18() {} +void BAbstractSpinner::_ReservedAbstractSpinner17() {} +void BAbstractSpinner::_ReservedAbstractSpinner16() {} +void BAbstractSpinner::_ReservedAbstractSpinner15() {} +void BAbstractSpinner::_ReservedAbstractSpinner14() {} +void BAbstractSpinner::_ReservedAbstractSpinner13() {} +void BAbstractSpinner::_ReservedAbstractSpinner12() {} +void BAbstractSpinner::_ReservedAbstractSpinner11() {} +void BAbstractSpinner::_ReservedAbstractSpinner10() {} +void BAbstractSpinner::_ReservedAbstractSpinner9() {} +void BAbstractSpinner::_ReservedAbstractSpinner8() {} +void BAbstractSpinner::_ReservedAbstractSpinner7() {} +void BAbstractSpinner::_ReservedAbstractSpinner6() {} +void BAbstractSpinner::_ReservedAbstractSpinner5() {} +void BAbstractSpinner::_ReservedAbstractSpinner4() {} +void BAbstractSpinner::_ReservedAbstractSpinner3() {} +void BAbstractSpinner::_ReservedAbstractSpinner2() {} +void BAbstractSpinner::_ReservedAbstractSpinner1() {} diff --git a/src/kits/interface/DecimalSpinner.cpp b/src/kits/interface/DecimalSpinner.cpp new file mode 100644 index 0000000000..e772b53e59 --- /dev/null +++ b/src/kits/interface/DecimalSpinner.cpp @@ -0,0 +1,372 @@ +/* + * Copyright 2015 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + * + * Authors: + * John Scipione, jscipione@gmail.com + */ + + +#include + +#include +#include + +#include +#include + + +static double +roundTo(double value, uint32 n) +{ + return floor(value * pow(10.0, n) + 0.5) / pow(10.0, n); +} + + +static property_info sProperties[] = { + { + "MaxValue", + { B_GET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0 }, + "Returns the maximum value of the spinner.", + 0, + { B_DOUBLE_TYPE } + }, + { + "MaxValue", + { B_SET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0}, + "Sets the maximum value of the spinner.", + 0, + { B_DOUBLE_TYPE } + }, + + { + "MinValue", + { B_GET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0 }, + "Returns the minimum value of the spinner.", + 0, + { B_DOUBLE_TYPE } + }, + { + "MinValue", + { B_SET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0}, + "Sets the minimum value of the spinner.", + 0, + { B_DOUBLE_TYPE } + }, + + { + "Precision", + { B_SET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0}, + "Sets the number of decimal places of precision of the spinner.", + 0, + { B_UINT32_TYPE } + }, + { + "Precision", + { B_GET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0 }, + "Returns the number of decimal places of precision of the spinner.", + 0, + { B_UINT32_TYPE } + }, + + { + "Step", + { B_GET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0 }, + "Returns the step size of the spinner.", + 0, + { B_DOUBLE_TYPE } + }, + { + "Step", + { B_SET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0}, + "Sets the step size of the spinner.", + 0, + { B_DOUBLE_TYPE } + }, + + { + "Value", + { B_GET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0 }, + "Returns the value of the spinner.", + 0, + { B_DOUBLE_TYPE } + }, + { + "Value", + { B_SET_PROPERTY, 0 }, + { B_DIRECT_SPECIFIER, 0}, + "Sets the value of the spinner.", + 0, + { B_DOUBLE_TYPE } + }, + + { 0 } +}; + + +// #pragma mark - BDecimalSpinner + + +BDecimalSpinner::BDecimalSpinner(BRect frame, const char* name, + const char* label, BMessage* message, uint32 resizingMode, uint32 flags) + : + BAbstractSpinner(frame, name, label, message, resizingMode, flags) +{ + _InitObject(); +} + + +BDecimalSpinner::BDecimalSpinner(const char* name, const char* label, + BMessage* message, uint32 flags) + : + BAbstractSpinner(name, label, message, flags) +{ + _InitObject(); +} + + +BDecimalSpinner::BDecimalSpinner(BMessage* data) + : + BAbstractSpinner(data) +{ + _InitObject(); + + if (data->FindDouble("_max", &fMaxValue) != B_OK) + fMinValue = 100.0; + + if (data->FindDouble("_min", &fMinValue) != B_OK) + fMinValue = 0.0; + + if (data->FindUInt32("_precision", &fPrecision) != B_OK) + fPrecision = 2; + + if (data->FindDouble("_step", &fStep) != B_OK) + fStep = 1.0; + + if (data->FindDouble("_val", &fValue) != B_OK) + fValue = 0.0; +} + + +BDecimalSpinner::~BDecimalSpinner() +{ +} + + +BArchivable* +BDecimalSpinner::Instantiate(BMessage* data) +{ + if (validate_instantiation(data, "DecimalSpinner")) + return new BDecimalSpinner(data); + + return NULL; +} + + +status_t +BDecimalSpinner::Archive(BMessage* data, bool deep) const +{ + status_t status = BAbstractSpinner::Archive(data, deep); + data->AddString("class", "DecimalSpinner"); + + if (status == B_OK) + status = data->AddDouble("_max", fMaxValue); + + if (status == B_OK) + status = data->AddDouble("_min", fMinValue); + + if (status == B_OK) + status = data->AddUInt32("_precision", fPrecision); + + if (status == B_OK) + status = data->AddDouble("_step", fStep); + + if (status == B_OK) + status = data->AddDouble("_val", fValue); + + return status; +} + + +status_t +BDecimalSpinner::GetSupportedSuites(BMessage* message) +{ + message->AddString("suites", "suite/vnd.Haiku-decimal-spinner"); + + BPropertyInfo prop_info(sProperties); + message->AddFlat("messages", &prop_info); + + return BView::GetSupportedSuites(message); +} + + +void +BDecimalSpinner::AttachedToWindow() +{ + SetValue(fValue); + + BAbstractSpinner::AttachedToWindow(); +} + + +void +BDecimalSpinner::Decrement() +{ + SetValue(Value() - Step()); +} + + +void +BDecimalSpinner::Increment() +{ + SetValue(Value() + Step()); +} + + +void +BDecimalSpinner::SetEnabled(bool enable) +{ + if (IsEnabled() == enable) + return; + + SetIncrementEnabled(enable && Value() < fMaxValue); + SetDecrementEnabled(enable && Value() > fMinValue); + + BAbstractSpinner::SetEnabled(enable); +} + + +void +BDecimalSpinner::SetMaxValue(double max) +{ + fMaxValue = max; + if (fValue > fMaxValue) + SetValue(fMaxValue); +} + + +void +BDecimalSpinner::SetMinValue(double min) +{ + fMinValue = min; + if (fValue < fMinValue) + SetValue(fMinValue); +} + + +void +BDecimalSpinner::Range(double* min, double* max) +{ + *min = fMinValue; + *max = fMaxValue; +} + + +void +BDecimalSpinner::SetRange(double min, double max) +{ + SetMinValue(min); + SetMaxValue(max); +} + + +void +BDecimalSpinner::SetValue(int32 value) +{ + SetValue((double)value); +} + + +void +BDecimalSpinner::SetValue(double value) +{ + // clip to range + if (value < fMinValue) + value = fMinValue; + else if (value > fMaxValue) + value = fMaxValue; + + // update the text view + char* format; + asprintf(&format, "%%.%" B_PRId32 "f", fPrecision); + char* valueString; + asprintf(&valueString, format, value); + TextView()->SetText(valueString); + free(format); + free(valueString); + + // update the up and down arrows + SetIncrementEnabled(IsEnabled() && value < fMaxValue); + SetDecrementEnabled(IsEnabled() && value > fMinValue); + + if (value == fValue) + return; + + fValue = value; + ValueChanged(); + + Invoke(); + Invalidate(); +} + + +void +BDecimalSpinner::SetValueFromText() +{ + SetValue(roundTo(atof(TextView()->Text()), Precision())); +} + + +// #pragma mark - BDecimalSpinner private methods + + +void +BDecimalSpinner::_InitObject() +{ + fMaxValue = 100.0; + fMinValue = 0.0; + fPrecision = 2; + fStep = 1.0; + fValue = 0.0; + + TextView()->SetAlignment(B_ALIGN_RIGHT); + for (uint32 c = 0; c <= 42; c++) + TextView()->DisallowChar(c); + + TextView()->DisallowChar('/'); + for (uint32 c = 58; c <= 127; c++) + TextView()->DisallowChar(c); +} + + +// FBC padding + +void BDecimalSpinner::_ReservedDecimalSpinner20() {} +void BDecimalSpinner::_ReservedDecimalSpinner19() {} +void BDecimalSpinner::_ReservedDecimalSpinner18() {} +void BDecimalSpinner::_ReservedDecimalSpinner17() {} +void BDecimalSpinner::_ReservedDecimalSpinner16() {} +void BDecimalSpinner::_ReservedDecimalSpinner15() {} +void BDecimalSpinner::_ReservedDecimalSpinner14() {} +void BDecimalSpinner::_ReservedDecimalSpinner13() {} +void BDecimalSpinner::_ReservedDecimalSpinner12() {} +void BDecimalSpinner::_ReservedDecimalSpinner11() {} +void BDecimalSpinner::_ReservedDecimalSpinner10() {} +void BDecimalSpinner::_ReservedDecimalSpinner9() {} +void BDecimalSpinner::_ReservedDecimalSpinner8() {} +void BDecimalSpinner::_ReservedDecimalSpinner7() {} +void BDecimalSpinner::_ReservedDecimalSpinner6() {} +void BDecimalSpinner::_ReservedDecimalSpinner5() {} +void BDecimalSpinner::_ReservedDecimalSpinner4() {} +void BDecimalSpinner::_ReservedDecimalSpinner3() {} +void BDecimalSpinner::_ReservedDecimalSpinner2() {} +void BDecimalSpinner::_ReservedDecimalSpinner1() {} diff --git a/src/kits/interface/Jamfile b/src/kits/interface/Jamfile index 79a00d0d00..cec0540f17 100644 --- a/src/kits/interface/Jamfile +++ b/src/kits/interface/Jamfile @@ -42,6 +42,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { AboutWindow.cpp AbstractLayout.cpp AbstractLayoutItem.cpp + AbstractSpinner.cpp AffineTransform.cpp Alert.cpp Alignment.cpp @@ -60,6 +61,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { ControlLook.cpp DecorInfo.cpp Deskbar.cpp + DecimalSpinner.cpp Dragger.cpp Font.cpp Gradient.cpp diff --git a/src/kits/interface/Spinner.cpp b/src/kits/interface/Spinner.cpp index c3d4c057a8..e6d6a63b71 100644 --- a/src/kits/interface/Spinner.cpp +++ b/src/kits/interface/Spinner.cpp @@ -1,152 +1,30 @@ /* - * Copyright 2004 DarkWyrm - * Copyright 2013 FeemanLou - * Copyright 2014-2015 Haiku, Inc. All rights reserved. - * + * Copyright 2015 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT license. * - * Originally written by DarkWyrm - * Updated by FreemanLou as part of Google GCI 2013 - * * Authors: - * DarkWyrm, darkwyrm@earthlink.net - * FeemanLou * John Scipione, jscipione@gmail.com */ -#include "Spinner.h" +#include -#include -#include +#include #include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include #include -#include - -#include "Thread.h" - - -static const float kFrameMargin = 2.0f; - -const char* const kFrameField = "BSpinner:layoutItem:frame"; -const char* const kLabelItemField = "BSpinner:labelItem"; -const char* const kTextViewItemField = "BSpinner:textViewItem"; - - -static double -roundTo(double value, uint32 n) -{ - return floor(value * pow(10.0, n) + 0.5) / pow(10.0, n); -} static property_info sProperties[] = { - { - "Align", - { B_GET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0 }, - "Returns the alignment of the spinner label.", - 0, - { B_INT32_TYPE } - }, - { - "Align", - { B_SET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0}, - "Sets the alignment of the spinner label.", - 0, - { B_INT32_TYPE } - }, - - { - "Divider", - { B_GET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0 }, - "Returns the divider position of the spinner.", - 0, - { B_FLOAT_TYPE } - }, - { - "Divider", - { B_SET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0}, - "Sets the divider position of the spinner.", - 0, - { B_FLOAT_TYPE } - }, - - { - "Enabled", - { B_GET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0 }, - "Returns whether or not the spinner is enabled.", - 0, - { B_BOOL_TYPE } - }, - { - "Enabled", - { B_SET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0}, - "Sets whether or not the spinner is enabled.", - 0, - { B_BOOL_TYPE } - }, - - { - "Label", - { B_GET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0 }, - "Returns the spinner label.", - 0, - { B_STRING_TYPE } - }, - { - "Label", - { B_SET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0}, - "Sets the spinner label.", - 0, - { B_STRING_TYPE } - }, - - { - "Message", - { B_GET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0 }, - "Returns the spinner invocation message.", - 0, - { B_MESSAGE_TYPE } - }, - { - "Message", - { B_SET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0}, - "Sets the spinner invocation message.", - 0, - { B_MESSAGE_TYPE } - }, - { "MaxValue", { B_GET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 }, "Returns the maximum value of the spinner.", 0, - { B_DOUBLE_TYPE } + { B_INT32_TYPE } }, { "MaxValue", @@ -154,7 +32,7 @@ static property_info sProperties[] = { { B_DIRECT_SPECIFIER, 0}, "Sets the maximum value of the spinner.", 0, - { B_DOUBLE_TYPE } + { B_INT32_TYPE } }, { @@ -163,7 +41,7 @@ static property_info sProperties[] = { { B_DIRECT_SPECIFIER, 0 }, "Returns the minimum value of the spinner.", 0, - { B_DOUBLE_TYPE } + { B_INT32_TYPE } }, { "MinValue", @@ -171,41 +49,7 @@ static property_info sProperties[] = { { B_DIRECT_SPECIFIER, 0}, "Sets the minimum value of the spinner.", 0, - { B_DOUBLE_TYPE } - }, - - { - "Precision", - { B_SET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0}, - "Sets the number of decimal places of precision of the spinner.", - 0, - { B_UINT32_TYPE } - }, - { - "Precision", - { B_GET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0 }, - "Returns the number of decimal places of precision of the spinner.", - 0, - { B_UINT32_TYPE } - }, - - { - "Step", - { B_GET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0 }, - "Returns the step size of the spinner.", - 0, - { B_DOUBLE_TYPE } - }, - { - "Step", - { B_SET_PROPERTY, 0 }, - { B_DIRECT_SPECIFIER, 0}, - "Sets the step size of the spinner.", - 0, - { B_DOUBLE_TYPE } + { B_INT32_TYPE } }, { @@ -214,7 +58,7 @@ static property_info sProperties[] = { { B_DIRECT_SPECIFIER, 0 }, "Returns the value of the spinner.", 0, - { B_DOUBLE_TYPE } + { B_INT32_TYPE } }, { "Value", @@ -222,774 +66,53 @@ static property_info sProperties[] = { { B_DIRECT_SPECIFIER, 0}, "Sets the value of the spinner.", 0, - { B_DOUBLE_TYPE } + { B_INT32_TYPE } }, { 0 } }; -typedef enum { - SPINNER_INCREMENT, - SPINNER_DECREMENT -} spinner_direction; - - -class SpinnerArrow : public BView { -public: - SpinnerArrow(BRect frame, const char* name, - spinner_direction direction); - virtual ~SpinnerArrow(); - - virtual void AttachedToWindow(); - virtual void DetachedFromWindow(); - virtual void Draw(BRect updateRect); - virtual void MouseDown(BPoint where); - virtual void MouseUp(BPoint where); - virtual void MouseMoved(BPoint where, uint32 transit, - const BMessage* message); - - bool IsEnabled() const { return fIsEnabled; } - virtual void SetEnabled(bool enable) { fIsEnabled = enable; }; - -private: - void _DoneTracking(BPoint where); - void _Track(BPoint where, uint32); - - spinner_direction fSpinnerDirection; - BSpinner* fParent; - bool fIsEnabled; - bool fIsMouseDown; - bool fIsMouseOver; - bigtime_t fRepeatDelay; -}; - - -class SpinnerTextView : public BTextView { -public: - SpinnerTextView(BRect rect, BRect textRect); - virtual ~SpinnerTextView(); - - virtual void AttachedToWindow(); - virtual void DetachedFromWindow(); - virtual void KeyDown(const char* bytes, int32 numBytes); - virtual void MakeFocus(bool focus); - -private: - BSpinner* fParent; -}; - - -class BSpinner::LabelLayoutItem : public BAbstractLayoutItem { -public: - LabelLayoutItem(BSpinner* parent); - LabelLayoutItem(BMessage* archive); - - virtual bool IsVisible(); - virtual void SetVisible(bool visible); - - virtual BRect Frame(); - virtual void SetFrame(BRect frame); - - void SetParent(BSpinner* parent); - virtual BView* View(); - - virtual BSize BaseMinSize(); - virtual BSize BaseMaxSize(); - virtual BSize BasePreferredSize(); - virtual BAlignment BaseAlignment(); - - BRect FrameInParent() const; - - virtual status_t Archive(BMessage* into, bool deep = true) const; - static BArchivable* Instantiate(BMessage* from); - -private: - BSpinner* fParent; - BRect fFrame; -}; - - -class BSpinner::TextViewLayoutItem : public BAbstractLayoutItem { -public: - TextViewLayoutItem(BSpinner* parent); - TextViewLayoutItem(BMessage* archive); - - virtual bool IsVisible(); - virtual void SetVisible(bool visible); - - virtual BRect Frame(); - virtual void SetFrame(BRect frame); - - void SetParent(BSpinner* parent); - virtual BView* View(); - - virtual BSize BaseMinSize(); - virtual BSize BaseMaxSize(); - virtual BSize BasePreferredSize(); - virtual BAlignment BaseAlignment(); - - BRect FrameInParent() const; - - virtual status_t Archive(BMessage* into, bool deep = true) const; - static BArchivable* Instantiate(BMessage* from); - -private: - BSpinner* fParent; - BRect fFrame; -}; - - -struct BSpinner::LayoutData { - LayoutData(float width, float height) - : - label_layout_item(NULL), - text_view_layout_item(NULL), - label_width(0), - label_height(0), - text_view_width(0), - text_view_height(0), - previous_width(width), - previous_height(height), - valid(false) - { - } - - LabelLayoutItem* label_layout_item; - TextViewLayoutItem* text_view_layout_item; - - font_height font_info; - - float label_width; - float label_height; - float text_view_width; - float text_view_height; - - float previous_width; - float previous_height; - - BSize min; - BAlignment alignment; - - bool valid; -}; - - -// #pragma mark - SpinnerArrow - - -SpinnerArrow::SpinnerArrow(BRect frame, const char* name, - spinner_direction direction) - : - BView(frame, name, B_FOLLOW_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW), - fSpinnerDirection(direction), - fParent(NULL), - fIsEnabled(true), - fIsMouseDown(false), - fIsMouseOver(false), - fRepeatDelay(100000) -{ - rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR); - SetViewColor(bgColor); - SetLowColor(bgColor); -} - - -SpinnerArrow::~SpinnerArrow() -{ -} - - -void -SpinnerArrow::AttachedToWindow() -{ - fParent = static_cast(Parent()); - - BView::AttachedToWindow(); -} - - -void -SpinnerArrow::DetachedFromWindow() -{ - fParent = NULL; - - BView::DetachedFromWindow(); -} - - -void -SpinnerArrow::Draw(BRect updateRect) -{ - BRect rect(Bounds()); - if (!rect.IsValid() || !rect.Intersects(updateRect)) - return; - - BView::Draw(updateRect); - - float fgTint; - if (!fIsEnabled) - fgTint = B_DARKEN_1_TINT; - else if (fIsMouseDown) - fgTint = B_DARKEN_MAX_TINT; - else - fgTint = B_DARKEN_3_TINT; - - float bgTint; - if (fIsEnabled && fIsMouseOver) - bgTint = B_DARKEN_1_TINT; - else - bgTint = B_NO_TINT; - - rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR); - if (bgColor.red + bgColor.green + bgColor.blue <= 128 * 3) { - // if dark background make the tint lighter - fgTint = 2.0f - fgTint; - bgTint = 2.0f - bgTint; - } - - uint32 borders = be_control_look->B_TOP_BORDER - | be_control_look->B_BOTTOM_BORDER; - - if (fSpinnerDirection == SPINNER_INCREMENT) - borders |= be_control_look->B_RIGHT_BORDER; - else - borders |= be_control_look->B_LEFT_BORDER; - - // draw the button - be_control_look->DrawButtonFrame(this, rect, updateRect, - tint_color(bgColor, fgTint), bgColor, 0, borders); - be_control_look->DrawButtonBackground(this, rect, updateRect, - tint_color(bgColor, bgTint), 0, borders); - - BFont font; - fParent->GetFont(&font); - float inset = floorf(font.Size() / 4); - rect.InsetBy(inset, inset); - - if (rect.IntegerWidth() % 2 != 0) - rect.right -= 1; - - if (rect.IntegerHeight() % 2 != 0) - rect.bottom -= 1; - - SetHighColor(tint_color(bgColor, fgTint)); - - // draw the +/- - float halfHeight = floorf(rect.Height() / 2); - StrokeLine(BPoint(rect.left, rect.top + halfHeight), - BPoint(rect.right, rect.top + halfHeight)); - if (fSpinnerDirection == SPINNER_INCREMENT) { - float halfWidth = floorf(rect.Width() / 2); - StrokeLine(BPoint(rect.left + halfWidth, rect.top), - BPoint(rect.left + halfWidth, rect.bottom)); - } -} - - -void -SpinnerArrow::MouseDown(BPoint where) -{ - if (fIsEnabled) { - fIsMouseDown = true; - Invalidate(); - fRepeatDelay = 100000; - MouseDownThread::TrackMouse(this, - &SpinnerArrow::_DoneTracking, &SpinnerArrow::_Track); - } - - BView::MouseDown(where); -} - - -void -SpinnerArrow::MouseMoved(BPoint where, uint32 transit, - const BMessage* message) -{ - switch (transit) { - case B_ENTERED_VIEW: - case B_INSIDE_VIEW: - { - BPoint where; - uint32 buttons; - GetMouse(&where, &buttons); - fIsMouseOver = Bounds().Contains(where) && buttons == 0; - if (!fIsMouseDown) - Invalidate(); - - break; - } - - case B_EXITED_VIEW: - case B_OUTSIDE_VIEW: - fIsMouseOver = false; - MouseUp(Bounds().LeftTop()); - break; - } - - BView::MouseMoved(where, transit, message); -} - - -void -SpinnerArrow::MouseUp(BPoint where) -{ - fIsMouseDown = false; - Invalidate(); - - BView::MouseUp(where); -} - - -// #pragma mark - SpinnerArrow private methods - - -void -SpinnerArrow::_DoneTracking(BPoint where) -{ - if (fIsMouseDown || !Bounds().Contains(where)) - fIsMouseDown = false; -} - - -void -SpinnerArrow::_Track(BPoint where, uint32) -{ - if (fParent == NULL || !Bounds().Contains(where)) { - fIsMouseDown = false; - return; - } - fIsMouseDown = true; - - fSpinnerDirection == SPINNER_INCREMENT - ? fParent->Increment() - : fParent->Decrement(); - - snooze(fRepeatDelay); - fRepeatDelay = 10000; -} - - -// #pragma mark - SpinnerTextView - - -SpinnerTextView::SpinnerTextView(BRect rect, BRect textRect) - : - BTextView(rect, "textview", textRect, B_FOLLOW_ALL, - B_WILL_DRAW | B_NAVIGABLE), - fParent(NULL) -{ - rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR); - SetViewColor(bgColor); - SetLowColor(bgColor); - - SetAlignment(B_ALIGN_RIGHT); - for (uint32 c = 0; c <= 42; c++) - DisallowChar(c); - - DisallowChar('/'); - for (uint32 c = 58; c <= 127; c++) - DisallowChar(c); -} - - -SpinnerTextView::~SpinnerTextView() -{ -} - - -void -SpinnerTextView::AttachedToWindow() -{ - fParent = static_cast(Parent()); - - BTextView::AttachedToWindow(); -} - - -void -SpinnerTextView::DetachedFromWindow() -{ - fParent = NULL; - - BTextView::DetachedFromWindow(); -} - - -void -SpinnerTextView::KeyDown(const char* bytes, int32 numBytes) -{ - if (fParent == NULL) { - BTextView::KeyDown(bytes, numBytes); - return; - } - - switch (bytes[0]) { - case B_ENTER: - case B_SPACE: - fParent->SetValueFromText(); - break; - - case B_TAB: - fParent->KeyDown(bytes, numBytes); - break; - - case B_UP_ARROW: - case B_PAGE_UP: - fParent->Increment(); - break; - - case B_DOWN_ARROW: - case B_PAGE_DOWN: - fParent->Decrement(); - break; - - default: - BTextView::KeyDown(bytes, numBytes); - } -} - - -void -SpinnerTextView::MakeFocus(bool focus) -{ - BTextView::MakeFocus(focus); - - if (fParent == NULL) - return; - - if (focus) - SelectAll(); - else - fParent->SetValueFromText(); - - fParent->_DrawTextView(fParent->Bounds()); -} - - -// #pragma mark - BSpinner::LabelLayoutItem - - -BSpinner::LabelLayoutItem::LabelLayoutItem(BSpinner* parent) - : - fParent(parent), - fFrame() -{ -} - - -BSpinner::LabelLayoutItem::LabelLayoutItem(BMessage* from) - : - BAbstractLayoutItem(from), - fParent(NULL), - fFrame() -{ - from->FindRect(kFrameField, &fFrame); -} - - -bool -BSpinner::LabelLayoutItem::IsVisible() -{ - return !fParent->IsHidden(fParent); -} - - -void -BSpinner::LabelLayoutItem::SetVisible(bool visible) -{ -} - - -BRect -BSpinner::LabelLayoutItem::Frame() -{ - return fFrame; -} - - -void -BSpinner::LabelLayoutItem::SetFrame(BRect frame) -{ - fFrame = frame; - fParent->_UpdateFrame(); -} - - -void -BSpinner::LabelLayoutItem::SetParent(BSpinner* parent) -{ - fParent = parent; -} - - -BView* -BSpinner::LabelLayoutItem::View() -{ - return fParent; -} - - -BSize -BSpinner::LabelLayoutItem::BaseMinSize() -{ - fParent->_ValidateLayoutData(); - - if (fParent->Label() == NULL) - return BSize(-1.0f, -1.0f); - - return BSize(fParent->fLayoutData->label_width - + be_control_look->DefaultLabelSpacing(), - fParent->fLayoutData->label_height); -} - - -BSize -BSpinner::LabelLayoutItem::BaseMaxSize() -{ - return BaseMinSize(); -} - - -BSize -BSpinner::LabelLayoutItem::BasePreferredSize() -{ - return BaseMinSize(); -} - - -BAlignment -BSpinner::LabelLayoutItem::BaseAlignment() -{ - return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); -} - - -BRect -BSpinner::LabelLayoutItem::FrameInParent() const -{ - return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top); -} - - -status_t -BSpinner::LabelLayoutItem::Archive(BMessage* into, bool deep) const -{ - BArchiver archiver(into); - status_t result = BAbstractLayoutItem::Archive(into, deep); - - if (result == B_OK) - result = into->AddRect(kFrameField, fFrame); - - return archiver.Finish(result); -} - - -BArchivable* -BSpinner::LabelLayoutItem::Instantiate(BMessage* from) -{ - if (validate_instantiation(from, "BSpinner::LabelLayoutItem")) - return new LabelLayoutItem(from); - - return NULL; -} - - -// #pragma mark - BSpinner::TextViewLayoutItem - - -BSpinner::TextViewLayoutItem::TextViewLayoutItem(BSpinner* parent) - : - fParent(parent), - fFrame() -{ - SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); -} - - -BSpinner::TextViewLayoutItem::TextViewLayoutItem(BMessage* from) - : - BAbstractLayoutItem(from), - fParent(NULL), - fFrame() -{ - from->FindRect(kFrameField, &fFrame); -} - - -bool -BSpinner::TextViewLayoutItem::IsVisible() -{ - return !fParent->IsHidden(fParent); -} - - -void -BSpinner::TextViewLayoutItem::SetVisible(bool visible) -{ - // not allowed -} - - -BRect -BSpinner::TextViewLayoutItem::Frame() -{ - return fFrame; -} - - -void -BSpinner::TextViewLayoutItem::SetFrame(BRect frame) -{ - fFrame = frame; - fParent->_UpdateFrame(); -} - - -void -BSpinner::TextViewLayoutItem::SetParent(BSpinner* parent) -{ - fParent = parent; -} - - -BView* -BSpinner::TextViewLayoutItem::View() -{ - return fParent; -} - - -BSize -BSpinner::TextViewLayoutItem::BaseMinSize() -{ - fParent->_ValidateLayoutData(); - - BSize size(fParent->fLayoutData->text_view_width, - fParent->fLayoutData->text_view_height); - return size; -} - - -BSize -BSpinner::TextViewLayoutItem::BaseMaxSize() -{ - return BaseMinSize(); -} - - -BSize -BSpinner::TextViewLayoutItem::BasePreferredSize() -{ - return BaseMinSize(); -} - - -BAlignment -BSpinner::TextViewLayoutItem::BaseAlignment() -{ - return BAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); -} - - -BRect -BSpinner::TextViewLayoutItem::FrameInParent() const -{ - return fFrame.OffsetByCopy(-fParent->Frame().left, -fParent->Frame().top); -} - - -status_t -BSpinner::TextViewLayoutItem::Archive(BMessage* into, bool deep) const -{ - BArchiver archiver(into); - status_t result = BAbstractLayoutItem::Archive(into, deep); - - if (result == B_OK) - result = into->AddRect(kFrameField, fFrame); - - return archiver.Finish(result); -} - - -BArchivable* -BSpinner::TextViewLayoutItem::Instantiate(BMessage* from) -{ - if (validate_instantiation(from, "BSpinner::TextViewLayoutItem")) - return new LabelLayoutItem(from); - - return NULL; -} - - // #pragma mark - BSpinner BSpinner::BSpinner(BRect frame, const char* name, const char* label, BMessage* message, uint32 resizingMode, uint32 flags) : - BView(frame, name, resizingMode, flags | B_WILL_DRAW | B_FRAME_EVENTS), - fLabel(label) + BAbstractSpinner(frame, name, label, message, resizingMode, flags) { - SetMessage(message); _InitObject(); } -BSpinner::BSpinner(const char* name, const char* label, BMessage* message, - uint32 flags) +BSpinner::BSpinner(const char* name, const char* label, + BMessage* message, uint32 flags) : - BView(name, flags | B_WILL_DRAW | B_FRAME_EVENTS), - fLabel(label) + BAbstractSpinner(name, label, message, flags) { - SetMessage(message); _InitObject(); } BSpinner::BSpinner(BMessage* data) : - BView(data) + BAbstractSpinner(data) { _InitObject(); - if (data->FindInt32("_align") != B_OK) - fAlignment = B_ALIGN_LEFT; + if (data->FindInt32("_max", &fMaxValue) != B_OK) + fMinValue = INT32_MAX; - if (data->FindInt32("_divider") != B_OK) - fDivider = 0.0f; + if (data->FindInt32("_min", &fMinValue) != B_OK) + fMinValue = INT32_MIN; - if (data->FindBool("_enabled") != B_OK) - fIsEnabled = true; - - if (data->FindString("_label", &fLabel) != B_OK) - fLabel = NULL; - - BMessage* message = NULL; - if (data->FindMessage("_message", message) == B_OK) - SetMessage(message); - - if (data->FindDouble("_max", &fMaxValue) != B_OK) - fMinValue = 100.0; - - if (data->FindDouble("_min", &fMinValue) != B_OK) - fMinValue = 0.0; - - if (data->FindUInt32("_precision", &fPrecision) != B_OK) - fPrecision = 2; - - if (data->FindDouble("_step", &fStep) != B_OK) - fStep = 1.0; - - if (data->FindDouble("_value", &fValue) != B_OK) - fValue = 0.0; + if (data->FindInt32("_val", &fValue) != B_OK) + fValue = 0; } BSpinner::~BSpinner() { - delete fLayoutData; - fLayoutData = NULL; } @@ -1006,38 +129,17 @@ BSpinner::Instantiate(BMessage* data) status_t BSpinner::Archive(BMessage* data, bool deep) const { - status_t status = BView::Archive(data, deep); + status_t status = BAbstractSpinner::Archive(data, deep); data->AddString("class", "Spinner"); if (status == B_OK) - status = data->AddInt32("_align", fAlignment); + status = data->AddInt32("_max", fMaxValue); if (status == B_OK) - status = data->AddFloat("_divider", fDivider); + status = data->AddInt32("_min", fMinValue); if (status == B_OK) - status = data->AddBool("_enabled", fIsEnabled); - - if (status == B_OK && fLabel != NULL) - status = data->AddString("_label", fLabel); - - if (status == B_OK && Message() != NULL) - status = data->AddMessage("_message", Message()); - - if (status == B_OK) - status = data->AddDouble("_max", fMaxValue); - - if (status == B_OK) - status = data->AddDouble("_min", fMinValue); - - if (status == B_OK) - status = data->AddUInt32("_precision", fPrecision); - - if (status == B_OK) - status = data->AddDouble("_step", fStep); - - if (status == B_OK) - status = data->AddDouble("_value", fValue); + status = data->AddInt32("_val", fValue); return status; } @@ -1046,7 +148,7 @@ BSpinner::Archive(BMessage* data, bool deep) const status_t BSpinner::GetSupportedSuites(BMessage* message) { - message->AddString("suites", "suite/vnd.Haiku-spinner"); + message->AddString("suites", "suite/vnd.Haiku-intenger-spinner"); BPropertyInfo prop_info(sProperties); message->AddFlat("messages", &prop_info); @@ -1055,189 +157,26 @@ BSpinner::GetSupportedSuites(BMessage* message) } -BHandler* -BSpinner::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, - int32 form, const char* property) -{ - return BView::ResolveSpecifier(message, index, specifier, form, - property); -} - - void BSpinner::AttachedToWindow() { - if (!Messenger().IsValid()) - SetTarget(Window()); - SetValue(fValue); - // sets the text and enables or disables the arrows - _UpdateTextViewColors(IsEnabled()); - fTextView->MakeEditable(IsEnabled()); - BView::AttachedToWindow(); -} - - -void -BSpinner::Draw(BRect updateRect) -{ - _DrawLabel(updateRect); - _DrawTextView(updateRect); - fIncrement->Invalidate(); - fDecrement->Invalidate(); -} - - -void -BSpinner::FrameResized(float width, float height) -{ - BView::FrameResized(width, height); - - // TODO: this causes flickering still... - - // changes in width - - BRect bounds = Bounds(); - - if (bounds.Width() > fLayoutData->previous_width) { - // invalidate the region between the old and the new right border - BRect rect = bounds; - rect.left += fLayoutData->previous_width - kFrameMargin; - rect.right--; - Invalidate(rect); - } else if (bounds.Width() < fLayoutData->previous_width) { - // invalidate the region of the new right border - BRect rect = bounds; - rect.left = rect.right - kFrameMargin; - Invalidate(rect); - } - - // changes in height - - if (bounds.Height() > fLayoutData->previous_height) { - // invalidate the region between the old and the new bottom border - BRect rect = bounds; - rect.top += fLayoutData->previous_height - kFrameMargin; - rect.bottom--; - Invalidate(rect); - // invalidate label area - rect = bounds; - rect.right = fDivider; - Invalidate(rect); - } else if (bounds.Height() < fLayoutData->previous_height) { - // invalidate the region of the new bottom border - BRect rect = bounds; - rect.top = rect.bottom - kFrameMargin; - Invalidate(rect); - // invalidate label area - rect = bounds; - rect.right = fDivider; - Invalidate(rect); - } - - fLayoutData->previous_width = bounds.Width(); - fLayoutData->previous_height = bounds.Height(); -} - - -void -BSpinner::ValueChanged() -{ - // hook method - does nothing + BAbstractSpinner::AttachedToWindow(); } void BSpinner::Decrement() { - SetValue(Value() - Step()); + SetValue(Value() - 1); } void BSpinner::Increment() { - SetValue(Value() + Step()); -} - - -void -BSpinner::MakeFocus(bool focus) -{ - fTextView->MakeFocus(focus); -} - - -void -BSpinner::ResizeToPreferred() -{ - BView::ResizeToPreferred(); - - const char* label = Label(); - if (label != NULL) { - fDivider = ceilf(StringWidth(label)) - + be_control_look->DefaultLabelSpacing(); - } else - fDivider = 0.0f; - - _LayoutTextView(); -} - - -void -BSpinner::SetFlags(uint32 flags) -{ - // If the textview is navigable, set it to not navigable if needed, - // else if it is not navigable, set it to navigable if needed - if (fTextView->Flags() & B_NAVIGABLE) { - if (!(flags & B_NAVIGABLE)) - fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE); - } else { - if (flags & B_NAVIGABLE) - fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE); - } - - // Don't make this one navigable - flags &= ~B_NAVIGABLE; - - BView::SetFlags(flags); -} - - -void -BSpinner::WindowActivated(bool active) -{ - _DrawTextView(fTextView->Frame()); -} - - -void -BSpinner::SetAlignment(alignment align) -{ - fAlignment = align; -} - - -void -BSpinner::SetDivider(float position) -{ - position = roundf(position); - - float delta = fDivider - position; - if (delta == 0.0f) - return; - - fDivider = position; - - if ((Flags() & B_SUPPORTS_LAYOUT) != 0) { - // We should never get here, since layout support means, we also - // layout the divider, and don't use this method at all. - Relayout(); - } else { - _LayoutTextView(); - Invalidate(); - } + SetValue(Value() + 1); } @@ -1247,40 +186,15 @@ BSpinner::SetEnabled(bool enable) if (IsEnabled() == enable) return; - fIsEnabled = enable; - fTextView->MakeEditable(enable); - if (enable) - fTextView->SetFlags(fTextView->Flags() | B_NAVIGABLE); - else - fTextView->SetFlags(fTextView->Flags() & ~B_NAVIGABLE); + SetIncrementEnabled(enable && Value() < fMaxValue); + SetDecrementEnabled(enable && Value() > fMinValue); - _UpdateTextViewColors(enable); - fTextView->Invalidate(); - SetIncrementEnabled(enable && fValue < fMaxValue); - SetDecrementEnabled(enable && fValue > fMinValue); - - _LayoutTextView(); - Invalidate(); - if (Window() != NULL) - Window()->UpdateIfNeeded(); + BAbstractSpinner::SetEnabled(enable); } void -BSpinner::SetLabel(const char* label) -{ - fLabel = label; - if (Window() != NULL) { - Invalidate(); - Window()->UpdateIfNeeded(); - } - - InvalidateLayout(); -} - - -void -BSpinner::SetMaxValue(double max) +BSpinner::SetMaxValue(int32 max) { fMaxValue = max; if (fValue > fMaxValue) @@ -1289,7 +203,7 @@ BSpinner::SetMaxValue(double max) void -BSpinner::SetMinValue(double min) +BSpinner::SetMinValue(int32 min) { fMinValue = min; if (fValue < fMinValue) @@ -1298,7 +212,7 @@ BSpinner::SetMinValue(double min) void -BSpinner::Range(double* min, double* max) +BSpinner::Range(int32* min, int32* max) { *min = fMinValue; *max = fMaxValue; @@ -1306,7 +220,7 @@ BSpinner::Range(double* min, double* max) void -BSpinner::SetRange(double min, double max) +BSpinner::SetRange(int32 min, int32 max) { SetMinValue(min); SetMaxValue(max); @@ -1314,7 +228,7 @@ BSpinner::SetRange(double min, double max) void -BSpinner::SetValue(double value) +BSpinner::SetValue(int32 value) { // clip to range if (value < fMinValue) @@ -1323,13 +237,9 @@ BSpinner::SetValue(double value) value = fMaxValue; // update the text view - char* format; - asprintf(&format, "%%.%" B_PRId32 "f", fPrecision); - char* valueString; - asprintf(&valueString, format, value); - fTextView->SetText(valueString); - free(format); - free(valueString); + BString valueString; + valueString << value; + TextView()->SetText(valueString.String()); // update the up and down arrows SetIncrementEnabled(IsEnabled() && value < fMaxValue); @@ -1349,500 +259,31 @@ BSpinner::SetValue(double value) void BSpinner::SetValueFromText() { - SetValue(roundTo(atof(TextView()->Text()), Precision())); -} - - - -bool -BSpinner::IsDecrementEnabled() const -{ - return fDecrement->IsEnabled(); -} - - -void -BSpinner::SetDecrementEnabled(bool enable) -{ - if (IsDecrementEnabled() == enable) - return; - - fDecrement->SetEnabled(enable); - fDecrement->Invalidate(); -} - - -bool -BSpinner::IsIncrementEnabled() const -{ - return fIncrement->IsEnabled(); -} - - -void -BSpinner::SetIncrementEnabled(bool enable) -{ - if (IsIncrementEnabled() == enable) - return; - - fIncrement->SetEnabled(enable); - fIncrement->Invalidate(); -} - - -BSize -BSpinner::MinSize() -{ - _ValidateLayoutData(); - return BLayoutUtils::ComposeSize(ExplicitMinSize(), fLayoutData->min); -} - - -BSize -BSpinner::MaxSize() -{ - _ValidateLayoutData(); - - BSize max = fLayoutData->min; - max.width = B_SIZE_UNLIMITED; - - return BLayoutUtils::ComposeSize(ExplicitMaxSize(), max); -} - - -BSize -BSpinner::PreferredSize() -{ - _ValidateLayoutData(); - return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), - fLayoutData->min); -} - - -BAlignment -BSpinner::LayoutAlignment() -{ - _ValidateLayoutData(); - return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), - BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER)); -} - - -BLayoutItem* -BSpinner::CreateLabelLayoutItem() -{ - if (fLayoutData->label_layout_item == NULL) - fLayoutData->label_layout_item = new LabelLayoutItem(this); - - return fLayoutData->label_layout_item; -} - - -BLayoutItem* -BSpinner::CreateTextViewLayoutItem() -{ - if (fLayoutData->text_view_layout_item == NULL) - fLayoutData->text_view_layout_item = new TextViewLayoutItem(this); - - return fLayoutData->text_view_layout_item; -} - - -BTextView* -BSpinner::TextView() const -{ - return dynamic_cast(fTextView); -} - - -// #pragma mark - BSpinner protected methods - - -status_t -BSpinner::AllArchived(BMessage* into) const -{ - status_t result; - if ((result = BView::AllArchived(into)) != B_OK) - return result; - - BArchiver archiver(into); - - BArchivable* textViewItem = fLayoutData->text_view_layout_item; - if (archiver.IsArchived(textViewItem)) - result = archiver.AddArchivable(kTextViewItemField, textViewItem); - - if (result != B_OK) - return result; - - BArchivable* labelBarItem = fLayoutData->label_layout_item; - if (archiver.IsArchived(labelBarItem)) - result = archiver.AddArchivable(kLabelItemField, labelBarItem); - - return result; -} - - -status_t -BSpinner::AllUnarchived(const BMessage* from) -{ - BUnarchiver unarchiver(from); - - status_t result = B_OK; - if ((result = BView::AllUnarchived(from)) != B_OK) - return result; - - if (unarchiver.IsInstantiated(kTextViewItemField)) { - TextViewLayoutItem*& textViewItem - = fLayoutData->text_view_layout_item; - result = unarchiver.FindObject(kTextViewItemField, - BUnarchiver::B_DONT_ASSUME_OWNERSHIP, textViewItem); - - if (result == B_OK) - textViewItem->SetParent(this); - else - return result; - } - - if (unarchiver.IsInstantiated(kLabelItemField)) { - LabelLayoutItem*& labelItem = fLayoutData->label_layout_item; - result = unarchiver.FindObject(kLabelItemField, - BUnarchiver::B_DONT_ASSUME_OWNERSHIP, labelItem); - - if (result == B_OK) - labelItem->SetParent(this); - } - - return result; -} - - -void -BSpinner::DoLayout() -{ - if ((Flags() & B_SUPPORTS_LAYOUT) == 0) - return; - - if (GetLayout()) { - BView::DoLayout(); - return; - } - - _ValidateLayoutData(); - - BSize size(Bounds().Size()); - if (size.width < fLayoutData->min.width) - size.width = fLayoutData->min.width; - - if (size.height < fLayoutData->min.height) - size.height = fLayoutData->min.height; - - float divider = 0; - if (fLayoutData->label_layout_item != NULL - && fLayoutData->text_view_layout_item != NULL - && fLayoutData->label_layout_item->Frame().IsValid() - && fLayoutData->text_view_layout_item->Frame().IsValid()) { - divider = fLayoutData->text_view_layout_item->Frame().left - - fLayoutData->label_layout_item->Frame().left; - } else if (fLayoutData->label_width > 0) { - divider = fLayoutData->label_width - + be_control_look->DefaultLabelSpacing(); - } - fDivider = divider; - - BRect dirty(fTextView->Frame()); - _LayoutTextView(); - - // invalidate dirty region - dirty = dirty | fTextView->Frame(); - dirty = dirty | fIncrement->Frame(); - dirty = dirty | fDecrement->Frame(); - - Invalidate(dirty); -} - - -void -BSpinner::LayoutInvalidated(bool descendants) -{ - if (fLayoutData != NULL) - fLayoutData->valid = false; + SetValue(atol(TextView()->Text())); } // #pragma mark - BSpinner private methods -void -BSpinner::_DrawLabel(BRect updateRect) -{ - BRect rect(Bounds()); - rect.right = fDivider; - if (!rect.IsValid() || !rect.Intersects(updateRect)) - return; - - _ValidateLayoutData(); - - const char* label = Label(); - if (label == NULL) - return; - - // horizontal position - float x; - switch (fAlignment) { - case B_ALIGN_RIGHT: - x = fDivider - fLayoutData->label_width - 3.0f; - break; - - case B_ALIGN_CENTER: - x = fDivider - roundf(fLayoutData->label_width / 2.0f); - break; - - default: - x = 0.0f; - break; - } - - // vertical position - font_height& fontHeight = fLayoutData->font_info; - float y = rect.top - + roundf((rect.Height() + 1.0f - fontHeight.ascent - - fontHeight.descent) / 2.0f) - + fontHeight.ascent + kFrameMargin * 2; - - uint32 flags = 0; - if (!IsEnabled()) - flags |= BControlLook::B_DISABLED; - - be_control_look->DrawLabel(this, label, LowColor(), flags, BPoint(x, y)); -} - - -void -BSpinner::_DrawTextView(BRect updateRect) -{ - BRect rect = fTextView->Frame(); - rect.InsetBy(-kFrameMargin, -kFrameMargin); - if (!rect.IsValid() || !rect.Intersects(updateRect)) - return; - - rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); - uint32 flags = 0; - if (!IsEnabled()) - flags |= BControlLook::B_DISABLED; - - if (fTextView->IsFocus() && Window()->IsActive()) - flags |= BControlLook::B_FOCUSED; - - be_control_look->DrawTextControlBorder(this, rect, updateRect, base, - flags); -} - - void BSpinner::_InitObject() { - rgb_color bgColor = ui_color(B_PANEL_BACKGROUND_COLOR); - SetViewColor(bgColor); - SetLowColor(bgColor); + fMaxValue = INT32_MIN; + fMinValue = INT32_MAX; + fValue = 0; - fAlignment = B_ALIGN_LEFT; - if (Label() != NULL) { - fDivider = StringWidth(Label()) - + be_control_look->DefaultLabelSpacing(); - } else - fDivider = 0.0f; + TextView()->SetAlignment(B_ALIGN_RIGHT); + for (uint32 c = 0; c <= 42; c++) + TextView()->DisallowChar(c); - fIsEnabled = true; + TextView()->DisallowChar(','); - fMaxValue = 100.0; - fMinValue = 0.0; - fPrecision = 2; - fStep = 1.0; - fValue = 0.0; + for (uint32 c = 46; c <= 47; c++) + TextView()->DisallowChar(c); - BRect rect(Bounds()); - fLayoutData = new LayoutData(rect.Width(), rect.Height()); - - rect.left = fDivider; - rect.InsetBy(kFrameMargin, kFrameMargin); - rect.right -= rect.Height() * 2 + kFrameMargin * 2 + 1.0f; - BRect textRect(rect.OffsetToCopy(B_ORIGIN)); - - fTextView = new SpinnerTextView(rect, textRect); - AddChild(fTextView); - - rect.InsetBy(0.0f, -kFrameMargin); - - rect.left = rect.right + kFrameMargin * 2; - rect.right = rect.left + rect.Height() - kFrameMargin * 2; - - fDecrement = new SpinnerArrow(rect, "decrement", SPINNER_DECREMENT); - AddChild(fDecrement); - - rect.left = rect.right + 1.0f; - rect.right = rect.left + rect.Height() - kFrameMargin * 2; - - fIncrement = new SpinnerArrow(rect, "increment", SPINNER_INCREMENT); - AddChild(fIncrement); - - uint32 navigableFlags = Flags() & B_NAVIGABLE; - if (navigableFlags != 0) - BView::SetFlags(Flags() & ~B_NAVIGABLE); -} - - -void -BSpinner::_LayoutTextView() -{ - BRect rect; - if (fLayoutData->text_view_layout_item != NULL) { - rect = fLayoutData->text_view_layout_item->FrameInParent(); - } else { - rect = Bounds(); - rect.left = fDivider; - } - rect.InsetBy(kFrameMargin, kFrameMargin); - rect.right -= rect.Height() * 2 + kFrameMargin * 2 + 1.0f; - - fTextView->MoveTo(rect.left, rect.top); - fTextView->ResizeTo(rect.Width(), rect.Height()); - fTextView->SetTextRect(rect.OffsetToCopy(B_ORIGIN)); - - rect.InsetBy(0.0f, -kFrameMargin); - - rect.left = rect.right + kFrameMargin * 2; - rect.right = rect.left + rect.Height() - kFrameMargin * 2; - - fDecrement->ResizeTo(rect.Width(), rect.Height()); - fDecrement->MoveTo(rect.LeftTop()); - - rect.left = rect.right + 1.0f; - rect.right = rect.left + rect.Height() - kFrameMargin * 2; - - fIncrement->ResizeTo(rect.Width(), rect.Height()); - fIncrement->MoveTo(rect.LeftTop()); -} - - -void -BSpinner::_UpdateFrame() -{ - if (fLayoutData->label_layout_item == NULL - || fLayoutData->text_view_layout_item == NULL) { - return; - } - - BRect labelFrame = fLayoutData->label_layout_item->Frame(); - BRect textViewFrame = fLayoutData->text_view_layout_item->Frame(); - - if (!labelFrame.IsValid() || !textViewFrame.IsValid()) - return; - - // update divider - fDivider = textViewFrame.left - labelFrame.left; - - BRect frame = textViewFrame | labelFrame; - MoveTo(frame.left, frame.top); - BSize oldSize = Bounds().Size(); - ResizeTo(frame.Width(), frame.Height()); - BSize newSize = Bounds().Size(); - - // If the size changes, ResizeTo() will trigger a relayout, otherwise - // we need to do that explicitly. - if (newSize != oldSize) - Relayout(); -} - - -void -BSpinner::_UpdateTextViewColors(bool enable) -{ - rgb_color textColor; - rgb_color bgColor; - BFont font; - - fTextView->GetFontAndColor(0, &font); - - if (enable) - textColor = ui_color(B_DOCUMENT_TEXT_COLOR); - else { - textColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), - B_DISABLED_LABEL_TINT); - } - - fTextView->SetFontAndColor(&font, B_FONT_ALL, &textColor); - - if (enable) - bgColor = ui_color(B_DOCUMENT_BACKGROUND_COLOR); - else { - bgColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), - B_LIGHTEN_2_TINT); - } - - fTextView->SetViewColor(bgColor); - fTextView->SetLowColor(bgColor); -} - - -void -BSpinner::_ValidateLayoutData() -{ - if (fLayoutData->valid) - return; - - font_height fontHeight = fLayoutData->font_info; - GetFontHeight(&fontHeight); - - if (Label() != NULL) { - fLayoutData->label_width = StringWidth(Label()); - fLayoutData->label_height = ceilf(fontHeight.ascent - + fontHeight.descent + fontHeight.leading); - } else { - fLayoutData->label_width = 0; - fLayoutData->label_height = 0; - } - - float divider = 0; - if (fLayoutData->label_width > 0) { - divider = ceilf(fLayoutData->label_width - + be_control_look->DefaultLabelSpacing()); - } - - if ((Flags() & B_SUPPORTS_LAYOUT) == 0) - divider = std::max(divider, fDivider); - - char* format; - asprintf(&format, "%%.%" B_PRId32 "f", fPrecision); - char* maxValue; - asprintf(&maxValue, format, fMaxValue); - char* minValue; - asprintf(&minValue, format, fMinValue); - float longestValue = ceilf(std::max(fTextView->StringWidth(maxValue), - fTextView->StringWidth(minValue))); - free(format); - free(maxValue); - free(minValue); - - float textWidth = ceilf(std::max(longestValue, - fTextView->StringWidth("99999"))); - - float textViewHeight = fTextView->LineHeight(0) + kFrameMargin * 2; - float textViewWidth = textWidth + textViewHeight * 2; - - fLayoutData->text_view_width = textViewWidth; - fLayoutData->text_view_height = textViewHeight; - - BSize min(textViewWidth, textViewHeight); - if (divider > 0.0f) - min.width += divider; - - if (fLayoutData->label_height > min.height) - min.height = fLayoutData->label_height; - - fLayoutData->min = min; - fLayoutData->valid = true; - - ResetLayoutInvalidation(); + for (uint32 c = 58; c <= 127; c++) + TextView()->DisallowChar(c); } diff --git a/src/preferences/screen/ScreenWindow.cpp b/src/preferences/screen/ScreenWindow.cpp index 2037b9491f..3ccc5a3890 100644 --- a/src/preferences/screen/ScreenWindow.cpp +++ b/src/preferences/screen/ScreenWindow.cpp @@ -238,13 +238,11 @@ ScreenWindow::ScreenWindow(ScreenSettings* settings) fColumnsControl = new BSpinner("columns", B_TRANSLATE("Columns:"), new BMessage(kMsgWorkspaceColumnsChanged)); fColumnsControl->SetAlignment(B_ALIGN_RIGHT); - fColumnsControl->SetPrecision(0); fColumnsControl->SetRange(1, 32); fRowsControl = new BSpinner("rows", B_TRANSLATE("Rows:"), new BMessage(kMsgWorkspaceRowsChanged)); fRowsControl->SetAlignment(B_ALIGN_RIGHT); - fRowsControl->SetPrecision(0); fRowsControl->SetRange(1, 32); uint32 columns;