1 #ifdef BUILDING_ECERE_COM
2 namespace gui::controls;
7 public import static "ecere"
13 // class RepButton WAS ALREADY DEFINED IN date.ec! The version here broke CalendarControl behavior.
15 static define stackerScrolling = 16;
19 bool reverse:1, scrollable:1, flipSpring:1, autoSize:1, endButtons:1;
22 bool holdChildMonitoring:1;
25 public class Stacker : Window
29 property ScrollDirection direction { set { direction = value; } get { return direction; } };
30 property int gap { set { gap = value; } get { return gap; } };
31 property bool reverse { set { bits.reverse = value; } get { return bits.reverse; } };
33 property bool scrollable
37 if(value != bits.scrollable)
39 bits.scrollable = value;
40 // how to recall these?
41 //GetDecorationsSize(...);
43 OnResize(clientSize.w, clientSize.h);
46 get { return bits.scrollable; }
49 property Array<Window> controls { get { return controls; } };
51 property Window flipper { set { flipper = value; } get { return flipper; } };
52 property bool flipSpring { set { bits.flipSpring = value; } get { return bits.flipSpring; } };
53 property bool autoSize
57 bits.autoSize = value;
60 // Auto Size implementation conflicts with this base Window property, resulting in overhead and potential glitches:
61 // dontAutoScrollArea = false;
62 modifyVirtualArea = false;
65 get { return bits.autoSize; }
67 property int margin { set { margin = value; } get { return margin; } };
68 property bool endButtons
72 if(bits.endButtons && scrollable && !value)
75 right.visible = false;
77 bits.endButtons = value;
78 if(value && scrollable)
84 get { return bits.endButtons; }
89 ScrollDirection direction;
92 Array<Window> controls { };
95 void OnVScroll(ScrollBarAction action, int position, Key key)
99 bool ld = false, rd = false;
100 left.disabled = false;
101 right.disabled = false;
102 if(direction == horizontal)
104 if(position == 0) ld = true;
105 if(position + clientSize.w >= scrollArea.w) rd = true;
109 if(position == 0) ld = true;
110 if(position + clientSize.h >= scrollArea.h) rd = true;
112 if(left.disabled != ld) { left.disabled = ld; left.OnLeftButtonUp(-1,0,0); }
113 if(right.disabled != rd) { right.disabled = rd; right.OnLeftButtonUp(-1,0,0); }
119 nonClient = true, parent = this, visible = false, bevelOver = true, keyRepeat = true, opacity = 0; delay0 = 0.1;
121 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
123 if(direction == horizontal)
125 scroll.x -= stackerScrolling;
126 if(scroll.x == 0) { left.disabled = true; left.OnLeftButtonUp(-1,0,0); }
130 scroll.y -= stackerScrolling;
131 if(scroll.y == 0) { left.disabled = true; left.OnLeftButtonUp(-1,0,0); }
133 right.disabled = false;
134 size = size; // TRIGGER SCROLLING UPDATE (Currently required since we aren't using Window scrollbars)
140 nonClient = true, parent = this, visible = false, bevelOver = true, keyRepeat = true, opacity = 0; delay0 = 0.1;
142 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
144 if(direction == horizontal)
146 scroll.x += stackerScrolling;
147 if(scroll.x + clientSize.w >= scrollArea.w) { right.disabled = true; right.OnLeftButtonUp(-1,0,0); }
151 scroll.y += stackerScrolling;
152 if(scroll.y + clientSize.h >= scrollArea.h) { right.disabled = true; right.OnLeftButtonUp(-1,0,0); }
154 left.disabled = false;
155 size = size; // TRIGGER SCROLLING UPDATE (Currently required since we aren't using Window scrollbars)
162 void GetDecorationsSize(MinMaxValue * w, MinMaxValue * h)
164 Window::GetDecorationsSize(w, h);
165 if(bits.scrollable && bits.endButtons)
167 if(direction == vertical) *h += left.size.h + right.size.h + 8; else *w += left.size.w + right.size.w + 8;
171 void SetWindowArea(int * x, int * y, MinMaxValue * w, MinMaxValue * h, MinMaxValue * cw, MinMaxValue * ch)
173 Window::SetWindowArea(x, y, w, h, cw, ch);
174 if(bits.scrollable && bits.endButtons)
176 if(direction == vertical) *y += left.size.h + 4; else *x += left.size.w + 4;
187 bits.holdChildMonitoring = true;
193 bits.holdChildMonitoring = false;
194 OnResize(clientSize.w, clientSize.h);
196 if(direction == vertical)
198 left.bitmap = { "<:ecere>elements/arrowUp.png" };
199 left.anchor = { top = 2, left = 2, right = 2 };
201 right.bitmap = { "<:ecere>elements/arrowDown.png" };
202 right.anchor = { bottom = 2, left = 2, right = 2 };
206 left.bitmap = { "<:ecere>elements/arrowLeft.png" };
207 left.anchor = { left = 2, top = 2, bottom = 2 };
209 right.bitmap = { "<:ecere>elements/arrowRight.png" };
210 right.anchor = { right = 2, top = 2, bottom = 2 };
216 direction = vertical;
219 void OnChildAddedOrRemoved(Window child, bool removed)
225 if((child.destroyed && !destroyed) || child.parent != this)
227 Iterator<Window> it { controls };
237 if((child.created || (!created && child.autoCreate)) && !child.destroyed && child.parent == this)
239 if(!controls.Find(child))
246 if(!bits.holdChildMonitoring)
247 DoResize(size.w, size.h);
250 void OnChildVisibilityToggled(Window child, bool visible)
252 DoResize(size.w, size.h); // todo: improve with DoPartialResize(size.w, size.h, client);
253 // size = size; // TRIGGER SCROLLING UPDATE (Currently required since we aren't using Window scrollbars)
255 void OnChildResized(Window child, int x, int y, int w, int h)
257 DoResize(size.w, size.h); // todo: improve with DoPartialResize(size.w, size.h, client);
258 // size = size; // TRIGGER SCROLLING UPDATE (Currently required since we aren't using Window scrollbars)
261 /*void UpdateControls()
264 Array<Window> newControls { };
265 for(c : controls; !c.nonClient)
270 for(child = firstChild; child; child = child.next)
274 newControls.Add(child);
285 for(child = firstChild; child; child = child.next)
287 if(child.nonClient || child.destroyed || !child.created) continue;
288 if(!newControls.Find(child))
290 newControls.Add(child);
295 controls = newControls;
299 void OnResize(int width, int height)
301 if(!bits.holdChildMonitoring && !inAutoSize)
302 DoResize(width, height);
305 void DoResize(int width, int height)
307 // TOIMPROVE: this needs to maintain an order and allow for dynamically adding
308 // children. inserting in the order should also be possible.
309 // TOIMPROVE: in Window.ec... it should be possible to change the order of children
310 // at runtime. it should also be possible to choose where dynamically
311 // created children are inserted.
316 bool r = bits.reverse;
317 int inc = bits.reverse ? -1 : 1;
320 for(c = r ? controls.count-1 : 0; c<controls.count && c>-1; c += inc)
323 Window child = controls[c];
324 if(flip && child == flip) break;
325 if(child.nonClient || !child.visible) continue;
326 anchor = child.anchor;
327 if(direction == vertical)
331 if(!anchor.bottom.type || anchor.bottom.distance != y)
332 child.anchor.bottom = y;
336 if(!anchor.top.type || anchor.top.distance != y)
337 child.anchor.top = y;
339 y += child.size.h + gap;
345 if(!anchor.right.type || anchor.right.distance != y)
346 child.anchor.right = y;
350 if(!anchor.left.type || anchor.left.distance != y)
351 child.anchor.left = y;
353 y += child.size.w + gap;
355 // If this child is the flipper, we flip
356 if(flipper && !flip && child == flipper)
359 if(r) { r = false; inc = 1; c = -1; }
360 else { r = true; inc =-1; c = controls.count; }
369 if(direction == vertical)
371 if(r) flip.anchor.bottom = y;
372 else flip.anchor.top = y;
376 if(r) flip.anchor.right = y;
377 else flip.anchor.left = y;
381 else if(bits.autoSize)
384 if(direction == vertical)
385 //this.clientSize.h = y - gap + margin;
386 this.size.h = y - gap + margin + (this.size.h - this.clientSize.h);
388 //this.clientSize.w = y - gap + margin;
389 this.size.w = y - gap + margin + (this.size.w - this.clientSize.w);
393 if(bits.scrollable && y > ((direction == horizontal) ? width : height))
395 scrollArea = (direction == horizontal) ? { y, 0 } : { 0, y };
399 right.visible = true;
404 left.visible = false;
405 right.visible = false;
406 scrollArea = { 0, 0 };
411 // FOR WHEN SCROLLING OCCURED
412 for(child : controls; !child.nonClient && child.visible)
413 child.anchor = child.anchor;
415 if(direction == horizontal)
417 left.disabled = (scroll.x == 0);
418 right.disabled = (scroll.x + clientSize.w >= scrollArea.w);
422 left.disabled = (scroll.y == 0);
423 right.disabled = (scroll.y + clientSize.h >= scrollArea.h);
425 if(left.disabled && left.buttonState == down) left.OnLeftButtonUp(-1,0,0);
426 if(right.disabled && right.buttonState == down) right.OnLeftButtonUp(-1,0,0);
431 public void DestroyChildren()
433 // This safe loop with 'left' will jam if the Stacker is destroyed
434 if(!destroyed && created)
442 if(!w.destroyed && w.created)
453 // If the stacker is already destroyed, just clear everything
454 Iterator<Window> it { controls };
455 while(it.pointer = null, it.Next())
465 public void MakeControlVisible(Window control)
467 if(direction == horizontal)
470 if(control.position.x - stackerScrolling < scroll.x)
472 x = control.position.x;
473 if(clientSize.w > control.size.w)
474 x -=(clientSize.w - control.size.w - stackerScrolling) / 2;
475 scroll.x = Max(x, 0);
478 else if(control.position.x + control.size.w + stackerScrolling > scroll.x + clientSize.w)
480 x = control.position.x;
481 if(clientSize.w > control.size.w)
482 x -=(clientSize.w - control.size.w + stackerScrolling) / 2;
483 scroll.x = Max(x, 0);
490 if(control.position.y - stackerScrolling < scroll.y)
492 y = control.position.y;
493 if(clientSize.h > control.size.h)
494 y -=(clientSize.h - control.size.h - stackerScrolling) / 2;
495 scroll.y = Max(y, 0);
498 else if(control.position.y + control.size.h + stackerScrolling > scroll.y + clientSize.h)
500 y = control.position.y;
501 if(clientSize.h > control.size.h)
502 y -=(clientSize.h - control.size.h + stackerScrolling) / 2;
503 scroll.y = Max(y, 0);
509 public Window GetNextStackedItem(Window current, bool previous, Class filter)
511 Window result = null;
514 bool direction = !(reverse^previous);
516 for(c = (!direction) ? controls.count-1 : 0; c<controls.count && c>-1; c += (!direction) ? -1 : 1)
519 if(child.nonClient || !child.created || !child.visible) continue;
520 if(filter && !eClass_IsDerived(child._class, filter)) continue;
526 for(c = direction ? controls.count-1 : 0; c<controls.count && c>-1; c += direction ? -1 : 1)
529 if(child.nonClient || !child.created || !child.visible) continue;
530 if(!eClass_IsDerived(child._class, filter)) continue;