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