Thu, 05 Mar 2020 21:17:46 +0200
#68: Enabled/disabled states for widgets
=> closes #68
--- a/Source/Gorgon/UI/ComponentStack.cpp Thu Mar 05 17:27:42 2020 +0200 +++ b/Source/Gorgon/UI/ComponentStack.cpp Thu Mar 05 21:17:46 2020 +0200 @@ -75,11 +75,13 @@ disabled.erase(ComponentCondition::Down); } } + + if(!IsDisabled()) { + if(click_fn && downlocation.Distance(location) < WindowManager::ClickThreshold) + click_fn(ComponentTemplate::NoTag, location, btn); + } - if(click_fn && downlocation.Distance(location) < WindowManager::ClickThreshold) - click_fn(ComponentTemplate::NoTag, location, btn); - - if(up_fn) + if(!IsDisabled() && up_fn) up_fn(ComponentTemplate::NoTag, location, btn); });
--- a/Source/Gorgon/UI/ComponentStackWidget.h Thu Mar 05 17:27:42 2020 +0200 +++ b/Source/Gorgon/UI/ComponentStackWidget.h Thu Mar 05 21:17:46 2020 +0200 @@ -2,6 +2,7 @@ #include "WidgetBase.h" #include "ComponentStack.h" +#include "WidgetContainer.h" namespace Gorgon { namespace UI { @@ -12,11 +13,15 @@ */ class ComponentStackWidget : public WidgetBase { public: - ComponentStackWidget(const Template &temp) : stack(temp, temp.GetSize()) { } + ComponentStackWidget(const Template &temp) : stack(*new ComponentStack(temp, temp.GetSize())) { } ComponentStackWidget(ComponentStackWidget &&) = default; ComponentStackWidget &operator =(ComponentStackWidget &&) = default; + + virtual ~ComponentStackWidget() { + delete &stack; + } using WidgetBase::Move; @@ -44,8 +49,45 @@ return stack.GetSize(); } + virtual void SetEnabled(bool value) override { + if(enabled == value) + return; + + enabled = value; + + if(!value) { + stack.AddCondition(ComponentCondition::Disabled); + if(HasParent() && IsFocused()) { + GetParent().FocusNext(); + if(IsFocused()) + GetParent().ForceRemoveFocus(); + } + } + else { + if(!HasParent() || GetParent().IsEnabled()) { + stack.RemoveCondition(ComponentCondition::Disabled); + } + } + } + + virtual bool IsEnabled() const override { + return enabled; + } + protected: - mutable ComponentStack stack; + ComponentStack &stack; //allocate on heap due to its size. + + bool enabled = true; + + /// This function is called when the parent's enabled state changes. + virtual void parentenabledchanged(bool state) { + if(enabled && !state) { + stack.AddCondition(ComponentCondition::Disabled); + } + else if(enabled && state) { + stack.RemoveCondition(ComponentCondition::Disabled); + } + } virtual void focused() override { WidgetBase::focused();
--- a/Source/Gorgon/UI/WidgetBase.cpp Thu Mar 05 17:27:42 2020 +0200 +++ b/Source/Gorgon/UI/WidgetBase.cpp Thu Mar 05 21:17:46 2020 +0200 @@ -14,7 +14,7 @@ if(!parent) return false; - if(!allowfocus() || !visible) + if(!allowfocus() || !visible || !enabled) return false; return parent->SetFocusTo(*this);
--- a/Source/Gorgon/UI/WidgetBase.h Thu Mar 05 17:27:42 2020 +0200 +++ b/Source/Gorgon/UI/WidgetBase.h Thu Mar 05 21:17:46 2020 +0200 @@ -90,6 +90,21 @@ return visible; } + /// Enables the widget so that the user can interact with it + void Enable() { SetEnabled(true); } + + /// Disables the widget so that the user cannot interact with it + void Disable() { SetEnabled(false); } + + /// Toggles enabled state of the widget + void ToggleEnabled() { SetEnabled(!IsEnabled()); } + + /// Sets the enabled state of the widget + virtual void SetEnabled(bool value) = 0; + + /// Returns whether the widget is enabled. + virtual bool IsEnabled() const = 0; + /// Returns if this widget has a parent bool HasParent() const { return parent != nullptr; } @@ -148,6 +163,8 @@ if(IsVisible()) boundschanged(); + + parentenabledchanged(parent->IsEnabled()); } /// When called, widget should remove itself from the given layer @@ -179,6 +196,9 @@ /// Call this function when the widget bounds is changed virtual void boundschanged(); + + /// This function is called when the parent's enabled state changes. + virtual void parentenabledchanged(bool state) { } private: bool visible = true;
--- a/Source/Gorgon/UI/WidgetContainer.cpp Thu Mar 05 17:27:42 2020 +0200 +++ b/Source/Gorgon/UI/WidgetContainer.cpp Thu Mar 05 21:17:46 2020 +0200 @@ -148,7 +148,7 @@ return false; for(int i=after+1; i<widgets.GetSize(); i++) { - if(widgets[i].allowfocus() && widgets[i].IsVisible()) { + if(widgets[i].allowfocus() && widgets[i].IsVisible() && widgets[i].IsEnabled()) { auto prevfoc = focused; focused = &widgets[i]; @@ -186,7 +186,7 @@ //rollover for(int i=0; i<after; i++) { - if(widgets[i].allowfocus() && widgets[i].IsVisible()) { + if(widgets[i].allowfocus() && widgets[i].IsVisible() && widgets[i].IsEnabled()) { auto prevfoc = focused; focused = &widgets[i]; @@ -211,7 +211,7 @@ return false; for(int i=before-1; i>=0; i--) { - if(widgets[i].allowfocus() && widgets[i].IsVisible()) { + if(widgets[i].allowfocus() && widgets[i].IsVisible() && widgets[i].IsEnabled()) { auto prevfoc = focused; focused = &widgets[i]; @@ -260,7 +260,7 @@ //rollover for(int i=widgets.GetSize()-1; i>before; i--) { - if(widgets[i].allowfocus() && widgets[i].IsVisible()) { + if(widgets[i].allowfocus() && widgets[i].IsVisible() && widgets[i].IsEnabled()) { auto prevfoc = focused; focused = &widgets[i]; @@ -296,7 +296,7 @@ if(&widget == focused) return true; - if(!widget.allowfocus() && widget.IsVisible()) + if(!widget.allowfocus() || !widget.IsVisible() || !widget.IsEnabled()) return false; if(focused && !focused->canloosefocus()) @@ -396,6 +396,11 @@ return false; } + void WidgetContainer::distributeparentenabled(bool state) { + for(auto &w : widgets) + w.parentenabledchanged(state); + } + void WidgetContainer::childboundschanged(WidgetBase *) { if(organizer) organizer->Reorganize();
--- a/Source/Gorgon/UI/WidgetContainer.h Thu Mar 05 17:27:42 2020 +0200 +++ b/Source/Gorgon/UI/WidgetContainer.h Thu Mar 05 21:17:46 2020 +0200 @@ -164,45 +164,30 @@ virtual bool IsEnabled() const { return isenabled; } /// Enables the container, allowing interaction with the widgets - /// in it. This function will return true if the container is - /// enabled at the end of the call. - bool Enable() { - if(!IsEnabled()) { - isenabled = true; - return enable(); - } - - return true; + /// in it. + void Enable() { + SetEnabled(true); } /// Disables the container, disallowing interactions of all widgets - /// in it. This function will return true if the container is - /// enabled at the end of the call. - bool Disable() { - if(IsEnabled()) { - isenabled = false; - return disable(); - } - - return true; + /// in it. + void Disable() { + SetEnabled(false); } /// Toggles the enabled state of this container. If the state is /// toggled after the call, this function will return true. - bool ToggleEnabled() { - if(!isenabled) - return Enable(); - else - return Disable(); + void ToggleEnabled() { + SetEnabled(!IsEnabled()); } /// Sets the enabled state of this container. This function will /// return true if the container is enabled at the end of the call. - bool SetEnabled(bool enabled) { - if(enabled) - return Enable(); - else - return Disable(); + virtual void SetEnabled(bool value) { + if(value != isenabled) { + isenabled = value; + distributeparentenabled(value); + } } /// Should return the interior (usable) size of the container. @@ -392,16 +377,6 @@ /// This function is called after a widget is removed virtual void widgetremoved(WidgetBase &) { } - - /// This function is called when the container is to be enabled. This function - /// will only be called when the container was disabled prior to the call. - /// Return false if this container cannot be enabled. - virtual bool enable() { return true; } - - /// This function is called when the container is to be disabled. This function - /// will only be called when the container is enabled prior to the call. - /// Return false if this container cannot be disabled. - virtual bool disable() { return true; } /// If this widget is not top level, return the current strategy used by the /// parent. Never return Inherit from this function. @@ -424,7 +399,10 @@ /// Distributes a pressed character to the focused widget. bool distributecharevent(Char c); - + + /// Distributes a enabled state to children + void distributeparentenabled(bool state); + /// The boundary of any of the children is changed. Source could be nullptr virtual void childboundschanged(WidgetBase *source);
--- a/Source/Gorgon/Widgets/Inputbox.cpp Thu Mar 05 17:27:42 2020 +0200 +++ b/Source/Gorgon/Widgets/Inputbox.cpp Thu Mar 05 21:17:46 2020 +0200 @@ -230,7 +230,7 @@ display.insert(selstart.byte, s); - selstart.byte += s.size(); + selstart.byte += (int)s.size(); selstart.glyph += gcnt; glyphcount += gcnt;
--- a/Source/Gorgon/Widgets/Panel.h Thu Mar 05 17:27:42 2020 +0200 +++ b/Source/Gorgon/Widgets/Panel.h Thu Mar 05 21:17:46 2020 +0200 @@ -125,6 +125,17 @@ bool IsSmoothScrollEnabled() const { return scrollspeed != 0; } + + virtual void SetEnabled(bool value) override { + if(value != IsEnabled()) { + ComponentStackWidget::SetEnabled(value); + distributeparentenabled(value); + } + } + + virtual bool IsEnabled() const override { + return ComponentStackWidget::IsEnabled(); + } protected: virtual bool allowfocus() const override;
--- a/Source/Gorgon/Widgets/RadioButtons.h Thu Mar 05 17:27:42 2020 +0200 +++ b/Source/Gorgon/Widgets/RadioButtons.h Thu Mar 05 21:17:46 2020 +0200 @@ -118,6 +118,29 @@ using WidgetBase::IsVisible; + virtual void SetEnabled(bool value) override { + if(enabled == value) + return; + + enabled = value; + + if(!value) { + ForceRemoveFocus(); + + if(HasParent() && IsFocused()) { + GetParent().FocusNext(); + if(IsFocused()) + GetParent().ForceRemoveFocus(); + } + } + + distributeparentenabled(value); + } + + virtual bool IsEnabled() const override { + return enabled; + } + protected: virtual void addto(Layer &layer) override { layer.Add(contents); @@ -135,7 +158,7 @@ virtual bool allowfocus() const override { - return false; + return true; } Gorgon::Layer &getlayer() override { @@ -146,6 +169,7 @@ Geometry::Point location = {0, 0}; int spacing = 4; const UI::Template &temp; + bool enabled = true; private: virtual void show() override { @@ -155,6 +179,21 @@ virtual void hide() override { contents.Hide(); } + + virtual void focuschanged() { + if(HasFocusedWidget() && !IsFocused()) + Focus(); + else if(!HasFocusedWidget() && IsFocused() && HasParent()) + GetParent().RemoveFocus(); + } + + virtual void focuslost() { + WidgetBase::focuslost(); + + if(HasFocusedWidget()) { + ForceRemoveFocus(); + } + } Gorgon::Graphics::Layer contents; };
--- a/Testing/Source/Manual/UI_Generate.cpp Thu Mar 05 17:27:42 2020 +0200 +++ b/Testing/Source/Manual/UI_Generate.cpp Thu Mar 05 21:17:46 2020 +0200 @@ -40,6 +40,7 @@ gen2.Focus.Color = Graphics::Color::Red; gen2.Border.Radius = 2; + std::cout << "font height: " << gen.RegularFont.GetGlyphRenderer().GetLetterHeight(true).second << std::endl; auto btntemp = gen.Button(); @@ -51,10 +52,10 @@ auto pnltemp = gen.BlankPanel(); auto pnltemp2 = gen.Panel(); auto inptemp = gen2.Inputbox(); - Widgets::Button btn(btntemp, "Helloo...", []() { std::cout<<"Hello..."<<std::endl; }); btn.Move(5,5); + app.wind.Add(btn); btn.Focus(); @@ -75,18 +76,18 @@ app.wind.Add(rad); rad.Move(150, 4); - rad.ChangedEvent.Register([](int val) { - std::cout<<"Changed to "<<val<<std::endl; - }); - Widgets::Checkbox chk(chktemp, "Sugar", [](bool state) { std::cout<<(state ? "with sugar" : "without sugar")<<std::endl; }); + rad.ChangedEvent.Register([&](int val) { + std::cout<<"Changed to "<<val<<std::endl; + chk.ToggleEnabled(); + }); + //chk.Hide(); - //app.wind.Add(chk); chk.BoundsChangedEvent.Register([] { std::cout << "Bounds changed" << std::endl; @@ -95,6 +96,7 @@ chk.FocusEvent.Register([]{ std::cout << "Focus changed." << std::endl; }); chk.Move(rad.GetLocation() + Gorgon::Geometry::Point(0, rad.GetSize().Height + 4)); + chk.Disable(); Widgets::Button ib(icobtntemp); auto ico = Graphics::BlankImage({16, 16}, Graphics::Color::Black); @@ -163,7 +165,8 @@ pnl.Add(inp); inp.Move(5, 80); inp.SelectAll(); - + + pnl.Add(chk); pnl.Add(lbl); pnl.Add(error); pnl.Add(rad);