A '&' (reference) was missing on a specific line, causing font information to never be updated. Also removed extra padding dependent on a constant variable - if it is used, the label will be render too far down. Fixes #12981. Signed-off-by: Adrien Destugues <[email protected]>
1692 lines
34 KiB
C++
1692 lines
34 KiB
C++
/*
|
|
* Copyright 2004 DarkWyrm <[email protected]>
|
|
* Copyright 2013 FeemanLou
|
|
* Copyright 2014-2015 Haiku, Inc. All rights reserved.
|
|
*
|
|
* Distributed under the terms of the MIT license.
|
|
*
|
|
* Originally written by DarkWyrm <[email protected]>
|
|
* Updated by FreemanLou as part of Google GCI 2013
|
|
*
|
|
* Authors:
|
|
* DarkWyrm, [email protected]
|
|
* FeemanLou
|
|
* John Scipione, [email protected]
|
|
*/
|
|
|
|
|
|
#include <AbstractSpinner.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <AbstractLayoutItem.h>
|
|
#include <Alignment.h>
|
|
#include <ControlLook.h>
|
|
#include <Font.h>
|
|
#include <GradientLinear.h>
|
|
#include <LayoutItem.h>
|
|
#include <LayoutUtils.h>
|
|
#include <Message.h>
|
|
#include <MessageFilter.h>
|
|
#include <Point.h>
|
|
#include <PropertyInfo.h>
|
|
#include <TextView.h>
|
|
#include <View.h>
|
|
#include <Window.h>
|
|
|
|
#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)
|
|
{
|
|
}
|
|
|
|
|
|
SpinnerButton::~SpinnerButton()
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
SpinnerButton::AttachedToWindow()
|
|
{
|
|
fParent = static_cast<BAbstractSpinner*>(Parent());
|
|
|
|
AdoptParentColors();
|
|
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 frameTint = B_DARKEN_1_TINT;
|
|
|
|
float fgTint;
|
|
if (!fIsEnabled)
|
|
fgTint = B_DARKEN_1_TINT;
|
|
else if (fIsMouseDown)
|
|
fgTint = B_DARKEN_MAX_TINT;
|
|
else
|
|
fgTint = 1.777f; // 216 --> 48.2 (48)
|
|
|
|
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
|
|
frameTint = 2.0f - frameTint;
|
|
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, frameTint), 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<SpinnerButton>::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)
|
|
{
|
|
MakeResizable(true);
|
|
}
|
|
|
|
|
|
SpinnerTextView::~SpinnerTextView()
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
SpinnerTextView::AttachedToWindow()
|
|
{
|
|
fParent = static_cast<BAbstractSpinner*>(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::MessageReceived(BMessage* message)
|
|
{
|
|
if (!IsEnabled() && message->what == B_COLORS_UPDATED)
|
|
_UpdateTextViewColors(false);
|
|
|
|
BControl::MessageReceived(message);
|
|
}
|
|
|
|
|
|
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<BTextView*>(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;
|
|
|
|
uint32 flags = be_control_look->Flags(this);
|
|
|
|
// erase the is control flag before drawing the label so that the label
|
|
// will get drawn using B_PANEL_TEXT_COLOR.
|
|
flags &= ~BControlLook::B_IS_CONTROL;
|
|
|
|
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()
|
|
{
|
|
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)
|
|
{
|
|
// Mimick BTextControl's appearance.
|
|
rgb_color textColor = ui_color(B_DOCUMENT_TEXT_COLOR);
|
|
|
|
if (enable) {
|
|
fTextView->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
|
|
fTextView->SetLowUIColor(ViewUIColor());
|
|
} else {
|
|
rgb_color color = ui_color(B_DOCUMENT_BACKGROUND_COLOR);
|
|
color = disable_color(ViewColor(), color);
|
|
textColor = disable_color(textColor, ViewColor());
|
|
|
|
fTextView->SetViewColor(color);
|
|
fTextView->SetLowColor(color);
|
|
}
|
|
|
|
BFont font;
|
|
fTextView->GetFontAndColor(0, &font);
|
|
fTextView->SetFontAndColor(&font, B_FONT_ALL, &textColor);
|
|
}
|
|
|
|
|
|
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() {}
|