f4a02145f54307dde5134d2963d9f71fd88c42db
[sdk] / ecere / src / gui / skins / WindowsSkin.ec
1 namespace gui::skins;
2
3 import "Window"
4
5 #define BORDER       4
6 #define TOP          4
7 #define BOTTOM       4
8 #define CORNER       (BORDER * 2)
9 #define CAPTION      20
10 #define DEAD_BORDER  3
11 #define MIN_WIDTH    60
12 #define MIN_HEIGHT   3
13 #define BUTTON_OFFSET   2
14 #define NAME_OFFSET   2
15 #define NAME_OFFSETX  4
16
17 #define SB_WIDTH  16
18 #define SB_HEIGHT 16
19
20 #define MENU_HEIGHT     25
21 #define STATUS_HEIGHT   18
22
23 #define GRADIENT_SMOOTHNESS 1.0f
24
25 #define TEXT_COLOR   black
26 /*
27
28 #define GRADIENT_DIRECTION horizontal
29
30 static ColorKey gradient[] =
31 {
32    { Color { 128, 128, 255}, 0.00f },
33    { Color { 254, 254, 254}, 0.60f },
34    { Color {   0,   0, 255}, 1.00f }
35 };
36 */
37
38 #define GRADIENT_DIRECTION vertical
39 static ColorKey gradient[] =
40 {
41    { ColorAlpha { 255, { 180, 200, 220} }, 0.00f },
42    { ColorAlpha { 255, { 255, 255, 255} }, 0.60f },
43    { ColorAlpha { 255, { 128, 128, 130} }, 1.00f }
44 };
45
46 static ColorKey gradientInactive[] =
47 {
48    { ColorAlpha { 255, { 160, 180, 200} },  0.00f },
49    { ColorAlpha { 255, { 220, 220, 220} }, 0.60f },
50    { ColorAlpha { 255, { 100, 100, 100} }, 1.00f }
51 };
52
53 /*
54 #define GRADIENT_DIRECTION horizontal
55 #define TEXT_COLOR         white
56 */
57 //#define TEXT_INACTIVE      Color { 212,208,200 }
58 #define TEXT_INACTIVE      Color { 40, 50, 60 }
59 /*
60 static ColorKey gradient[] =
61 {
62    { ColorAlpha { 255, Color {  10,   36, 106 } }, 0.00f },
63    { ColorAlpha { 255, Color { 166,  202, 240 } }, 1.00f }
64 };
65 static ColorKey gradientInactive[] =
66 {
67    { ColorAlpha { 255, Color { 128, 128, 128 } }, 0.00f },
68    { ColorAlpha { 255, Color { 192, 192, 192 } }, 1.00f }
69 };
70 */
71 char * cursorsBitmaps[] = 
72 {
73    "<:ecere>cursors/arrow.png",
74    "<:ecere>cursors/iBeam.png",
75    "<:ecere>cursors/cross.png",
76    "<:ecere>cursors/move.png",
77    "<:ecere>cursors/sizeNorthEastSouthWest.png",
78    "<:ecere>cursors/sizeNorthSouth.png",
79    "<:ecere>cursors/sizeNorthWestSouthEast.png",
80    "<:ecere>cursors/sizeWestEast.png",
81    "<:ecere>cursors/move.png"
82 };
83
84 static Point cursorsHotSpots[] =
85 {
86    { 0, 0 },
87    { 0, 0 },
88    { 8, 8 },
89    { 10, 10 },
90    { 8, 8 },
91    { 4, 10 },
92    { 7, 7 },
93    { 5, 0 }
94 };
95
96 static char * skinBitmaps[SkinBitmap] =
97 {
98    "<:ecere>elements/areaMinimize.png",
99    "<:ecere>elements/areaMaximize.png",
100    "<:ecere>elements/areaRestore.png",
101    "<:ecere>elements/areaClose.png"
102 };
103
104 class WindowsSkin : Skin
105 {
106    class_property(name) = "Windows";
107    class_property(selectionColor) = Color { 10, 36, 106 };
108    class_property(selectionText)  = (Color)white;
109    class_property(disabledFrontColor) = Color { 128,128,128 };
110    class_property(disabledBackColor) = (Color)white;
111
112    FontResource ::SystemFont()
113    {
114       return FontResource { faceName = "Tahoma", size = 8.25f };
115    }
116
117    FontResource ::CaptionFont()
118    {
119       return FontResource { faceName = "Tahoma", size = 8.25f, bold = true };
120    }
121
122    char * ::CursorsBitmaps(uint id, int * hotSpotX, int *hotSpotY, byte ** paletteShades)
123    {
124       *hotSpotX = cursorsHotSpots[id].x;
125       *hotSpotY = cursorsHotSpots[id].y;
126       *paletteShades = null;
127       return cursorsBitmaps[id];
128    }
129
130    BitmapResource ::GetBitmap(SkinBitmap id)
131    {
132       return BitmapResource { fileName = skinBitmaps[id] };
133    }
134
135    int ::VerticalSBW() { return SB_WIDTH; }
136    int ::HorizontalSBH() { return SB_HEIGHT; }
137 };
138
139
140 public class WindowsSkin_Window : Window
141 {
142    void GetDecorationsSize(MinMaxValue * w, MinMaxValue * h)
143    {
144       *w = *h = 0;
145
146       if(hasMenuBar && state != minimized)
147       {
148          *h += MENU_HEIGHT;
149       }
150       if(statusBar && state != minimized)
151       {
152          *h += STATUS_HEIGHT;
153       }
154
155       if(nativeDecorations) return;
156       if((((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel) && state != minimized)
157       {
158          *w += 4;
159          *h += 4;
160       }
161       if(((BorderBits)borderStyle).sizable && (state == normal))
162       {
163          *w += 2 * BORDER;
164          *h += TOP + BOTTOM;
165       }
166       if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
167       {
168          *h += CAPTION;
169          if(!((BorderBits)borderStyle).sizable || state == minimized)
170          {
171             *h += 2*DEAD_BORDER;
172             *w += 2*DEAD_BORDER;
173          }
174       }
175       if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
176       {
177          *w += 2;
178          *h += 2;
179       }
180    }
181
182    void SetWindowMinimum(MinMaxValue * mw, MinMaxValue * mh)
183    {
184       bool isNormal = (state == normal);
185       if(nativeDecorations) return;
186       if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
187       {
188          *mw = MIN_WIDTH;
189          *mh = MIN_HEIGHT;
190       }
191       else
192          *mw = *mh = 0;
193       if(((BorderBits)borderStyle).sizable && isNormal)
194          *mw += 2*CORNER;
195       // GetDecorationsSize(window, mw, mh);
196
197       if(hasVertScroll)
198          *mw += SB_WIDTH;
199       if(hasHorzScroll)
200          *mh += SB_HEIGHT;
201       if(hasVertScroll && hasHorzScroll)
202       {
203          *mw += 2 * SB_WIDTH + SB_WIDTH;
204          *mh += 2 * SB_HEIGHT + SB_HEIGHT;
205          if(((BorderBits)borderStyle).sizable && isNormal)
206             *mw -= 2*CORNER;
207       }
208    }
209
210    void SetWindowArea(int * x, int * y, MinMaxValue * w, MinMaxValue * h, MinMaxValue * cw, MinMaxValue * ch)
211    {
212       bool isNormal = (state == normal);
213       MinMaxValue aw = 0, ah = 0;
214
215       *x = *y = 0;
216
217       if(hasMenuBar)
218       {
219          *y += MENU_HEIGHT;
220       }
221
222       GetDecorationsSize(&aw, &ah);
223
224       if(!nativeDecorations)
225       {
226          // Compute client area start
227          if(((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel)
228          {
229             *x += 2;
230             *y += 2;
231          }
232
233          if(((BorderBits)borderStyle).sizable && isNormal)
234          {
235             *x += BORDER;
236             *y += TOP;
237          }
238
239          if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
240          {
241             *y += CAPTION;
242             if(!((BorderBits)borderStyle).sizable || state == minimized)
243             {
244                *y += DEAD_BORDER;
245                *x += DEAD_BORDER;
246             }
247          }
248
249          if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
250          {
251             *x += 1;
252             *y += 1;
253          }
254       }
255
256       // Reduce client area
257       *cw = *w - aw;
258       *ch = *h - ah;
259
260       *cw = Max(*cw, 0);
261       *ch = Max(*ch, 0);
262    }
263
264    void ShowDecorations(Font captionFont, Surface surface, char * name, bool active, bool moving)
265    {
266       bool isNormal = (state == normal);
267       int top = 0, border = 0, bottom = 0;
268
269       if(nativeDecorations) return;
270
271       if(state == minimized)
272          top = border = bottom = DEAD_BORDER;
273       else if(((BorderBits)borderStyle).sizable)
274       {
275          top = isNormal ? TOP : 0;
276          border = isNormal ? BORDER : 0;
277          bottom = BOTTOM;
278       }
279       else if(((BorderBits)borderStyle).fixed)
280       {
281          top = DEAD_BORDER;
282          border = DEAD_BORDER;
283          bottom = DEAD_BORDER;
284       }
285       else if(((BorderBits)borderStyle).contour)
286       {
287          top = 1;
288          border = 1;
289          bottom = 1;
290       }
291
292       if(((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel)
293       {
294          int deepTop = 0, deepBottom = 0, deepBorder = 0;
295          if(((BorderBits)borderStyle).contour)
296          {
297             deepBorder = border;
298             deepTop = (((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar)) ? (top + CAPTION) : top;
299             deepBottom = (((BorderBits)borderStyle).sizable && isNormal) ? bottom : border;
300          }
301
302          surface.Bevel(((BorderBits)borderStyle).bevel ? false : true, deepBorder, deepTop, 
303             size.w - deepBorder - deepBorder, size.h - deepBottom - deepTop);
304       }
305
306       if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
307       {
308          if(state != maximized || !((BorderBits)borderStyle).sizable)
309          {
310             // Frame for ES_CAPTION windows
311             surface.Bevel(false, 0, 0, size.w, size.h);
312             surface.SetForeground(activeBorder);
313             surface.Rectangle(2, 2, size.w-3, size.h-3);
314
315             // Resizeable frame is 1 pixel thicker 
316             if(((BorderBits)borderStyle).sizable && isNormal)
317                surface.Rectangle(3, 3, size.w - 4, size.h - 4);
318          }
319
320          // Caption
321          if(active)
322             surface.Gradient(gradient, sizeof(gradient) / sizeof(ColorKey), GRADIENT_SMOOTHNESS, GRADIENT_DIRECTION,
323                border, top, size.w - border - 1, top + CAPTION - 2);
324          else
325             surface.Gradient(gradientInactive, sizeof(gradientInactive) / sizeof(ColorKey), 
326             GRADIENT_SMOOTHNESS, GRADIENT_DIRECTION,
327                border, top, size.w - border - 1, top + CAPTION - 2);
328
329          surface.SetForeground(activeBorder);
330          if(state != minimized)
331             surface.HLine(border, size.w-border-1, top + CAPTION-1);
332
333          surface.SetForeground((active ? TEXT_COLOR : TEXT_INACTIVE));
334          surface.TextOpacity(false);
335          surface.TextFont(captionFont);
336          if(name)
337          {
338             int buttonsSize = border +
339                ((hasMaximize || hasMinimize) ? 52 : 18);
340             surface.WriteTextDots(left, border + NAME_OFFSETX, top + NAME_OFFSET, 
341                size.w - (buttonsSize + border + 4), name, strlen(name));
342          }
343       }
344       if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
345       {
346          surface.SetForeground(black);
347          surface.Rectangle(0, 0, size.w - 1, size.h - 1);
348       }
349
350       if(state != minimized && hasHorzScroll && hasVertScroll)
351       {
352          if(sbh && sbh.visible && sbv && sbv.visible)
353          {
354             surface.SetBackground(activeBorder);
355             surface.Area(
356                clientStart.x + clientSize.w,
357                clientStart.y + clientSize.h,
358                clientStart.x + clientSize.w + SB_WIDTH - 1,
359                clientStart.y + clientSize.h + SB_HEIGHT - 1);
360          }
361       }
362    }
363
364    bool IsMouseMoving(int x, int y, int w, int h)
365    {
366       bool isNormal = (state == normal);
367       bool result = false;
368       if(nativeDecorations) return false;
369
370       if(((BorderBits)borderStyle).fixed && (state != maximized || !parent.menuBar))
371       {
372          int corner = 0, border = 0, top = 0;
373          if(((BorderBits)borderStyle).sizable && isNormal)
374          {
375             corner = CORNER;
376             border = BORDER;
377             top    = TOP;
378          }
379          // Special case for having caption on resize bar
380          if(!CAPTION)
381             result = Box { corner, 0, w-corner-1, TOP-1 }.IsPointInside({x,y});
382          else
383             result = Box { border, top, w-border-1, top+CAPTION-1 }.IsPointInside({x, y});
384       }
385       return result;
386    }
387
388    bool IsMouseResizing(int x, int y, int w, int h, bool *resizeX, bool *resizeY, bool *resizeEndX, bool *resizeEndY)
389    {
390       bool result = false;
391
392       *resizeX = *resizeY = *resizeEndX = *resizeEndY = false;
393       if(nativeDecorations) return false;
394
395       if(((BorderBits)borderStyle).sizable && (state == normal))
396       {
397          // TopLeft Corner
398          if(Box { 0, 0,CORNER-1, TOP-1 }.IsPointInside({x, y}))
399             result = *resizeX = *resizeY = true;
400          // TopRight Corner
401          if(Box { w-CORNER-1, 0, w-1, TOP-1 }.IsPointInside({x, y}))
402             result = *resizeEndX = *resizeY = true;
403          // BottomLeft Corner
404          if(Box { 0, h-BOTTOM-1, CORNER-1, h-1 }.IsPointInside({x, y}))
405             result = *resizeX = *resizeEndY = true;
406          // BottomRight Corner
407          if(Box { w-CORNER-1, h-BOTTOM-1, w-1, h-1 }.IsPointInside({x, y}))
408             result = *resizeEndX = *resizeEndY = true;
409          // Left Border
410          if(Box { 0,TOP, BORDER, h-BOTTOM-1 }.IsPointInside({x, y}))
411             result = *resizeX = true;
412          // Right Border
413          if(Box { w-BORDER-1, TOP, w-1, h-BOTTOM-1 }.IsPointInside({x, y}))
414             result = *resizeEndX = true;
415          // Top Border
416          if(Box { CORNER, 0, w-CORNER-1, TOP-1 }.IsPointInside({x, y}))
417             result = *resizeY = true;
418          // Bottom Border
419          if(Box { CORNER, h-BOTTOM-1, w-CORNER-1, h-1 }.IsPointInside({x, y}))
420             result = *resizeEndY = true;
421       }
422       return result;
423    }
424
425    void UpdateNonClient()
426    {
427       bool isNormal = (state == normal);
428       int top = 0, border = 0;
429       int insideBorder = 0;
430
431       if(!nativeDecorations) 
432       {
433          if(state == minimized)
434             top = border = DEAD_BORDER;
435          else if(((BorderBits)borderStyle).sizable)
436          {
437             if(state == maximized && parent.menuBar)
438             {
439                top = 2;
440                border = 2;
441             }
442             else
443             {
444                top = isNormal ? TOP : 0;
445                border = isNormal ? BORDER : 0;
446             }
447          }
448          else if(((BorderBits)borderStyle).fixed)
449          {
450             top = DEAD_BORDER;
451             border = DEAD_BORDER;
452          }
453          else if(((BorderBits)borderStyle).contour)
454          {
455             top = 1;
456             border = 1;
457          }
458          insideBorder = border;
459          if(((BorderBits)borderStyle).deep)
460             insideBorder += 2;
461       }
462
463       if(menuBar)
464       {
465          if(state == minimized)
466             menuBar.visible = false;
467          else
468             menuBar.visible = true;
469          menuBar.Move(clientStart.x, clientStart.y - MENU_HEIGHT, size.w - insideBorder * 2, MENU_HEIGHT);
470       }
471       if(statusBar)
472       {
473          if(state == minimized)
474             statusBar.visible = false;
475          else
476          {
477             statusBar.visible = true;
478             statusBar.anchor = { left = clientStart.x, bottom = border };
479             statusBar.size.w = size.w - insideBorder * 2;
480          }
481       }
482       if(!nativeDecorations)
483       {
484          if(sysButtons[0])
485          {
486             sysButtons[0].anchor = { right = 35 + border, top = top + BUTTON_OFFSET };
487             sysButtons[0].size = { 15, 15 };
488             sysButtons[0].bevel = true;
489             sysButtons[0].bitmap = { skinBitmaps[(state == minimized) ? restore : minimize] };
490             sysButtons[0].visible = true;
491          }
492          if(sysButtons[1])
493          {
494             sysButtons[1].anchor = { right = 20 + border, top = top + BUTTON_OFFSET };
495             sysButtons[1].size = { 15, 15 };
496             sysButtons[1].bevel = true;
497             sysButtons[1].bitmap = { skinBitmaps[(state == maximized) ? restore : maximize] };
498             sysButtons[1].visible = true;
499          }
500          if(sysButtons[2])
501          {
502             sysButtons[2].anchor = { right = 2 + border, top = top + BUTTON_OFFSET };
503             sysButtons[2].size = { 15, 15 };
504             sysButtons[2].bevel = true;
505             sysButtons[2].bitmap = { skinBitmaps[close] };
506             sysButtons[2].visible = true;
507          }
508       }
509    }
510 }