#68: Enabled/disabled states for widgets 4.x-dev

Thu, 05 Mar 2020 21:17:46 +0200

author
cemkalyoncu
date
Thu, 05 Mar 2020 21:17:46 +0200
branch
4.x-dev
changeset 1369
5e3e8482f94b
parent 1368
9135822b5605
child 1370
be2cd62a4c20

#68: Enabled/disabled states for widgets
=> closes #68

Source/Gorgon/UI/ComponentStack.cpp file | annotate | diff | comparison | revisions
Source/Gorgon/UI/ComponentStackWidget.h file | annotate | diff | comparison | revisions
Source/Gorgon/UI/WidgetBase.cpp file | annotate | diff | comparison | revisions
Source/Gorgon/UI/WidgetBase.h file | annotate | diff | comparison | revisions
Source/Gorgon/UI/WidgetContainer.cpp file | annotate | diff | comparison | revisions
Source/Gorgon/UI/WidgetContainer.h file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Inputbox.cpp file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Panel.h file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/RadioButtons.h file | annotate | diff | comparison | revisions
Testing/Source/Manual/UI_Generate.cpp file | annotate | diff | comparison | revisions
--- 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);

mercurial