#201: Flow organizer 4.x-dev

Sat, 21 Nov 2020 18:47:12 +0200

author
cemkalyoncu
date
Sat, 21 Nov 2020 18:47:12 +0200
branch
4.x-dev
changeset 1493
b50cb1ee1caf
parent 1492
a7d9f275d5dc
child 1494
33abdc8b5e5b

#201: Flow organizer
* UnitWidth and Spacing is now handled down from the container.

Source/Gorgon/UI/ComponentStackWidget.h file | annotate | diff | comparison | revisions
Source/Gorgon/UI/LayerAdapter.cpp file | annotate | diff | comparison | revisions
Source/Gorgon/UI/LayerAdapter.h file | annotate | diff | comparison | revisions
Source/Gorgon/UI/Organizers/Base.cpp file | annotate | diff | comparison | revisions
Source/Gorgon/UI/Organizers/Flow.cpp file | annotate | diff | comparison | revisions
Source/Gorgon/UI/Organizers/Flow.h file | annotate | diff | comparison | revisions
Source/Gorgon/UI/Organizers/List.cpp file | annotate | diff | comparison | revisions
Source/Gorgon/UI/Organizers/List.h file | annotate | diff | comparison | revisions
Source/Gorgon/UI/Organizers/dir.cmake file | annotate | diff | comparison | revisions
Source/Gorgon/UI/WidgetContainer.h file | annotate | diff | comparison | revisions
Source/Gorgon/UI/Window.cpp file | annotate | diff | comparison | revisions
Source/Gorgon/UI/Window.h file | annotate | diff | comparison | revisions
Source/Gorgon/UI/dir.cmake file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Composer.cpp file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Composer.h file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Dropdown.h file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Panel.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/ComponentStackWidget.h	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/UI/ComponentStackWidget.h	Sat Nov 21 18:47:12 2020 +0200
@@ -49,8 +49,15 @@
 		
 		/// Sets the width of the widget in unit widths.
 		void SetWidthInUnits(int n) {
-            int w = stack.GetTemplate().GetUnitWidth();
-            int s = stack.GetTemplate().GetSpacing();
+            int w, s;
+            if(HasParent()) {
+                w = GetParent().GetUnitWidth();
+                s = GetParent().GetSpacing();
+            }
+            else {
+                w = stack.GetTemplate().GetUnitWidth();
+                s = stack.GetTemplate().GetSpacing();
+            }
             SetWidth(w * n + s * (n-1));
         }
 
--- a/Source/Gorgon/UI/LayerAdapter.cpp	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/UI/LayerAdapter.cpp	Sat Nov 21 18:47:12 2020 +0200
@@ -1,5 +1,6 @@
 #include "LayerAdapter.h"
 #include "Window.h"
+#include "../Widgets/Registry.h"
 
 
 namespace Gorgon { namespace UI {
@@ -12,5 +13,23 @@
         else
             return {false, this, self.GetLocation(), GetInteriorSize()};
     }
+    
+    int LayerAdapter::GetSpacing() const {
+        if(issizesset) {
+            return spacing;
+        }
+        else {
+            return Widgets::Registry::Active().GetSpacing();
+        }
+    }
+
+    int LayerAdapter::GetUnitWidth() const {
+        if(issizesset) {
+            return unitwidth;
+        }
+        else {
+            return Widgets::Registry::Active().GetUnitWidth();
+        }
+    }
 
 } }
--- a/Source/Gorgon/UI/LayerAdapter.h	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/UI/LayerAdapter.h	Sat Nov 21 18:47:12 2020 +0200
@@ -40,26 +40,65 @@
             return base->IsVisible();
         }
         
+        /// Returns true if this adapter can be used.
         bool IsReady() const {
             return base != nullptr;
         }
         
+        /// Sets the base layer
         void SetLayer(Gorgon::Layer &value) {
             base = &value;
         }
         
+        /// Returns the base layer
         Gorgon::Layer &GetLayer() const {
             return *base;
         }
         
         virtual ExtenderRequestResponse RequestExtender(const Gorgon::Layer &self) override;
         
+        /// The spacing should be left between widgets
+        virtual int GetSpacing() const override;
+        
+        /// Returns the unit width for a widget. This size is enough to
+        /// have a bordered icon. Widgets should be sized according to unit
+        /// width and spacing. A single unit width would be too small for
+        /// most widgets.
+        virtual int GetUnitWidth() const override;
+        
+        /// Overrides default spacing and unitwidth
+        void SetSizes(int spacing, int unitwidth) {
+            this->spacing = spacing;
+            this->unitwidth = unitwidth;
+            issizesset = true;
+        }
+        
+        /// Sets the unit size automatically. Full width will be at least
+        /// given units wide. Returns remaining size.
+        int AutomaticUnitSize(int spacing, int units = 6) {
+            ASSERT(base, "Base layer is not set");
+            
+            this->spacing   = spacing;
+            this->unitwidth = ( base->GetWidth() - spacing * (units-1) ) / units;
+            
+            return base->GetWidth() - (this->unitwidth * units + this->spacing * (units-1));
+        }
+        
+        /// Return to use default sizes
+        void UseDefaultSizes() {
+            issizesset = false;
+        }
+        
     protected:
         virtual Gorgon::Layer &getlayer() override {
             return *base;
         }
         
         Layer *base = nullptr;
+        
+        int spacing   = 0;
+        int unitwidth = 0;
+        bool issizesset = false;
     };
     
 } }
--- a/Source/Gorgon/UI/Organizers/Base.cpp	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/UI/Organizers/Base.cpp	Sat Nov 21 18:47:12 2020 +0200
@@ -29,6 +29,7 @@
     
 
     void Base::Reorganize(){
+        //TODO queue organizing
         if(organizing)
             return;
         
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/Gorgon/UI/Organizers/Flow.cpp	Sat Nov 21 18:47:12 2020 +0200
@@ -0,0 +1,98 @@
+#include "Flow.h"
+#include "../WidgetContainer.h"
+#include "../../Widgets/Registry.h"
+
+namespace Gorgon { namespace UI { namespace Organizers {
+    
+    void Flow::reorganize() {
+        auto &att = GetAttached();
+        int x = 0;
+        int width = att.GetInteriorSize().Width;
+        int s = GetSpacing();
+        
+        if(tight) {
+            Utils::NotImplemented("Tight organization is not implemented yet.");
+        }
+        else {
+            int uw = GetAttached().GetUnitWidth();
+            int y = 0;
+            int maxy = 0;
+            int rowc = 0;
+            int ind = -1;
+            int breaks = BreakCount(-1);
+            
+            for(auto &widget : att)  {
+                ind++;
+                
+                if(!widget.IsVisible() || widget.IsFloating()) {
+                    breaks = 0;
+                    continue;
+                }
+                
+                int w = widget.GetWidth();
+                
+                breaks += (x + w > width && rowc > 0) ? 1 : 0;
+                
+                if(breaks) {
+                    if(maxy == 0)
+                        maxy = uw;
+                    
+                    y += maxy + s;
+                    
+                    if(breaks > 0)
+                        y += (breaks-1) * (uw + s);
+                    
+                    x = 0;
+                    rowc = 0;
+                    maxy = 0;
+                }
+                
+                int h = widget.GetHeight();
+                if(h > maxy) {
+                    maxy = h;
+                }
+                
+                widget.Move(x, y);
+                x += w + s;
+                rowc++;
+                breaks = BreakCount(ind);
+            }
+        }
+    }
+    
+    int Flow::GetSpacing() const {
+        if(usedefaultspacing) {
+            if(IsAttached()) {
+                return GetAttached().GetSpacing();
+            }
+            else {
+                return Widgets::Registry::Active().GetSpacing();
+            }
+        }
+        else {
+            return spacing;
+        }
+    }
+
+    void Flow::InsertBreak() {
+        if(!IsAttached()) {
+            throw std::runtime_error("Organizer is not attached.");
+        }
+        
+        int order = GetAttached().GetCount() - 1;
+        
+        InsertBreak(order);
+    }
+
+    void Flow::InsertBreak(const Widget &widget) {
+        if(!IsAttached()) {
+            throw std::runtime_error("Organizer is not attached.");
+        }
+        
+        int order = GetAttached().GetFocusOrder(widget);
+        
+        if(order != -1)
+            InsertBreak(order);
+    }
+
+} } }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/Gorgon/UI/Organizers/Flow.h	Sat Nov 21 18:47:12 2020 +0200
@@ -0,0 +1,120 @@
+#pragma once
+
+#include "Base.h"
+
+#include <unordered_set>
+
+namespace Gorgon { namespace UI { 
+
+class Widget;
+    
+namespace Organizers {
+    
+    /** 
+     * Flow organizer places widgets side by side until they won't
+     * fit in the same row any more. In that case it will flow to
+     * next line. If tight option is not set, rows will have the
+     * height of the highest widget. If it is set, next widget is
+     * placed as close as possible to the widget above. This will
+     * also mean there could be widgets wrapping around a longer
+     * one.
+     * 
+     * Important: Flow organizer will work best if unit sizes are
+     * used.
+     */
+    class Flow : public Base {
+    public:
+        /// Constructs a new flow organizer specifying spacing between widgets
+        explicit Flow(int spacing) : usedefaultspacing(false), spacing(spacing) {
+        }
+        
+        /// Constructs a new list organizer, spaces between widgets are obtained
+        /// from the active parent
+        Flow() = default;
+        
+        /// Sets the spacing between the lines
+        void SetSpacing(int value) {
+            if(value == spacing && !usedefaultspacing)
+                return;
+            
+            usedefaultspacing = false;
+            spacing = value;
+            
+            if(IsAttached())
+                reorganize();
+        }
+        
+        /// Starts using default spacing.
+        void UseDefaultSpacing() {
+            if(usedefaultspacing)
+                return;
+            
+            usedefaultspacing = true;
+            
+            if(IsAttached())
+                reorganize();
+        }
+        
+        /// Returns the spacing between the lines
+        int GetSpacing() const;
+        
+        /// Sets tight arrangement
+        void SetTight(bool value) {
+            if(value == tight)
+                return;
+            
+            tight = value;
+            
+            if(IsAttached())
+                reorganize();
+        }
+        
+        /// Inserts a line break after the last widget. Reordering
+        /// widgets do not automatically arrange breaks. Multiple breaks
+        /// will leave unit width space between the lines.
+        void InsertBreak();
+        
+        /// Inserts a line break after the widget at the given index.
+        /// Reordering widgets do not automatically arrange breaks.
+        /// Multiple breaks will leave unit width space between the lines.
+        /// -1 will add a space at the front.
+        void InsertBreak(int index) {
+            breaks.insert(index);
+            Reorganize();
+        }
+        
+        /// Inserts a line break after the given widget. This only works
+        /// if the widget is currently in the container.
+        /// Reordering widgets do not automatically arrange breaks.
+        /// Multiple breaks will leave unit width space between the lines.
+        void InsertBreak(const Widget &widget);
+        
+        /// Removes the break at the given index. If it does not exist,
+        /// nothing will be done. If there are multiple breaks at the
+        /// same point, all of them will be removed.
+        void RemoveBreak(int index) {
+            breaks.erase(index);
+            Reorganize();
+        }
+        
+        /// Returns the number of breaks at the given index.
+        int BreakCount(int index) const {
+            return breaks.count(index);
+        }
+        
+        /// Removes all breaks in the organizer
+        void RemoveAllBreaks() {
+            breaks.clear();
+            Reorganize();
+        }
+        
+    protected:
+        virtual void reorganize() override;
+        
+        bool usedefaultspacing = true;
+        int spacing = 0;
+        bool tight = false;
+        std::unordered_multiset<int> breaks;
+    };
+    
+} } }
--- a/Source/Gorgon/UI/Organizers/List.cpp	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/UI/Organizers/List.cpp	Sat Nov 21 18:47:12 2020 +0200
@@ -1,20 +1,37 @@
 #include "List.h"
 #include "../WidgetContainer.h"
+#include "../../Widgets/Registry.h"
 
 namespace Gorgon { namespace UI { namespace Organizers {
     
     void List::reorganize() {
         int y = 0;
         int w = GetAttached().GetInteriorSize().Width;
+        int s = GetSpacing();
         
         for(auto &widget : GetAttached()) {
             if(widget.IsVisible() && !widget.IsFloating()) {
                 widget.Move(0, y);
                 widget.SetWidth(w);
                 
-                y += widget.GetHeight() + spacing;
-                }
+                y += widget.GetHeight() + s;
+            }
         }
     }
     
+    int List::GetSpacing() const {
+        if(usedefaultspacing) {
+            if(IsAttached()) {
+                return GetAttached().GetSpacing();
+            }
+            else {
+                return Widgets::Registry::Active().GetSpacing();
+            }
+        }
+        else {
+            return spacing;
+        }
+    }
+    
+
 } } }
--- a/Source/Gorgon/UI/Organizers/List.h	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/UI/Organizers/List.h	Sat Nov 21 18:47:12 2020 +0200
@@ -11,25 +11,45 @@
      */
     class List : public Base {
     public:
-        /// Constructs a new list organizer, spacing between lines can be
-        /// specified.
-        explicit List(int spacing = 5) : spacing(spacing) {
+        /// Constructs a new list organizer specifying spacing between lines
+        explicit List(int spacing) : usedefaultspacing(false), spacing(spacing) {
         }
         
+        /// Constructs a new list organizer, spaces between lines are obtained
+        /// from the active parent 
+        List() = default;
+        
         /// Sets the spacing between the lines
         void SetSpacing(int value) {
+            if(value == spacing && !usedefaultspacing)
+                return;
+            
+            usedefaultspacing = false;
             spacing = value;
+            
+            if(IsAttached())
+                reorganize();
+        }
+        
+        /// Starts using default spacing.
+        void UseDefaultSpacing() {
+            if(usedefaultspacing)
+                return;
+            
+            usedefaultspacing = true;
+            
+            if(IsAttached())
+                reorganize();
         }
         
         /// Returns the spacing between the lines
-        int GetSpacing() const {
-            return spacing;
-        }
+        int GetSpacing() const;
         
     protected:
         virtual void reorganize() override;
         
-        int spacing;
+        bool usedefaultspacing = true;
+        int spacing = 0;
     };
     
 } } }
--- a/Source/Gorgon/UI/Organizers/dir.cmake	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/UI/Organizers/dir.cmake	Sat Nov 21 18:47:12 2020 +0200
@@ -2,6 +2,9 @@
 	Base.h
 	Base.cpp
 	
+	Flow.h
+	Flow.cpp
+	
 	List.h
 	List.cpp
 )
--- a/Source/Gorgon/UI/WidgetContainer.h	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/UI/WidgetContainer.h	Sat Nov 21 18:47:12 2020 +0200
@@ -389,6 +389,15 @@
         
         /// This function will return a container that will act as an extender.
         virtual ExtenderRequestResponse RequestExtender(const Gorgon::Layer &self) = 0;
+    
+        /// The spacing should be left between widgets
+        virtual int GetSpacing() const = 0;
+        
+        /// Returns the unit width for a widget. This size is enough to
+        /// have a bordered icon. Widgets should be sized according to unit
+        /// width and spacing. A single unit width would be too small for
+        /// most widgets. Multiple units can be obtained by GetUnitWidth(n)
+        virtual int GetUnitWidth() const = 0;
 
     protected:
         /// This container is sorted by the focus order
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/Gorgon/UI/Window.cpp	Sat Nov 21 18:47:12 2020 +0200
@@ -0,0 +1,26 @@
+#include "Window.h"
+#include "../Widgets/Registry.h"
+
+
+namespace Gorgon { namespace UI {
+
+    
+    int Window::GetSpacing() const {
+        if(issizesset) {
+            return spacing;
+        }
+        else {
+            return Widgets::Registry::Active().GetSpacing();
+        }
+    }
+
+    int Window::GetUnitWidth() const {
+        if(issizesset) {
+            return unitwidth;
+        }
+        else {
+            return Widgets::Registry::Active().GetUnitWidth();
+        }
+    }
+    
+} }
--- a/Source/Gorgon/UI/Window.h	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/UI/Window.h	Sat Nov 21 18:47:12 2020 +0200
@@ -108,6 +108,36 @@
             return {true, &adapter, self.TranslateToTopLevel(), GetSize()};
         }
         
+        /// The spacing should be left between widgets
+        virtual int GetSpacing() const override;
+        
+        /// Returns the unit width for a widget. This size is enough to
+        /// have a bordered icon. Widgets should be sized according to unit
+        /// width and spacing. A single unit width would be too small for
+        /// most widgets.
+        virtual int GetUnitWidth() const override;
+        
+        /// Overrides default spacing and unitwidth
+        void SetSizes(int spacing, int unitwidth) {
+            this->spacing = spacing;
+            this->unitwidth = unitwidth;
+            issizesset = true;
+        }
+        
+        /// Sets the unit size automatically. Full width will be at least
+        /// given units wide. Returns remaining size.
+        int AutomaticUnitSize(int spacing, int units = 6) {
+            this->spacing   = spacing;
+            this->unitwidth = ( GetInteriorSize().Width - spacing * (units-1) ) / units;
+            
+            return GetInteriorSize().Width - (this->unitwidth * units + this->spacing * (units-1));
+        }
+        
+        /// Return to use default sizes
+        void UseDefaultSizes() {
+            issizesset = false;
+        }
+        
         using WidgetContainer::Add;
         using Gorgon::Window::Add;
         using Gorgon::Window::KeyEvent;
@@ -175,6 +205,10 @@
 
         decltype(KeyEvent)::Token inputtoken = keyinit(); //to initialize token after window got constructed
         decltype(CharacterEvent)::Token chartoken = charinit(); //to initialize token after window got constructed
+        
+        int spacing   = 0;
+        int unitwidth = 0;
+        bool issizesset = false;
     };
     
 } }
--- a/Source/Gorgon/UI/dir.cmake	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/UI/dir.cmake	Sat Nov 21 18:47:12 2020 +0200
@@ -20,4 +20,5 @@
 	WidgetContainer.h
 	WidgetContainer.cpp
 	Window.h
+	Window.cpp
 )
--- a/Source/Gorgon/Widgets/Composer.cpp	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/Widgets/Composer.cpp	Sat Nov 21 18:47:12 2020 +0200
@@ -1,4 +1,5 @@
 #include "Composer.h"
+#include "Registry.h"
 
 
 namespace Gorgon { namespace Widgets {
@@ -68,21 +69,55 @@
     }
 
 
-        UI::ExtenderRequestResponse Composer::RequestExtender(const Layer &self) {
-            if(HasParent()) {
-                auto
-                ans = GetParent().RequestExtender(self);
+    UI::ExtenderRequestResponse Composer::RequestExtender(const Layer &self) {
+        if(HasParent()) {
+            auto
+            ans = GetParent().RequestExtender(self);
 
-                if(ans.Extender) {
-                    if(!ans.Transformed)
-                        ans.CoordinatesInExtender += GetLocation();
+            if(ans.Extender) {
+                if(!ans.Transformed)
+                    ans.CoordinatesInExtender += GetLocation();
 
-                    return ans;
-                }
+                return ans;
             }
-
-            return {
-                false, this, self.GetLocation()};
         }
 
+        return {
+            false, this, self.GetLocation()};
+    }
+
+    int Composer::GetSpacing() const {
+        if(issizesset) {
+            return spacing;
+        }
+        else {
+            return Widgets::Registry::Active().GetSpacing();
+        }
+    }
+
+    int Composer::GetUnitWidth() const {
+        if(issizesset) {
+            return unitwidth;
+        }
+        else {
+            return Widgets::Registry::Active().GetUnitWidth();
+        }
+    }
+    
+
+    void Composer::SetWidthInUnits(int n) {
+        int w, s;
+
+        if(HasParent()) {
+            w = GetParent().GetUnitWidth();
+            s = GetParent().GetSpacing();
+        }
+        else {
+            w = Registry::Active().GetUnitWidth();
+            s = Registry::Active().GetSpacing();
+        }
+
+        SetWidth(w * n + s * (n - 1));
+    }
+
 } }
--- a/Source/Gorgon/Widgets/Composer.h	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/Widgets/Composer.h	Sat Nov 21 18:47:12 2020 +0200
@@ -35,9 +35,6 @@
             return base.IsVisible();
         }
 
-        virtual Geometry::Size GetInteriorSize() const override {
-            return base.GetSize();
-        }
         
         virtual Geometry::Size GetSize() const override {
             return base.GetSize();
@@ -59,9 +56,6 @@
 
         using Widget::EnsureVisible;
         
-        bool EnsureVisible(const UI::Widget &widget) override {
-            return true;
-        }
 
         using Widget::Enable;
         using Widget::Disable;
@@ -78,6 +72,9 @@
             return enabled;
         }
 
+		/// Sets the width of the widget in unit widths.
+		void SetWidthInUnits(int n);
+        
         /// This function should be called whenever a key is pressed or released.
         virtual bool KeyEvent(Input::Key key, float state) override { return distributekeyevent(key, state, true); }
 
@@ -86,8 +83,6 @@
         virtual bool CharacterEvent(Char c) override { return distributecharevent(c); }
         
         
-        virtual UI::ExtenderRequestResponse RequestExtender(const Gorgon::Layer &self) override;
-
     protected:
         //ensure this object is derived
         Composer() {
@@ -117,6 +112,47 @@
             base.PlaceBefore(order);
         }
         
+        /// The spacing should be left between widgets
+        virtual int GetSpacing() const override;
+        
+        /// Returns the unit width for a widget. This size is enough to
+        /// have a bordered icon. Widgets should be sized according to unit
+        /// width and spacing. A single unit width would be too small for
+        /// most widgets.
+        virtual int GetUnitWidth() const override;
+        
+        /// Overrides default spacing and unitwidth
+        void SetSizes(int spacing, int unitwidth) {
+            this->spacing = spacing;
+            this->unitwidth = unitwidth;
+            issizesset = true;
+        }
+        
+        /// Sets the unit size automatically. Full width will be at least
+        /// given units wide. Returns remaining size.
+        int AutomaticUnitSize(int spacing, int units = 6) {
+            this->spacing   = spacing;
+            this->unitwidth = ( GetInteriorSize().Width - spacing * (units-1) ) / units;
+            
+            return GetInteriorSize().Width - (this->unitwidth * units + this->spacing * (units-1));
+        }
+        
+        /// Return to use default sizes
+        void UseDefaultSizes() {
+            issizesset = false;
+        }
+        
+        virtual UI::ExtenderRequestResponse RequestExtender(const Gorgon::Layer &self) override;
+        
+        bool EnsureVisible(const UI::Widget &) override {
+            return true;
+        }
+        
+        virtual Geometry::Size GetInteriorSize() const override {
+            return base.GetSize();
+        }
+        
+        
     private:
         bool enabled = true;
         
@@ -125,6 +161,10 @@
         virtual void show() override;
         
         Layer base;
+        
+        int spacing   = 0;
+        int unitwidth = 0;
+        bool issizesset = false;
     };
     
 } }
--- a/Source/Gorgon/Widgets/Dropdown.h	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/Widgets/Dropdown.h	Sat Nov 21 18:47:12 2020 +0200
@@ -281,7 +281,8 @@
     }
     
     /**
-     * This is a single selection drop down list. It can be used 
+     * This is a single selection drop down list. It should be used for regular types. It can also be
+     * used for enumerations.
      * 
      * **Example**
      * @code
@@ -346,6 +347,9 @@
     template <class T_, void (*TW_)(const T_ &, ListItem &) = internal::SetTextUsingFrom<T_, ListItem>>
     using DropdownList = SingleSelectionDropdown<T_, TW_, SimpleListbox<T_, TW_>>;
     
+    /**
+     * This dropdown is for reference objects.
+     */
     template <class T_, void (*TW_)(const T_ &, ListItem &) = internal::SetTextUsingFrom<T_, ListItem>>
     using DropdownCollection = SingleSelectionDropdown<T_, TW_, SimpleCollectionbox<T_, TW_>>;
     
--- a/Source/Gorgon/Widgets/Panel.cpp	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/Widgets/Panel.cpp	Sat Nov 21 18:47:12 2020 +0200
@@ -514,7 +514,23 @@
         auto size = GetInteriorSize();
         return {false, this, self.GetLocation(), {hscroll ? -1 : size.Width, vscroll ? -1 : size.Height}};
     }
+    
+    int Panel::GetSpacing() const {
+        if(issizesset) {
+            return spacing;
+        }
+        else {
+            return stack.GetTemplate().GetSpacing();
+        }
+    }
 
-
+    int Panel::GetUnitWidth() const {
+        if(issizesset) {
+            return unitwidth;
+        }
+        else {
+            return stack.GetTemplate().GetUnitWidth();
+        }
+    }
 } }
 
--- a/Source/Gorgon/Widgets/Panel.h	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/Widgets/Panel.h	Sat Nov 21 18:47:12 2020 +0200
@@ -176,6 +176,36 @@
         
         virtual UI::ExtenderRequestResponse RequestExtender(const Gorgon::Layer &self) override;
         
+        /// The spacing should be left between widgets
+        virtual int GetSpacing() const override;
+        
+        /// Returns the unit width for a widget. This size is enough to
+        /// have a bordered icon. Widgets should be sized according to unit
+        /// width and spacing. A single unit width would be too small for
+        /// most widgets.
+        virtual int GetUnitWidth() const override;
+        
+        /// Overrides default spacing and unitwidth
+        void SetSizes(int spacing, int unitwidth) {
+            this->spacing = spacing;
+            this->unitwidth = unitwidth;
+            issizesset = true;
+        }
+        
+        /// Sets the unit size automatically. Full width will be at least
+        /// given units wide. Returns remaining size.
+        int AutomaticUnitSize(int spacing, int units = 6) {
+            this->spacing   = spacing;
+            this->unitwidth = ( GetInteriorSize().Width - spacing * (units-1) ) / units;
+            
+            return GetInteriorSize().Width - (this->unitwidth * units + this->spacing * (units-1));
+        }
+        
+        /// Return to use default sizes
+        void UseDefaultSizes() {
+            issizesset = false;
+        }
+        
     protected:
         virtual bool allowfocus() const override;
         
@@ -236,6 +266,10 @@
         bool isscrolling = false;
         float scrollleftover = 0;
         bool vscroll = true, hscroll = false;
+        
+        int spacing   = 0;
+        int unitwidth = 0;
+        bool issizesset = false;
     };
     
 } }
--- a/Source/Gorgon/Widgets/RadioButtons.h	Sat Nov 21 11:49:58 2020 +0200
+++ b/Source/Gorgon/Widgets/RadioButtons.h	Sat Nov 21 18:47:12 2020 +0200
@@ -8,6 +8,7 @@
 #include "Checkbox.h"
 #include "../UI/WidgetContainer.h"
 #include "Registry.h"
+#include "Composer.h"
 
 namespace Gorgon { namespace Widgets {
     
@@ -17,7 +18,7 @@
      * widget might cause unexpected behavior. All other container functionality should work as intended.
      */
     template<class T_, class W_ = Checkbox>
-    class RadioButtons : public UI::Widget, protected UI::RadioControl<T_, W_>, protected UI::WidgetContainer {
+    class RadioButtons : public Composer, protected UI::RadioControl<T_, W_> {
         friend class UI::WidgetContainer;
     public:
         explicit RadioButtons(const UI::Template &temp) : temp(temp) { 
@@ -53,8 +54,15 @@
         
         /// Sets the width of the widget in unit widths.
         void SetWidthInUnits(int n) {
-            int w = temp.GetUnitWidth();
-            int s = temp.GetSpacing();
+            int w, s;
+            if(HasParent()) {
+                w = GetParent().GetUnitWidth();
+                s = GetParent().GetSpacing();
+            }
+            else {
+                w = temp.GetUnitWidth();
+                s = temp.GetSpacing();
+            }
             SetWidth(w * n + s * (n-1));
         }
 
--- a/Testing/Source/Manual/UI_Generate.cpp	Sat Nov 21 11:49:58 2020 +0200
+++ b/Testing/Source/Manual/UI_Generate.cpp	Sat Nov 21 18:47:12 2020 +0200
@@ -18,6 +18,7 @@
 #include <Gorgon/Widgets/ListItem.h>
 #include <Gorgon/UI/RadioControl.h>
 #include <Gorgon/UI/Organizers/List.h>
+#include <Gorgon/UI/Organizers/Flow.h>
 #include <Gorgon/Graphics/BlankImage.h>
 #include <Gorgon/Graphics/TintedObject.h>
 #include <Gorgon/Widgets/Dropdown.h>
@@ -165,7 +166,7 @@
     //blank.CreateOrganizer<Gorgon::UI::Organizers::List>().SetSpacing(Gorgon::Widgets::Registry::Active().GetSpacing());
 
     auto addme = [&](auto &pnl, UI::Widget &w) {
-        Geometry::Point offsetx = {Widgets::Registry::Active().GetSpacing(), 0};
+        /*Geometry::Point offsetx = {Widgets::Registry::Active().GetSpacing(), 0};
         if(pnl.UI::WidgetContainer::begin() != pnl.UI::WidgetContainer::end()) {
             auto &last = *(pnl.UI::WidgetContainer::end() - 1);
             auto lastb = last.GetBounds();
@@ -173,7 +174,7 @@
                 w.Move(last.GetBounds().TopRight() + offsetx);
             else
                 w.Move(0, last.GetBounds().Bottom + Widgets::Registry::Active().GetSpacing());
-        }
+        }*/
 
         pnl.Add(w);
     };
@@ -280,9 +281,9 @@
     app.wind.Add(blank);
     addme(blank, Coffee);
     addme(blank, btn);
+    addme(blank, list);
     addme(blank, icnbtn);
     addme(blank, icnbtn2);
-    addme(blank, list);
     addme(blank, dlist);
     addme(blank, icnbtn3);
     addme(blank, l);
@@ -296,6 +297,11 @@
     addme(blank, scroll1);
     addme(blank, scroll2);
     addme(blank, sizef);
+    auto &org = blank.CreateOrganizer<UI::Organizers::Flow>();
+    org.InsertBreak(-1);
+    org.InsertBreak(list);
+    org.InsertBreak(list);
+    
     
     
 

mercurial