#176 ModifyPositionAndSize and similar modification functionality 4.x-dev

Wed, 23 Sep 2020 22:21:50 +0300

author
cemkalyoncu
date
Wed, 23 Sep 2020 22:21:50 +0300
branch
4.x-dev
changeset 1449
db5111962661
parent 1448
2fdfbbe4a22e
child 1450
0c2bcf1bf63a

#176 ModifyPositionAndSize and similar modification functionality
#7 Scrollbar template
#7 Partial working scrollbar

Source/Gorgon/UI/ComponentStack.cpp file | annotate | diff | comparison | revisions
Source/Gorgon/UI/ComponentStack.h file | annotate | diff | comparison | revisions
Source/Gorgon/UI/Template.h file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Common.h file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Generator.cpp file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Generator.h file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Progressbar.h file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Registry.h file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Scrollbar.h file | annotate | diff | comparison | revisions
Source/Gorgon/Widgets/Slider.h file | annotate | diff | comparison | revisions
Testing/Source/Manual/UI_Generate.cpp file | annotate | diff | comparison | revisions
--- a/Source/Gorgon/UI/ComponentStack.cpp	Sun Sep 20 14:04:59 2020 +0300
+++ b/Source/Gorgon/UI/ComponentStack.cpp	Wed Sep 23 22:21:50 2020 +0300
@@ -2376,12 +2376,37 @@
                     heightch = 1;
                 }
             }
+            else if(temp.GetValueModification() == temp.ModifyPositionAndSize) {
+                switch(NumberOfSetBits(temp.GetValueSource())) {
+                    case 2:
+                        (ishor ? widthch : heightch) = 1;
+                        break;
+                        
+                    case 3:
+                        (ishor ? widthch : heightch) = 2;
+                        break;
+                        
+                    case 4:
+                        widthch  = 2;
+                        heightch = 3;
+                        break;
+                        
+                    default:
+                        break;
+                }
+            }
             else if(temp.GetValueModification() == temp.ModifyWidth) {
                 widthch = 0;
             }
             else if(temp.GetValueModification() == temp.ModifyHeight) {
                 heightch = 0;
             }
+            else if(temp.GetValueModification() == temp.ModifyXAndWidth || temp.GetValueModification() == temp.ModifyYAndWidth) {
+                widthch = 1;
+            }
+            else if(temp.GetValueModification() == temp.ModifyXAndHeight || temp.GetValueModification() == temp.ModifyYAndHeight) {
+                heightch = 1;
+            }
             
             
             //****** Tag size
@@ -2731,12 +2756,35 @@
                     ych = 1;
                 }
             }
+            else if(temp.GetValueModification() == temp.ModifyPositionAndSize) {
+                switch(NumberOfSetBits(temp.GetValueSource())) {
+                    case 1:
+                    case 2:
+                        (ishor ? xch : ych) = 0;
+                        break;
+                        
+                    case 3:
+                    case 4:
+                        xch  = 2;
+                        ych = 3;
+                        break;
+                        
+                    default:
+                        break;
+                }
+            }
             else if(temp.GetValueModification() == temp.ModifyX) {
                 xch = 0;
             }
             else if(temp.GetValueModification() == temp.ModifyY) {
                 ych = 0;
             }
+            else if(temp.GetValueModification() == temp.ModifyXAndWidth || temp.GetValueModification() == temp.ModifyXAndHeight) {
+                xch = 0;
+            }
+            else if(temp.GetValueModification() == temp.ModifyYAndWidth || temp.GetValueModification() == temp.ModifyYAndHeight) {
+                ych = 0;
+            }
 
             //****** Pixel conversion
             
--- a/Source/Gorgon/UI/ComponentStack.h	Sun Sep 20 14:04:59 2020 +0300
+++ b/Source/Gorgon/UI/ComponentStack.h	Wed Sep 23 22:21:50 2020 +0300
@@ -112,6 +112,9 @@
         /// Returns the value of the stack
         std::array<float, 4> GetValue() const { return returntarget ? targetvalue : value; }
 
+        /// Returns the value of the stack
+        std::array<float, 4> GetTargetValue() const { return targetvalue; }
+
         /// Changes the value transition speed. A speed of 0 will disable smooth transition.
         /// The unit is values per second
         void SetValueTransitionSpeed(std::array<float, 4> val) {
--- a/Source/Gorgon/UI/Template.h	Sun Sep 20 14:04:59 2020 +0300
+++ b/Source/Gorgon/UI/Template.h	Wed Sep 23 22:21:50 2020 +0300
@@ -719,6 +719,26 @@
             /// and will modify Size property. Useful for sliders and progress bars. The direction
             /// of the container is used to determine which axis will get affected.
             ModifyHeight,
+            
+            
+            /// This is a combined modification of position and size. Single channel will control
+            /// the position along the orientation. Two channels will control position and size along
+            /// the orientation. Three channels will control xy position and size along the 
+            /// orientation. Finally four channels will control every aspect of the component. 
+            ModifyPositionAndSize,
+            
+            /// Modifies X position and width
+            ModifyXAndWidth,
+            
+            /// Modifies X position and height
+            ModifyXAndHeight,
+            
+            /// Modifies Y position and width
+            ModifyYAndWidth,
+            
+            /// Modifies Y position and height
+            ModifyYAndHeight,
+            
         };
         
         /// Which data channels should be used as the value, common combinations are listed, however, all
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/Gorgon/Widgets/Common.h	Wed Sep 23 22:21:50 2020 +0300
@@ -0,0 +1,22 @@
+#pragma once
+
+
+namespace Gorgon { namespace Widgets {
+    
+    ///@cond internal
+    
+    template<class T_>
+    float FloatDivider(T_ left, T_ min, T_ max) {
+        if(min == max) return 0.f;
+        
+        return float(left - min) / float(max - min);
+    }
+    
+    template<class T_>
+    T_ FloatToValue(float value, T_ min, T_ max) {
+        return (max - min) * value + min;
+    }
+    
+    ///@endcond
+
+} }
--- a/Source/Gorgon/Widgets/Generator.cpp	Sun Sep 20 14:04:59 2020 +0300
+++ b/Source/Gorgon/Widgets/Generator.cpp	Wed Sep 23 22:21:50 2020 +0300
@@ -101,6 +101,7 @@
         delete hoverborder;
         delete downborder;
         delete disabledborder;
+        delete grooveborder;
         delete normaleditborder;
         delete hovereditborder;
         delete readonlyborder;
@@ -110,8 +111,13 @@
         delete hoverbg;
         delete downbg;
         delete disabledbg;
+        delete normalrbg;
+        delete hoverrbg;
+        delete downrbg;
+        delete disabledrbg;
         delete objectshape;
         delete innerobjectshape;
+        delete groovebg;
         
         for(auto p : panelborders) {
             delete p;
@@ -202,6 +208,13 @@
         return *panelborders[missingedge];
     }
     
+    Graphics::BitmapRectangleProvider &SimpleGenerator::GrooveBorder() {
+        if(!grooveborder)
+            grooveborder = makeborder(Border.Color, Background.Groove);
+        
+        return *grooveborder;
+    }
+
     Graphics::BitmapRectangleProvider &SimpleGenerator::NormalEditBorder() {
         if(!normaleditborder)
             normaleditborder = makeborder(Border.Color, Background.Edit);
@@ -268,6 +281,52 @@
         return *disabledbg;
     }
     
+    Graphics::BitmapRectangleProvider &SimpleGenerator::NormalRBG() {
+        if(!normalrbg)
+            normalrbg = makeborder(0x0, Background.Regular, 0, 0, Border.Radius/2);
+        
+        return *normalrbg;
+    }
+    
+    Graphics::BitmapRectangleProvider &SimpleGenerator::HoverRBG() {
+        if(!hoverrbg) {
+            auto c = Background.Regular;
+            c.Blend(Background.Hover);
+            hoverrbg = makeborder(0x0, c, 0, 0, Border.Radius/2);
+        }
+        
+        return *hoverrbg;
+    }
+
+    Graphics::BitmapRectangleProvider &SimpleGenerator::DownRBG() {
+        if(!downrbg) {
+            auto c = Background.Regular;
+            c.Blend(Background.Down);
+            downrbg = makeborder(0x0, c, 0, 0, Border.Radius/2);
+        }
+
+        return *downrbg;
+    }
+    
+    Graphics::BitmapRectangleProvider &SimpleGenerator::DisabledRBG() {
+        if(!disabledrbg) {
+            auto c = Background.Regular;
+            c.Blend(Background.Disabled);
+            disabledrbg = makeborder(0x0, c, 0, 0, Border.Radius/2);
+        }
+
+        return *disabledrbg;
+    }
+    
+    Graphics::BitmapRectangleProvider &SimpleGenerator::GrooveBG() {
+        if(!groovebg) {
+            auto c = Background.Groove;
+            groovebg = makeborder(0x0, c);
+        }
+
+        return *groovebg;
+    }
+    
     Graphics::BitmapRectangleProvider &SimpleGenerator::ObjectShape() {
         if(!objectshape) {
             auto c = Forecolor.Regular;
@@ -311,8 +370,9 @@
         if(r == -1)
             r = Border.Radius;
         
-        int bsize = (w + r + 2) * 2 + 16;
-        float off = float(w / 2.0f); //prefer integers
+        int coff = r + (border.A > 0 ? w+1 : 0);
+        int bsize = coff * 2 + 16;
+        float off = (border.A ? float(w / 2.0f) : 0);
         
         auto &bi = *new Graphics::Bitmap({bsize, bsize}, Graphics::ColorMode::RGBA);
         bi.Clear();
@@ -402,10 +462,10 @@
         
 
         auto ret = new Graphics::BitmapRectangleProvider(Graphics::Slice(bi, {
-            int(r+w+2), 
-            int(r+w+2), 
-            int(bsize-r-w-2),
-            int(bsize-r-w-2)
+            coff, 
+            coff, 
+            bsize-coff,
+            bsize-coff
         }));
         
         ret->Prepare();
@@ -1241,7 +1301,8 @@
     }
 
     UI::Template SimpleGenerator::Progressbar() {
-        Geometry::Size defsize = {WidgetWidth * 2 + Spacing, WidgetHeight};
+        int h = std::max(Border.Radius * 2 + Border.Width * 2 + 4, Spacing * 3);
+        Geometry::Size defsize = {WidgetWidth * 2 + Spacing, h};
         
         UI::Template temp;
         temp.SetSpacing(Spacing);
@@ -1321,7 +1382,7 @@
     }
     
     UI::Template SimpleGenerator::VScrollbar() {
-        int w = std::max(Border.Radius * 2 + 1, Spacing * 3);
+        int w = std::max(Border.Radius * 2 + 2, Spacing * 3);
         
         Geometry::Size defsize = {w, BorderedWidgetHeight * 3 - Border.Width * 2};
         
@@ -1358,39 +1419,39 @@
     }
     
     UI::Template SimpleGenerator::HScrollbar() {
-        int h = std::max(Border.Radius * 2 + 1, Spacing * 3);
+        auto dist = int(std::round(Spacing / 3.f));
+        int h = std::max(Border.Radius * 2 + std::max(0, dist - Border.Radius / 2) * 2, Spacing * 2);
         
         Geometry::Size defsize = {WidgetWidth * 2 + Spacing, h};
         
         UI::Template temp;
         temp.SetSize(defsize);
         
-        temp.AddContainer(0, UI::ComponentCondition::Always)
-            .AddIndex(1) //movement control
+        auto &cont = temp.AddContainer(0, UI::ComponentCondition::Always)
+            .AddIndex(1) //handle control
         ;
+        cont.Background.SetAnimation(GrooveBG());
+        cont.SetPadding(dist + Border.Radius/2, dist);
+        
         
-        auto &move = temp.AddContainer(1, UI::ComponentCondition::Always)
-            .AddIndex(2) //size control
-        ;
-        move.SetSizing(UI::ComponentTemplate::Fixed, UI::ComponentTemplate::Automatic);
-        move.SetValueModification(UI::ComponentTemplate::ModifyX);
-        move.SetPositioning(UI::ComponentTemplate::AbsoluteSliding);
+        auto setupbar = [&](auto &border, auto cond) {
+            auto &size = temp.AddGraphics(1, cond);
+            size.SetValueModification(UI::ComponentTemplate::ModifyPositionAndSize, UI::ComponentTemplate::UseXZ);
+            size.SetPositioning(UI::ComponentTemplate::AbsoluteSliding);
+            size.SetSize({h, UI::Dimension::Pixel}, {100, UI::Dimension::Percent});
+            size.SetTag(UI::ComponentTemplate::DragBarTag);
+            size.Content.SetAnimation(border);
+        };
+        
+        setupbar(NormalRBG(), UI::ComponentCondition::Always);
+        setupbar(HoverRBG(), UI::ComponentCondition::Hover);
+        setupbar(DownRBG(), UI::ComponentCondition::Down);
+        setupbar(DisabledRBG(), UI::ComponentCondition::Disabled);
         
         //remove handle when there is nothing to scroll
         temp.AddIgnored(1, UI::ComponentCondition::Ch2V1);
         
-        auto setupbar = [&](auto &border, auto cond) {
-            auto &size = temp.AddGraphics(2, cond);
-            size.SetValueModification(UI::ComponentTemplate::ModifyWidth, UI::ComponentTemplate::UseSecond);
-            size.SetPositioning(UI::ComponentTemplate::AbsoluteSliding);
-            size.SetSize(h, h);
-            size.SetTag(UI::ComponentTemplate::DragBarTag);
-            size.Content.SetAnimation(border);
-        };
-        
-        setupbar(NormalEditBorder(), UI::ComponentCondition::Always);
-        setupbar(HoverEditBorder(), UI::ComponentCondition::Hover);
-        
         return temp;
     }
+    
 }}
--- a/Source/Gorgon/Widgets/Generator.h	Sun Sep 20 14:04:59 2020 +0300
+++ b/Source/Gorgon/Widgets/Generator.h	Wed Sep 23 22:21:50 2020 +0300
@@ -103,6 +103,10 @@
                 return *new UI::Template(Layerbox());
             case Layerbox_Blank:
                 return *new UI::Template(BlankLayerbox());
+            case Scrollbar_Vertical:
+                return *new UI::Template(VScrollbar());
+            case Scrollbar_Horizontal:
+                return *new UI::Template(HScrollbar());
             default:
                 return *new UI::Template();
             }
@@ -193,6 +197,8 @@
         
         Graphics::BitmapRectangleProvider &PanelBorder(int missingedge = 0);
         
+        Graphics::BitmapRectangleProvider &GrooveBorder();
+        
         Graphics::BitmapRectangleProvider &NormalEditBorder();
         Graphics::BitmapRectangleProvider &HoverEditBorder();
         Graphics::BitmapRectangleProvider &ReadonlyBorder();
@@ -203,9 +209,15 @@
         Graphics::BitmapRectangleProvider &HoverBG();
         Graphics::BitmapRectangleProvider &DownBG();
         Graphics::BitmapRectangleProvider &DisabledBG();
+        Graphics::BitmapRectangleProvider &NormalRBG();
+        Graphics::BitmapRectangleProvider &HoverRBG();
+        Graphics::BitmapRectangleProvider &DownRBG();
+        Graphics::BitmapRectangleProvider &DisabledRBG();
         Graphics::BitmapRectangleProvider &ObjectShape();
         Graphics::MaskedObjectProvider &InnerObjectShape();
         
+        Graphics::BitmapRectangleProvider &GrooveBG();
+        
         Graphics::RectangleProvider &FocusBorder();
         
         int Spacing       = 4;
@@ -248,6 +260,8 @@
             Graphics::RGBA Edit    = {Graphics::Color::White};
             Graphics::RGBA Panel   = {Graphics::Color::Grey, Graphics::Color::Ivory, 0.5};
             
+            Graphics::RGBA Groove  = {Graphics::Color::Charcoal, 0.5};
+            
             Graphics::RGBA Selected= {Graphics::Color::Charcoal, 0.4};
         } Background;
         
@@ -291,16 +305,24 @@
         
         Graphics::BitmapRectangleProvider *panelborders[5] = {};
         
+        Graphics::BitmapRectangleProvider *grooveborder = nullptr;
+        
         Graphics::BitmapRectangleProvider *normaleditborder = nullptr;
         Graphics::BitmapRectangleProvider *hovereditborder = nullptr;
         Graphics::BitmapRectangleProvider *readonlyborder = nullptr;
         
         Graphics::BitmapRectangleProvider *normalemptyborder = nullptr;
         
+        Graphics::BitmapRectangleProvider *normalrbg = nullptr;
+        Graphics::BitmapRectangleProvider *hoverrbg = nullptr;
+        Graphics::BitmapRectangleProvider *downrbg = nullptr;
+        Graphics::BitmapRectangleProvider *disabledrbg = nullptr;
+        
         Graphics::BitmapRectangleProvider *normalbg = nullptr;
         Graphics::BitmapRectangleProvider *hoverbg = nullptr;
         Graphics::BitmapRectangleProvider *downbg = nullptr;
         Graphics::BitmapRectangleProvider *disabledbg = nullptr;
+        Graphics::BitmapRectangleProvider *groovebg = nullptr;
         Graphics::BitmapRectangleProvider *objectshape = nullptr;
         Graphics::MaskedObjectProvider *innerobjectshape = nullptr;
         
--- a/Source/Gorgon/Widgets/Progressbar.h	Sun Sep 20 14:04:59 2020 +0300
+++ b/Source/Gorgon/Widgets/Progressbar.h	Wed Sep 23 22:21:50 2020 +0300
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "Common.h"
 #include "../UI/ComponentStackWidget.h"
 #include "../UI/Helpers.h"
 #include "../Property.h"
@@ -7,13 +8,20 @@
 
 namespace Gorgon { namespace Widgets {
     
-    template<class T_>
-    float FloatDivider(T_ left, T_ min, T_ max) {
-        if(min == max) return 0.f;
-        
-        return float(left - min) / float(max - min);
-    }
 
+    /**
+     * This widget displays a progress bar to track progress of an on going 
+     * process. For regular integer based progressor use Progressbar. This
+     * is the base widget that can be used with any type, including types
+     * that are not inherintly numeric. The completion is calculated using
+     * DIV_ function which takes current, minimum and maximum values and
+     * should return the progress between 0 and 1. Property type should be
+     * adjusted if numeric operators does not exist for the given type.
+     * 
+     * If the type is numeric, using NumericProperty will allow you to use
+     * the widget as a regular numeric variable with operators like =, +=,
+     * ++, --. 
+     */
     template<
         class T_ = int, 
         float(*DIV_)(T_, T_, T_) = FloatDivider<T_>, 
@@ -260,6 +268,7 @@
                 stack.AddCondition(UI::ComponentCondition::Ch1V05);
             }
             else {
+                stack.RemoveCondition(UI::ComponentCondition::Ch1V05);
             }
             if(v == 1000) {
                 stack.AddCondition(UI::ComponentCondition::Ch1V1);
@@ -269,9 +278,9 @@
             }
         }
         
-        T_ value;
+        T_ value = T_{};
         T_ min = T_{};
-        T_ max;
+        T_ max = T_{};
         
         float progressspeed = 1;
         
@@ -281,6 +290,12 @@
 
     };
     
+    /**
+     * This widget displays the progress. It can be used as an integer variable through
+     * NumericProperty<int> with operators like =, +=, ++, --. Also this widget can be
+     * implicitly casted to integer. If you need a type other than int, you may use
+     * Progressor<T_> for any type, even non-numeric.
+     */
     using Progressbar = Progressor<int>;
     
 } }
--- a/Source/Gorgon/Widgets/Registry.h	Sun Sep 20 14:04:59 2020 +0300
+++ b/Source/Gorgon/Widgets/Registry.h	Wed Sep 23 22:21:50 2020 +0300
@@ -41,6 +41,9 @@
             Layerbox_Regular,
             Layerbox_Blank,
             
+            Scrollbar_Horizontal,
+            Scrollbar_Vertical,
+            
             ///Do not use this value
             Max 
         };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/Gorgon/Widgets/Scrollbar.h	Wed Sep 23 22:21:50 2020 +0300
@@ -0,0 +1,81 @@
+#pragma once
+
+#include "Slider.h"
+
+
+namespace Gorgon { namespace Widgets {
+    
+    /**
+     * This widget is a scrollbar. It is basically a slider without ticks and 
+     * potentially has buttons to adjust value. This widget has no ability to
+     * provide scroll capability to another widget automatically. Therefore, it
+     * has no additional capability on top of slider.
+     * 
+     * Scrollbar orientation is controlled by its template. You may use 
+     * HScrollbar and VScrollbar to denote the orientation. Additionally, these
+     * variants have their type fixed to int.
+     */
+    template<
+        class T_, 
+        float(*DIV_)(T_, T_, T_) = FloatDivider<T_>, 
+        T_(*VAL_)(float, T_, T_) = FloatToValue<T_>,
+        template<class C_, class PT_, PT_(C_::*Getter_)() const, void(C_::*Setter_)(const PT_ &)> class P_ = Gorgon::NumericProperty
+    >
+    class Scrollbar : public SliderBase<T_, DIV_, VAL_, P_, true> {
+        using Base = SliderBase<T_, DIV_, VAL_, P_, true>;
+    public:
+        
+        Scrollbar(const Scrollbar &) = delete;
+        
+        explicit Scrollbar(T_ cur, T_ max, Registry::TemplateType type = Registry::Scrollbar_Horizontal) : 
+            Scrollbar(Registry::Active()[type], cur, max) 
+        {
+        }
+        
+        explicit Scrollbar(T_ cur = T_{}, Registry::TemplateType type = Registry::Scrollbar_Horizontal) : 
+            Scrollbar(Registry::Active()[type], cur) 
+        {
+        }
+
+        explicit Scrollbar(const UI::Template &temp, T_ cur = T_{}) : Scrollbar(temp, cur, T_{100}) { }
+
+        Scrollbar(const UI::Template &temp, T_ cur, T_ max) : 
+            Base(temp, cur, max)
+        {
+            this->stack.HandleMouse();
+        }
+        
+        using Base::SetMaximum;
+        using Base::GetMaximum;
+        
+        using Base::SetRange;
+        using Base::GetRange;
+        
+        using Base::operator =;
+        using Base::operator T_;
+        
+        using Base::DisableSmoothChange;
+        using Base::SetSmoothChangeSpeed;
+        using Base::SetSmoothChangeSpeedRatio;
+        using Base::GetSmoothChangeSpeed;
+        using Base::GetSmoothChangeSpeedRatio;
+        using Base::IsSmoothChangeEnabled;
+        
+        using Base::Maximum;
+        using Base::Range;
+        
+        /// This event is fired when the scroll is changed
+        Event<Scrollbar, T_> ValueChanged = Event<Scrollbar, T_>(this);
+        
+    protected:
+        virtual void valuechanged(T_ value) override {
+            ValueChanged(value);
+        }
+        
+        virtual void refreshvalue() override {
+            Base::refreshvalue();
+            
+        }
+    };
+    
+} }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/Gorgon/Widgets/Slider.h	Wed Sep 23 22:21:50 2020 +0300
@@ -0,0 +1,366 @@
+#pragma once
+
+#include "Common.h"
+#include "../UI/ComponentStackWidget.h"
+#include "../UI/Helpers.h"
+#include "../Property.h"
+#include "Registry.h"
+
+namespace Gorgon { namespace Widgets {
+    
+    /**
+     * This is an internal basis for slider. It is not very useful by its
+     * own as many of its functions are protected. It is used as a base
+     * for Slider, Progressbar (not yet), and Scrollbar widgets.
+     */
+    template<
+        class T_ = int, 
+        float(*DIV_)(T_, T_, T_) = FloatDivider<T_>, 
+        T_(*VAL_)(float, T_, T_) = FloatToValue<T_>, 
+        template<class C_, class PT_, PT_(C_::*Getter_)() const, void(C_::*Setter_)(const PT_ &)> class P_ = Gorgon::NumericProperty,
+        bool interactive = false
+    >
+    class SliderBase: 
+        public UI::ComponentStackWidget,
+        public P_<
+            UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive>, T_>, 
+            T_, 
+            &UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive>, T_>::get_, 
+            &UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive>, T_>::set_
+        >
+    {
+    public:
+        using Type     = T_;
+        using PropType = P_<
+            UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive>, T_>, 
+            T_, 
+            &UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive>, T_>::get_, 
+            &UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive>, T_>::set_
+        >;
+        
+        friend class P_<
+            UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive>, T_>, 
+            T_, 
+            &UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive>, T_>::get_, 
+            &UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive>, T_>::set_
+        >;
+        
+        template<
+            class T1_, 
+            float(*DIV1_)(T1_, T1_, T1_), 
+            T1_(*VAL1_)(float, T1_, T1_),
+            template<
+                class C_, 
+                class PT_, 
+                PT_(C_::*Getter_)() const, 
+                void(C_::*Setter_)(const PT_&)
+            > class P1_,
+            bool I2_
+        >
+        friend class SliderBase;
+        
+        friend struct UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive>, T_>;
+
+        SliderBase(const SliderBase &) = delete;
+        
+        explicit SliderBase(T_ cur, T_ max, Registry::TemplateType type = Registry::Progress_Regular) : 
+            SliderBase(Registry::Active()[type], cur, max) 
+        {
+        }
+        
+        explicit SliderBase(T_ cur = T_{}, Registry::TemplateType type = Registry::Progress_Regular) : 
+            SliderBase(Registry::Active()[type], cur) 
+        {
+        }
+
+        explicit SliderBase(const UI::Template &temp, T_ cur = T_{}) : SliderBase(temp, cur, T_{100}) { }
+
+        SliderBase(const UI::Template &temp, T_ cur, T_ max) : 
+            ComponentStackWidget(temp),
+            PropType(&helper), 
+            Maximum(this),
+            Minimum(this),
+            Range(this),
+            value(cur), max(max)
+        {
+            refreshvalue();
+            stack.SetValueTransitionSpeed({changespeed, 0, 0, 0});
+            
+            if(interactive)
+                stack.HandleMouse();
+        }
+        
+    protected: //these methods are here to be elevated to public if necessary.
+        
+        /// Sets the maximum value that this slider reaches up to. If equal to minimum,
+        /// progress will display 0. SliderBase will always keep the value between minimum
+        /// and maximum. If maximum is less than minimum, this function will automatically
+        /// exchange these values.
+        void SetMaximum(const T_ &value) {
+            if(value < min) {
+                max = min;
+                min = value;
+            }
+            else {
+                max = value;
+            }
+            
+            if(!setval(this->value)) //if returns true, refresh is already called
+                refreshvalue();
+        }
+        
+        /// Returns the current maximum value.
+        T_ GetMaximum() const {
+            return max;
+        }
+        
+        /// Sets the minimum value that this slider reaches up to. If equal to maximum,
+        /// progress will display 0. SliderBase will always keep the value between minimum
+        /// and maximum. If maximum is less than minimum, this function will automatically
+        /// exchange these values.
+        void SetMinimum(const T_ &value) {
+            if(value > max) {
+                min = max;
+                max = value;
+            }
+            else {
+                min = value;
+            }
+            
+            if(!setval(this->value)) //if returns true, refresh is already called
+                refreshvalue();
+        }
+        
+        /// Sets minimum and maximum limits. If minimum is equal to maximum,
+        /// progress will display 0. SliderBase will always keep the value between minimum
+        /// and maximum. If maximum is less than minimum, this function will automatically
+        /// exchange these values if exchange is set. If exchange is not set, they will both
+        /// be set to T_{}, effectively locking progress at 0.
+        void SetLimits(T_ min, T_ max, bool exchange = true) {
+            if(min > max) {
+                if(exchange) {
+                    using std::swap;
+                    
+                    swap(min, max);
+                }
+                else {
+                    min = T_{};
+                    max = T_{};
+                }
+            }
+            
+            this->min = min;
+            this->max = max;
+            
+            if(setval(this->value)) //if returns true, refresh is already called
+                refreshvalue();
+        }
+        
+        /// Returns the current minimum value. 
+        T_ GetMinimum() const {
+            return min;
+        }
+        
+        /// Sets the current value of the slider
+        SliderBase &operator =(T_ value) {
+            set(value);
+            
+            return *this;
+        }
+        
+        /// Sets the range the container can display. This is used to show how 
+        /// much more the scroller can be scrolled.
+        void SetRange(const T_ &value) {
+            range = value;
+            
+            if(range < this->min)
+                range = this->min;
+            
+            if(range > this->max)
+                range = this->max;
+            
+            this->refreshvalue();
+        }
+        
+        /// Returns the range the container can display. This is used to show 
+        /// how much more the scroller can be scrolled.
+        T_ GetRange() const {
+            return range;
+        }
+        
+        /// Returns the current value of the slider
+        operator T_() const {
+            return get();
+        }
+
+        virtual bool Activate() override {
+            return Focus();
+        }
+        
+        /// Disables smooth change
+        void DisableSmoothChange() {
+            SetSmoothChangeSpeed(0);
+        }
+        
+        /// Adjusts the smooth change speed. Given value is in values per second, 
+        /// default value is max and will be sync to the maximum value.
+        void SetSmoothChangeSpeed(T_ value) {
+            SetSmoothChangeSpeedRatio(DIV_(value, min, max));
+        }
+        
+        /// Adjusts the smooth change speed. Given value is in values per second, 
+        /// default value is 1 and will be sync to the maximum value.
+        void SetSmoothChangeSpeedRatio(float value) {
+            changespeed = value;
+            stack.SetValueTransitionSpeed({value, 0, 0, 0});
+        }
+        
+        /// Returns the smooth change speed. If smooth change is disabled, this 
+        /// value will be 0.
+        T_ GetSmoothChangeSpeed() const {
+            return changespeed * (max - min);
+        }
+        
+        /// Returns the smooth change in ratio to the maximum. 1 means full progress
+        /// will be done in 1 second.
+        float GetSmoothChangeSpeedRatio() const {
+            return changespeed;
+        }
+        
+        /// Returns if the smooth change is enabled.
+        bool IsSmoothChangeEnabled() const {
+            return changespeed != 0;
+        }
+        
+        /// This property controls the maximum value that the slider can have
+        NumericProperty<SliderBase, T_, &SliderBase::GetMaximum, &SliderBase::SetMaximum> Maximum;
+        
+        /// This property controls the minimum value that the slider can have
+        NumericProperty<SliderBase, T_, &SliderBase::GetMinimum, &SliderBase::SetMinimum> Minimum;
+        
+        /// This is used to show how much more the scroller can be scrolled.
+        NumericProperty<SliderBase, T_, &SliderBase::GetRange, &SliderBase::SetRange> Range;
+        
+        
+    protected:
+        virtual bool allowfocus() const override { return false; }
+        
+        /// Returns the value in the box
+        T_ get() const {
+            return value;
+        }
+        
+        /// Changes the value in the box
+        void set(const T_ &val) {
+            setval(val);
+        }
+        
+        bool setval(T_ val) {
+            if(val > max)
+                val = max;
+            
+            if(val < min)
+                val = min;
+            
+            if(value != val) {
+                value = val;
+                
+                valuechanged(value);
+            
+                refreshvalue();
+                
+                return true;
+            }
+            
+            return false;
+        }
+        
+        virtual void refreshvalue() {
+            float val = DIV_(this->value, min, max);
+            float val2 = DIV_(this->value+this->range, this->min, this->max);
+            float r = DIV_(this->range, this->min, this->max);
+            
+            stack.SetValue(val, val2, r);
+            
+            val = std::round(val*1000);
+            
+            if(val == 0) {
+                stack.AddCondition(UI::ComponentCondition::Ch1V0);
+            }
+            else {
+                stack.RemoveCondition(UI::ComponentCondition::Ch1V0);
+            }
+            if(val == 500) {
+                stack.AddCondition(UI::ComponentCondition::Ch1V05);
+            }
+            else {
+                stack.RemoveCondition(UI::ComponentCondition::Ch1V05);
+            }
+            if(val == 1000) {
+                stack.AddCondition(UI::ComponentCondition::Ch1V1);
+            }
+            else {
+                stack.RemoveCondition(UI::ComponentCondition::Ch1V1);
+            }
+            
+            val2 = std::round(val2*1000);
+            
+            if(val2 == 0) {
+                this->stack.AddCondition(UI::ComponentCondition::Ch2V0);
+            }
+            else {
+                this->stack.RemoveCondition(UI::ComponentCondition::Ch2V0);
+            }
+            if(val2 == 500) {
+                this->stack.AddCondition(UI::ComponentCondition::Ch2V05);
+            }
+            else {
+                this->stack.RemoveCondition(UI::ComponentCondition::Ch2V05);
+            }
+            if(val2 == 1000) {
+                this->stack.AddCondition(UI::ComponentCondition::Ch2V1);
+            }
+            else {
+                this->stack.RemoveCondition(UI::ComponentCondition::Ch2V1);
+            }
+            
+            r = std::round(r*1000);
+            
+            if(r == 0) {
+                this->stack.AddCondition(UI::ComponentCondition::Ch3V0);
+            }
+            else {
+                this->stack.RemoveCondition(UI::ComponentCondition::Ch3V0);
+            }
+            if(r == 500) {
+                this->stack.AddCondition(UI::ComponentCondition::Ch3V05);
+            }
+            else {
+                this->stack.RemoveCondition(UI::ComponentCondition::Ch3V05);
+            }
+            if(r == 1000) {
+                this->stack.AddCondition(UI::ComponentCondition::Ch3V1);
+            }
+            else {
+                this->stack.RemoveCondition(UI::ComponentCondition::Ch3V1);
+            }
+        }
+        
+        T_ value = T_{};
+        T_ min = T_{};
+        T_ max = T_{};
+        
+        //this is range that is covered by the scrollbar
+        T_ range = T_{};
+        
+        float changespeed = 1;
+        
+        virtual void valuechanged(T_) = 0;
+        
+    private:
+        struct UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive>, T_> helper = UI::internal::prophelper<SliderBase<T_, DIV_, VAL_, P_, interactive>, T_>(this);
+
+    };
+    
+    
+} }
--- a/Testing/Source/Manual/UI_Generate.cpp	Sun Sep 20 14:04:59 2020 +0300
+++ b/Testing/Source/Manual/UI_Generate.cpp	Wed Sep 23 22:21:50 2020 +0300
@@ -12,6 +12,7 @@
 #include <Gorgon/Widgets/Numberbox.h>
 #include <Gorgon/Widgets/GeometryBoxes.h>
 #include <Gorgon/Widgets/Progressbar.h>
+#include <Gorgon/Widgets/Scrollbar.h>
 #include <Gorgon/UI/RadioControl.h>
 #include <Gorgon/UI/Organizers/List.h>
 #include <Gorgon/Graphics/BlankImage.h>
@@ -39,8 +40,8 @@
     ///Blank Panel & elements with Registry & Regulars 
     /*Widgets::SimpleGenerator generator;
     generator.Init(13);
-    //generator.Border.Radius = 0;
     generator.UpdateBorders();
+    generator.Border.Radius = 0;
     generator.UpdateDimensions();
     generator.Activate();*/
 
@@ -132,6 +133,11 @@
         return false;
     });
     
+    Widgets::Scrollbar<int> scroll(50);
+    scroll = 40;
+    scroll.Range = 20;
+    
+    
     app.wind.Add(blank);
     addme(blank, btn);
     addme(blank, icnbtn);
@@ -145,6 +151,7 @@
     addme(blank, chk2);
     addme(blank, chkbutton);
     addme(blank, bar);
+    addme(blank, scroll);
     addme(blank, toppanel);
     addme(blank, bottompanel);
     addme(blank, leftpanel);

mercurial