ecere/gui/controls/Stacker: Freeing space for scrollers when not needed
[sdk] / ecere / src / gui / controls / Stacker.ec
index fd1815c..fa89484 100644 (file)
@@ -10,98 +10,13 @@ public import "ecere"
 #endif
 #endif
 
-public class RepButton : Button
-{
-public:
-   bool pressing;
-   isRemote = true;
-   inactive = true;
-   
-   property Seconds delay { set { timer2.delay = value; } }
-   property Seconds delay0 { set { timer.delay = value; } }
-   
-   bool OnKeyHit(Key key, unichar ch)
-   {
-      return true;
-   }
-
-   bool OnKeyDown(Key key, unichar ch)
-   {
-      if(key == hotKey)
-      {
-         NotifyPushed(master, this, 0,0, key.modifiers);
-         return false;
-      }
-      return true;
-   }
-
-   bool OnKeyUp(Key key, unichar ch)
-   {
-      if(key == hotKey)
-      {
-         NotifyReleased(master, this, 0,0, key.modifiers);
-         return false;
-      }
-      return true;
-   }
-
-   bool NotifyPushed(RepButton button, int x, int y, Modifiers mods)
-   {
-      button.pressing = true;
-      button.NotifyClicked(this, button, x, y, mods);
-      button.timer.Start();
-      return true;
-   }
-
-   bool NotifyMouseLeave(RepButton button, Modifiers mods)
-   {
-      button.timer.Stop();
-      button.timer2.Stop();
-      return true;
-   }
-
-   bool NotifyReleased(RepButton button, int x, int y, Modifiers mods)
-   {
-      button.pressing = false;
-      button.NotifyMouseLeave(this, button, mods);
-      return false;
-   }
-
-   bool NotifyMouseOver(RepButton button, int x, int y, Modifiers mods)
-   {
-      if(button.pressing)
-         button.timer2.Start();
-      return true;
-   }
-
-   Timer timer
-   {
-      this, delay = 0.1;
-
-      bool DelayExpired()
-      {
-         timer.Stop();
-         timer2.Start();
-         timer2.DelayExpired(this);
-         return true;
-      }
-   };
-   Timer timer2
-   {
-      this, delay = 0.1;
-      bool DelayExpired()
-      {
-         NotifyClicked(master, this, 0, 0, 0);
-         return true;
-      }
-   };
-}
+// class RepButton WAS ALREADY DEFINED IN date.ec! The version here broke CalendarControl behavior.
 
 static define stackerScrolling = 16;
 
 class StackerBits
 {
-   bool reverse:1, scrollable:1, flipSpring:1, autoSize:1;
+   bool reverse:1, scrollable:1, flipSpring:1, autoSize:1, endButtons:1;
 
    // internals
    bool holdChildMonitoring:1;
@@ -135,8 +50,39 @@ public:
 
    property Window flipper { set { flipper = value; } get { return flipper; } };
    property bool flipSpring { set { bits.flipSpring = value; } get { return bits.flipSpring; } };
-   property bool autoSize { set { bits.autoSize = value; } get { return bits.autoSize; } };
+   property bool autoSize
+   {
+      set
+      {
+         bits.autoSize = value;
+         if(value)
+         {
+            // Auto Size implementation conflicts with this base Window property, resulting in overhead and potential glitches:
+            // dontAutoScrollArea = false;
+            modifyVirtualArea = false;
+         }
+      }
+      get { return bits.autoSize; }
+   };
    property int margin { set { margin = value; } get { return margin; } };
+   property bool endButtons
+   {
+      set
+      {
+         if(bits.endButtons && scrollable && !value)
+         {
+            left.visible = false;
+            right.visible = false;
+         }
+         bits.endButtons = value;
+         if(value && scrollable)
+         {
+            left.visible = true;
+            right.visible = true;
+         }
+      }
+      get { return bits.endButtons; }
+   };
 
 private:
    StackerBits bits;
@@ -146,9 +92,31 @@ private:
    Array<Window> controls { };
    Window flipper;
 
+   void OnVScroll(ScrollBarAction action, int position, Key key)
+   {
+      if(bits.endButtons)
+      {
+         bool ld = false, rd = false;
+         left.disabled = false;
+         right.disabled = false;
+         if(direction == horizontal)
+         {
+            if(position == 0) ld = true;
+            if(position + clientSize.w >= scrollArea.w) rd = true;
+         }
+         else
+         {
+            if(position == 0) ld = true;
+            if(position + clientSize.h >= scrollArea.h) rd = true;
+         }
+         if(left.disabled != ld)  { left.disabled = ld;  left.OnLeftButtonUp(-1,0,0); }
+         if(right.disabled != rd) { right.disabled = rd; right.OnLeftButtonUp(-1,0,0); }
+      }
+   }
+
    RepButton left
    {
-      nonClient = true, parent = this, visible = false, bevelOver = true, keyRepeat = true, opacity = 0;
+      nonClient = true, parent = this, visible = false, bevelOver = true, keyRepeat = true, opacity = 0; delay0 = 0.1;
 
       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
       {
@@ -169,7 +137,7 @@ private:
    };
    RepButton right
    {
-      nonClient = true, parent = this, visible = false, bevelOver = true, keyRepeat = true, opacity = 0;
+      nonClient = true, parent = this, visible = false, bevelOver = true, keyRepeat = true, opacity = 0; delay0 = 0.1;
 
       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
       {
@@ -194,18 +162,18 @@ private:
    void GetDecorationsSize(MinMaxValue * w, MinMaxValue * h)
    {
       Window::GetDecorationsSize(w, h);
-      if(scrollable)
+      if(bits.scrollable && bits.endButtons && left.visible)
       {
-         if(direction == vertical) *h += left.size.w + right.size.w + 8; else *w += left.size.h + right.size.h + 8;
+         if(direction == vertical) *h += left.size.h + right.size.h + 8; else *w += left.size.w + right.size.w + 8;
       }
    }
 
    void SetWindowArea(int * x, int * y, MinMaxValue * w, MinMaxValue * h, MinMaxValue * cw, MinMaxValue * ch)
    {
       Window::SetWindowArea(x, y, w, h, cw, ch);
-      if(scrollable)
+      if(bits.scrollable && bits.endButtons && left.visible)
       {
-         if(direction == vertical) *y += left.size.w + 4; else *x += left.size.h + 4;
+         if(direction == vertical) *y += left.size.h + 4; else *x += left.size.w + 4;
       }
    }
 
@@ -214,16 +182,23 @@ private:
       controls.Free();
    }
 
+   bool OnCreate()
+   {
+      bits.holdChildMonitoring = true;
+      return true;
+   }
+
    bool OnPostCreate()
    {
+      bits.holdChildMonitoring = false;
       OnResize(clientSize.w, clientSize.h);
 
       if(direction == vertical)
       {
-         left.bitmap = { "<:ecere>elements/arrowTop.png" };
+         left.bitmap = { "<:ecere>elements/arrowUp.png" };
          left.anchor = { top = 2, left = 2, right = 2 };
 
-         right.bitmap = { "<:ecere>elements/arrowBottom.png" };
+         right.bitmap = { "<:ecere>elements/arrowDown.png" };
          right.anchor = { bottom = 2, left = 2, right = 2 };
       }
       else
@@ -239,34 +214,66 @@ private:
 
    gap = 5;
    direction = vertical;
+   endButtons = true;
 
    void OnChildAddedOrRemoved(Window child, bool removed)
    {
-      if(!bits.holdChildMonitoring)
-         UpdateControls();
+      if(!child.nonClient)
+      {
+         if(removed)
+         {
+            if((child.destroyed && !destroyed) || child.parent != this)
+            {
+               Iterator<Window> it { controls };
+               if(it.Find(child))
+               {
+                  it.Remove();
+                  delete child;
+               }
+            }
+         }
+         else
+         {
+            if((child.created || (!created && child.autoCreate)) && !child.destroyed && child.parent == this)
+            {
+               if(!controls.Find(child))
+               {
+                  controls.Add(child);
+                  incref child;
+               }
+            }
+         }
+         if(!bits.holdChildMonitoring)
+            DoResize(size.w, size.h);
+      }
    }
    void OnChildVisibilityToggled(Window child, bool visible)
    {
       DoResize(size.w, size.h); // todo: improve with DoPartialResize(size.w, size.h, client);
+      // size = size;   // TRIGGER SCROLLING UPDATE (Currently required since we aren't using Window scrollbars)
    }
    void OnChildResized(Window child, int x, int y, int w, int h)
    {
       DoResize(size.w, size.h); // todo: improve with DoPartialResize(size.w, size.h, client);
+      // size = size;   // TRIGGER SCROLLING UPDATE (Currently required since we aren't using Window scrollbars)
    }
 
-   void UpdateControls()
+   /*void UpdateControls()
    {
       Window child;
       Array<Window> newControls { };
-      for(c : controls)
+      for(c : controls; !c.nonClient)
       {
-         for(child = firstChild; child; child = child.next)
+         child = null;
+         if(!c.destroyed)
          {
-            if(child.nonClient) continue;
-            if(c == child)
+            for(child = firstChild; child; child = child.next)
             {
-               newControls.Add(child);
-               break;
+               if(c == child)
+               {
+                  newControls.Add(child);
+                  break;
+               }
             }
          }
          if(!child)
@@ -277,7 +284,7 @@ private:
       }
       for(child = firstChild; child; child = child.next)
       {
-         if(child.nonClient) continue;
+         if(child.nonClient || child.destroyed || !child.created) continue;
          if(!newControls.Find(child))
          {
             newControls.Add(child);
@@ -287,16 +294,24 @@ private:
       delete controls;
       controls = newControls;
       newControls = null;
-   }
+   }*/
 
    void OnResize(int width, int height)
    {
-      if(!inAutoSize)
+      if(!bits.holdChildMonitoring && !inAutoSize)
          DoResize(width, height);
    }
 
    void DoResize(int width, int height)
    {
+      if(left.visible)
+      {
+         // Take into consideration the space we would gain back by getting rid of the scrolling buttons
+         if(direction == horizontal)
+            width += 2 * (left.size.w + 4);
+         else
+            height += 2 * (left.size.h + 4);
+      }
       // TOIMPROVE: this needs to maintain an order and allow for dynamically adding
       //            children. inserting in the order should also be possible.
       // TOIMPROVE: in Window.ec... it should be possible to change the order of children
@@ -305,23 +320,21 @@ private:
 
       if(created)
       {
-         int y, c;
+         int y = margin, c;
          bool r = bits.reverse;
          int inc = bits.reverse ? -1 : 1;
-         Window child;
          Window flip = null;
-         y = margin;
 
-         for(c = bits.reverse ? controls.count-1 : 0; c<controls.count && c>-1; c += inc)
+         for(c = r ? controls.count-1 : 0; c<controls.count && c>-1; c += inc)
          {
             Anchor anchor;
-            child = controls[c];
+            Window child = controls[c];
             if(flip && child == flip) break;
             if(child.nonClient || !child.visible) continue;
             anchor = child.anchor;
             if(direction == vertical)
             {
-               if(bits.reverse)
+               if(r)
                {
                   if(!anchor.bottom.type || anchor.bottom.distance != y)
                      child.anchor.bottom = y;
@@ -335,7 +348,7 @@ private:
             }
             else
             {
-               if(bits.reverse)
+               if(r)
                {
                   if(!anchor.right.type || anchor.right.distance != y)
                      child.anchor.right = y;
@@ -347,7 +360,14 @@ private:
                }
                y += child.size.w + gap;
             }
-            Flip(flipper, child, controls, margin, &bits, &inc, &c, &y, &flip);
+            // If this child is the flipper, we flip
+            if(flipper && !flip && child == flipper)
+            {
+               flip = child;
+               if(r) { r = false; inc = 1; c = -1; }
+               else  { r = true;  inc =-1; c = controls.count; }
+               y = margin;
+            }
          }
 
          if(flip)
@@ -356,13 +376,13 @@ private:
             {
                if(direction == vertical)
                {
-                  if(bits.reverse) flip.anchor.bottom = y;
-                  else             flip.anchor.top = y;
+                  if(r) flip.anchor.bottom = y;
+                  else  flip.anchor.top = y;
                }
                else
                {
-                  if(bits.reverse) flip.anchor.right = y;
-                  else             flip.anchor.left = y;
+                  if(r) flip.anchor.right = y;
+                  else  flip.anchor.left = y;
                }
             }
          }
@@ -375,15 +395,17 @@ private:
             else
                //this.clientSize.w = y - gap + margin;
                this.size.w = y - gap + margin + (this.size.w - this.clientSize.w);
-            //Update(null);
             inAutoSize = false;
          }
 
          if(bits.scrollable && y > ((direction == horizontal) ? width : height))
          {
             scrollArea = (direction == horizontal) ? { y, 0 } : { 0, y };
-            left.visible = true;
-            right.visible = true;
+            if(bits.endButtons)
+            {
+               left.visible = true;
+               right.visible = true;
+            }
          }
          else
          {
@@ -395,7 +417,7 @@ private:
          if(bits.scrollable)
          {
             // FOR WHEN SCROLLING OCCURED
-            for(child : controls)
+            for(child : controls; !child.nonClient && child.visible)
                child.anchor = child.anchor;
 
             if(direction == horizontal)
@@ -416,19 +438,36 @@ private:
 
    public void DestroyChildren()
    {
-      Window child, next;
-
-      bits.holdChildMonitoring = true;
-      for(child = firstChild; child; child = next)
+      // This safe loop with 'left' will jam if the Stacker is destroyed
+      if(!destroyed && created)
       {
-         next = child ? child.next : null;
-         if(!child.nonClient)
+         bool left = true;
+         while(left)
          {
-            child.Destroy(0);
-            child.parent = null;
+            left = false;
+            for(w : controls)
+            {
+               if(!w.destroyed && w.created)
+               {
+                  w.Destroy(0);
+                  left = true;
+                  break;
+               }
+            }
+         }
+      }
+      else
+      {
+         // If the stacker is already destroyed, just clear everything
+         Iterator<Window> it { controls };
+         while(it.pointer = null, it.Next())
+         {
+            Window w = it.data;
+            it.Remove();
+            w.Destroy(0);
+            delete w;
          }
       }
-      bits.holdChildMonitoring = false;
    }
 
    public void MakeControlVisible(Window control)
@@ -508,15 +547,3 @@ private:
       return result;
    }
 }
-
-static void Flip(Window flipper, Window child, Array<Window> controls, int margin, StackerBits * bits, int * inc, int * c, int * y, Window * flip)
-{
-   if(flipper && !*flip && child == flipper)
-   {
-      *flip = child;
-      (*bits).reverse = !(*bits).reverse;
-      *inc = (*bits).reverse ? -1 : 1;
-      *c = (*bits).reverse ? controls.count : -1;
-      *y = margin;
-   }
-}