Revert "ecere:Stacker: fixed order of non visible controls not remembered. setting...
[sdk] / ecere / src / gui / controls / Stacker.ec
1 #ifdef BUILDING_ECERE_COM
2 namespace gui::controls;
3 import "Window"
4 import "Array"
5 #else
6 #ifdef ECERE_STATIC
7 public import static "ecere"
8 #else
9 public import "ecere"
10 #endif
11 #endif
12
13 public class RepButton : Button
14 {
15 public:
16    bool pressing;
17    isRemote = true;
18    inactive = true;
19    
20    property Seconds delay { set { timer2.delay = value; } }
21    property Seconds delay0 { set { timer.delay = value; } }
22    
23    bool OnKeyHit(Key key, unichar ch)
24    {
25       return true;
26    }
27
28    bool OnKeyDown(Key key, unichar ch)
29    {
30       if(key == hotKey)
31       {
32          NotifyPushed(master, this, 0,0, key.modifiers);
33          return false;
34       }
35       return true;
36    }
37
38    bool OnKeyUp(Key key, unichar ch)
39    {
40       if(key == hotKey)
41       {
42          NotifyReleased(master, this, 0,0, key.modifiers);
43          return false;
44       }
45       return true;
46    }
47
48    bool NotifyPushed(RepButton button, int x, int y, Modifiers mods)
49    {
50       button.pressing = true;
51       button.NotifyClicked(this, button, x, y, mods);
52       button.timer.Start();
53       return true;
54    }
55
56    bool NotifyMouseLeave(RepButton button, Modifiers mods)
57    {
58       button.timer.Stop();
59       button.timer2.Stop();
60       return true;
61    }
62
63    bool NotifyReleased(RepButton button, int x, int y, Modifiers mods)
64    {
65       button.pressing = false;
66       button.NotifyMouseLeave(this, button, mods);
67       return false;
68    }
69
70    bool NotifyMouseOver(RepButton button, int x, int y, Modifiers mods)
71    {
72       if(button.pressing)
73          button.timer2.Start();
74       return true;
75    }
76
77    Timer timer
78    {
79       this, delay = 0.1;
80
81       bool DelayExpired()
82       {
83          timer.Stop();
84          timer2.Start();
85          timer2.DelayExpired(this);
86          return true;
87       }
88    };
89    Timer timer2
90    {
91       this, delay = 0.1;
92       bool DelayExpired()
93       {
94          NotifyClicked(master, this, 0, 0, 0);
95          return true;
96       }
97    };
98 }
99
100 static define stackerScrolling = 16;
101
102 public enum FlipStackerSpringMode { none, previous, next };
103
104 public class FlipStacker : Window
105 {
106    size = { };
107 public:
108    FlipStackerSpringMode spring;
109 }
110
111 public class Stacker : Window
112 {
113 public:
114
115    property ScrollDirection direction { set { direction = value; } get { return direction; } };
116    property int gap { set { gap = value; } get { return gap; } };
117    property bool reverse { set { reverse = value; } get { return reverse; } };
118
119    property bool scrollable
120    {
121       set
122       {
123          if(value != scrollable)
124          {
125             scrollable = value;
126             // how to recall these?
127             //GetDecorationsSize(...);
128             //SetWindowArea(...);
129             OnResize(clientSize.w, clientSize.h);
130          }
131       }
132       get { return scrollable; }
133    }
134
135    property Array<Window> controls { get { return controls; } };
136
137 private:
138    ScrollDirection direction;
139    int gap;
140    bool scrollable;
141    Array<Window> controls { };
142    bool reverse;
143    RepButton left
144    {
145       this, visible = false, bevelOver = true, nonClient = true, keyRepeat = true, opacity = 0;
146
147       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
148       {
149          if(direction == horizontal)
150          {
151             scroll.x -= stackerScrolling;
152             if(scroll.x == 0) { left.disabled = true; left.OnLeftButtonUp(-1,0,0); }
153          }
154          else
155          {
156             scroll.y -= stackerScrolling;
157             if(scroll.y == 0) { left.disabled = true; left.OnLeftButtonUp(-1,0,0); }
158          }
159          right.disabled = false;
160          size = size;   // TRIGGER SCROLLING UPDATE (Currently required since we aren't using Window scrollbars)
161          return true;
162       }
163    };
164    RepButton right
165    {
166       this, visible = false, bevelOver = true, nonClient = true, keyRepeat = true, opacity = 0;
167
168       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
169       {
170          if(direction == horizontal)
171          {
172             scroll.x += stackerScrolling;
173             if(scroll.x + clientSize.w >= scrollArea.w) { right.disabled = true; right.OnLeftButtonUp(-1,0,0); }
174          }
175          else
176          {
177             scroll.y += stackerScrolling;
178             if(scroll.y + clientSize.h >= scrollArea.h) { right.disabled = true; right.OnLeftButtonUp(-1,0,0); }
179          }
180          left.disabled = false;
181          size = size;   // TRIGGER SCROLLING UPDATE (Currently required since we aren't using Window scrollbars)
182          return true;
183       }
184    };
185
186    void GetDecorationsSize(MinMaxValue * w, MinMaxValue * h)
187    {
188       Window::GetDecorationsSize(w, h);
189       if(scrollable)
190       {
191          if(direction == vertical) *h += left.size.w + right.size.w + 8; else *w += left.size.h + right.size.h + 8;
192       }
193    }
194
195    void SetWindowArea(int * x, int * y, MinMaxValue * w, MinMaxValue * h, MinMaxValue * cw, MinMaxValue * ch)
196    {
197       Window::SetWindowArea(x, y, w, h, cw, ch);
198       if(scrollable)
199       {
200          if(direction == vertical) *y += left.size.w + 4; else *x += left.size.h + 4;
201       }
202    }
203
204    ~Stacker()
205    {
206       controls.Free();
207    }
208
209    bool OnPostCreate()
210    {
211       OnResize(clientSize.w, clientSize.h);
212
213       if(direction == vertical)
214       {
215          left.bitmap = { "<:ecere>elements/arrowTop.png" };
216          left.anchor = { top = 2, left = 2, right = 2 };
217
218          right.bitmap = { "<:ecere>elements/arrowBottom.png" };
219          right.anchor = { bottom = 2, left = 2, right = 2 };
220       }
221       else
222       {
223          left.bitmap = { "<:ecere>elements/arrowLeft.png" };
224          left.anchor = { left = 2, top = 2, bottom = 2 };
225
226          right.bitmap = { "<:ecere>elements/arrowRight.png" };
227          right.anchor = { right = 2, top = 2, bottom = 2 };
228       }
229       return true;
230    }
231
232    gap = 5;
233    direction = vertical;
234
235    void OnResize(int width, int height)
236    {
237       if(created)
238       {
239          int y = 0;
240          Array<Window> oldControls = controls;
241          Array<Window> orderedControls;
242          Array<Window> controlsDirA { };
243          Array<Window> controlsDirB { };
244          Window child;
245
246          controls = { };     
247
248          // TOFIX: this needs to maintain an order and allow for dynamically adding
249          //        children. inserting in the order should also be possible.
250          for(c : oldControls)
251          {
252             for(child = firstChild; child; child = child.next)
253             {
254                if(child.nonClient || !child.visible /*|| !child.created*/) continue;
255                if(c == child)
256                {
257                   controls.Add(child);
258                   incref child;
259                   break;
260                }
261             }
262          }
263          for(child = firstChild; child; child = child.next)
264          {
265             if(child.nonClient || !child.visible /*|| !child.created*/) continue;
266             if(!controls.Find(child))
267             {
268                controls.Add(child);
269                incref child;
270             }
271          }
272
273          oldControls.Free();
274          delete oldControls;
275
276          if(scrollable)
277          {
278             if(reverse)
279             {
280                int c;
281                orderedControls = { };
282                for(c = controls.count-1; c >= 0; c--)
283                {
284                   child = controls[c];
285                   orderedControls.Add(child);
286                   incref child;
287                }
288             }
289             else
290                orderedControls = controls;
291
292             for(child : orderedControls)
293             {
294                if(direction == vertical)
295                {
296                   if(reverse)
297                      child.anchor.bottom = y;
298                   else
299                      child.anchor.top = y;
300                   y += child.size.h + gap;
301                }
302                else
303                {
304                   if(reverse)
305                      child.anchor.right = y;
306                   else
307                      child.anchor.left = y;
308                   y += child.size.w + gap;
309                }
310             }
311             if(reverse)
312             {
313                orderedControls.Free();
314                delete orderedControls;
315             }
316          }
317          else
318          {
319             int c;
320             int limit = 0;
321             Window previousChild = 0;
322             FlipStackerSpringMode spring = none;
323             if(reverse)
324             {
325                for(c = controls.count-1; c >= 0; c--)
326                {
327                   child = controls[c];
328                   if(child._class == class(FlipStacker) || eClass_IsDerived(child._class, class(FlipStacker)))
329                   {
330                      FlipStacker flip = (FlipStacker)child;
331                      spring = flip.spring;
332                      break;
333                   }
334                   previousChild = child;
335                   controlsDirA.Add(child);
336                   incref child;
337                }
338                if(c >= 0)
339                {
340                   limit = c;
341                   for(c = 0; c < limit; c++)
342                   {
343                      child = controls[c];
344                      if(child._class != class(FlipStacker) && !eClass_IsDerived(child._class, class(FlipStacker)))
345                      {
346                         controlsDirB.Add(child);
347                         incref child;
348                      }
349                   }
350                }
351             }
352             else
353             {
354                for(c = 0; c < controls.count; c++)
355                {
356                   child = controls[c];
357                   if(child._class == class(FlipStacker) || eClass_IsDerived(child._class, class(FlipStacker)))
358                   {
359                      FlipStacker flip = (FlipStacker)child;
360                      spring = flip.spring;
361                      break;
362                   }
363                   previousChild = child;
364                   controlsDirA.Add(child);
365                   incref child;
366                }
367                if(c < controls.count)
368                {
369                   limit = c;
370                   for(c = controls.count-1; c > limit; c--)
371                   {
372                      child = controls[c];
373                      if(child._class != class(FlipStacker) && !eClass_IsDerived(child._class, class(FlipStacker)))
374                      {
375                         controlsDirB.Add(child);
376                         incref child;
377                      }
378                   }
379                }
380             }
381
382             y = 0;
383             for(child : controlsDirA)
384             {
385                if(direction == vertical)
386                {
387                   if(reverse)
388                      child.anchor.bottom = y;
389                   else
390                      child.anchor.top = y;
391                   y += child.size.h + gap;
392                }
393                else
394                {
395                   if(reverse)
396                      child.anchor.right = y;
397                   else
398                      child.anchor.left = y;
399                   y += child.size.w + gap;
400                }
401             }
402             y = 0;
403             for(child : controlsDirB)
404             {
405                if(direction == vertical)
406                {
407                   if(reverse)
408                      child.anchor.top = y;
409                   else
410                      child.anchor.bottom = y;
411                   y += child.size.h + gap;
412                }
413                else
414                {
415                   if(reverse)
416                      child.anchor.left = y;
417                   else
418                      child.anchor.right = y;
419                   y += child.size.w + gap;
420                }
421             }
422             if(spring == previous && previousChild)
423             {
424                if(reverse)
425                   previousChild.anchor.left = y;
426                else
427                   previousChild.anchor.right = y;
428             }
429
430             controlsDirA.Free();
431             delete controlsDirA;
432             controlsDirB.Free();
433             delete controlsDirB;
434          }
435
436          if(scrollable && y > ((direction == horizontal) ? width : height))
437          {
438             scrollArea = (direction == horizontal) ? { y, 0 } : { 0, y };
439             left.visible = true;
440             right.visible = true;
441          }
442          else
443          {
444             left.visible = false;
445             right.visible = false;
446             scrollArea = { 0, 0 };
447          }
448
449          // FOR WHEN SCROLLING OCCURED
450          for(child : controls)
451             child.anchor = child.anchor;
452
453          if(scrollable)
454          {
455             if(direction == horizontal)
456             {
457                left.disabled = (scroll.x == 0);
458                right.disabled = (scroll.x + clientSize.w >= scrollArea.w);
459             }
460             else
461             {
462                left.disabled = (scroll.y == 0);
463                right.disabled = (scroll.y + clientSize.h >= scrollArea.h);
464             }
465             if(left.disabled && left.buttonState == down) left.OnLeftButtonUp(-1,0,0);
466             if(right.disabled && right.buttonState == down) right.OnLeftButtonUp(-1,0,0);
467          }
468       }
469    }
470
471    public void DestroyChildren()
472    {
473       Window child, next;
474
475       for(child = firstChild; child; child = next)
476       {
477          next = child ? child.next : null;
478          if(!child.nonClient)
479          {
480             child.Destroy(0);
481             child.parent = null;
482             delete child;
483          }
484       }
485    }
486
487    public void MakeControlVisible(Window control)
488    {
489       if(direction == horizontal)
490       {
491          int x;
492          if(control.position.x - stackerScrolling < scroll.x)
493          {
494             x = control.position.x;
495             if(clientSize.w > control.size.w)
496                x -=(clientSize.w - control.size.w - stackerScrolling) / 2;
497             scroll.x = Max(x, 0);
498             size = size;
499          }
500          else if(control.position.x + control.size.w + stackerScrolling > scroll.x + clientSize.w)
501          {
502             x = control.position.x;
503             if(clientSize.w > control.size.w)
504                x -=(clientSize.w - control.size.w + stackerScrolling) / 2;
505             scroll.x = Max(x, 0);
506             size = size;
507          }
508       }
509       else
510       {
511          int y;
512          if(control.position.y - stackerScrolling < scroll.y)
513          {
514             y = control.position.y;
515             if(clientSize.h > control.size.h)
516                y -=(clientSize.h - control.size.h - stackerScrolling) / 2;
517             scroll.y = Max(y, 0);
518             size = size;
519          }
520          else if(control.position.y + control.size.h + stackerScrolling > scroll.y + clientSize.h)
521          {
522             y = control.position.y;
523             if(clientSize.h > control.size.h)
524                y -=(clientSize.h - control.size.h + stackerScrolling) / 2;
525             scroll.y = Max(y, 0);
526             size = size;
527          }
528       }
529    }
530 }