Revert "ecere:Stacker: fixed spring feature not properly implemented for all directio...
[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(!child.visible) continue;
295                if(direction == vertical)
296                {
297                   if(reverse)
298                      child.anchor.bottom = y;
299                   else
300                      child.anchor.top = y;
301                   y += child.size.h + gap;
302                }
303                else
304                {
305                   if(reverse)
306                      child.anchor.right = y;
307                   else
308                      child.anchor.left = y;
309                   y += child.size.w + gap;
310                }
311             }
312             if(reverse)
313             {
314                orderedControls.Free();
315                delete orderedControls;
316             }
317          }
318          else
319          {
320             int c;
321             int limit = 0;
322             Window previousChild = 0;
323             FlipStackerSpringMode spring = none;
324             if(reverse)
325             {
326                for(c = controls.count-1; c >= 0; c--)
327                {
328                   child = controls[c];
329                   if(child._class == class(FlipStacker) || eClass_IsDerived(child._class, class(FlipStacker)))
330                   {
331                      FlipStacker flip = (FlipStacker)child;
332                      spring = flip.spring;
333                      break;
334                   }
335                   previousChild = child;
336                   controlsDirA.Add(child);
337                   incref child;
338                }
339                if(c >= 0)
340                {
341                   limit = c;
342                   for(c = 0; c < limit; c++)
343                   {
344                      child = controls[c];
345                      if(child._class != class(FlipStacker) && !eClass_IsDerived(child._class, class(FlipStacker)))
346                      {
347                         controlsDirB.Add(child);
348                         incref child;
349                      }
350                   }
351                }
352             }
353             else
354             {
355                for(c = 0; c < controls.count; c++)
356                {
357                   child = controls[c];
358                   if(child._class == class(FlipStacker) || eClass_IsDerived(child._class, class(FlipStacker)))
359                   {
360                      FlipStacker flip = (FlipStacker)child;
361                      spring = flip.spring;
362                      break;
363                   }
364                   previousChild = child;
365                   controlsDirA.Add(child);
366                   incref child;
367                }
368                if(c < controls.count)
369                {
370                   limit = c;
371                   for(c = controls.count-1; c > limit; c--)
372                   {
373                      child = controls[c];
374                      if(child._class != class(FlipStacker) && !eClass_IsDerived(child._class, class(FlipStacker)))
375                      {
376                         controlsDirB.Add(child);
377                         incref child;
378                      }
379                   }
380                }
381             }
382
383             y = 0;
384             for(child : controlsDirA)
385             {
386                if(!child.visible) continue;
387                if(direction == vertical)
388                {
389                   if(reverse)
390                      child.anchor.bottom = y;
391                   else
392                      child.anchor.top = y;
393                   y += child.size.h + gap;
394                }
395                else
396                {
397                   if(reverse)
398                      child.anchor.right = y;
399                   else
400                      child.anchor.left = y;
401                   y += child.size.w + gap;
402                }
403             }
404             y = 0;
405             for(child : controlsDirB)
406             {
407                if(!child.visible) continue;
408                if(direction == vertical)
409                {
410                   if(reverse)
411                      child.anchor.top = y;
412                   else
413                      child.anchor.bottom = y;
414                   y += child.size.h + gap;
415                }
416                else
417                {
418                   if(reverse)
419                      child.anchor.left = y;
420                   else
421                      child.anchor.right = y;
422                   y += child.size.w + gap;
423                }
424             }
425             if(spring == previous && previousChild)
426             {
427                if(reverse)
428                   previousChild.anchor.left = y;
429                else
430                   previousChild.anchor.right = y;
431             }
432
433             controlsDirA.Free();
434             delete controlsDirA;
435             controlsDirB.Free();
436             delete controlsDirB;
437          }
438
439          if(scrollable && y > ((direction == horizontal) ? width : height))
440          {
441             scrollArea = (direction == horizontal) ? { y, 0 } : { 0, y };
442             left.visible = true;
443             right.visible = true;
444          }
445          else
446          {
447             left.visible = false;
448             right.visible = false;
449             scrollArea = { 0, 0 };
450          }
451
452          // FOR WHEN SCROLLING OCCURED
453          for(child : controls)
454             child.anchor = child.anchor;
455
456          if(scrollable)
457          {
458             if(direction == horizontal)
459             {
460                left.disabled = (scroll.x == 0);
461                right.disabled = (scroll.x + clientSize.w >= scrollArea.w);
462             }
463             else
464             {
465                left.disabled = (scroll.y == 0);
466                right.disabled = (scroll.y + clientSize.h >= scrollArea.h);
467             }
468             if(left.disabled && left.buttonState == down) left.OnLeftButtonUp(-1,0,0);
469             if(right.disabled && right.buttonState == down) right.OnLeftButtonUp(-1,0,0);
470          }
471       }
472    }
473
474    public void DestroyChildren()
475    {
476       Window child, next;
477
478       for(child = firstChild; child; child = next)
479       {
480          next = child ? child.next : null;
481          if(!child.nonClient)
482          {
483             child.Destroy(0);
484             child.parent = null;
485             delete child;
486          }
487       }
488    }
489
490    public void MakeControlVisible(Window control)
491    {
492       if(direction == horizontal)
493       {
494          int x;
495          if(control.position.x - stackerScrolling < scroll.x)
496          {
497             x = control.position.x;
498             if(clientSize.w > control.size.w)
499                x -=(clientSize.w - control.size.w - stackerScrolling) / 2;
500             scroll.x = Max(x, 0);
501             size = size;
502          }
503          else if(control.position.x + control.size.w + stackerScrolling > scroll.x + clientSize.w)
504          {
505             x = control.position.x;
506             if(clientSize.w > control.size.w)
507                x -=(clientSize.w - control.size.w + stackerScrolling) / 2;
508             scroll.x = Max(x, 0);
509             size = size;
510          }
511       }
512       else
513       {
514          int y;
515          if(control.position.y - stackerScrolling < scroll.y)
516          {
517             y = control.position.y;
518             if(clientSize.h > control.size.h)
519                y -=(clientSize.h - control.size.h - stackerScrolling) / 2;
520             scroll.y = Max(y, 0);
521             size = size;
522          }
523          else if(control.position.y + control.size.h + stackerScrolling > scroll.y + clientSize.h)
524          {
525             y = control.position.y;
526             if(clientSize.h > control.size.h)
527                y -=(clientSize.h - control.size.h + stackerScrolling) / 2;
528             scroll.y = Max(y, 0);
529             size = size;
530          }
531       }
532    }
533 }