#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;
-public enum FlipStackerSpringMode { none, previous, next };
-
-public class FlipStacker : Window
+class StackerBits
{
- size = { };
-public:
- FlipStackerSpringMode spring;
+ bool reverse:1, scrollable:1, flipSpring:1, autoSize:1, endButtons:1;
+
+ // internals
+ bool holdChildMonitoring:1;
}
public class Stacker : Window
property ScrollDirection direction { set { direction = value; } get { return direction; } };
property int gap { set { gap = value; } get { return gap; } };
- property bool reverse { set { reverse = value; } get { return reverse; } };
+ property bool reverse { set { bits.reverse = value; } get { return bits.reverse; } };
property bool scrollable
{
set
{
- if(value != scrollable)
+ if(value != bits.scrollable)
{
- scrollable = value;
+ bits.scrollable = value;
// how to recall these?
//GetDecorationsSize(...);
//SetWindowArea(...);
OnResize(clientSize.w, clientSize.h);
}
}
- get { return scrollable; }
+ get { return bits.scrollable; }
}
property Array<Window> controls { get { return controls; } };
+ 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;
+ 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;
ScrollDirection direction;
int gap;
- bool scrollable;
+ int margin;
Array<Window> controls { };
- bool reverse;
+ 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
{
- this, visible = false, bevelOver = true, nonClient = 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)
{
};
RepButton right
{
- this, visible = false, bevelOver = true, nonClient = 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)
{
}
};
+ bool inAutoSize;
+
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;
}
}
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
gap = 5;
direction = vertical;
+ endButtons = true;
- void OnResize(int width, int height)
+ void OnChildAddedOrRemoved(Window child, bool removed)
{
- if(created)
+ if(!child.nonClient)
{
- int y = 0;
- Array<Window> oldControls = controls;
- Array<Window> orderedControls;
- Array<Window> controlsDirA { };
- Array<Window> controlsDirB { };
- Window child;
-
- controls = { };
-
- // TOFIX: this needs to maintain an order and allow for dynamically adding
- // children. inserting in the order should also be possible.
- for(c : oldControls)
+ if(removed)
{
- for(child = firstChild; child; child = child.next)
+ if((child.destroyed && !destroyed) || child.parent != this)
{
- if(child.nonClient/* || !child.visible*/ /*|| !child.created*/) continue;
- if(c == child)
+ Iterator<Window> it { controls };
+ if(it.Find(child))
{
- controls.Add(child);
- incref child;
- break;
+ it.Remove();
+ delete child;
}
}
}
- for(child = firstChild; child; child = child.next)
- {
- if(child.nonClient/* || !child.visible*/ /*|| !child.created*/) continue;
- if(!controls.Find(child))
- {
- controls.Add(child);
- incref child;
- }
- }
-
- oldControls.Free();
- delete oldControls;
-
- if(scrollable)
+ else
{
- if(reverse)
+ if((child.created || (!created && child.autoCreate)) && !child.destroyed && child.parent == this)
{
- int c;
- orderedControls = { };
- for(c = controls.count-1; c >= 0; c--)
+ if(!controls.Find(child))
{
- child = controls[c];
- orderedControls.Add(child);
+ controls.Add(child);
incref child;
}
}
- else
- orderedControls = controls;
+ }
+ 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)
+ }
- for(child : orderedControls)
+ /*void UpdateControls()
+ {
+ Window child;
+ Array<Window> newControls { };
+ for(c : controls; !c.nonClient)
+ {
+ child = null;
+ if(!c.destroyed)
+ {
+ for(child = firstChild; child; child = child.next)
{
- if(!child.visible) continue;
- if(direction == vertical)
- {
- if(reverse)
- child.anchor.bottom = y;
- else
- child.anchor.top = y;
- y += child.size.h + gap;
- }
- else
+ if(c == child)
{
- if(reverse)
- child.anchor.right = y;
- else
- child.anchor.left = y;
- y += child.size.w + gap;
+ newControls.Add(child);
+ break;
}
}
- if(reverse)
- {
- orderedControls.Free();
- delete orderedControls;
- }
}
+ if(!child)
+ {
+ child = c;
+ delete child;
+ }
+ }
+ for(child = firstChild; child; child = child.next)
+ {
+ if(child.nonClient || child.destroyed || !child.created) continue;
+ if(!newControls.Find(child))
+ {
+ newControls.Add(child);
+ incref child;
+ }
+ }
+ delete controls;
+ controls = newControls;
+ newControls = null;
+ }*/
+
+ void OnResize(int width, int height)
+ {
+ 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
+ // at runtime. it should also be possible to choose where dynamically
+ // created children are inserted.
+
+ if(created)
+ {
+ int y = margin, c;
+ bool r = bits.reverse;
+ int inc = bits.reverse ? -1 : 1;
+ Window flip = null;
+
+ for(c = r ? controls.count-1 : 0; c<controls.count && c>-1; c += inc)
{
- int c;
- int limit = 0;
- Window previousChild = 0;
- FlipStackerSpringMode spring = none;
- if(reverse)
+ Anchor anchor;
+ Window child = controls[c];
+ if(flip && child == flip) break;
+ if(child.nonClient || !child.visible) continue;
+ anchor = child.anchor;
+ if(direction == vertical)
{
- for(c = controls.count-1; c >= 0; c--)
+ if(r)
{
- child = controls[c];
- if(child._class == class(FlipStacker) || eClass_IsDerived(child._class, class(FlipStacker)))
- {
- FlipStacker flip = (FlipStacker)child;
- spring = flip.spring;
- break;
- }
- previousChild = child;
- controlsDirA.Add(child);
- incref child;
+ if(!anchor.bottom.type || anchor.bottom.distance != y)
+ child.anchor.bottom = y;
}
- if(c >= 0)
+ else
{
- limit = c;
- for(c = 0; c < limit; c++)
- {
- child = controls[c];
- if(child._class != class(FlipStacker) && !eClass_IsDerived(child._class, class(FlipStacker)))
- {
- controlsDirB.Add(child);
- incref child;
- }
- }
+ if(!anchor.top.type || anchor.top.distance != y)
+ child.anchor.top = y;
}
+ y += child.size.h + gap;
}
else
{
- for(c = 0; c < controls.count; c++)
- {
- child = controls[c];
- if(child._class == class(FlipStacker) || eClass_IsDerived(child._class, class(FlipStacker)))
- {
- FlipStacker flip = (FlipStacker)child;
- spring = flip.spring;
- break;
- }
- previousChild = child;
- controlsDirA.Add(child);
- incref child;
- }
- if(c < controls.count)
- {
- limit = c;
- for(c = controls.count-1; c > limit; c--)
- {
- child = controls[c];
- if(child._class != class(FlipStacker) && !eClass_IsDerived(child._class, class(FlipStacker)))
- {
- controlsDirB.Add(child);
- incref child;
- }
- }
- }
- }
-
- y = 0;
- for(child : controlsDirA)
- {
- if(!child.visible) continue;
- if(direction == vertical)
+ if(r)
{
- if(reverse)
- child.anchor.bottom = y;
- else
- child.anchor.top = y;
- y += child.size.h + gap;
+ if(!anchor.right.type || anchor.right.distance != y)
+ child.anchor.right = y;
}
else
{
- if(reverse)
- child.anchor.right = y;
- else
+ if(!anchor.left.type || anchor.left.distance != y)
child.anchor.left = y;
- y += child.size.w + gap;
}
+ y += child.size.w + gap;
+ }
+ // 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;
}
- y = 0;
- for(child : controlsDirB)
+ }
+
+ if(flip)
+ {
+ if(bits.flipSpring)
{
- if(!child.visible) continue;
if(direction == vertical)
{
- if(reverse)
- child.anchor.top = y;
- else
- child.anchor.bottom = y;
- y += child.size.h + gap;
+ if(r) flip.anchor.bottom = y;
+ else flip.anchor.top = y;
}
else
{
- if(reverse)
- child.anchor.left = y;
- else
- child.anchor.right = y;
- y += child.size.w + gap;
+ if(r) flip.anchor.right = y;
+ else flip.anchor.left = y;
}
}
- if(spring == previous && previousChild)
- {
- if(reverse)
- previousChild.anchor.left = y;
- else
- previousChild.anchor.right = y;
- }
-
- controlsDirA.Free();
- delete controlsDirA;
- controlsDirB.Free();
- delete controlsDirB;
+ }
+ else if(bits.autoSize)
+ {
+ inAutoSize = true;
+ if(direction == vertical)
+ //this.clientSize.h = y - gap + margin;
+ this.size.h = y - gap + margin + (this.size.h - this.clientSize.h);
+ else
+ //this.clientSize.w = y - gap + margin;
+ this.size.w = y - gap + margin + (this.size.w - this.clientSize.w);
+ inAutoSize = false;
}
- if(scrollable && y > ((direction == horizontal) ? width : height))
+ 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
{
scrollArea = { 0, 0 };
}
- // FOR WHEN SCROLLING OCCURED
- for(child : controls)
- child.anchor = child.anchor;
-
- if(scrollable)
+ if(bits.scrollable)
{
+ // FOR WHEN SCROLLING OCCURED
+ for(child : controls; !child.nonClient && child.visible)
+ child.anchor = child.anchor;
+
if(direction == horizontal)
{
left.disabled = (scroll.x == 0);
public void DestroyChildren()
{
- Window child, next;
-
- 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;
- delete child;
+ 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;
}
}
}
}
}
}
+
+ public Window GetNextStackedItem(Window current, bool previous, Class filter)
+ {
+ Window result = null;
+ Window next = null;
+ Window child;
+ bool direction = !(reverse^previous);
+ int c;
+ for(c = (!direction) ? controls.count-1 : 0; c<controls.count && c>-1; c += (!direction) ? -1 : 1)
+ {
+ child = controls[c];
+ if(child.nonClient || !child.created || !child.visible) continue;
+ if(filter && !eClass_IsDerived(child._class, filter)) continue;
+ next = child;
+ break;
+ }
+ if(current)
+ {
+ for(c = direction ? controls.count-1 : 0; c<controls.count && c>-1; c += direction ? -1 : 1)
+ {
+ child = controls[c];
+ if(child.nonClient || !child.created || !child.visible) continue;
+ if(!eClass_IsDerived(child._class, filter)) continue;
+ if(child == current)
+ break;
+ next = child;
+ }
+ result = next;
+ }
+ else
+ result = next;
+ return result;
+ }
}