diff --git a/src/tests/kits/interface/layout/widget_layout_test/AbstractButton.cpp b/src/tests/kits/interface/layout/widget_layout_test/AbstractButton.cpp new file mode 100644 index 0000000000..99f30498bb --- /dev/null +++ b/src/tests/kits/interface/layout/widget_layout_test/AbstractButton.cpp @@ -0,0 +1,201 @@ +/* + * Copyright 2007, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "AbstractButton.h" + +#include + + +// #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() +{ +} + diff --git a/src/tests/kits/interface/layout/widget_layout_test/AbstractButton.h b/src/tests/kits/interface/layout/widget_layout_test/AbstractButton.h new file mode 100644 index 0000000000..fd69219cfb --- /dev/null +++ b/src/tests/kits/interface/layout/widget_layout_test/AbstractButton.h @@ -0,0 +1,74 @@ +/* + * Copyright 2007, Ingo Weinhold . + * 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 +#include + +#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 diff --git a/src/tests/kits/interface/layout/widget_layout_test/BoxTest.cpp b/src/tests/kits/interface/layout/widget_layout_test/BoxTest.cpp index e8b2d416a2..23f6d0cc56 100644 --- a/src/tests/kits/interface/layout/widget_layout_test/BoxTest.cpp +++ b/src/tests/kits/interface/layout/widget_layout_test/BoxTest.cpp @@ -10,11 +10,34 @@ #include #include +#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( + parent); + if (button) + fBox->SetBorder(button->fBorderStyle); + } +} diff --git a/src/tests/kits/interface/layout/widget_layout_test/BoxTest.h b/src/tests/kits/interface/layout/widget_layout_test/BoxTest.h index 2184d2c7a2..61913d6dfc 100644 --- a/src/tests/kits/interface/layout/widget_layout_test/BoxTest.h +++ b/src/tests/kits/interface/layout/widget_layout_test/BoxTest.h @@ -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; }; diff --git a/src/tests/kits/interface/layout/widget_layout_test/CheckBox.cpp b/src/tests/kits/interface/layout/widget_layout_test/CheckBox.cpp index 46a3fd07d7..65985071f9 100644 --- a/src/tests/kits/interface/layout/widget_layout_test/CheckBox.cpp +++ b/src/tests/kits/interface/layout/widget_layout_test/CheckBox.cpp @@ -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 diff --git a/src/tests/kits/interface/layout/widget_layout_test/CheckBox.h b/src/tests/kits/interface/layout/widget_layout_test/CheckBox.h index 2b36ffe0ff..d56261aa66 100644 --- a/src/tests/kits/interface/layout/widget_layout_test/CheckBox.h +++ b/src/tests/kits/interface/layout/widget_layout_test/CheckBox.h @@ -8,37 +8,20 @@ #include +#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; }; diff --git a/src/tests/kits/interface/layout/widget_layout_test/Jamfile b/src/tests/kits/interface/layout/widget_layout_test/Jamfile index c148949bab..e7341e1ae4 100644 --- a/src/tests/kits/interface/layout/widget_layout_test/Jamfile +++ b/src/tests/kits/interface/layout/widget_layout_test/Jamfile @@ -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 diff --git a/src/tests/kits/interface/layout/widget_layout_test/RadioButton.cpp b/src/tests/kits/interface/layout/widget_layout_test/RadioButton.cpp new file mode 100644 index 0000000000..07c70dea20 --- /dev/null +++ b/src/tests/kits/interface/layout/widget_layout_test/RadioButton.cpp @@ -0,0 +1,246 @@ +/* + * Copyright 2007, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "RadioButton.h" + +#include + +#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); + } +} diff --git a/src/tests/kits/interface/layout/widget_layout_test/RadioButton.h b/src/tests/kits/interface/layout/widget_layout_test/RadioButton.h new file mode 100644 index 0000000000..12d412c26f --- /dev/null +++ b/src/tests/kits/interface/layout/widget_layout_test/RadioButton.h @@ -0,0 +1,79 @@ +/* + * Copyright 2007, Ingo Weinhold . + * 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 +#include + +#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