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)
163 bool NeedScrollers(int width, int height)
166 if(bits.scrollable && bits.endButtons)
169 bool r = bits.reverse;
170 int inc = bits.reverse ? -1 : 1;
173 for(c = r ? controls.count-1 : 0; c<controls.count && c>-1; c += inc)
175 Window child = controls[c];
176 if(flip && child == flip) break;
177 if(child.nonClient || !child.visible) continue;
178 if(direction == vertical)
179 y += child.size.h + gap;
181 y += child.size.w + gap;
183 // If this child is the flipper, we flip
184 if(flipper && !flip && child == flipper)
187 if(r) { r = false; inc = 1; c = -1; }
188 else { r = true; inc =-1; c = controls.count; }
192 result = (y > ((direction == horizontal) ? width : height));
197 void GetDecorationsSize(MinMaxValue * w, MinMaxValue * h)
199 Window::GetDecorationsSize(w, h);
202 if(direction == vertical) *h += left.size.h + right.size.h + 8; else *w += left.size.w + right.size.w + 8;
206 void SetWindowArea(int * x, int * y, MinMaxValue * w, MinMaxValue * h, MinMaxValue * cw, MinMaxValue * ch)
208 needScrollers = NeedScrollers(*w, *h);
209 Window::SetWindowArea(x, y, w, h, cw, ch);
212 if(direction == vertical) *y += left.size.h + 4; else *x += left.size.w + 4;
223 bits.holdChildMonitoring = true;
229 bits.holdChildMonitoring = false;
230 OnResize(clientSize.w, clientSize.h);
232 if(direction == vertical)
234 left.bitmap = { "<:ecere>elements/arrowUp.png" };
236 left.anchor = { top = 2, left = 2, right = 2 };
238 right.bitmap = { "<:ecere>elements/arrowDown.png" };
240 right.anchor = { bottom = 2, left = 2, right = 2 };
244 left.bitmap = { "<:ecere>elements/arrowLeft.png" };
246 left.anchor = { left = 2, top = 2, bottom = 2 };
248 right.bitmap = { "<:ecere>elements/arrowRight.png" };
250 right.anchor = { right = 2, top = 2, bottom = 2 };
256 direction = vertical;
259 void OnChildAddedOrRemoved(Window child, bool removed)
265 if((child.destroyed && !destroyed) || child.parent != this)
267 Iterator<Window> it { controls };
277 if((child.created || (!created && child.autoCreate)) && !child.destroyed && child.parent == this)
279 if(!controls.Find(child))
286 if(!bits.holdChildMonitoring)
287 DoResize(clientSize.w, clientSize.h);
290 void OnChildVisibilityToggled(Window child, bool visible)
292 DoResize(clientSize.w, clientSize.h); // todo: improve with DoPartialResize(size.w, size.h, client);
293 // size = size; // TRIGGER SCROLLING UPDATE (Currently required since we aren't using Window scrollbars)
295 void OnChildResized(Window child, int x, int y, int w, int h)
297 DoResize(clientSize.w, clientSize.h); // todo: improve with DoPartialResize(size.w, size.h, client);
298 // size = size; // TRIGGER SCROLLING UPDATE (Currently required since we aren't using Window scrollbars)
301 /*void UpdateControls()
304 Array<Window> newControls { };
305 for(c : controls; !c.nonClient)
310 for(child = firstChild; child; child = child.next)
314 newControls.Add(child);
325 for(child = firstChild; child; child = child.next)
327 if(child.nonClient || child.destroyed || !child.created) continue;
328 if(!newControls.Find(child))
330 newControls.Add(child);
335 controls = newControls;
339 void OnResize(int width, int height)
341 if(!bits.holdChildMonitoring && !inAutoSize)
342 DoResize(width, height);
345 void DoResize(int width, int height)
347 // TOIMPROVE: this needs to maintain an order and allow for dynamically adding
348 // children. inserting in the order should also be possible.
349 // TOIMPROVE: in Window.ec... it should be possible to change the order of children
350 // at runtime. it should also be possible to choose where dynamically
351 // created children are inserted.
356 bool r = bits.reverse;
357 int inc = bits.reverse ? -1 : 1;
360 for(c = r ? controls.count-1 : 0; c<controls.count && c>-1; c += inc)
363 Window child = controls[c];
364 if(flip && child == flip) break;
365 if(child.nonClient || !child.visible) continue;
366 anchor = child.anchor;
367 if(direction == vertical)
371 if(!anchor.bottom.type || anchor.bottom.distance != y)
372 child.anchor.bottom = y;
376 if(!anchor.top.type || anchor.top.distance != y)
377 child.anchor.top = y;
379 y += child.size.h + gap;
385 if(!anchor.right.type || anchor.right.distance != y)
386 child.anchor.right = y;
390 if(!anchor.left.type || anchor.left.distance != y)
391 child.anchor.left = y;
393 y += child.size.w + gap;
395 // If this child is the flipper, we flip
396 if(flipper && !flip && child == flipper)
399 if(r) { r = false; inc = 1; c = -1; }
400 else { r = true; inc =-1; c = controls.count; }
405 if(needScrollers) //y > ((direction == horizontal) ? size.w : size.h))
407 scrollArea = (direction == horizontal) ? { y, 0 } : { 0, y };
408 if(bits.endButtons && !left.visible)
411 right.visible = true;
418 left.visible = false;
419 right.visible = false;
421 scrollArea = { 0, 0 };
428 if(direction == vertical)
430 if(r) flip.anchor.bottom = y;
431 else flip.anchor.top = y;
435 if(r) flip.anchor.right = y;
436 else flip.anchor.left = y;
440 else if(bits.autoSize)
443 if(direction == vertical)
444 //this.clientSize.h = y - gap + margin;
445 this.size.h = y - gap + margin + (this.size.h - this.clientSize.h);
447 //this.clientSize.w = y - gap + margin;
448 this.size.w = y - gap + margin + (this.size.w - this.clientSize.w);
454 // FOR WHEN SCROLLING OCCURED
455 for(child : controls; !child.nonClient && child.visible)
456 child.anchor = child.anchor;
458 if(direction == horizontal)
460 left.disabled = (scroll.x == 0);
461 right.disabled = (scroll.x + clientSize.w >= scrollArea.w);
465 left.disabled = (scroll.y == 0);
466 right.disabled = (scroll.y + clientSize.h >= scrollArea.h);
468 if(left.disabled && left.buttonState == down) left.OnLeftButtonUp(-1,0,0);
469 if(right.disabled && right.buttonState == down) right.OnLeftButtonUp(-1,0,0);
474 public void DestroyChildren()
476 // This safe loop with 'left' will jam if the Stacker is destroyed
477 if(!destroyed && created)
485 if(!w.destroyed && w.created)
496 // If the stacker is already destroyed, just clear everything
497 Iterator<Window> it { controls };
498 while(it.pointer = null, it.Next())
508 public void MakeControlVisible(Window control)
510 if(direction == horizontal)
513 if(control.position.x - stackerScrolling < scroll.x)
515 x = control.position.x;
516 if(clientSize.w > control.size.w)
517 x -=(clientSize.w - control.size.w - stackerScrolling) / 2;
518 scroll.x = Max(x, 0);
521 else if(control.position.x + control.size.w + stackerScrolling > scroll.x + clientSize.w)
523 x = control.position.x;
524 if(clientSize.w > control.size.w)
525 x -=(clientSize.w - control.size.w + stackerScrolling) / 2;
526 scroll.x = Max(x, 0);
533 if(control.position.y - stackerScrolling < scroll.y)
535 y = control.position.y;
536 if(clientSize.h > control.size.h)
537 y -=(clientSize.h - control.size.h - stackerScrolling) / 2;
538 scroll.y = Max(y, 0);
541 else if(control.position.y + control.size.h + stackerScrolling > scroll.y + clientSize.h)
543 y = control.position.y;
544 if(clientSize.h > control.size.h)
545 y -=(clientSize.h - control.size.h + stackerScrolling) / 2;
546 scroll.y = Max(y, 0);
552 public Window GetNextStackedItem(Window current, bool previous, Class filter)
554 Window result = null;
557 bool direction = !(reverse^previous);
559 for(c = (!direction) ? controls.count-1 : 0; c<controls.count && c>-1; c += (!direction) ? -1 : 1)
562 if(child.nonClient || !child.created || !child.visible) continue;
563 if(filter && !eClass_IsDerived(child._class, filter)) continue;
569 for(c = direction ? controls.count-1 : 0; c<controls.count && c>-1; c += direction ? -1 : 1)
572 if(child.nonClient || !child.created || !child.visible) continue;
573 if(!eClass_IsDerived(child._class, filter)) continue;