ecere/gui/Window: Call OnMouseLeave on Release for Android
[sdk] / ecere / src / gui / controls / Button.ec
1 namespace gui::controls;
2
3 import "Window"
4
5 class ButtonBits { bool bevel:1, scale:1, offset:1, stayDown:1, checkBox:1, toggle:1, radio:1, keyRepeat:1, bevelOver:1, ellipsis:1, stayUp:1; };
6
7 #if defined(HIGH_DPI)
8 #define CAPTION_DISTANCE   36
9 #else
10 #define CAPTION_DISTANCE   18
11 #endif
12
13 public enum ButtonState /*: byte*/ { up, down, over, disabled, upChecked, downChecked, overChecked, disabledChecked };
14 //use and extend preexisting enum? -> public enum Alignment { left, right, center };
15 public enum BoxAlignment { center, left, right, top, bottom }; // topLeftCorner, topRightCorner, bottomLeftCorner, bottomRightCorner
16
17 public class Button : CommonControl
18 {
19    /*
20    private(opacity);
21    private(drawBehind);
22    */
23    public property Percentage opacity
24    {
25       isset { return (buttonStyle.checkBox || buttonStyle.radio) ? (Window::opacity != 0) : Window::opacity != 1; }
26       get { return Window::opacity; }
27       set { Window::opacity = value; }
28    }
29    public property bool drawBehind
30    {
31       isset { return (buttonStyle.checkBox || buttonStyle.radio) ? (Window::drawBehind != drawBehind) : Window::drawBehind != false; }
32       get { return Window::drawBehind; }
33       set { Window::drawBehind = value; }
34    }
35    class_property(icon) = "<:ecere>controls/button.png";
36
37    watch(background)
38    {
39       Color background = this.background;
40
41       if(colors[down] == colors[up])      colors[down] = background;
42       if(colors[over] == colors[up])      colors[over] = background;
43       if(colors[disabled] == colors[up])  colors[disabled] = background;
44       colors[0] = background;
45    };
46
47    watch(foreground)
48    {
49       Color foreground = this.foreground;
50
51       if(textColor[down] == textColor[up])      textColor[down] = foreground;
52       if(textColor[over] == textColor[up])      textColor[over] = foreground;
53       if(textColor[disabled] == textColor[up])  textColor[disabled] = foreground;
54       textColor[0] = foreground;
55    };
56
57    watch(font)
58    {
59       OnLoadGraphics();
60       SetInitSize(initSize);
61    };
62
63    watch(caption)
64    {
65       OnLoadGraphics();
66       SetInitSize(initSize);
67    };
68
69    ButtonBits buttonStyle;
70    ButtonState state;
71    BitmapResource bitmaps[ButtonState];
72         int captionHeight, captionWidth;
73    unichar symbol;
74    Color textColor[ButtonState];
75    Font font;
76    Color colors[ButtonState];
77    Alignment alignment;
78    BoxAlignment bitmapAlignment;
79    struct { bool hint:1, key:1, captured:1, over:1, checked:1; };
80
81    Button()
82    {
83       background = formColor;
84       foreground = black;
85       //opacity = 0;
86       //drawBehind = true;
87       borderStyle = 0;
88       bevel = true;
89       buttonStyle.offset = true;
90
91       // SetCursor(eInterface_GetCursor(__ecereModule->app, CUR_ARROW));
92
93       alignment = center;
94       state = up;
95       hint = false;
96       textColor[up] = textColor[down] =
97       textColor[over] = black;
98       textColor[disabled] = Color { 85, 85, 85 };
99
100       colors[up] = formColor;
101       colors[down] = formColor;
102       colors[over] = formColor;
103       colors[disabled] = white;
104       colors[upChecked] = Color { 233,229,221 };
105       colors[overChecked] = formColor;
106       colors[downChecked] = Color { 233,229,221 };
107       colors[disabledChecked] = formColor;
108    }
109
110    Bitmap GetButtonBitmap()
111    {
112       ButtonState state = this.state;
113       Bitmap buttonBitmap = bitmaps[(int)checked*4 + state].bitmap;
114       if(!buttonBitmap) buttonBitmap = bitmaps[(int)checked * 4+up].bitmap;
115       if(!buttonBitmap) buttonBitmap = bitmaps[up].bitmap;
116       if(!isEnabled && bitmaps[disabled]) buttonBitmap = bitmaps[disabled].bitmap;
117       return buttonBitmap;
118    }
119
120    void OnRedraw(Surface surface)
121    {
122       ButtonState state = this.state;
123       Bitmap buttonBitmap = GetButtonBitmap();
124       const char * text = this.text;
125       int offset = (state == down && buttonStyle.offset) ? 1 : 0;
126       Color backColor = colors[(int)checked*4 + state];
127       int isDefault = this.isDefault;
128       int tw = 0, th = 0;
129       int bw = 0, bh = 0;
130
131       if(!backColor) backColor = colors[up];
132
133       font = fontObject;
134       surface.TextFont(font);
135       if(text)
136          surface.TextExtent(text, strlen(text),&tw, &th);
137
138       if(buttonStyle.bevelOver && checked)
139          offset = 1;
140
141       if(!isEnabled)
142       {
143          if(bitmaps[disabled]) buttonBitmap = bitmaps[disabled].bitmap;
144          state = disabled;
145       }
146
147       if(buttonBitmap && buttonStyle.bevelOver && !buttonStyle.checkBox && !buttonStyle.radio && text)
148          isDefault = 0;
149
150       // Background
151       if((buttonStyle.bevel || buttonStyle.bevelOver) /* && opacity && backColor*/)
152       {
153          if(!buttonStyle.scale || !buttonBitmap)
154          {
155             surface.SetBackground({(byte)(opacity * 255), backColor});
156             //surface.Area(1, 1,clientSize.w-3+offset,clientSize.h-3+offset);
157             surface.Area(0, 0,clientSize.w-1,clientSize.h-1);
158          }
159       }
160
161       // Checkbox
162       if(buttonStyle.checkBox && !buttonBitmap)
163       {
164 #if defined(HIGH_DPI)
165          int height = 32;
166 #else
167          int height = 16;
168 #endif
169          int start = (clientSize.h - height) / 2;
170
171          if(!isEnabled)
172             // surface.SetBackground(formColor);
173             surface.SetBackground(gainsboro);
174          else if(active && !text)
175             surface.SetBackground((offset ? formColor : Color { 0,0,170 }));
176          else
177             surface.SetBackground((offset ? formColor : background /*white*/));
178          surface.Area(2, start+2,height-3,start+height-3);
179
180          surface.SetForeground(Color { 85, 85, 85 });
181          surface.HLine(0, height - 2, start + 0);
182          surface.VLine(start+1, start+height - 2, 0);
183
184          surface.SetForeground(Color { 64,64,64 });
185          surface.HLine(1, height - 3, start + 1);
186          surface.VLine(start+2, start+height - 3, 1);
187
188          surface.SetForeground(Color { 212,208,200 });
189          surface.HLine(1, height - 2, start + height-2);
190          surface.VLine(start+1, start+height - 3, height-2);
191
192          surface.SetForeground(white);
193          surface.HLine(0, height - 1, start + height-1);
194          surface.VLine(start+0, start+height - 2, height-1);
195
196          if(checked)
197          {
198             if(active && !text)
199                surface.SetForeground(white);
200             else if(!isEnabled)
201                surface.SetForeground(Color { 85, 85, 85 });
202             else
203                surface.SetForeground(foreground /*black*/);
204 #ifdef HIGH_DPI
205                surface.DrawLine(8, start+16, 14,start+22);
206                surface.DrawLine(8, start+18, 14,start+24);
207                surface.DrawLine(14, start+22, 19,start+6);
208                surface.DrawLine(14, start+24, 19,start+8);
209 #else
210             surface.DrawLine(4, start+8, 7,start+11);
211             surface.DrawLine(4, start+9, 7,start+12);
212             surface.DrawLine(7, start+11, 11,start+3);
213             surface.DrawLine(7, start+12, 11,start+4);
214 #endif
215          }
216       }
217
218       // Bitmaps
219       if(buttonBitmap)
220       {
221          surface.SetForeground(white);
222          if(buttonStyle.radio || buttonStyle.checkBox)
223          {
224             int x = 0, y = (clientSize.h-buttonBitmap.height)/2;
225             if(buttonStyle.bevelOver && text)
226             {
227                x = (CAPTION_DISTANCE-buttonBitmap.width)/2 + offset;
228                y = (clientSize.h-buttonBitmap.height)/2 + offset;
229             }
230
231             // Radio Buttons and Checkboxes
232             surface.Blit(buttonBitmap,
233                x, y,
234                0,0,buttonBitmap.width,buttonBitmap.height);
235          }
236          else
237          {
238             // Push Buttons
239             if(buttonStyle.scale)
240             {
241                if(buttonStyle.bevel || buttonStyle.offset)
242                   surface.Stretch(buttonBitmap,
243                      1 + offset, 1 + offset,0,0,
244                      clientSize.w-3,clientSize.h-3,buttonBitmap.width,buttonBitmap.height);
245                else
246                   surface.Stretch(buttonBitmap, 0,0, 0,0,
247                      clientSize.w,clientSize.h,buttonBitmap.width,buttonBitmap.height);
248             }
249             else
250             {
251                int x, y;
252                bw = buttonBitmap.width;
253                bh = buttonBitmap.height;
254
255                if(bitmapAlignment == left || bitmapAlignment == right)
256                {
257                   if(bitmapAlignment == left)
258                      x = 2;
259                   else
260                      x = clientSize.w-bw-2;
261                   y = (clientSize.h-bh)/2;
262                }
263                else if(bitmapAlignment == top || bitmapAlignment == bottom)
264                {
265                   x = (clientSize.w-bw)/2;
266                   if(bitmapAlignment == top)
267                      y = 2;
268                   else
269                      y = clientSize.h-bh-2;
270                }
271                else
272                {
273                   x = (clientSize.w-bw)/2;
274                   y = (clientSize.h-bh - (int)(buttonStyle.bevelOver && text) * th)/2;
275                }
276                if(buttonStyle.bevel || buttonStyle.offset)
277                {
278                   x += offset;
279                   y += offset;
280                }
281                surface.Blit(buttonBitmap, x,y, 0,0, bw,bh);
282             }
283          }
284       }
285
286       // Shadows
287       if(buttonStyle.bevel ||
288          (buttonStyle.bevelOver &&
289             (state == down || state == over || checked)))
290       {
291          if(state == down || checked)
292          {
293             surface.SetForeground(Color { 85, 85, 85 });
294             surface.HLine(isDefault + 0, clientSize.w-2-isDefault, 0);
295             surface.VLine(isDefault + 1, clientSize.h-2-isDefault, 0);
296             surface.SetForeground(white);
297             surface.HLine(isDefault + 0, clientSize.w-1-isDefault, clientSize.h-1-isDefault);
298             surface.VLine(isDefault + 0, clientSize.h-2-isDefault, clientSize.w-1-isDefault);
299          }
300          else
301          {
302             surface.SetForeground(white);
303             surface.HLine(0 + isDefault, clientSize.w-2 - isDefault,  isDefault);
304             surface.VLine(1 + isDefault, clientSize.h-2 - isDefault,  isDefault);
305             surface.SetForeground(Color { 85, 85, 85 });
306             surface.HLine(1 + isDefault, clientSize.w-2 - isDefault, clientSize.h-2 - isDefault);
307             surface.VLine(1 + isDefault, clientSize.h-3 - isDefault, clientSize.w-2 - isDefault);
308
309             if(buttonStyle.bevel)
310             {
311                surface.SetForeground(black);
312                surface.HLine( isDefault, clientSize.w-1 - isDefault, clientSize.h-1 - isDefault);
313                surface.VLine( isDefault, clientSize.h-2 - isDefault, clientSize.w-1 - isDefault);
314             }
315          }
316       }
317
318       // Text
319       surface.TextOpacity(false);
320       surface.SetForeground((textColor[state] ? textColor[state] : textColor[up]));
321       if(text)
322       {
323          if((buttonStyle.radio || buttonStyle.checkBox) && !(buttonStyle.bevelOver))
324             WriteCaption(surface, /*clientSize.h +*/ CAPTION_DISTANCE + 3,
325                (clientSize.h - th - 4)/2);
326          else
327          {
328             int x, y;
329
330             if(buttonStyle.bevelOver && buttonBitmap && !buttonStyle.checkBox && !buttonStyle.radio)
331             {
332                if(bitmapAlignment == top)
333                   y = (clientSize.h - bh - 4 - th - 5)/2 + offset + bh + 4;
334                else if(bitmapAlignment == bottom)
335                   y = (clientSize.h - bh - 4 - th - 5)/2 + offset;
336                else//if(bitmapAlignment == left || bitmapAlignment == right)
337                   y = clientSize.h - th - 5 + offset;
338             }
339             else
340                y = (clientSize.h - th - 1)/2 + offset;
341
342             if(buttonStyle.ellipsis)
343             {
344                int width = clientSize.w - 2*6;
345                int x = 6 + offset;
346
347                surface.WriteTextDots(alignment, x, y, width, text, strlen(text));
348             }
349             else
350             {
351                int width;
352                x = 6 + offset;
353                width = clientSize.w - 2 * 6;
354                if(bitmapAlignment == left || bitmapAlignment == right)
355                {
356                   if(bitmapAlignment == left)
357                      x += bw + 4;
358                   width -= bw + 4;
359                }
360                if(buttonStyle.checkBox || ((buttonStyle.radio /*|| buttonStyle.bevelOver*/) && buttonBitmap))
361                {
362                   x += CAPTION_DISTANCE + 3;
363                }
364
365                if(tw < width)
366                {
367                   if(alignment == right)
368                      x += width - tw - 1;
369                   else if(alignment == center)
370                      x += (width - tw) / 2;
371                }
372                WriteCaption(surface, x, y);
373             }
374          }
375       }
376
377       // Activation Highlight
378       if(isDefault)
379       {
380          surface.SetForeground(black);
381          surface.Rectangle(0,0,clientSize.w-1,clientSize.h-1);
382       }
383       if(!(buttonStyle.bevelOver) && !isRemote)
384       {
385          if(active/* && (text || !(buttonStyle.radio || buttonStyle.checkBox))*/)
386          {
387             int x1,y1,x2,y2;
388             surface.SetForeground(black);
389             surface.LineStipple(0x5555);
390
391             if((buttonStyle.radio || buttonStyle.checkBox) && text)
392             {
393                x1 = /*clientSize.h + */CAPTION_DISTANCE;
394                y1 = 0;
395                x2 = clientSize.w-4;
396                y2 = clientSize.h-4;
397             }
398             else
399             {
400                x1 = 3+offset;
401                y1 = 3+offset;
402                x2 = clientSize.w - 5+offset;
403                y2 = clientSize.h - 5+offset;
404
405                if(buttonStyle.radio || buttonStyle.checkBox)
406                {
407                   x1-=3;
408                   y1-=3;
409                   x2+=1;
410                   y2+=1;
411                }
412             }
413             if((x2 - x1) & 1) x2++;
414             if((y2 - y1) & 1) y2++;
415
416             surface.Rectangle(x1, y1, x2, y2);
417             surface.LineStipple(0);
418          }
419       }
420    }
421
422    /*
423    void TVisionOnRedraw(Window window,Surface surface)
424    {
425       Button * button = (Button *)window->data;
426       int state = this.state;
427       int tw, th;
428
429       int offset = (buttonStyle.bevel && state == down) ? textCellW : 0;
430
431       if(clientSize.h <= textCellH || clientSize.w <= 2*textCellW) offset = 0;
432
433       if(!isEnabled)
434          state = disabled;
435
436       surface.TextOpacity(false);
437       surface.SetForeground((active ? white : black) );
438
439       surface.SetBackground(GREEN);
440       // Background
441       if(buttonStyle.bevel)
442       {
443          if(state == down)
444          {
445             if(clientSize.h > textCellH && clientSize.w > 2*textCellW)
446                surface.Area(textCellW,0,clientSize.w-textCellW,clientSize.h-textCellH-1);
447             else if(!window->background)
448                surface.Area(0,0,clientSize.w-1,clientSize.h-1);
449          }
450          else
451          {
452             if(clientSize.h > textCellH && clientSize.w > 2*textCellW)
453             {
454                surface.Area(0,0,clientSize.w-2*textCellW,clientSize.h-textCellH-1);
455                surface.SetForeground(black);
456                eSurface_DrawingChar(surface, 223);
457                surface.HLine(textCellW,clientSize.w-textCellW,window->h-textCellH);
458                if(clientSize.h>32)
459                {
460                   eSurface_DrawingChar(surface, 219);
461                   surface.VLine(1,clientSize.h-textCellH,clientSize.w-textCellW);
462                }
463                eSurface_DrawingChar(surface, 220);
464                surface.HLine(clientSize.w - textCellW, clientSize.w - textCellW, 0);
465             }
466             else if(!window->background)
467                surface.Area(0,0,clientSize.w-1,clientSize.h-1);
468          }
469       }
470
471       surface.SetForeground(((active || state == down) ? ((BITwindow->buttonStyle, ES_DEFAULT)) ? LT_RED : white) : ((BITwindow->buttonStyle, ES_DEFAULT)) ? LT_CYAN : textColor[state])) );
472
473       if(buttonStyle.checkBox)
474          surface.WriteText(0,0, checked ? "[\373]" : "[ ] = 3);
475       if(buttonStyle.radio)
476          surface.WriteText(0,0, checked ? "(\007)" : "( ) = 3);
477
478       if(text)
479          surface.TextExtent(text,strlen(text),&tw, &th);
480
481       if(buttonStyle.radio || buttonStyle.checkBox)
482          surface.SetForeground(((state == down) ? ((BITwindow->buttonStyle, ES_DEFAULT)) ? LT_RED : white) : ((BITwindow->buttonStyle, ES_DEFAULT)) ? LT_CYAN : textColor[state])));
483
484       if(buttonStyle.radio || buttonStyle.checkBox)
485          WriteCaption(surface, 4*textCellW, (clientSize.h - th - 2)/2);
486       else if(!text)
487       {
488          if(symbol)
489          {
490             tw = textCellW; th = textCellH;
491             surface.WriteTextf((clientSize.w - tw)/2 + offset, (clientSize.h - th - 2)/2 + offset.%c = symbol);
492          }
493       }
494       else
495          WriteCaption(surface,(clientSize.w - tw)/2 + offset,(clientSize.h - th - 2)/2 + offset);
496    }
497    */
498
499    bool OnMouseMove(int x, int y, Modifiers mods)
500    {
501       if(!mods.isSideEffect)
502          return NotifyMouseMove(master, this, x,y, mods);
503       return true;
504    }
505
506    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
507    {
508       //eSystem_Logf("%s %s\n = text, active ? "Activated" : "Deactivated;
509       if(!active)
510       {
511          if(!buttonStyle.toggle)
512          {
513             if(captured)
514             {
515                captured = false;
516                ReleaseCapture();
517             }
518             if(hint && NotifyReleased(master, this, 0, 0, 0))
519             {
520                state = up;
521                hint = false;
522                key = false;
523             }
524          }
525       }
526       Update(null);
527       return true;
528    }
529
530    bool OnLeftButtonDown(int x, int y, Modifiers mods)
531    {
532       if(x >= 0 && y >= 0 && x < size.w && y < size.h)
533       {
534          if(buttonStyle.toggle)
535          {
536             state = (state == down) ? up : down;
537             if(NotifyPushed(master, this, x,y, mods) &&
538                NotifyClicked(master, this, x,y, mods))
539                Update(null);
540          }
541          else
542          {
543             if(key || Capture())
544             {
545                Window master = this.master;
546                if(!key)
547                   captured = true;
548                if(NotifyPushed(master, this, x,y, mods))
549                {
550                   hint = true;
551                   if(!buttonStyle.stayUp)
552                      state = down;
553                   Update(null);
554                }
555             }
556          }
557       }
558       return true;
559    }
560
561    bool OnLeftButtonUp(int x, int y, Modifiers mods)
562    {
563       bool result = true;
564       if(!(buttonStyle.toggle))
565       {
566          bool releasedResult;
567          if(captured)
568          {
569             captured = false;
570             ReleaseCapture();
571          }
572
573          releasedResult = NotifyReleased(master, this, x,y, mods);
574          if(hint)
575          {
576             if(x >= 0 && y >= 0 && x < size.w && y < size.h)
577             {
578                if(buttonStyle.checkBox)
579                   property::checked = !checked;
580                else if(buttonStyle.radio)
581                   property::checked = true;
582
583                // state = mods ? up : over;
584                // TESTING THIS OUT:
585                state = over ? over : up;
586                hint = false;
587                Update(null);
588                if(releasedResult)
589                {
590                   if(!NotifyClicked(master, this, x,y, mods))
591                      result = false;
592                }
593             }
594             else
595             {
596                state = up;
597                hint = false;
598                Update(null);
599             }
600          }
601       }
602       return result;
603    }
604
605    bool OnLeftDoubleClick(int x, int y, Modifiers mods)
606    {
607       return NotifyDoubleClick(master, this, x,y, mods);
608    }
609
610    bool OnMouseOver(int x, int y, Modifiers mods)
611    {
612       over = true;
613       if(!key)
614       {
615          if(!(buttonStyle.toggle || buttonStyle.stayUp))
616          {
617             if(!hint)
618                state = over;
619             else
620             {
621                state = down;
622             }
623          }
624          else
625          {
626             if(state != down)
627                state = over;
628          }
629
630          if(NotifyMouseOver(master, this, x,y, mods))
631             Update(null);
632       }
633       return true;
634    }
635
636    void OnMouseCaptureLost()
637    {
638       if(captured)
639       {
640          captured = false;
641          if(hint)
642          {
643             state = up;
644             hint = false;
645             Update(null);
646          }
647       }
648    }
649
650    bool OnMouseLeave(Modifiers mods)
651    {
652       if(!key)
653       {
654          if( (!buttonStyle.stayDown || !hint) /*&&  !buttonStyle.toggle */)
655          {
656             if(!(buttonStyle.toggle) || state == over)
657                state = up;
658          }
659
660          if(NotifyMouseLeave(master, this, mods))
661             Update(null);
662       }
663       over = false;
664       return true;
665    }
666
667    bool OnLoadGraphics()
668    {
669       font = fontObject;
670       if(text)
671          display.FontExtent(font,text,strlen(text), &captionWidth, &captionHeight);
672       else
673       {
674          display.FontExtent(font," ", 1, &captionWidth, &captionHeight);
675          captionWidth = 80;
676       }
677       if(bitmapAlignment != center && !buttonStyle.checkBox && !buttonStyle.radio && !buttonStyle.scale)
678       {
679          Bitmap buttonBitmap = GetButtonBitmap();
680          if(buttonBitmap)
681          {
682             if(bitmapAlignment == left || bitmapAlignment == right)
683                captionWidth += buttonBitmap.width + 4;
684             else
685                captionHeight += buttonBitmap.height + 4;
686          }
687       }
688       return true;
689    }
690
691    bool OnResizing(int *width, int *height)
692    {
693       // Checking *width/*height for zero does not work with minClientSize being set
694       Size initSize = this.initSize;
695       if(this.anchor.left.type != none && this.anchor.right.type != none) initSize.w = 1;
696       if(this.anchor.top.type != none && this.anchor.bottom.type != none) initSize.h = 1;
697
698       if(buttonStyle.checkBox || buttonStyle.radio || buttonStyle.bevelOver)
699       {
700          Bitmap bitmap0 = bitmaps[0] ? bitmaps[0].bitmap : null;
701          if(!initSize.w /**width*/ && bitmap0) *width = Max(*width, bitmap0.width);
702          if(!initSize.h /**height*/ && bitmap0) *height = Max(*height, bitmap0.height);
703
704          *height = Max(*height, captionHeight + 2);
705          if(text)
706          {
707             if(guiApp.textMode)
708                *width = Max(*width, /**height + */captionWidth + CAPTION_DISTANCE + 16);
709             else
710                *width = Max(*width, /**height + */captionWidth + CAPTION_DISTANCE + 8);
711          }
712          else
713          {
714             *width = Max(*width, *height);
715             if(guiApp.textMode)
716                *width = Max(*width, 32);
717          }
718       }
719       else
720       {
721          if(!caption && bitmap && bitmap.bitmap)
722          {
723             if(!initSize.w /**width*/)
724                *width = Max(*width, bitmap.bitmap.width);
725             if(!initSize.h /**height*/)
726                *height = Max(*height, bitmap.bitmap.height);
727          }
728          else if(guiApp.textMode && buttonStyle.bevel)
729          {
730             if(!initSize.w /**width*/)
731                *width = Max(*width, captionWidth + 8);
732             if(!initSize.h /**height*/)
733                *height = Max(*height, captionHeight + 16);
734          }
735          else
736          {
737             if(!initSize.w /**width*/)
738                *width = Max(*width, captionWidth + /*4*/12);
739             if(!initSize.h /**height*/)
740                *height = Max(*height, captionHeight + /*6*/ 8);
741          }
742       }
743       return true;
744    }
745
746
747    bool OnKeyUp(Key key, unichar character)
748    {
749       if(key == space || key.code == enter || key.code == keyPadEnter || (key == hotKey && !buttonStyle.keyRepeat))
750       {
751          if(this.key)
752          {
753             this.key = false;
754             /*return */OnLeftButtonUp(0,0, key.modifiers);
755             if(key == hotKey)
756                return false;
757          }
758       }
759       return true;
760    }
761
762    bool OnKeyDown(Key key, unichar character)
763    {
764       if(!key.alt && !key.ctrl)
765       {
766          switch(key.code)
767          {
768             case hotKey:
769                if(buttonStyle.keyRepeat) break;
770             case space:
771             case defaultKey:
772                this.key = true;
773                OnLeftButtonDown(0,0, key.modifiers);
774                if(key.code == hotKey)
775                   return true;
776                else
777                   return false;
778             case escape:
779                if(this.key)
780                {
781                   OnActivate(false, null, null, true);
782                   return false;
783                }
784                break;
785          }
786       }
787       return true;
788    }
789
790    bool OnKeyHit(Key key, unichar character)
791    {
792       switch(key)
793       {
794          case left: case up:
795             if(parent.CycleChildren(false, false, false, !(buttonStyle.bevelOver && buttonStyle.radio)))
796             {
797                if(buttonStyle.bevelOver && buttonStyle.radio)
798                {
799                   Button b = (Button)parent.activeChild;
800                   if(b && b != this && eClass_IsDerived(b._class, class(Button)) && b.buttonStyle.radio && b.buttonStyle.bevelOver)
801                   {
802                      b.OnLeftButtonDown(0,0,(Modifiers)key);   // Casting Key to Modifiers is bad
803                      b.OnLeftButtonUp(0,0,(Modifiers)key);
804                   }
805                }
806                return false;
807             }
808             break;
809          case right: case down:
810             if(parent.CycleChildren(true, false, false, !(buttonStyle.bevelOver && buttonStyle.radio)))
811             {
812                if(buttonStyle.bevelOver && buttonStyle.radio)
813                {
814                   Button b = (Button)parent.activeChild;
815                   if(b && b != this && eClass_IsDerived(b._class, class(Button)) && b.buttonStyle.radio && b.buttonStyle.bevelOver)
816                   {
817                      b.OnLeftButtonDown(0,0,(Modifiers)key);
818                      b.OnLeftButtonUp(0,0,(Modifiers)key);
819                   }
820                }
821                return false;
822             }
823             break;
824          case hotKey:
825             if(buttonStyle.keyRepeat)
826             {
827                this.key = true;
828                OnLeftButtonDown(0,0, key.modifiers);
829                this.key = false;
830                /*return */OnLeftButtonUp(0,0, key.modifiers);
831                return false;
832             }
833             break;
834       }
835       return true;
836    }
837 public:
838    void SetColor(ButtonState state, Color value)
839    {
840       colors[state] = value;
841    }
842
843    void SetTextColor(ButtonState state, Color value)
844    {
845       textColor[state] = value;
846    }
847
848    void RemoveRadio()
849    {
850       if(buttonStyle.radio)
851       {
852          RemoveResource(bitmaps[up]);
853          RemoveResource(bitmaps[down]);
854          RemoveResource(bitmaps[disabled]);
855          RemoveResource(bitmaps[upChecked]);
856          RemoveResource(bitmaps[downChecked]);
857          RemoveResource(bitmaps[disabledChecked]);
858
859          bitmaps[up] = null;
860          bitmaps[down] = null;
861          bitmaps[disabled] = null;
862          bitmaps[upChecked] = null;
863          bitmaps[downChecked] = null;
864          bitmaps[disabledChecked] = null;
865       }
866       buttonStyle.radio = false;
867    }
868
869    // Notifications
870    virtual bool Window::NotifyClicked(Button button, int x, int y, Modifiers mods);
871    virtual bool Window::NotifyDoubleClick(Button button, int x, int y, Modifiers mods);
872    virtual bool Window::NotifyPushed(Button button, int x, int y, Modifiers mods);
873    virtual bool Window::NotifyReleased(Button button, int x, int y, Modifiers mods);
874    virtual bool Window::NotifyMouseMove(Button button, int x, int y, Modifiers mods);
875    virtual bool Window::NotifyMouseOver(Button button, int x, int y, Modifiers mods);
876    virtual bool Window::NotifyMouseLeave(Button button, Modifiers mods);
877
878    // Properties
879    property bool isRadio
880    {
881       property_category $"Behavior"
882       set
883       {
884          if(value)
885          {
886             if(!bitmaps[up])
887             {
888                RemoveResource(bitmaps[up]);
889                RemoveResource(bitmaps[down]);
890                RemoveResource(bitmaps[disabled]);
891                RemoveResource(bitmaps[upChecked]);
892                RemoveResource(bitmaps[downChecked]);
893                RemoveResource(bitmaps[disabledChecked]);
894
895                bitmaps[up] = BitmapResource { };
896                bitmaps[down] = BitmapResource { };
897                bitmaps[disabled] = BitmapResource { };
898                bitmaps[upChecked] = BitmapResource { };
899                bitmaps[downChecked] = BitmapResource { };
900                bitmaps[disabledChecked] = BitmapResource { };
901                bitmaps[up].fileName = "<:ecere>elements/optionBoxUp.png";
902                bitmaps[down].fileName = "<:ecere>elements/optionBoxDown.png";
903                bitmaps[disabled].fileName = "<:ecere>elements/optionBoxDisabled.png";
904                bitmaps[upChecked].fileName = "<:ecere>elements/optionBoxSelectedUp.png";
905                bitmaps[downChecked].fileName = "<:ecere>elements/optionBoxSelectedDown.png";
906                bitmaps[disabledChecked].fileName = "<:ecere>elements/optionBoxDisabledSelected.png";
907
908                AddResource(bitmaps[up]);
909                AddResource(bitmaps[down]);
910                AddResource(bitmaps[disabled]);
911                AddResource(bitmaps[upChecked]);
912                AddResource(bitmaps[downChecked]);
913                AddResource(bitmaps[disabledChecked]);
914             }
915
916             buttonStyle.radio = true;
917             buttonStyle.checkBox = false;
918             buttonStyle.bevel = false;
919             opacity = 0;
920             drawBehind = true;
921          }
922          else
923          {
924             RemoveRadio();
925          }
926          OnLoadGraphics();
927
928          SetInitSize(initSize);
929       }
930       get { return buttonStyle.radio; }
931    };
932    property bool isCheckbox
933    {
934       property_category $"Behavior"
935       set
936       {
937          if(value)
938          {
939             buttonStyle.checkBox = true;
940             background = white;
941             RemoveRadio();
942             buttonStyle.radio = false;
943             buttonStyle.bevel = false;
944             opacity = 0;
945             drawBehind = true;
946          }
947          else
948             buttonStyle.checkBox = false;
949          OnLoadGraphics();
950
951          SetInitSize(initSize);
952       }
953       get { return buttonStyle.checkBox; }
954    };
955    property bool bevel
956    {
957       property_category $"Behavior"
958       set
959       {
960          if(value)
961          {
962             buttonStyle.bevel = true;
963             RemoveRadio();
964             buttonStyle.checkBox = false;
965             buttonStyle.radio = false;
966             buttonStyle.bevelOver = false;
967             opacity = 1;
968             drawBehind = false;
969          }
970          else
971          {
972             buttonStyle.bevel = false;
973             opacity = 0;
974             drawBehind = true;
975          }
976       }
977       get { return buttonStyle.bevel; }
978    };
979    property bool bevelOver
980    {
981       property_category $"Behavior"
982       set
983       {
984          if(value)
985          {
986             buttonStyle.bevelOver = true;
987             buttonStyle.offset = true;
988             buttonStyle.bevel = false;
989          }
990          else
991          {
992             buttonStyle.bevelOver = false;
993             buttonStyle.offset = false;
994          }
995       }
996       get { return buttonStyle.bevelOver; }
997    };
998    property bool toggle { property_category $"Behavior" set { buttonStyle.toggle = value; } get { return buttonStyle.toggle; } };
999    property bool checked
1000    {
1001       property_category $"Appearance"
1002       set
1003       {
1004          if(buttonStyle.toggle)
1005          {
1006             state = value ? down : up;
1007          }
1008          else
1009          {
1010             if(buttonStyle.radio && value)
1011             {
1012                Window parent = this.parent;
1013                if(parent)
1014                {
1015                   Window otherWindow;
1016                   for(otherWindow = parent.firstChild; otherWindow; otherWindow = otherWindow.next)
1017                   {
1018                      if(otherWindow != this && eClass_IsDerived(otherWindow._class, _class))
1019                      {
1020                         Button otherButton = (Button)otherWindow;
1021                         if(otherButton.buttonStyle.radio)
1022                            otherButton.checked = false;
1023                      }
1024                   }
1025                }
1026             }
1027             checked = value;
1028          }
1029          Update(null);
1030       }
1031       get { return buttonStyle.toggle ? (state == down) : checked; }
1032    };
1033    property Alignment alignment { property_category $"Appearance" set { alignment = value; } get { return alignment; } };
1034    property BitmapResource bitmap
1035    {
1036       property_category $"Appearance"
1037       set
1038       {
1039          AddResource(value);
1040          RemoveResource(bitmaps[up]);
1041          bitmaps[up] = value;
1042
1043          RemoveResource(bitmaps[over]);
1044          RemoveResource(bitmaps[down]);
1045          RemoveResource(bitmaps[disabled]);
1046          RemoveResource(bitmaps[upChecked]);
1047          RemoveResource(bitmaps[overChecked]);
1048          RemoveResource(bitmaps[downChecked]);
1049          RemoveResource(bitmaps[disabledChecked]);
1050
1051          bitmaps[over] = null;
1052          bitmaps[down] = null;
1053          bitmaps[disabled] = null;
1054          bitmaps[upChecked] = null;
1055          bitmaps[overChecked] = null;
1056          bitmaps[downChecked] = null;
1057          bitmaps[disabledChecked] = null;
1058
1059          /*
1060          if(buttonStyle.radio && !value)
1061          {
1062             AddResource((bitmaps[up] = BitmapResource { fileName = "<:ecere>elements/optionBox.png" }));
1063             AddResource((bitmaps[down] = BitmapResource { fileName = "<:ecere>elements/optionBox.png" }));
1064             AddResource((bitmaps[disabled] = BitmapResource { fileName = "<:ecere>elements/optionBoxDisabled.png" }));
1065             AddResource((bitmaps[upChecked] = BitmapResource { fileName = "<:ecere>elements/optionBoxSelected.png" }));
1066             AddResource((bitmaps[downChecked] = BitmapResource { fileName = "<:ecere>elements/optionBoxSelected.png" }));
1067             AddResource((bitmaps[disabledChecked] = BitmapResource { fileName = "<:ecere>elements/optionBoxDisabledSelected.png" }));
1068          }
1069          else */if(!bitmaps[disabled])
1070          {
1071             bitmaps[disabled] = BitmapResource { fileName = bitmap.fileName, grayed = true };
1072             AddResource(bitmaps[disabled]);
1073          }
1074       }
1075       get { return bitmaps[up]; }
1076    };
1077    property bool stayUp { property_category $"Behavior" set { buttonStyle.stayUp = value; } get { return buttonStyle.stayUp; } };
1078    property bool scaleBitmap { property_category $"Appearance" set { buttonStyle.scale = value; } get { return buttonStyle.scale; } };
1079    property bool keyRepeat { property_category $"Behavior" set { buttonStyle.keyRepeat = value; } get { return buttonStyle.keyRepeat; } };
1080    property unichar symbol { property_category $"Appearance" set { this.symbol = value; } get { return symbol; } };
1081    property bool ellipsis { property_category $"Appearance" set { buttonStyle.ellipsis = value; } get { return buttonStyle.ellipsis; } };
1082    property bool stayDown { property_category $"Behavior" set { buttonStyle.stayDown = value; } get { return buttonStyle.stayDown; } };
1083    property bool offset { property_category $"Behavior" set { buttonStyle.offset = value; } get { return buttonStyle.offset; } };
1084
1085    property ButtonState buttonState { get { return state; } set { state = value; } };
1086    property BoxAlignment bitmapAlignment { property_category $"Appearance" set { bitmapAlignment = value; } get { return bitmapAlignment; } };
1087 };