root/trunk/luigi/themes/std.d

Revision 51, 26.1 kB (checked in by baxissimo, 2 years ago)

Various small fixes and improvements.
event.d: added is_left_press et al properties (I found myselft getting confused as to whether 'left_down' mean it was a left down event or just that left was generally down. Maybe that should become left_is_down...

gui.d: made mixins refer to fully qualified names. Fixes problems with mixing-in in a different context.

theme stuff: Fixed dxut to not crash when presented with an unknown subclass.

Line 
1 //---------------------------------------------------------------------
2 /*
3   luigi/themes/std.d -- A standard Windows-classic like theme.
4
5   Copyright (C) 2006 William V. Baxter III
6
7   This software is provided 'as-is', without any express or implied
8   warranty.  In no event will the authors be held liable for any
9   damages arising from the use of this software.
10
11   Permission is granted to anyone to use this software for any
12   purpose, including commercial applications, and to alter it and
13   redistribute it freely, subject to the following restrictions:
14
15   1. The origin of this software must not be misrepresented; you must
16      not claim that you wrote the original software. If you use this
17      software in a product, an acknowledgment in the product
18      documentation would be appreciated but is not required.
19   2. Altered source versions must be plainly marked as such, and must
20      not be misrepresented as being the original software.
21   3. This notice may not be removed or altered from any source distribution.
22
23   William Baxter wbaxter@gmail.com
24 */
25 module luigi.themes.std;
26
27 import luigi.base;
28 import luigi.theme;
29 import luigi.opengl;
30 import luigi.themes.bitmaps;
31 import luigi.gldraw;
32 import gldraw = luigi.gldraw;
33 import luigi.font;
34 import luigi.event;
35 import luigi.gui;
36
37 alias StdTheme Theme;
38 class StdTheme : ThemeBase
39 {
40     this() {
41         // Register all known size and draw functions.
42         label_register();
43         button_register();
44         checkbox_register();
45         textfield_register();
46         slider_register();
47         panel_register();
48         spinner_register();
49         /*radiogroup_register();*/
50         radiobutton_register();
51
52         m_default_font = new ASCIIBitmapFont();
53
54         bkg_color.set(212,208,200);
55         text_color.set(0,0,0);
56
57         sel_text_color.set(255,255,255);
58         sel_text_bkg_color.set(10,36,106);
59         sel_text_unfocus_bkg_color.set(176,176,176);
60
61         hilite_color[0].set(64,64,64);
62         hilite_color[1].set(128,128,128);
63         hilite_color[2].set(255,255,255);
64
65         m_bitmaps = new StdBitmapSet;
66
67         // Some default colors
68         set_background_color(Overlay.classinfo, Color(0,0,0,0));
69     }
70
71     class ThemeData {
72         Color[char[]] colors;
73     }
74
75     // Public interface functions for users
76     Color get_theme_color(Widget w, char[] key, inout Color default_) {
77         ThemeData td = get_theme_data(w);
78         if (td && key in td.colors) {  return td.colors[key]; }
79         return get_theme_color(w.classinfo, key, default_);
80     }
81     Color get_theme_color(ClassInfo klass, char[] key, inout Color default_) {
82         // Search parents too?
83         ThemeData td = get_theme_data(klass);
84         if (td && key in td.colors) {  return td.colors[key]; }
85         return default_;
86     }
87
88     Color get_background_color(Widget w) {
89         return get_theme_color(w, "background", bkg_color);
90     }
91     Color get_background_color(ClassInfo klass) {
92         return get_theme_color(klass, "background", bkg_color);
93     }
94     void set_background_color(Widget w, Color c) {
95         getadd_theme_data(w).colors["background"] = c;
96     }
97     void set_background_color(ClassInfo klass, Color c) {
98         getadd_theme_data(klass).colors["background"] = c;
99     }
100
101     //============================================================================
102     // Built-in Drawing and size routines
103
104     //----HELPERS-----------------------------------------------------------------
105     Font get_font() { return m_default_font; }
106     bool set_color(float g)
107     {
108         glColor3f(g,g,g);
109         return true;
110     }
111     bool set_color(Color c)
112     {
113         glColor4ubv(c.ptr);
114         return c.a!=0;
115     }
116
117     //----------------------------------------------------------------------
118     ThemeData get_theme_data(Widget w) {
119         Object td=null;
120         if (w && (td=w.theme_instance_data)!is null) {
121             return cast(ThemeData)td;
122         }
123         return null;
124     }
125     ThemeData getadd_theme_data(Widget w) {
126         Object td;
127         if (w && (td=w.theme_instance_data)!is null) {
128             return cast(ThemeData)td;
129         }
130         else {
131             ThemeData td2 = new ThemeData;
132             w.theme_instance_data = td2;
133             return td2;
134         }
135     }
136     ThemeData add_theme_data(Widget w) {
137         ThemeData td = new ThemeData;
138         w.theme_instance_data = td;
139         return td;
140     }
141
142     ThemeData get_theme_data(ClassInfo klass) {
143         if (klass in m_classThemeData) {
144             return m_classThemeData[klass];
145         }
146         return null;
147     }
148     ThemeData getadd_theme_data(ClassInfo klass) {
149         if (klass in m_classThemeData) {
150             return m_classThemeData[klass];
151         }
152         else {
153             ThemeData td = new ThemeData;
154             m_classThemeData[klass] = td;
155             return td;
156         }
157     }
158     ThemeData add_theme_data(ClassInfo klass) {
159         ThemeData td = new ThemeData;
160         m_classThemeData[klass] = td;
161         return td;
162     }
163     //----------------------------------------------------------------------
164
165
166     void draw_text_plain(char[] label, float x, float y)
167     {
168         alias m_default_font font;
169         Point trans = font.origin; trans.x+=x; trans.y+=y;
170         translate(trans);
171         font.draw_string(label);
172         untranslate(trans);
173     }
174
175     /** Draw text so the upper left corner is at (x,y) */
176     void draw_text(char[] label, float x, float y, bool enabled=true)
177     {
178         alias m_default_font font;
179         Point trans = font.origin; trans.x+=x; trans.y+=y;
180         translate(trans);
181         if (!enabled) {
182             set_color( hilite_color[2] );
183             translate(1,1);
184               font.draw_string(label);
185             translate(-1,-1);
186         }
187         set_color( enabled ? text_color : hilite_color[0] );
188         font.draw_string(label);
189         untranslate(trans);
190     }
191
192     /** Draw text so the first character's origin is at x,y.  Not so useful. */
193     void draw_label_text(char[] label, float x, float y, bool enabled=true)
194     {
195         alias m_default_font font;
196         translate(x,y);
197         if (!enabled) {
198             set_color( hilite_color[2] );
199             translate(1,1);
200             font.draw_string(label);
201             translate(-1,-1);
202         }
203         set_color( enabled ? text_color : hilite_color[0] );
204         font.draw_string(label);
205         translate(-x,-y);
206     }
207
208
209     //----OVERLAY-----------------------------------------------------------------
210     void begin_drawing(Rect r) {
211         gldraw.push_graphics_state(r);
212     }
213     void end_drawing() {
214         gldraw.pop_graphics_state();
215     }
216     //----PANEL-------------------------------------------------------------------
217     void panel_register() {
218         m_drawFuncs.add(Panel.classinfo, &panel_draw);
219     }
220
221     void panel_draw(Widget widget) {
222         auto w = cast(Panel)widget; assert(w);
223         if (set_color(get_background_color(widget)))
224             fill_rect(w.rect);
225     }
226
227     //----LABEL-------------------------------------------------------------------
228     void label_register() {
229         addHandlers!(Label)(&label_draw, &label_best_size);
230     }
231     Size label_best_size(Widget widget, Size bounds) {
232         auto w = cast(Label)widget; assert(w);
233         Rect r = m_default_font.string_rect(w.label);
234         return Size(r.width+2,r.height+2);
235     }
236     void label_draw(Widget widget) {
237         auto w = cast(Label)widget; assert(w);
238         set_color(get_background_color(widget));
239         fill_rect(w.rect);
240         with (w) {
241             alias m_default_font f;
242            
243             Rect sr = f.string_rect(label);
244
245             float x = w.rect.x + min(0.0,(w.rect.w-sr.w)/2);
246             float y = w.rect.y + min(0.0,(w.rect.h-sr.h)/2);
247             draw_text(label,x,y,enabled);
248         }
249     }
250
251     //----BUTTON------------------------------------------------------------------
252     void button_register() {
253         addHandlers!(Button)(&button_draw, &button_best_size, &button_min_size);
254     }
255     Size button_min_size(Widget widget, Size bounds) {
256         auto w = cast(Button)widget; assert(w);
257         Rect r = m_default_font.string_rect(w.label);
258         return Size(r.width+5,r.height+5);
259     }
260     Size button_best_size(Widget widget, Size bounds) {
261         auto w = cast(Button)widget; assert(w);
262         Size minsz = button_min_size(w,bounds);
263         return Size(max(50.,minsz.w), max(22.,minsz.h));
264     }
265     void button_draw(Widget widget) {
266         auto w = cast(Button)widget; assert(w);
267         with (w) {
268             alias m_default_font f;
269            
270             set_color(get_background_color(widget));
271             fill_rect(rect);
272             if (w.depressed ^ w.checked) {
273                 stroke_sunken_rect(
274                     rect,bkg_color,hilite_color[0],hilite_color[1],hilite_color[2]
275                     );
276             } else {
277                 stroke_raised_rect(
278                     rect,bkg_color,hilite_color[0],hilite_color[1],hilite_color[2]
279                     );
280             }
281
282             Rect sr = f.string_rect(label);
283
284             float shift = (w.depressed^w.checked)?1:0;
285             float x = w.rect.x+(w.rect.w-sr.w)/2 + shift;
286             float y = w.rect.y+(w.rect.h-sr.h)/2 + shift;
287             draw_text(label, x, y, enabled);
288
289             if (w.focused()) {
290                 set_color(hilite_color[0]);
291                 Rect r = w.rect();
292                 r.inset(3);
293                 glEnable(GL_LINE_STIPPLE);
294                 glLineStipple( 1, 0x5555 );
295                 stroke_rect(r);
296                 glDisable(GL_LINE_STIPPLE);
297             }
298         }
299     }
300
301     //----CHECKBOX----------------------------------------------------------------
302     void checkbox_register() {
303         addHandlers!(Checkbox)(&checkbox_draw, &checkbox_best_size);
304     }
305     //Size checkbox_min_size(Widget widget, Size bounds) {
306     //    auto w = cast(Checkbox)widget; assert(w);
307     //    return Size(0,0);
308     //}
309     Size checkbox_best_size(Widget widget, Size bounds) {
310         // Drawing basics
311         //    [x] the label string
312         // we want 2 pixel clearance around string, 1 pixel around box
313
314         auto w = cast(Checkbox)widget; assert(w);
315         Size cbsize = m_bitmaps[StdBitmaps.Checkbox_On].size;
316         Size text_size = m_default_font.string_rect( w.label ).size;
317         Size sz;
318         sz.width = (cbsize.width+2) + (text_size.width+4);
319         sz.height = max(cbsize.height+2, text_size.height+4);
320         return sz;
321     }
322     void checkbox_draw(Widget widget) {
323         alias m_default_font font;
324         auto w = cast(Checkbox)widget; assert(w);
325         set_color(get_background_color(widget));
326         fill_rect(w.rect);
327         with (w)
328         {
329             Size cbsize = m_bitmaps[StdBitmaps.Checkbox_On].size;
330             Point cbpos = rect.pos;
331             cbpos.x += 1;
332             cbpos.y = rect.y+(rect.h-cbsize.h)/2;
333             if (value != 0 ^ depressed) {
334                 if ( enabled )
335                     m_bitmaps.draw( StdBitmaps.Checkbox_On, cbpos );
336                 else
337                     m_bitmaps.draw( StdBitmaps.Checkbox_On_Dis, cbpos );
338             }
339             else {
340                 if ( enabled )
341                     m_bitmaps.draw( StdBitmaps.Checkbox_Off, cbpos );
342                 else
343                     m_bitmaps.draw( StdBitmaps.Checkbox_Off_Dis, cbpos );     
344             }
345
346             Rect text_rect = font.string_rect( label );
347             text_rect.x = rect.x + (cbsize.w+2)+2;
348             text_rect.y = rect.y + 2;
349             {
350                 if ( focused ) {
351                     glEnable( GL_LINE_STIPPLE );
352                     glLineStipple( 1, 0x5555 );
353                     set_color( hilite_color[0] );
354
355                     Rect focus_rect = text_rect;
356                     focus_rect.inset(-1);
357                     stroke_rect(focus_rect);
358  
359                     glDisable( GL_LINE_STIPPLE );
360                 }
361             }
362            
363             draw_text( label, text_rect.x, text_rect.y, enabled);
364         }
365     }
366    
367     //----TEXTFIELD-------------------------------------------------------------
368     void textfield_register() {
369         addHandlers!(TextField)(&textfield_draw, &textfield_best_size, &textfield_min_size);
370         addEventHandlers!(TextField)(&textfield_mouse_button, &textfield_mouse_move);
371     }
372     const int textfield_base_scroll = 3;
373
374     /** Returns a double indicating the position of the x value.
375         If exactly on a char boundary then the return value will be
376         integral, if right in the middle of a character it will be
377         0.5 offset from an integer.
378     */
379     void textfield_get_rects(TextField w, inout Rect entry, inout Rect txt)
380     {
381         entry = w.rect;
382         entry.y+=1;
383         entry.h-=2;
384         txt = entry;
385         txt.inset(1); txt.w-=1; txt.h-=1;
386     }
387     float textfield_get_char_pos(TextField txtf, float clickx)
388     {
389         Rect entry_rect,inside_rect;
390         textfield_get_rects(txtf, entry_rect, inside_rect);
391         alias m_default_font font;
392         float offset = inside_rect.x + textfield_base_scroll + txtf.xscroll_offset;
393         float x = offset;
394         float lastx = x-10;
395         int imax = txtf.text.length;
396         // binary search would of course be better here
397         int i;
398         for(i=0; x<clickx && i<imax; x=offset+font.string_rect(txtf.text[0..++i]).w)
399         {
400             lastx = x;
401         }
402         if (i==0) return 0;
403         else if (i>=imax) return imax;
404        
405         float frac = (clickx-lastx)/(x-lastx);
406         return i-1.0+frac;
407
408     }
409     Size textfield_min_size(Widget widget, Size bounds) {
410         auto w = cast(TextField)widget; assert(w);
411         Size sz = Size(20,2);
412         return sz;
413     }
414     Size textfield_best_size(Widget widget, Size bounds) {
415         auto w = cast(TextField)widget; assert(w);
416         Size sz = textfield_min_size(w,bounds);
417         return Size(max(130.,sz.x),max(20.,sz.h));
418     }
419     void textfield_draw(Widget widget)
420     {
421         auto txtf = cast(TextField)widget; assert(txtf);
422         set_color(get_background_color(widget));
423         fill_rect(txtf.rect);
424         translate(txtf.rect.x, txtf.rect.y);
425
426         alias m_default_font font;
427         with (txtf) {
428             Rect entry_rect,inside_rect;
429             textfield_get_rects(txtf, entry_rect, inside_rect);
430             entry_rect.pos -= rect.pos;
431             inside_rect.pos -= rect.pos;
432
433             set_color(hilite_color[2]);
434             fill_rect( entry_rect );
435
436             // Find where to draw the text
437
438             // Clip to insides of entry rect
439             push_clip_rect( inside_rect );
440
441             float offset = inside_rect.x + textfield_base_scroll + xscroll_offset;
442             float sel_offset = font.string_rect(text[0..sel_begin]).width;
443             Size sel_size = font.string_rect(text[sel_begin..sel_end]).size;
444
445             // make sure active end of selection is visible
446             {
447                 float sel_x = offset + sel_offset;
448                 if (sel_active==1) sel_x += sel_size.width;
449                 float dx;
450                 if ((dx=sel_x-inside_rect.x)<0) {
451                     offset-=dx-2; xscroll_offset-=dx-2;
452                 }
453                 else if ((dx=sel_x-inside_rect.x2)>0) {
454                     offset-=dx+1; xscroll_offset-=dx+1;
455                 }
456             }
457
458             if (selection_length)
459             {
460                 Rect rsel;
461                 rsel.x = offset + sel_offset;
462                 rsel.y = 4;
463                 rsel.size = sel_size;
464
465                 // draw unselected first part of text
466                 if (sel_begin!=0) {
467                     draw_text(text[0..sel_begin], offset, inside_rect.y);
468                     offset += sel_offset;
469                 }
470                
471                 // Draw selection bkg
472                 if (focused) {
473                     set_color(sel_text_bkg_color);
474                 } else {
475                     set_color(sel_text_unfocus_bkg_color);
476                 }
477                 fill_rect( rsel );
478
479                 // draw selected text
480                 set_color(sel_text_color);
481                 draw_text_plain(text[sel_begin..sel_end],
482                                 offset,
483                                 inside_rect.y);
484                 offset += rsel.width;
485
486                 // draw rest of unselected text
487                 if (sel_end!=text.length) {
488                     draw_text(text[sel_end..$], offset, inside_rect.y);
489                 }
490             }
491             else {
492                 // Draw all the text in one go
493                 draw_text(text, offset, inside_rect.y);
494             }
495  
496             // Draw some outline rects;
497             float w = rect.width-1;
498             float h = rect.height-1;
499
500             // Caret
501             if ( enabled && selection_length == 0 ) {
502                 if (focused && selection_length==0)
503                 {
504                     float x = offset + sel_offset;
505                     set_color(hilite_color[0]);
506                     glBegin( GL_LINE_LOOP );
507                     glVertex2f( x, 0 + 4 );
508                     glVertex2f( x, 0 + 4 );
509                     glVertex2f( x, 0 + h - 3 );
510                     glVertex2f( x, 0 + h - 3 );
511                     glEnd();
512                 }
513             }
514             pop_clip_rect();
515
516             translate(0.5,0.5);
517             glBegin( GL_LINES );
518             set_color(hilite_color[1]);
519             glVertex2f( entry_rect.x, 0 );     glVertex2f( w, 0 );
520             glVertex2f( entry_rect.x, 0 );     glVertex2f( entry_rect.x, h );     
521
522             set_color(hilite_color[2]);
523             glVertex2f( entry_rect.x, h );      glVertex2f( w, h );
524             glVertex2f( w, h );                 glVertex2f( w, 0 );
525
526             if ( enabled )
527                 set_color( hilite_color[0] );
528             else
529                 set_color( .25 );
530
531             glVertex2f( entry_rect.x+1, 1 );     glVertex2f( w-1, 1 );
532             glVertex2f( entry_rect.x+1,</