* Pulled generic button code out of CheckBox into a new base class

AbstractButton.
* Added RadioButton and RadioButtonGroup.
* In the BBox test the border style can be switched now.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21343 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2007-06-07 04:11:17 +00:00
parent 3ff21b6bf7
commit 0ce9bc6895
9 changed files with 703 additions and 110 deletions
@@ -0,0 +1,201 @@
/*
* Copyright 2007, Ingo Weinhold <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "AbstractButton.h"
#include <View.h>
// #pragma mark - AbstractButton
AbstractButton::AbstractButton(button_policy policy, BMessage* message,
BMessenger target)
: View(BRect(0, 0, 0, 0)),
BInvoker(message, target),
fPolicy(policy),
fSelected(false),
fPressed(false),
fPressedInBounds(false)
{
}
void
AbstractButton::SetPolicy(button_policy policy)
{
fPolicy = policy;
}
button_policy
AbstractButton::Policy() const
{
return fPolicy;
}
void
AbstractButton::SetSelected(bool selected)
{
if (selected != fSelected) {
fSelected = selected;
Invalidate();
// check whether to notify the listeners depending on the button policy
bool notify = false;
switch (fPolicy) {
case BUTTON_POLICY_TOGGLE_ON_RELEASE:
case BUTTON_POLICY_SELECT_ON_RELEASE:
// always notify on selection changes
notify = true;
break;
case BUTTON_POLICY_INVOKE_ON_RELEASE:
// only notify when the user interaction has been finished
notify = !fPressed;
break;
}
if (notify) {
// notify synchronous listeners
_NotifyListeners();
// send the message
if (Message()) {
BMessage message(*Message());
message.AddBool("selected", IsSelected());
InvokeNotify(&message);
}
}
}
}
bool
AbstractButton::IsSelected() const
{
return fSelected;
}
void
AbstractButton::MouseDown(BPoint where, uint32 buttons, int32 modifiers)
{
if (fPressed)
return;
fPressed = true;
_PressedUpdate(where);
}
void
AbstractButton::MouseUp(BPoint where, uint32 buttons, int32 modifiers)
{
if (!fPressed || (buttons & B_PRIMARY_MOUSE_BUTTON))
return;
_PressedUpdate(where);
fPressed = false;
if (fPressedInBounds) {
fPressedInBounds = false;
// update selected state according to policy
bool selected = fSelected;
switch (fPolicy) {
case BUTTON_POLICY_TOGGLE_ON_RELEASE:
selected = !fSelected;
break;
case BUTTON_POLICY_SELECT_ON_RELEASE:
selected = true;
break;
case BUTTON_POLICY_INVOKE_ON_RELEASE:
selected = false;
break;
}
SetSelected(selected);
}
Invalidate();
}
void
AbstractButton::MouseMoved(BPoint where, uint32 buttons, int32 modifiers)
{
if (!fPressed)
return;
_PressedUpdate(where);
}
void
AbstractButton::AddListener(Listener* listener)
{
if (listener && !fListeners.HasItem(listener))
fListeners.AddItem(listener);
}
void
AbstractButton::RemoveListener(Listener* listener)
{
if (listener)
fListeners.RemoveItem(listener);
}
bool
AbstractButton::IsPressed() const
{
return (fPressed && fPressedInBounds);
}
void
AbstractButton::_PressedUpdate(BPoint where)
{
bool pressedInBounds = Bounds().Contains(where);
if (pressedInBounds == fPressedInBounds)
return;
fPressedInBounds = pressedInBounds;
// update the selected state according to the button policy
switch (fPolicy) {
case BUTTON_POLICY_TOGGLE_ON_RELEASE:
case BUTTON_POLICY_SELECT_ON_RELEASE:
// nothing to do
break;
case BUTTON_POLICY_INVOKE_ON_RELEASE:
SetSelected(fPressedInBounds);
break;
}
Invalidate();
}
void
AbstractButton::_NotifyListeners()
{
if (!fListeners.IsEmpty()) {
BList listeners(fListeners);
for (int32 i = 0; Listener* listener = (Listener*)listeners.ItemAt(i);
i++) {
listener->SelectionChanged(this);
}
}
}
// #pragma mark - AbstractButton::Listener
AbstractButton::Listener::~Listener()
{
}
@@ -0,0 +1,74 @@
/*
* Copyright 2007, Ingo Weinhold <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef WIDGET_LAYOUT_TEST_ABSTRACT_BUTTON_H
#define WIDGET_LAYOUT_TEST_ABSTRACT_BUTTON_H
#include <Invoker.h>
#include <List.h>
#include "View.h"
// button behavior policy
enum button_policy {
BUTTON_POLICY_TOGGLE_ON_RELEASE, // check box
BUTTON_POLICY_SELECT_ON_RELEASE, // radio button
BUTTON_POLICY_INVOKE_ON_RELEASE // button
};
// AbstractButton
class AbstractButton : public View, public BInvoker {
public:
AbstractButton(button_policy policy,
BMessage* message = NULL,
BMessenger target = BMessenger());
void SetPolicy(button_policy policy);
button_policy Policy() const;
void SetSelected(bool selected);
bool IsSelected() const;
virtual void MouseDown(BPoint where, uint32 buttons,
int32 modifiers);
virtual void MouseUp(BPoint where, uint32 buttons,
int32 modifiers);
virtual void MouseMoved(BPoint where, uint32 buttons,
int32 modifiers);
public:
class Listener;
void AddListener(Listener* listener);
void RemoveListener(Listener* listener);
protected:
bool IsPressed() const;
private:
void _PressedUpdate(BPoint where);
void _NotifyListeners();
private:
button_policy fPolicy;
bool fSelected;
bool fPressed;
bool fPressedInBounds;
BList fListeners;
};
// synchronous listener interface
class AbstractButton::Listener {
public:
virtual ~Listener();
virtual void SelectionChanged(AbstractButton* button) = 0;
};
#endif // WIDGET_LAYOUT_TEST_ABSTRACT_BUTTON_H
@@ -10,11 +10,34 @@
#include <Box.h>
#include <Message.h>
#include "GroupView.h"
#include "RadioButton.h"
// messages
enum {
MSG_BORDER_STYLE_CHANGED = 'bstc',
};
// BorderStyleRadioButton
class BoxTest::BorderStyleRadioButton : public LabeledRadioButton {
public:
BorderStyleRadioButton(const char* label, border_style style)
: LabeledRadioButton(label),
fBorderStyle(style)
{
}
border_style fBorderStyle;
};
// constructor
BoxTest::BoxTest()
: Test("Box", NULL),
fBox(new BBox(BRect(0, 0, 9, 9), "test box", B_FOLLOW_NONE))
fBox(new BBox(BRect(0, 0, 9, 9), "test box", B_FOLLOW_NONE)),
fBorderStyleRadioGroup(NULL)
// TODO: Layout-friendly constructor
{
SetView(fBox);
@@ -24,6 +47,7 @@ BoxTest::BoxTest()
// destructor
BoxTest::~BoxTest()
{
delete fBorderStyleRadioGroup;
}
@@ -31,26 +55,41 @@ BoxTest::~BoxTest()
void
BoxTest::ActivateTest(View* controls)
{
/* GroupView* group = new GroupView(B_VERTICAL);
// BBox sets its background color to that of its parent in
// AttachedToWindow(). Override.
rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
fBox->SetViewColor(background);
fBox->SetLowColor(background);
GroupView* group = new GroupView(B_VERTICAL);
group->SetFrame(controls->Bounds());
group->SetSpacing(0, 8);
controls->AddChild(group);
// long button text
fLongTextCheckBox = new LabeledCheckBox("Long Button Text",
new BMessage(MSG_CHANGE_BUTTON_TEXT), this);
group->AddChild(fLongTextCheckBox);
// the radio button group for selecting the border style
fBorderStyleRadioGroup = new RadioButtonGroup(
new BMessage(MSG_BORDER_STYLE_CHANGED), this);
// big font
fBigFontCheckBox = new LabeledCheckBox("Big Button Font",
new BMessage(MSG_CHANGE_BUTTON_FONT), this);
group->AddChild(fBigFontCheckBox);
// no border
LabeledRadioButton* button = new BorderStyleRadioButton("no border",
B_NO_BORDER);
group->AddChild(button);
fBorderStyleRadioGroup->AddButton(button->GetRadioButton());
// plain border
button = new BorderStyleRadioButton("plain border", B_PLAIN_BORDER);
group->AddChild(button);
fBorderStyleRadioGroup->AddButton(button->GetRadioButton());
// fancy border
button = new BorderStyleRadioButton("fancy border", B_FANCY_BORDER);
group->AddChild(button);
fBorderStyleRadioGroup->AddButton(button->GetRadioButton());
// default to no border
fBorderStyleRadioGroup->SelectButton(0L);
group->AddChild(new Glue());
_SetButtonText(false);
_SetButtonFont(false);
*/
}
@@ -59,3 +98,37 @@ void
BoxTest::DectivateTest()
{
}
// MessageReceived
void
BoxTest::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_BORDER_STYLE_CHANGED:
_UpdateBorderStyle();
break;
default:
Test::MessageReceived(message);
break;
}
}
// _UpdateBorderStyle
void
BoxTest::_UpdateBorderStyle()
{
if (fBorderStyleRadioGroup) {
// We need to get the parent of the actually selected button, since
// that is the labeled radio button we've derived our
// BorderStyleRadioButton from.
AbstractButton* selectedButton
= fBorderStyleRadioGroup->SelectedButton();
View* parent = (selectedButton ? selectedButton->Parent() : NULL);
BorderStyleRadioButton* button = dynamic_cast<BorderStyleRadioButton*>(
parent);
if (button)
fBox->SetBorder(button->fBorderStyle);
}
}
@@ -10,7 +10,7 @@
class BBox;
class View;
class RadioButtonGroup;
class BoxTest : public Test {
@@ -21,8 +21,16 @@ public:
virtual void ActivateTest(View* controls);
virtual void DectivateTest();
virtual void MessageReceived(BMessage* message);
private:
void _UpdateBorderStyle();
private:
class BorderStyleRadioButton;
BBox* fBox;
RadioButtonGroup* fBorderStyleRadioGroup;
};
@@ -14,39 +14,12 @@
CheckBox::CheckBox(BMessage* message, BMessenger target)
: View(BRect(0, 0, 12, 12)),
BInvoker(message, target),
fSelected(false),
fPressed(false)
: AbstractButton(BUTTON_POLICY_TOGGLE_ON_RELEASE, message, target)
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
}
void
CheckBox::SetSelected(bool selected)
{
if (selected != fSelected) {
fSelected = selected;
Invalidate();
// send the message
if (Message()) {
BMessage message(*Message());
message.AddBool("selected", IsSelected());
InvokeNotify(&message);
}
}
}
bool
CheckBox::IsSelected() const
{
return fSelected;
}
BSize
CheckBox::MinSize()
{
@@ -66,14 +39,14 @@ CheckBox::Draw(BView* container, BRect updateRect)
{
BRect rect(Bounds());
if (fPressed)
if (IsPressed())
container->SetHighColor((rgb_color){ 120, 0, 0, 255 });
else
container->SetHighColor((rgb_color){ 0, 0, 0, 255 });
container->StrokeRect(rect);
if (fPressed ? fPressedSelected : fSelected) {
if (IsSelected()) {
rect.InsetBy(2, 2);
container->StrokeLine(rect.LeftTop(), rect.RightBottom());
container->StrokeLine(rect.RightTop(), rect.LeftBottom());
@@ -81,52 +54,6 @@ CheckBox::Draw(BView* container, BRect updateRect)
}
void
CheckBox::MouseDown(BPoint where, uint32 buttons, int32 modifiers)
{
if (fPressed)
return;
fPressed = true;
fPressedSelected = fSelected;
_PressedUpdate(where);
}
void
CheckBox::MouseUp(BPoint where, uint32 buttons, int32 modifiers)
{
if (!fPressed || (buttons & B_PRIMARY_MOUSE_BUTTON))
return;
_PressedUpdate(where);
fPressed = false;
SetSelected(fPressedSelected);
Invalidate();
}
void
CheckBox::MouseMoved(BPoint where, uint32 buttons, int32 modifiers)
{
if (!fPressed)
return;
_PressedUpdate(where);
}
void
CheckBox::_PressedUpdate(BPoint where)
{
bool pressedSelected = Bounds().Contains(where) ^ fSelected;
if (pressedSelected != fPressedSelected) {
fPressedSelected = pressedSelected;
Invalidate();
}
}
// #pragma mark - LabeledCheckBox
@@ -8,37 +8,20 @@
#include <Invoker.h>
#include "AbstractButton.h"
#include "GroupView.h"
// CheckBox
class CheckBox : public View, public BInvoker {
class CheckBox : public AbstractButton {
public:
CheckBox(BMessage* message = NULL,
BMessenger target = BMessenger());
void SetSelected(bool selected);
bool IsSelected() const;
virtual BSize MinSize();
virtual BSize MaxSize();
virtual void Draw(BView* container, BRect updateRect);
virtual void MouseDown(BPoint where, uint32 buttons,
int32 modifiers);
virtual void MouseUp(BPoint where, uint32 buttons,
int32 modifiers);
virtual void MouseMoved(BPoint where, uint32 buttons,
int32 modifiers);
private:
void _PressedUpdate(BPoint where);
private:
bool fSelected;
bool fPressed;
bool fPressedSelected;
};
@@ -5,10 +5,12 @@ SetSubDirSupportedPlatforms haiku libbe_test ;
SimpleTest WidgetLayoutTest :
WidgetLayoutTest.cpp
AbstractButton.cpp
BoxTest.cpp
ButtonTest.cpp
CheckBox.cpp
GroupView.cpp
RadioButton.cpp
StringView.cpp
Test.cpp
TwoDimensionalSliderView.cpp
@@ -0,0 +1,246 @@
/*
* Copyright 2007, Ingo Weinhold <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "RadioButton.h"
#include <View.h>
#include "StringView.h"
// #pragma mark - RadioButton
RadioButton::RadioButton(BMessage* message, BMessenger target)
: AbstractButton(BUTTON_POLICY_SELECT_ON_RELEASE, message, target)
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
}
BSize
RadioButton::MinSize()
{
return BSize(12, 12);
}
BSize
RadioButton::MaxSize()
{
return MinSize();
}
void
RadioButton::Draw(BView* container, BRect updateRect)
{
BRect rect(Bounds());
if (IsPressed())
container->SetHighColor((rgb_color){ 120, 0, 0, 255 });
else
container->SetHighColor((rgb_color){ 0, 0, 0, 255 });
container->StrokeRect(rect);
if (IsSelected()) {
rect.InsetBy(4, 4);
container->FillRect(rect);
}
}
// #pragma mark - LabeledRadioButton
LabeledRadioButton::LabeledRadioButton(const char* label, BMessage* message,
BMessenger target)
: GroupView(B_HORIZONTAL),
fRadioButton(new RadioButton(message, target))
{
SetSpacing(8, 0);
AddChild(fRadioButton);
if (label)
AddChild(new StringView(label));
}
void
LabeledRadioButton::SetTarget(BMessenger messenger)
{
fRadioButton->SetTarget(messenger);
}
void
LabeledRadioButton::SetSelected(bool selected)
{
fRadioButton->SetSelected(selected);
}
bool
LabeledRadioButton::IsSelected() const
{
return fRadioButton->IsSelected();
}
// #pragma mark - RadioButtonGroup
RadioButtonGroup::RadioButtonGroup(BMessage* message, BMessenger target)
: BInvoker(message, target),
fButtons(10),
fSelected(NULL)
{
}
RadioButtonGroup::~RadioButtonGroup()
{
// remove as listener from buttons
for (int32 i = 0; AbstractButton* button = ButtonAt(i); i++)
button->RemoveListener(this);
}
void
RadioButtonGroup::AddButton(AbstractButton* button)
{
if (!button || fButtons.HasItem(button))
return;
// force radio button policy
button->SetPolicy(BUTTON_POLICY_SELECT_ON_RELEASE);
// deselect the button, if we do already have a selected one
if (fSelected)
button->SetSelected(false);
// add ourselves as listener
button->AddListener(this);
// add the button to our list
fButtons.AddItem(button);
}
bool
RadioButtonGroup::RemoveButton(AbstractButton* button)
{
return RemoveButton(IndexOfButton(button));
}
AbstractButton*
RadioButtonGroup::RemoveButton(int32 index)
{
// remove the button from our list
AbstractButton* button = (AbstractButton*)fButtons.RemoveItem(index);
if (!button)
return NULL;
// remove ourselves as listener
button->RemoveListener(this);
// if it was the selected one, we don't have a selection anymore
if (button == fSelected) {
fSelected = NULL;
_SelectionChanged();
}
return button;
}
int32
RadioButtonGroup::CountButtons() const
{
return fButtons.CountItems();
}
AbstractButton*
RadioButtonGroup::ButtonAt(int32 index) const
{
return (AbstractButton*)fButtons.ItemAt(index);
}
int32
RadioButtonGroup::IndexOfButton(AbstractButton* button) const
{
return fButtons.IndexOf(button);
}
void
RadioButtonGroup::SelectButton(AbstractButton* button)
{
if (button && fButtons.HasItem(button))
button->SetSelected(true);
}
void
RadioButtonGroup::SelectButton(int32 index)
{
if (AbstractButton* button = ButtonAt(index))
button->SetSelected(true);
}
AbstractButton*
RadioButtonGroup::SelectedButton() const
{
return fSelected;
}
int32
RadioButtonGroup::SelectedIndex() const
{
return (fSelected ? IndexOfButton(fSelected) : -1);
}
void
RadioButtonGroup::SelectionChanged(AbstractButton* button)
{
// We're only interested in a notification when one of our buttons that
// has not been selected one before has become selected.
if (!button || !fButtons.HasItem(button) || !button->IsSelected()
|| button == fSelected) {
return;
}
// set the new selection
AbstractButton* oldSelected = fSelected;
fSelected = button;
// deselect the old selected button
if (oldSelected)
oldSelected->SetSelected(false);
// send out notifications
_SelectionChanged();
}
void
RadioButtonGroup::_SelectionChanged()
{
// send the message
if (Message()) {
BMessage message(*Message());
message.AddPointer("button group", this);
message.AddPointer("selected button", fSelected);
message.AddInt32("selected index", SelectedIndex());
InvokeNotify(&message);
}
}
@@ -0,0 +1,79 @@
/*
* Copyright 2007, Ingo Weinhold <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef WIDGET_LAYOUT_TEST_CHECK_BOX_H
#define WIDGET_LAYOUT_TEST_CHECK_BOX_H
#include <Invoker.h>
#include <List.h>
#include "AbstractButton.h"
#include "GroupView.h"
// RadioButton
class RadioButton : public AbstractButton {
public:
RadioButton(BMessage* message = NULL,
BMessenger target = BMessenger());
virtual BSize MinSize();
virtual BSize MaxSize();
virtual void Draw(BView* container, BRect updateRect);
};
// LabeledRadioButton
class LabeledRadioButton : public GroupView {
public:
LabeledRadioButton(const char* label,
BMessage* message = NULL,
BMessenger target = BMessenger());
RadioButton* GetRadioButton() const { return fRadioButton; }
void SetTarget(BMessenger messenger);
void SetSelected(bool selected);
bool IsSelected() const;
private:
RadioButton* fRadioButton;
};
// RadioButtonGroup
class RadioButtonGroup : public BInvoker, private AbstractButton::Listener {
public:
RadioButtonGroup(BMessage* message = NULL,
BMessenger target = BMessenger());
virtual ~RadioButtonGroup();
void AddButton(AbstractButton* button);
bool RemoveButton(AbstractButton* button);
AbstractButton* RemoveButton(int32 index);
int32 CountButtons() const;
AbstractButton* ButtonAt(int32 index) const;
int32 IndexOfButton(AbstractButton* button) const;
void SelectButton(AbstractButton* button);
void SelectButton(int32 index);
AbstractButton* SelectedButton() const;
int32 SelectedIndex() const;
private:
virtual void SelectionChanged(AbstractButton* button);
void _SelectionChanged();
private:
BList fButtons;
AbstractButton* fSelected;
};
#endif // WIDGET_LAYOUT_TEST_CHECK_BOX_H