eab07467c4479bad6abe45a513313e295103156c
[sdk] / ecere / src / gui / skins / WindowsSkin.ec
1 #if defined(WIN32)
2 #define WIN32_LEAN_AND_MEAN
3 #define String _String
4 #define Method _Method
5 #define strlen _strlen
6 #include <windows.h>
7 #undef Method
8 #undef String
9 #undef strlen
10 #endif
11
12 import "Window"
13
14 #if !defined(WIN32)
15 bool gui::drivers::XGetBorderWidths(Window window, Box box);
16 #endif
17
18 namespace gui::skins;
19
20 #define BORDER       4
21 #define TOP          4
22 #define BOTTOM       4
23 #define CORNER       (BORDER * 2)
24 #if defined(HIGH_DPI)
25 #define BUTTON_SIZE  45
26 #define CAPTION      60
27 #else
28 #define BUTTON_SIZE  15
29 #define CAPTION      20
30 #endif
31 #define DEAD_BORDER  3
32 #define MIN_WIDTH    60
33 #define MIN_HEIGHT   3
34 #define BUTTON_OFFSET   2
35 #if defined(HIGH_DPI)
36 #define NAME_OFFSET   12
37 #else
38 #define NAME_OFFSET   2
39 #endif
40 #define NAME_OFFSETX  4
41
42 #define SB_WIDTH  16
43 #define SB_HEIGHT 16
44
45
46 #define GRADIENT_SMOOTHNESS 1.0f
47
48 #define TEXT_COLOR   black
49 /*
50
51 #define GRADIENT_DIRECTION horizontal
52
53 static ColorKey gradient[] =
54 {
55    { Color { 128, 128, 255}, 0.00f },
56    { Color { 254, 254, 254}, 0.60f },
57    { Color {   0,   0, 255}, 1.00f }
58 };
59 */
60
61 #define GRADIENT_DIRECTION vertical
62 static ColorKey gradient[] =
63 {
64    //{ ColorAlpha { 255, { 180, 200, 220} }, 0.00f },
65    { ColorAlpha { 255, menuBarColor }, 0.00f },
66    { ColorAlpha { 255, { 255, 255, 255} }, 0.60f },
67    { ColorAlpha { 255, { 158, 158, 160} }, 1.00f }
68 };
69
70 static ColorKey gradientInactive[] =
71 {
72    //{ ColorAlpha { 255, { 160, 180, 200} },  0.00f },
73    { ColorAlpha { 255, popupMenuColor }, 0.00f },
74    { ColorAlpha { 255, { 220, 220, 220} }, 0.60f },
75    { ColorAlpha { 255, { 120, 120, 120} }, 1.00f }
76 };
77
78 /*
79 #define GRADIENT_DIRECTION horizontal
80 #define TEXT_COLOR         white
81 */
82 //#define TEXT_INACTIVE      Color { 212,208,200 }
83 #define TEXT_INACTIVE      Color { 40, 50, 60 }
84 /*
85 static ColorKey gradient[] =
86 {
87    { ColorAlpha { 255, Color {  10,   36, 106 } }, 0.00f },
88    { ColorAlpha { 255, Color { 166,  202, 240 } }, 1.00f }
89 };
90 static ColorKey gradientInactive[] =
91 {
92    { ColorAlpha { 255, Color { 128, 128, 128 } }, 0.00f },
93    { ColorAlpha { 255, Color { 192, 192, 192 } }, 1.00f }
94 };
95 */
96 static const char * cursorsBitmaps[] =
97 {
98    "<:ecere>cursors/arrow.png",
99    "<:ecere>cursors/iBeam.png",
100    "<:ecere>cursors/cross.png",
101    "<:ecere>cursors/move.png",
102    "<:ecere>cursors/sizeNorthEastSouthWest.png",
103    "<:ecere>cursors/sizeNorthSouth.png",
104    "<:ecere>cursors/sizeNorthWestSouthEast.png",
105    "<:ecere>cursors/sizeWestEast.png",
106    "<:ecere>cursors/move.png"
107 };
108
109 static Point cursorsHotSpots[] =
110 {
111    { 0, 0 },
112    { 0, 0 },
113    { 8, 8 },
114    { 10, 10 },
115    { 8, 8 },
116    { 4, 10 },
117    { 7, 7 },
118    { 5, 0 }
119 };
120
121 static const char * skinBitmaps[SkinBitmap] =
122 {
123    "<:ecere>elements/areaMinimize.png",
124    "<:ecere>elements/areaMaximize.png",
125    "<:ecere>elements/areaRestore.png",
126    "<:ecere>elements/areaClose.png"
127 };
128
129 class WindowsSkin : Skin
130 {
131    class_property(name) = "Windows";
132    class_property(selectionColor) = Color { 10, 36, 106 };
133    class_property(selectionText)  = (Color)white;
134    class_property(disabledFrontColor) = Color { 128,128,128 };
135    class_property(disabledBackColor) = (Color)white;
136
137    FontResource ::SystemFont()
138    {
139 #if defined(HIGH_DPI)
140       return FontResource { faceName = $"Tahoma", size = 18.25f };
141 #else
142       return FontResource { faceName = $"Tahoma", size = 8.25f };
143 #endif
144    }
145
146    FontResource ::CaptionFont()
147    {
148 #if defined(HIGH_DPI)
149       return FontResource { faceName = $"Tahoma", size = 18.25f, bold = true };
150 #else
151       return FontResource { faceName = $"Tahoma", size = 8.25f, bold = true };
152 #endif
153    }
154
155    const char * ::CursorsBitmaps(uint id, int * hotSpotX, int *hotSpotY, byte ** paletteShades)
156    {
157       *hotSpotX = cursorsHotSpots[id].x;
158       *hotSpotY = cursorsHotSpots[id].y;
159       *paletteShades = null;
160       return cursorsBitmaps[id];
161    }
162
163    BitmapResource ::GetBitmap(SkinBitmap id)
164    {
165       return BitmapResource { fileName = skinBitmaps[id] };
166    }
167
168    int ::VerticalSBW() { return SB_WIDTH; }
169    int ::HorizontalSBH() { return SB_HEIGHT; }
170 };
171
172
173 public class WindowsSkin_Window : Window
174 {
175    void GetDecorationsSize(MinMaxValue * w, MinMaxValue * h)
176    {
177       *w = *h = 0;
178
179       if(hasMenuBar && state != minimized)
180       {
181          *h += skinMenuHeight;
182       }
183       if(statusBar && state != minimized)
184       {
185          *h += statusBarHeight;
186       }
187
188       if(nativeDecorations && rootWindow == this && windowHandle && !is3D)
189       {
190 #if defined(WIN32)
191          RECT rcClient = { 0 }, rcWindow = { 0 };
192          if(GetClientRect(windowHandle, &rcClient) && GetWindowRect(windowHandle, &rcWindow))
193          {
194             *w += (rcWindow.right - rcWindow.left) - rcClient.right;
195             *h += (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
196          }
197
198          // PrintLn(_class.name, " is at l = ", rcWindow.left, ", r = ", rcWindow.right);
199 #else
200          Box widths = { 0 };
201 #if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
202          XGetBorderWidths(this, widths);
203 #endif
204          *w += widths.left + widths.right;
205          *h += widths.top + widths.bottom;
206 #endif
207          return;
208       }
209       if((((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel) && state != minimized)
210       {
211          *w += 4;
212          *h += 4;
213       }
214       if(((BorderBits)borderStyle).sizable && (state == normal))
215       {
216          *w += 2 * BORDER;
217          *h += TOP + BOTTOM;
218       }
219       if(((BorderBits)borderStyle).fixed && (state != maximized || !GetParentMenuBar()))
220       {
221          *h += CAPTION;
222          if(!((BorderBits)borderStyle).sizable || state == minimized)
223          {
224             *h += 2*DEAD_BORDER;
225             *w += 2*DEAD_BORDER;
226          }
227       }
228       if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
229       {
230          *w += 2;
231          *h += 2;
232       }
233    }
234
235    void SetWindowMinimum(MinMaxValue * mw, MinMaxValue * mh)
236    {
237       bool isNormal = (state == normal);
238       if(nativeDecorations && rootWindow == this && windowHandle && !is3D) return;
239       if(((BorderBits)borderStyle).fixed && (state != maximized || !GetParentMenuBar()))
240       {
241          *mw = MIN_WIDTH;
242          *mh = MIN_HEIGHT;
243       }
244       else
245          *mw = *mh = 0;
246       if(((BorderBits)borderStyle).sizable && isNormal)
247          *mw += 2*CORNER;
248       // GetDecorationsSize(window, mw, mh);
249
250       if(hasVertScroll)
251          *mw += SB_WIDTH;
252       if(hasHorzScroll)
253          *mh += SB_HEIGHT;
254       if(hasVertScroll && hasHorzScroll)
255       {
256          *mw += 2 * SB_WIDTH + SB_WIDTH;
257          *mh += 2 * SB_HEIGHT + SB_HEIGHT;
258          if(((BorderBits)borderStyle).sizable && isNormal)
259             *mw -= 2*CORNER;
260       }
261    }
262
263    void SetWindowArea(int * x, int * y, MinMaxValue * w, MinMaxValue * h, MinMaxValue * cw, MinMaxValue * ch)
264    {
265       bool isNormal = (state == normal);
266       MinMaxValue aw = 0, ah = 0;
267
268       *x = *y = 0;
269
270       if(hasMenuBar)
271       {
272          *y += skinMenuHeight;
273       }
274
275       GetDecorationsSize(&aw, &ah);
276
277       if(nativeDecorations && rootWindow == this && windowHandle && !is3D)
278       {
279 #if defined(WIN32)
280          RECT rcWindow = { 0, 0, 0, 0 };
281          POINT client00 = { 0, 0 };
282          ClientToScreen(windowHandle, &client00);
283          GetWindowRect(windowHandle, &rcWindow);
284          *x += client00.x - rcWindow.left;
285          *y += client00.y - rcWindow.top;
286 #else
287          Box widths = { 0 };
288 #if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__)
289          XGetBorderWidths(this, widths);
290 #endif
291          *x += widths.left;
292          *y += widths.top;
293 #endif
294       }
295       else
296       {
297          // Compute client area start
298          if(((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel)
299          {
300             *x += 2;
301             *y += 2;
302          }
303
304          if(((BorderBits)borderStyle).sizable && isNormal)
305          {
306             *x += BORDER;
307             *y += TOP;
308          }
309
310          if(((BorderBits)borderStyle).fixed && (state != maximized || !GetParentMenuBar()))
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
327       // Reduce client area
328       *cw = *w - aw;
329       *ch = *h - ah;
330
331       *cw = Max(*cw, 0);
332       *ch = Max(*ch, 0);
333    }
334
335    void ShowDecorations(Font captionFont, Surface surface, const char * name, bool active, bool moving)
336    {
337       bool isNormal = (state == normal);
338       int top = 0, border = 0, bottom = 0;
339       Window parentMenuBar = GetParentMenuBar();
340
341       if(nativeDecorations && rootWindow == this && windowHandle && !is3D) return;
342
343       if(state == minimized)
344          top = border = bottom = DEAD_BORDER;
345       else if(((BorderBits)borderStyle).sizable)
346       {
347          top = isNormal ? TOP : 0;
348          border = isNormal ? BORDER : 0;
349          bottom = BOTTOM;
350       }
351       else if(((BorderBits)borderStyle).fixed)
352       {
353          top = DEAD_BORDER;
354          border = DEAD_BORDER;
355          bottom = DEAD_BORDER;
356       }
357       else if(((BorderBits)borderStyle).contour)
358       {
359          top = 1;
360          border = 1;
361          bottom = 1;
362       }
363
364       if(((BorderBits)borderStyle).deep || ((BorderBits)borderStyle).bevel)
365       {
366          int deepTop = 0, deepBottom = 0, deepBorder = 0;
367          if(((BorderBits)borderStyle).contour)
368          {
369             deepBorder = border;
370             deepTop = (((BorderBits)borderStyle).fixed && (state != maximized || !parentMenuBar)) ? (top + CAPTION) : top;
371             deepBottom = (((BorderBits)borderStyle).sizable && isNormal) ? bottom : border;
372          }
373
374          surface.Bevel(((BorderBits)borderStyle).bevel ? false : true, deepBorder, deepTop,
375             size.w - deepBorder - deepBorder, size.h - deepBottom - deepTop);
376       }
377
378       if(((BorderBits)borderStyle).fixed && (state != maximized || !parentMenuBar))
379       {
380          if(state != maximized || !((BorderBits)borderStyle).sizable)
381          {
382             // Frame for ES_CAPTION windows
383             surface.Bevel(false, 0, 0, size.w, size.h);
384             surface.SetForeground(formColor);
385             surface.Rectangle(2, 2, size.w-3, size.h-3);
386
387             // Resizeable frame is 1 pixel thicker
388             if(((BorderBits)borderStyle).sizable && isNormal)
389                surface.Rectangle(3, 3, size.w - 4, size.h - 4);
390          }
391
392          // Caption
393          if(active)
394             surface.Gradient(gradient, sizeof(gradient) / sizeof(ColorKey), GRADIENT_SMOOTHNESS, GRADIENT_DIRECTION,
395                border, top, size.w - border - 1, top + CAPTION - 2);
396          else
397             surface.Gradient(gradientInactive, sizeof(gradientInactive) / sizeof(ColorKey),
398             GRADIENT_SMOOTHNESS, GRADIENT_DIRECTION,
399                border, top, size.w - border - 1, top + CAPTION - 2);
400
401          surface.SetForeground(formColor);
402          if(state != minimized)
403             surface.HLine(border, size.w-border-1, top + CAPTION-1);
404
405          surface.SetForeground((active ? TEXT_COLOR : TEXT_INACTIVE));
406          surface.TextOpacity(false);
407          surface.TextFont(captionFont);
408          if(name)
409          {
410             int buttonsSize = border +
411                ((hasMaximize || hasMinimize) ? (BUTTON_SIZE*3)+7 : (BUTTON_SIZE+3));
412             surface.WriteTextDots(left, border + NAME_OFFSETX, top + NAME_OFFSET,
413                size.w - (buttonsSize + border + 4), name, strlen(name));
414          }
415       }
416       if(((BorderBits)borderStyle).contour && !((BorderBits)borderStyle).fixed)
417       {
418          surface.SetForeground(black);
419          surface.Rectangle(0, 0, size.w - 1, size.h - 1);
420       }
421
422       if(state != minimized && hasHorzScroll && hasVertScroll)
423       {
424          if(sbh && sbh.visible && sbv && sbv.visible)
425          {
426             surface.SetBackground(formColor);
427             surface.Area(
428                clientStart.x + clientSize.w,
429                clientStart.y + clientSize.h,
430                clientStart.x + clientSize.w + SB_WIDTH - 1,
431                clientStart.y + clientSize.h + SB_HEIGHT - 1);
432          }
433       }
434    }
435
436    bool IsMouseMoving(int x, int y, int w, int h)
437    {
438       bool isNormal = (state == normal);
439       bool result = false;
440       if(nativeDecorations && rootWindow == this && windowHandle && !is3D) return false;
441
442       if(((BorderBits)borderStyle).fixed && (state != maximized || !GetParentMenuBar()))
443       {
444          int corner = 0, border = 0, top = 0;
445          if(((BorderBits)borderStyle).sizable && isNormal)
446          {
447             corner = CORNER;
448             border = BORDER;
449             top    = TOP;
450          }
451          // Special case for having caption on resize bar
452          if(!CAPTION)
453             result = Box { corner, 0, w-corner-1, TOP-1 }.IsPointInside({x,y});
454          else
455             result = Box { border, top, w-border-1, top+CAPTION-1 }.IsPointInside({x, y});
456       }
457       return result;
458    }
459
460    bool IsMouseResizing(int x, int y, int w, int h, bool *resizeX, bool *resizeY, bool *resizeEndX, bool *resizeEndY)
461    {
462       bool result = false;
463
464       *resizeX = *resizeY = *resizeEndX = *resizeEndY = false;
465       if(nativeDecorations && rootWindow == this && windowHandle && !is3D) return false;
466
467       if(((BorderBits)borderStyle).sizable && (state == normal))
468       {
469          // TopLeft Corner
470          if(Box { 0, 0,CORNER-1, TOP-1 }.IsPointInside({x, y}))
471             result = *resizeX = *resizeY = true;
472          // TopRight Corner
473          if(Box { w-CORNER-1, 0, w-1, TOP-1 }.IsPointInside({x, y}))
474             result = *resizeEndX = *resizeY = true;
475          // BottomLeft Corner
476          if(Box { 0, h-BOTTOM-1, CORNER-1, h-1 }.IsPointInside({x, y}))
477             result = *resizeX = *resizeEndY = true;
478          // BottomRight Corner
479          if(Box { w-CORNER-1, h-BOTTOM-1, w-1, h-1 }.IsPointInside({x, y}))
480             result = *resizeEndX = *resizeEndY = true;
481          // Left Border
482          if(Box { 0,TOP, BORDER, h-BOTTOM-1 }.IsPointInside({x, y}))
483             result = *resizeX = true;
484          // Right Border
485          if(Box { w-BORDER-1, TOP, w-1, h-BOTTOM-1 }.IsPointInside({x, y}))
486             result = *resizeEndX = true;
487          // Top Border
488          if(Box { CORNER, 0, w-CORNER-1, TOP-1 }.IsPointInside({x, y}))
489             result = *resizeY = true;
490          // Bottom Border
491          if(Box { CORNER, h-BOTTOM-1, w-CORNER-1, h-1 }.IsPointInside({x, y}))
492             result = *resizeEndY = true;
493       }
494       return result;
495    }
496
497    void UpdateNonClient()
498    {
499       bool isNormal = (state == normal);
500       int top = 0, border = 0;
501       int insideBorder = 0;
502
503       if(!nativeDecorations || rootWindow != this || !windowHandle || is3D)
504       {
505          if(state == minimized)
506             top = border = DEAD_BORDER;
507          else if(((BorderBits)borderStyle).sizable)
508          {
509             if(state == maximized && GetParentMenuBar())
510             {
511                top = 2;
512                border = 2;
513             }
514             else
515             {
516                top = isNormal ? TOP : 0;
517                border = isNormal ? BORDER : 0;
518             }
519          }
520          else if(((BorderBits)borderStyle).fixed)
521          {
522             top = DEAD_BORDER;
523             border = DEAD_BORDER;
524          }
525          else if(((BorderBits)borderStyle).contour)
526          {
527             top = 1;
528             border = 1;
529          }
530          insideBorder = border;
531          if(((BorderBits)borderStyle).deep)
532             insideBorder += 2;
533       }
534       else
535       {
536          border = clientStart.x;
537          insideBorder = border;
538       }
539
540       if(menuBar)
541       {
542          if(state == minimized)
543             menuBar.visible = false;
544          else
545             menuBar.visible = true;
546          menuBar.Move(clientStart.x, clientStart.y - skinMenuHeight, size.w - insideBorder * 2, skinMenuHeight);
547       }
548       if(statusBar)
549       {
550          if(state == minimized)
551             statusBar.visible = false;
552          else
553          {
554             statusBar.visible = true;
555             if(nativeDecorations && rootWindow == this && windowHandle && !is3D)
556             {
557                statusBar.anchor = { left = clientStart.x, bottom = (int)(size.h - clientSize.h - clientStart.y - statusBarHeight ) };
558                statusBar.size.w = size.w - insideBorder * 2;
559             }
560             else
561             {
562                statusBar.anchor = { left = clientStart.x, bottom = border };
563                statusBar.size.w = size.w - insideBorder * 2;
564             }
565          }
566       }
567       if(!nativeDecorations || rootWindow != this || !windowHandle || is3D)
568       {
569          if(sysButtons[0])
570          {
571             sysButtons[0].anchor = { right = 2+(2*BUTTON_SIZE+3) + border, top = top + BUTTON_OFFSET };
572             sysButtons[0].size = { BUTTON_SIZE, BUTTON_SIZE };
573             sysButtons[0].bevel = true;
574             sysButtons[0].bitmap = { skinBitmaps[(state == minimized) ? restore : minimize] };
575             sysButtons[0].visible = true;
576          }
577          if(sysButtons[1])
578          {
579             sysButtons[1].anchor = { right = 2+(BUTTON_SIZE+3) + border, top = top + BUTTON_OFFSET };
580             sysButtons[1].size = { BUTTON_SIZE, BUTTON_SIZE };
581             sysButtons[1].bevel = true;
582             sysButtons[1].bitmap = { skinBitmaps[(state == maximized) ? restore : maximize] };
583             sysButtons[1].visible = true;
584          }
585          if(sysButtons[2])
586          {
587             sysButtons[2].anchor = { right = 2 + border, top = top + BUTTON_OFFSET };
588             sysButtons[2].size = { BUTTON_SIZE, BUTTON_SIZE };
589             sysButtons[2].bevel = true;
590             sysButtons[2].bitmap = { skinBitmaps[close] };
591             sysButtons[2].visible = true;
592          }
593       }
594       else
595       {
596          if(sysButtons[0])
597             sysButtons[0].visible = false;
598          if(sysButtons[1])
599             sysButtons[1].visible = false;
600          if(sysButtons[2])
601             sysButtons[2].visible = false;
602       }
603    }
604 }
605
606
607 #define PUREVTBL(c)     (*(void ***)((byte *)class(c).data + sizeof(uintptr)))
608 #if defined(HIGH_DPI)
609 #define CAPTION_DISTANCE   36
610 #else
611 #define CAPTION_DISTANCE   18
612 #endif
613
614 default:
615 static __attribute__((unused)) void Dummy()
616 {
617    Window a = null;
618    a.OnApplyGraphics();
619    a.OnRedraw(null);
620 }
621
622 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnApplyGraphics;
623 extern int __ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRedraw;
624 private:
625
626 public class WindowsSkin_Button : Button
627 {
628    void OnRedraw(Surface surface)
629    {
630       if(isRadio)
631       {
632          ((void (*)(Window, Surface))PUREVTBL(Button)[__ecereVMethodID___ecereNameSpace__ecere__gui__Window_OnRedraw])(this, surface);
633          return;
634       }
635       // if(bevel)
636       {
637          ButtonState state = this.buttonState;
638          Bitmap buttonBitmap = bitmap ? bitmap.bitmap : null;
639          const char * text = this.text;
640          int offset = (state == down && this.offset) ? 1 : 0;
641          Color backColor = background;
642          int isDefault = this.isDefault;
643          Font font;
644          int tw = 0, th = 0;
645          int bw = 0, bh = 0;
646
647          font = fontObject;
648          surface.TextFont(font);
649          if(text)
650             surface.TextExtent(text, strlen(text),&tw, &th);
651
652          if(bevelOver && checked)
653             offset = 1;
654
655          if(!isEnabled)
656          {
657             if(bitmaps[disabled]) buttonBitmap = bitmaps[disabled].bitmap;
658             state = disabled;
659          }
660
661          if(buttonBitmap && buttonStyle.bevelOver && !buttonStyle.checkBox && !buttonStyle.radio && text)
662             isDefault = 0;
663
664          // Background
665          if((bevel /*|| bevelOver*/) && opacity && backColor)
666          {
667             if(!scaleBitmap || !buttonBitmap)
668             {
669                ColorKey keys[2] = { { silver, 0.0f }, { white, 1.0f } };
670                surface.Gradient(keys, sizeof(keys) / sizeof(ColorKey), 1, vertical, 0, 0, clientSize.w-1, clientSize.h-1);
671             }
672          }
673
674          // Checkbox
675          if(isCheckbox && !buttonBitmap)
676          {
677 #if defined(HIGH_DPI)
678             int height = 32;
679 #else
680             int height = 16;
681 #endif
682             int start = (clientSize.h - height) / 2;
683
684             if(!isEnabled)
685                // surface.SetBackground(formColor);
686                surface.SetBackground(gainsboro);
687             else if(active && !text)
688                surface.SetBackground((offset ? formColor : Color { 0,0,170 }));
689             else
690                surface.SetBackground((offset ? formColor : white));
691             surface.Area(2, start+2,height-3,start+height-3);
692
693             surface.SetForeground(Color { 85, 85, 85 });
694             surface.HLine(0, height - 2, start + 0);
695             surface.VLine(start+1, start+height - 2, 0);
696
697             surface.SetForeground(Color { 64,64,64 });
698             surface.HLine(1, height - 3, start + 1);
699             surface.VLine(start+2, start+height - 3, 1);
700
701             surface.SetForeground(Color { 212,208,200 });
702             surface.HLine(1, height - 2, start + height-2);
703             surface.VLine(start+1, start+height - 3, height-2);
704
705             surface.SetForeground(white);
706             surface.HLine(0, height - 1, start + height-1);
707             surface.VLine(start+0, start+height - 2, height-1);
708
709             if(checked)
710             {
711                if(active && !text)
712                   surface.SetForeground(white);
713                else if(!isEnabled)
714                   surface.SetForeground(Color { 85, 85, 85 });
715                else
716                   surface.SetForeground(black);
717 #ifdef HIGH_DPI
718                surface.DrawLine(8, start+16, 14,start+22);
719                surface.DrawLine(8, start+18, 14,start+24);
720                surface.DrawLine(14, start+22, 19,start+6);
721                surface.DrawLine(14, start+24, 19,start+8);
722 #else
723                surface.DrawLine(4, start+8, 7,start+11);
724                surface.DrawLine(4, start+9, 7,start+12);
725                surface.DrawLine(7, start+11, 11,start+3);
726                surface.DrawLine(7, start+12, 11,start+4);
727 #endif
728             }
729          }
730
731          // Bitmaps
732          if(buttonBitmap)
733          {
734             surface.SetForeground(white);
735             if(isRadio || isCheckbox)
736             {
737                int x = 0, y = (clientSize.h-buttonBitmap.height)/2;
738                if(bevelOver && text)
739                {
740                   x = (CAPTION_DISTANCE-buttonBitmap.width)/2 + offset;
741                   y = (clientSize.h-buttonBitmap.height)/2 + offset;
742
743
744                }
745
746                // Radio Buttons and Checkboxes
747                surface.Blit(buttonBitmap,
748                   x, y,
749                   0,0,buttonBitmap.width,buttonBitmap.height);
750             }
751             else
752             {
753                // Push Buttons
754                if(scaleBitmap)
755                {
756                   if(bevel || offset)
757                      surface.Stretch(buttonBitmap,
758                         1 + offset, 1 + offset,0,0,
759                         clientSize.w-3,clientSize.h-3,buttonBitmap.width,buttonBitmap.height);
760                   else
761                      surface.Stretch(buttonBitmap, 0,0, 0,0,
762                         clientSize.w,clientSize.h,buttonBitmap.width,buttonBitmap.height);
763                }
764                else
765                {
766                   int x, y;
767                   bw = buttonBitmap.width;
768                   bh = buttonBitmap.height;
769
770                   if(bitmapAlignment == left || bitmapAlignment == right)
771                   {
772                      if(bitmapAlignment == left)
773                         x = 2;
774                      else
775                         x = clientSize.w-bw-2;
776                      y = (clientSize.h-bh)/2;
777                   }
778                   else if(bitmapAlignment == top || bitmapAlignment == bottom)
779                   {
780                      x = (clientSize.w-bw)/2;
781                      if(bitmapAlignment == top)
782                         y = 2;
783                      else
784                         y = clientSize.h-bh-2;
785                   }
786                   else
787                   {
788                      x = (clientSize.w-bw)/2;
789                      y = (clientSize.h-bh - (int)(buttonStyle.bevelOver && text) * th)/2;
790                   }
791                   if(buttonStyle.bevel || buttonStyle.offset)
792                   {
793                      x += offset;
794                      y += offset;
795                   }
796                   surface.Blit(buttonBitmap, x,y, 0,0, bw,bh);
797                }
798             }
799          }
800
801          // Shadows
802          if(bevel || (bevelOver && (state == down || state == over || checked)))
803          {
804             if(state == down || checked)
805             {
806                surface.SetForeground(Color { 85, 85, 85 });
807                surface.HLine(isDefault + 0, clientSize.w-2-isDefault, 0);
808                surface.VLine(isDefault + 1, clientSize.h-2-isDefault, 0);
809                surface.SetForeground(white);
810                surface.HLine(isDefault + 0, clientSize.w-1-isDefault, clientSize.h-1-isDefault);
811                surface.VLine(isDefault + 0, clientSize.h-2-isDefault, clientSize.w-1-isDefault);
812             }
813             else
814             {
815                surface.SetForeground(white);
816                surface.HLine(0 + isDefault, clientSize.w-2 - isDefault,  isDefault);
817                surface.VLine(1 + isDefault, clientSize.h-2 - isDefault,  isDefault);
818                surface.SetForeground(Color { 85, 85, 85 });
819                surface.HLine(1 + isDefault, clientSize.w-2 - isDefault, clientSize.h-2 - isDefault);
820                surface.VLine(1 + isDefault, clientSize.h-3 - isDefault, clientSize.w-2 - isDefault);
821
822                if(bevel)
823                {
824                   surface.SetForeground(black);
825                   surface.HLine( isDefault, clientSize.w-1 - isDefault, clientSize.h-1 - isDefault);
826                   surface.VLine( isDefault, clientSize.h-2 - isDefault, clientSize.w-1 - isDefault);
827                }
828             }
829          }
830
831          // Text
832          surface.TextOpacity(false);
833          surface.SetForeground(foreground);
834          if(text)
835          {
836             int tw, th;
837             surface.TextExtent(text, strlen(text),&tw, &th);
838
839             if((isRadio || isCheckbox) && !bevelOver)
840                WriteCaption(surface, CAPTION_DISTANCE + 3, // + clientSize.h,
841                   (clientSize.h - th - 4)/2);
842             else
843             {
844                int x, y = (clientSize.h - th - 1)/2 + offset;
845
846                if(buttonStyle.bevelOver && buttonBitmap && !buttonStyle.checkBox && !buttonStyle.radio)
847                {
848                   if(bitmapAlignment == top)
849                      y = (clientSize.h - bh - 4 - th - 5)/2 + offset + bh + 4;
850                   else if(bitmapAlignment == bottom)
851                      y = (clientSize.h - bh - 4 - th - 5)/2 + offset;
852                   else//if(bitmapAlignment == left || bitmapAlignment == right)
853                      y = clientSize.h - th - 5 + offset;
854                }
855                else
856                   y = (clientSize.h - th - 1)/2 + offset;
857
858                if(ellipsis)
859                {
860                   int width = clientSize.w - 2*6;
861                   int x = 6 + offset;
862
863                   surface.WriteTextDots(alignment, x, y, width, text, strlen(text));
864                }
865                else
866                {
867                   int width = clientSize.w - 2 * 6;
868                   x = 6 + offset;
869                   if(bitmapAlignment == left || bitmapAlignment == right)
870                   {
871                      if(bitmapAlignment == left)
872                         x += bw + 4;
873                      width -= bw + 4;
874                   }
875                   if(isCheckbox || ((isRadio /*|| bevelOver*/) && buttonBitmap))
876                   {
877                      x += CAPTION_DISTANCE + 3;
878                   }
879
880                   if(tw < width)
881                   {
882                      if(alignment == right)
883                         x += width - tw - 1;
884                      else if(alignment == center)
885                         x += (width - tw) / 2;
886                   }
887                   WriteCaption(surface, x, y);
888                }
889             }
890          }
891
892          // Activation Highlight
893          if(isDefault)
894          {
895             surface.SetForeground(black);
896             surface.Rectangle(0,0,clientSize.w-1,clientSize.h-1);
897          }
898          if(!bevelOver && !isRemote)
899          {
900             if(active) // && (text || !(buttonStyle.radio || buttonStyle.checkBox)))
901             {
902                int x1,y1,x2,y2;
903                surface.SetForeground(black);
904                surface.LineStipple(0x5555);
905
906                if((isRadio || isCheckbox) && text)
907                {
908                   x1 = CAPTION_DISTANCE;  // + clientSize.h;
909                   y1 = 0;
910                   x2 = clientSize.w-4;
911                   y2 = clientSize.h-4;
912                }
913                else
914                {
915                   x1 = 3+offset;
916                   y1 = 3+offset;
917                   x2 = clientSize.w - 5+offset;
918                   y2 = clientSize.h - 5+offset;
919
920                   if(isRadio || isCheckbox)
921                   {
922                      x1-=3;
923                      y1-=3;
924                      x2+=1;
925                      y2+=1;
926                   }
927                }
928                if((x2 - x1) & 1) x2++;
929                if((y2 - y1) & 1) y2++;
930
931                surface.Rectangle(x1, y1, x2, y2);
932                surface.LineStipple(0);
933             }
934          }
935       }
936    }
937 }