eda, compiler, documentor: Proper prototypes for invoking virtual methods directly
[sdk] / samples / guiAndGfx / skinning / AcovelSkin.ec
1 public import "ecere"
2
3 #define DARK_SKIN
4
5 #ifdef DARK_SKIN
6 define skinBackground = Color { r = 40, g = 40, b = 40 };
7 define skinForeground = white;
8 define skinTextForeground = lightGray;
9 define evenRowBackground = Color { 80, 70, 60 };
10 define selectionColor = steelBlue;
11
12 ColorKey skinGradient[3] =
13 {
14    { Color { 40,40,40 }, 0.0f },
15    { darkGray, 0.5f },
16    { Color { 40,40,40 }, 1.0f },
17 };
18
19 #else
20
21 define skinForeground = black;
22 define skinTextForeground = black;
23 define skinBackground = white;
24 define evenRowBackground = lavender;
25 define selectionColor = cornflowerBlue;
26
27 ColorKey skinGradient[2] =
28 {
29    { lightGray, 0.0f }
30    { white, 1.0f },
31 };
32
33 #endif
34
35 #define BORDER       2
36 #define TOP          2
37 #define BOTTOM       2
38 #define CORNER       (BORDER * 2)
39 #define CAPTION      12
40 #define DEAD_BORDER  2
41 #define MIN_WIDTH    60
42 #define MIN_HEIGHT   3
43 #define BUTTON_OFFSET   0
44 #define NAME_OFFSET   0
45 #define NAME_OFFSETX  4
46
47 #define SB_WIDTH  16
48 #define SB_HEIGHT 16
49
50 #define MENU_HEIGHT     25
51 #define STATUS_HEIGHT   18
52
53 #define TEXT_COLOR         skinForeground
54 #define TEXT_INACTIVE      darkGray
55
56 class AcovelSkin_Window : Window
57 {
58    void ShowDecorations(Font captionFont, Surface surface, char * name, bool active, bool moving)
59    {
60       bool isNormal = (state == normal || state == maximized);
61       int top = 0, border = 0, bottom = 0;
62       if(state == minimized)
63          top = border = bottom = DEAD_BORDER;
64       else if(((BorderBits)borderStyle).sizable)
65       {
66          top = isNormal ? TOP : 0;
67          border = isNormal ? BORDER : 0;
68          bottom = BOTTOM;
69       }
70       else if(((BorderBits)borderStyle).fixed)
71       {
72          top = 2; //DEAD_BORDER;
73          border = DEAD_BORDER;
74          bottom = DEAD_BORDER;
75       }
76       else if(((BorderBits)borderStyle).contour)
77       {
78          top = 1;
79          border = 1;
80          bottom = 1;
81       }
82
83       if(((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel)
84       {
85          int deepTop = 0, deepBottom = 0, deepBorder = 0;
86          if(((BorderBits)borderStyle).contour)
87          {
88             deepBorder = border;
89             deepTop = (((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar)) ? (top + CAPTION) : top;
90             deepBottom = (((BorderBits)borderStyle).sizable && isNormal) ? bottom : border;
91          }
92
93          surface.Bevel(((BorderBits)borderStyle).bevel ? false : true, deepBorder, deepTop, 
94             size.w - deepBorder - deepBorder, size.h - deepBottom - deepTop);
95       }
96
97       if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
98       {
99          /*
100          if(state != maximized || !((BorderBits)borderStyle).sizable)
101          {
102             // Frame for ES_CAPTION windows
103             surface.Bevel(false, 0, 0, size.w, size.h);
104             surface.SetForeground(activeBorder);
105             surface.Rectangle(2, 2, size.w-3, size.h-3);
106
107             // Resizeable frame is 1 pixel thicker 
108             if(((BorderBits)borderStyle).sizable && isNormal)
109                surface.Rectangle(3, 3, size.w - 4, size.h - 4);
110          }
111
112          // Caption
113          if(active)
114             surface.Gradient(gradient, sizeof(gradient) / sizeof(ColorKey), GRADIENT_SMOOTHNESS, GRADIENT_DIRECTION,
115                border, top, size.w - border - 1, top + CAPTION - 2);
116          else
117             surface.Gradient(gradientInactive, sizeof(gradientInactive) / sizeof(ColorKey), 
118             GRADIENT_SMOOTHNESS, GRADIENT_DIRECTION,
119                border, top, size.w - border - 1, top + CAPTION - 2);
120
121          surface.SetForeground(activeBorder);
122          if(state != minimized)
123             surface.HLine(border, size.w-border-1, top + CAPTION-1);
124          */
125
126          surface.SetForeground(skinBackground);
127          surface.Rectangle(0,0, size.w-1, size.h-1);
128          surface.SetForeground(active ? TEXT_COLOR : TEXT_INACTIVE /*skinForeground*/);
129          surface.Rectangle(1,1, size.w-2, size.h-2);
130          surface.SetBackground(skinBackground);
131          surface.Area(2, 2, size.w-3, CAPTION + 2);
132
133          // surface.TextFont(captionFont);
134          // surface.WriteText(4,2, name, strlen(name));
135
136          surface.SetForeground((active ? TEXT_COLOR : TEXT_INACTIVE));
137          surface.TextOpacity(false);
138          surface.TextFont(captionFont);
139          if(name)
140          {
141             int buttonsSize = border +
142                ((hasMaximize || hasMinimize) ? 52 : 18);
143             surface.WriteTextDots(left, border + NAME_OFFSETX, top + NAME_OFFSET, 
144                size.w - (buttonsSize + border + 4), name, strlen(name));
145          }
146       }
147       if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
148       {
149          surface.SetForeground(skinForeground /*black*/);
150          surface.Rectangle(0, 0, size.w - 1, size.h - 1);
151       }
152
153       if(state != minimized && hasHorzScroll && hasVertScroll)
154       {
155          if(sbh && sbh.visible && sbv && sbv.visible)
156          {
157             surface.SetBackground(activeBorder);
158             surface.Area(
159                clientStart.x + clientSize.w,
160                clientStart.y + clientSize.h,
161                clientStart.x + clientSize.w + SB_WIDTH - 1,
162                clientStart.y + clientSize.h + SB_HEIGHT - 1);
163          }
164       }
165    }
166
167    void GetDecorationsSize(MinMaxValue * w, MinMaxValue * h)
168    {
169       *w = *h = 0;
170       if((((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel) && state != minimized)
171       {
172          *w += 4;
173          *h += 4;
174       }
175       if(((BorderBits)borderStyle).sizable && (state == normal || state == maximized))
176       {
177          *w += 2 * BORDER;
178          *h += TOP + BOTTOM;
179       }
180       if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
181       {
182          *h += CAPTION;
183          if(!((BorderBits)borderStyle).sizable || state == minimized)
184          {
185             *h += 2*DEAD_BORDER;
186             *w += 2*DEAD_BORDER;
187          }
188       }
189       if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
190       {
191          *w += 2;
192          *h += 2;
193       }
194       if(hasMenuBar && state != minimized)
195       {
196          *h += MENU_HEIGHT;
197       }
198       if(statusBar && state != minimized)
199       {
200          *h += STATUS_HEIGHT;
201       }
202    }
203
204    bool IsMouseMoving(int x, int y, int w, int h)
205    {
206       BorderBits style = (BorderBits)borderStyle; // TOFIX: borderStyle.fixed doesn't work
207       if(style.fixed)
208       {
209          bool resizeX, resizeY, resizeEndX, resizeEndY;
210          if(!IsMouseResizing(x, y, w, h, &resizeX, &resizeY, &resizeEndX, &resizeEndY))
211             return true;
212       }
213       return false;
214    }
215
216    bool IsMouseResizing(int x, int y, int w, int h, bool *resizeX, bool *resizeY, bool *resizeEndX, bool *resizeEndY)
217    {
218       bool result = false;
219
220       *resizeX = *resizeY = *resizeEndX = *resizeEndY = false;
221
222       if(((BorderBits)borderStyle).sizable && (state == normal))
223       {
224          // TopLeft Corner
225          if(Box { 0, 0,CORNER-1, TOP-1 }.IsPointInside({x, y}))
226             result = *resizeX = *resizeY = true;
227          // TopRight Corner
228          if(Box { w-CORNER-1, 0, w-1, TOP-1 }.IsPointInside({x, y}))
229             result = *resizeEndX = *resizeY = true;
230          // BottomLeft Corner
231          if(Box { 0, h-BOTTOM-1, CORNER-1, h-1 }.IsPointInside({x, y}))
232             result = *resizeX = *resizeEndY = true;
233          // BottomRight Corner
234          if(Box { w-CORNER-1, h-BOTTOM-1, w-1, h-1 }.IsPointInside({x, y}))
235             result = *resizeEndX = *resizeEndY = true;
236          // Left Border
237          if(Box { 0,TOP, BORDER, h-BOTTOM-1 }.IsPointInside({x, y}))
238             result = *resizeX = true;
239          // Right Border
240          if(Box { w-BORDER-1, TOP, w-1, h-BOTTOM-1 }.IsPointInside({x, y}))
241             result = *resizeEndX = true;
242          // Top Border
243          if(Box { CORNER, 0, w-CORNER-1, TOP-1 }.IsPointInside({x, y}))
244             result = *resizeY = true;
245          // Bottom Border
246          if(Box { CORNER, h-BOTTOM-1, w-CORNER-1, h-1 }.IsPointInside({x, y}))
247             result = *resizeEndY = true;
248       }
249       return result;
250    }
251
252    void SetWindowMinimum(MinMaxValue * mw, MinMaxValue * mh)
253    {
254       bool isNormal = (state == normal || state == maximized);
255       if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
256       {
257          *mw = MIN_WIDTH;
258          *mh = MIN_HEIGHT;
259       }
260       else
261          *mw = *mh = 0;
262       /*
263       if(((BorderBits)borderStyle).sizable && isNormal)
264          *mw += 2*CORNER;
265       // GetDecorationsSize(window, mw, mh);
266       */
267
268       if(hasVertScroll)
269          *mw += SB_WIDTH;
270       if(hasHorzScroll)
271          *mh += SB_HEIGHT;
272       if(hasVertScroll && hasHorzScroll)
273       {
274          *mw += 2 * SB_WIDTH + SB_WIDTH;
275          *mh += 2 * SB_HEIGHT + SB_HEIGHT;
276          /*
277          if(((BorderBits)borderStyle).sizable && isNormal)
278             *mw -= 2*CORNER;
279          */
280       }
281    }
282
283    void SetWindowArea(int * x, int * y, MinMaxValue * w, MinMaxValue * h, MinMaxValue * cw, MinMaxValue * ch)
284    {
285       bool isNormal = (state == normal || state == maximized);
286       MinMaxValue aw = 0, ah = 0;
287
288       *x = *y = 0;
289
290       GetDecorationsSize(&aw, &ah);
291
292       // Compute client area start
293       if(((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel)
294       {
295          *x += 2;
296          *y += 2;
297       }
298
299       if(((BorderBits)borderStyle).sizable && isNormal)
300       {
301          *x += BORDER;
302          *y += TOP;
303       }
304
305       if(hasMenuBar)
306       {
307          *y += MENU_HEIGHT;
308       }
309
310       if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
311       {
312          *y += CAPTION;
313          if(!((BorderBits)borderStyle).sizable || state == minimized)
314          {
315             *y += DEAD_BORDER;
316             *x += DEAD_BORDER;
317          }
318       }
319
320       if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
321       {
322          *x += 1;
323          *y += 1;
324       }
325
326       // Reduce client area
327       *cw = *w - aw;
328       *ch = *h - ah;
329
330       *cw = Max(*cw, 0);
331       *ch = Max(*ch, 0);
332    }
333
334    void UpdateNonClient()
335    {
336       bool isNormal = (state == normal || state == maximized);
337       int top = 0, border = 0;
338       int insideBorder;
339       if(state == minimized)
340          top = border = DEAD_BORDER;
341       else if(((BorderBits)borderStyle).sizable)
342       {
343          if(state == maximized && parent.menuBar)
344          {
345             top = 2;
346             border = 2;
347          }
348          else
349          {
350             top = isNormal ? TOP : 0;
351             border = isNormal ? BORDER : 0;
352          }
353       }
354       else if(((BorderBits)borderStyle).fixed)
355       {
356          top = 2;
357          border = 2;
358       }
359       else if(((BorderBits)borderStyle).contour)
360       {
361          top = 1;
362          border = 1;
363       }
364       insideBorder = border;
365       if(((BorderBits)borderStyle).deep)
366          insideBorder += 2;
367
368       if(menuBar)
369       {
370          if(state == minimized)
371             menuBar.visible = false;
372          else
373             menuBar.visible = true;
374          menuBar.Move(clientStart.x, clientStart.y - MENU_HEIGHT, size.w - insideBorder * 2, MENU_HEIGHT);
375       }
376       if(statusBar)
377       {
378          if(state == minimized)
379             statusBar.visible = false;
380          else
381          {
382             statusBar.visible = true;
383             statusBar.anchor = { left = clientStart.x, bottom = border };
384             statusBar.size.w = size.w - insideBorder * 2;
385          }
386       }
387
388       if(minimizeButton)
389       {
390          minimizeButton.anchor = { right = ((maximizeButton && !maximizeButton.disabled) ? 49 : 24) + border, top = top + BUTTON_OFFSET };
391          minimizeButton.size = { 20, 10 };
392          minimizeButton.foreground = skinForeground;
393          minimizeButton.background = skinBackground;
394          minimizeButton.bevel = false;
395          minimizeButton.OnRedraw = Minimize_OnRedraw;
396          minimizeButton.visible = true;
397       }
398       if(maximizeButton && !maximizeButton.disabled)
399       {
400          maximizeButton.anchor = { right = 24 + border, top = top + BUTTON_OFFSET };
401          maximizeButton.size = { 20, 10 };
402          maximizeButton.bevel = false;
403          maximizeButton.foreground = skinForeground;
404          maximizeButton.background = skinBackground;
405          maximizeButton.OnRedraw = Maximize_OnRedraw;
406          maximizeButton.visible = true;
407       }
408       if(closeButton)
409       {
410          closeButton.anchor = { right = -1 + border, top = top + BUTTON_OFFSET };
411          closeButton.size = { 20, 10 };
412          closeButton.bevel = false;
413          closeButton.foreground = skinForeground;
414          closeButton.background = skinBackground;
415          closeButton.OnRedraw = Close_OnRedraw;
416          closeButton.visible = true;
417       }
418    }
419
420    void OnApplyGraphics()
421    {
422       background = skinBackground;
423       foreground = skinForeground;
424    }
425 }
426
427 static void Button::SmallButton_OnRedraw(Surface surface)
428 {
429    surface.SetForeground(parent.active ? TEXT_COLOR : TEXT_INACTIVE);
430    surface.VLine(0, clientSize.h-2, 0);
431    surface.VLine(0, clientSize.h-2, clientSize.w-1);
432    surface.HLine(1,clientSize.w-2, clientSize.h-1);
433 }
434
435 static void Button::Minimize_OnRedraw(Surface surface)
436 {
437    SmallButton_OnRedraw(this, surface);
438    surface.DrawLine( 5, 3, 14, 3);
439    surface.DrawLine( 5, 4, 14, 4);
440 }
441
442 static void Button::Maximize_OnRedraw(Surface surface)
443 {
444    SmallButton_OnRedraw(this, surface);
445    surface.Rectangle( 5, 2, 14, 7);
446    surface.DrawLine( 5, 3, 14, 3);
447 }
448
449 static void Button::Close_OnRedraw(Surface surface)
450 {
451    SmallButton_OnRedraw(this, surface);
452    surface.DrawLine( 7, 2, 12, 7);
453    surface.DrawLine( 8, 2, 13, 7);
454    surface.DrawLine( 12, 2, 7, 7);
455    surface.DrawLine( 13, 2, 8, 7);
456 }
457
458 class AcovelSkin_StatusBar : StatusBar
459 {
460    void OnApplyGraphics()
461    {
462       background = skinBackground;
463       foreground = skinForeground;
464    }
465 }
466
467 class AcovelSkin_FileDialog : FileDialog
468 {
469    void OnApplyGraphics()
470    {
471       Window child;
472       background = skinBackground;
473       foreground = skinForeground;
474       for(child = firstChild; child; child = child.next)
475       {
476          if(!child.opacity)
477             child.foreground = skinForeground;
478       }
479    }
480 }
481
482 #define PUREVTBL(c)     ((int (**)())*(void **)((byte *)class(c).data + sizeof(void *)))
483
484 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnApplyGraphics;
485 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRedraw;
486
487 static void Dummy()
488 {
489    Window a;
490    a.OnApplyGraphics();
491    a.OnRedraw(null);
492 }
493
494 class AcovelSkin_Button : Button
495 {
496    void OnApplyGraphics()
497    {
498       //background = skinBackground;
499       background = Color { (int)70 * 6/10, (int)130* 6/10, (int)180* 6/10 };
500       foreground = skinForeground;
501    }
502
503    void OnRedraw(Surface surface)
504    {
505       int isDefault = (int)this.isDefault;
506       ((void (*)(Window, Surface))(void *)PUREVTBL(Button)[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRedraw])(this, surface);
507       if(bevel || (bevelOver && (buttonState == down || buttonState == over || checked)))
508       {
509          Color c = steelBlue;
510          if(buttonState == down || checked)
511          {
512             surface.SetForeground(selectionColor);
513             surface.HLine(isDefault + 0, clientSize.w-2-isDefault, 0);
514             surface.VLine(isDefault + 1, clientSize.h-2-isDefault, 0);
515             surface.SetForeground(Color { Min((int)c.r * 16/10, 255), Min((int)c.g * 16/10, 255), Min((int)c.b * 16/10,255) });
516             surface.HLine(isDefault + 0, clientSize.w-1-isDefault, clientSize.h-1-isDefault);
517             surface.VLine(isDefault + 0, clientSize.h-2-isDefault, clientSize.w-1-isDefault);
518          }
519          else
520          {
521             surface.SetForeground(Color { Min((int)c.r * 16/10, 255), Min((int)c.g * 16/10, 255), Min((int)c.b * 16/10,255) });
522             surface.HLine(0 + isDefault, clientSize.w-2 - isDefault,  isDefault);
523             surface.VLine(1 + isDefault, clientSize.h-2 - isDefault,  isDefault);
524             
525             
526             surface.SetForeground(selectionColor);
527             surface.HLine(1 + isDefault, clientSize.w-2 - isDefault, clientSize.h-2 - isDefault);
528             surface.VLine(1 + isDefault, clientSize.h-3 - isDefault, clientSize.w-2 - isDefault);
529             
530             if(bevel)
531             {
532                //surface.SetForeground(skinForeground);
533                surface.SetForeground(Color { c.r * 4/10, c.g * 4/10, c.b * 4/10 });
534                surface.HLine( isDefault, clientSize.w-1 - isDefault, clientSize.h-1 - isDefault);
535                surface.VLine( isDefault, clientSize.h-2 - isDefault, clientSize.w-1 - isDefault);
536             }
537          }
538       }
539       if(isDefault)
540       {
541          surface.SetForeground(skinForeground);
542          surface.Rectangle(0,0,clientSize.w-1,clientSize.h-1);
543       }
544       if(!(bevelOver) && !isRemote)
545       {
546          if(active)
547          {
548             int x1,y1,x2,y2;
549             int offset = (buttonState == down && this.offset) ? 1 : 0;
550             surface.SetForeground(skinForeground);
551             surface.LineStipple(0x5555);
552
553             #define CAPTION_DISTANCE   18
554             if((isRadio || isCheckbox) && text)
555             {
556                x1 = /*clientSize.h + */CAPTION_DISTANCE;
557                y1 = 0;
558                x2 = clientSize.w-4;
559                y2 = clientSize.h-4;
560             }
561             else
562             {
563                x1 = 3+offset;
564                y1 = 3+offset;
565                x2 = clientSize.w - 5+offset;
566                y2 = clientSize.h - 5+offset;
567
568                if(isRadio || isCheckbox)
569                {
570                   x1-=3;
571                   y1-=3;
572                   x2+=1;
573                   y2+=1;
574                }
575             }
576             if((x2 - x1) & 1) x2++;
577             if((y2 - y1) & 1) y2++;
578
579             surface.Rectangle(x1, y1, x2, y2);
580             surface.LineStipple(0);
581          }
582       }
583    }
584 }
585
586 class AcovelSkin_ScrollBar : ScrollBar
587 {
588    void OnApplyGraphics()
589    {
590       ((void (*)(Window))(void *)PUREVTBL(ScrollBar)[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnApplyGraphics])(this);
591
592       background = { skinBackground.r * 9 / 6, skinBackground.g * 9 / 6, skinBackground.b * 9 / 6 };
593
594       upBtn.background = Color {  70 * 6/10, 130* 6/10, 180* 6/10 }; //skinBackground;
595       upBtn.bitmap = { "<:ecere>elements/arrowUp.png", monochrome = true };
596       downBtn.background = Color {  70 * 6/10, 130* 6/10, 180* 6/10 }; //skinBackground;
597       downBtn.bitmap = { "<:ecere>elements/arrowDown.png", monochrome = true };
598       thumb.background = Color {  70 * 6/10, 130* 6/10, 180* 6/10 }; //skinBackground;
599    }   
600 }
601
602 class AcovelSkin_DropBox : DropBox
603 {
604    void OnApplyGraphics()
605    {
606       ((void (*)(Window))(void *)PUREVTBL(DropBox)[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnApplyGraphics])(this);
607       button.bitmap = { "<:ecere>elements/arrowDown.png", monochrome = true };
608       background = skinBackground;
609       foreground = skinForeground;
610       this.selectionColor = ::selectionColor;
611    }   
612 }
613
614 class AcovelSkin_ListBox : ListBox
615 {
616    void OnApplyGraphics()
617    {
618       ((void (*)(Window))(void *)PUREVTBL(ListBox)[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnApplyGraphics])(this);
619       background = skinBackground;
620       foreground = skinForeground;
621       this.selectionColor = ::selectionColor;
622    }   
623 }
624
625 public class AcovelSkin : Skin
626 {
627    class_property(name) = "Acovel";
628    class_property(selectionColor) = (Color)selectionColor;
629    class_property(disabledBackColor) = (Color)Color{ 0,0,0 };
630    class_property(disabledFrontColor) = (Color)Color{ 128,128,128 };
631
632    FontResource ::SystemFont()
633    {
634       return { faceName = "Tahoma", size = 8.25f };
635    }
636
637    FontResource ::CaptionFont()
638    {
639       return { faceName = "Verdana", size = 6.5f };
640    }
641
642    char * ::CursorsBitmaps(uint id, int *hotSpotX, int *hotSpotY, byte ** paletteShades)
643    {
644       return null;
645    }
646
647    BitmapResource ::GetBitmap(int id)
648    {
649       return null;
650    }
651
652    int ::VerticalSBW()
653    {
654       return 16;
655    }
656
657    int ::HorizontalSBH()
658    {
659       return 16;
660    }
661 }