root/trunk/luigi/themes/dxut.d

Revision 51, 54.7 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/dxut.d -- A clone of the DXUT texture-based 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
26
27 module luigi.themes.dxut;
28
29 import luigi.base;
30 import luigi.theme;
31 import luigi.opengl;
32 import luigi.gldraw;
33 import luigi.font;
34 import luigi.event;
35 import luigi.gui;
36
37 import std.math : pow;
38 import std.perf;
39
40 version (useDerelict) {
41 import derelict.opengl.extension.arb.texture_compression;
42 } else {
43     private
44     {
45         PFNGLCOMPRESSEDTEXIMAGE3DARBPROC         glCompressedTexImage3DARB;
46         PFNGLCOMPRESSEDTEXIMAGE2DARBPROC         glCompressedTexImage2DARB;
47         PFNGLCOMPRESSEDTEXIMAGE1DARBPROC         glCompressedTexImage1DARB;
48         PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC      glCompressedTexSubImage3DARB;
49         PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC      glCompressedTexSubImage2DARB;
50         PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC      glCompressedTexSubImage1DARB;
51         PFNGLGETCOMPRESSEDTEXIMAGEARBPROC        glGetCompressedTexImageARB;
52
53         version(Windows)
54             import win32.wingdi; // for wgl funcs
55         else version(linux)
56             version = UsingGLX;
57         version(UsingGLX)
58             import x11.glx;
59         import string=std.string;
60
61         bool glBindExtFunc(void **ptr, char[] funcName)
62         {
63             version(Windows)
64                 *ptr = wglGetProcAddress(string.toStringz(funcName));
65             else version(UsingGLX)
66                 *ptr = glXGetProcAddress(string.toStringz(funcName));
67             return (*ptr !is null);
68         }
69     }
70 }
71
72 static const GLint GL_COMPRESSED_RGB_S3TC_DXT1_EXT  = 0x83F0;
73 static const GLint GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1;
74 static const GLint GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2;
75 static const GLint GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3;
76
77
78
79 void initialize_compressed_texture_extension()
80 {
81     bool allOk = true;
82     version(useDerelict) {
83         if (!ARBTextureCompression.load("GL_ARB_texture_compression"))
84             allOk=false;
85     }
86     else {
87         if(!glBindExtFunc(cast(void**)&glCompressedTexImage3DARB, "glCompressedTexImage3DARB"))
88             allOk = false;
89         if(!glBindExtFunc(cast(void**)&glCompressedTexImage2DARB, "glCompressedTexImage2DARB"))
90             allOk = false;
91         if(!glBindExtFunc(cast(void**)&glCompressedTexImage1DARB, "glCompressedTexImage1DARB"))
92             allOk = false;
93         if(!glBindExtFunc(cast(void**)&glCompressedTexSubImage3DARB, "glCompressedTexSubImage3DARB"))
94             allOk = false;
95         if(!glBindExtFunc(cast(void**)&glCompressedTexSubImage2DARB, "glCompressedTexSubImage2DARB"))
96             allOk = false;
97         if(!glBindExtFunc(cast(void**)&glCompressedTexSubImage1DARB, "glCompressedTexSubImage1DARB"))
98             allOk = false;
99         if(!glBindExtFunc(cast(void**)&glGetCompressedTexImageARB, "glGetCompressedTexImageARB"))
100             allOk = false;
101     }
102     if (!allOk) {
103         throw new Exception("Unable to initialize texture compression extension");
104     }
105 }
106
107 alias DXUTTheme Theme;
108
109 class DXUTTheme : ThemeBase
110 {
111     this() {
112         // Load texture first
113         try {
114             initialize_compressed_texture_extension();
115
116             char[] filepath = Luigi().find_resource_location(
117                 "dxutcontrols.dds",
118                 "themes/dxutcontrols.dds",
119                 "resource/dxutcontrols.dds"
120                 );
121             if (filepath) {
122                 loadCompressedTexture(filepath, m_texid);
123             } else {
124                 report_error("Couldn't find, or failed to load dxutcontrols.dds");
125                 return;
126             }
127
128             glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &m_texw);
129             glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &m_texh);
130         }
131         catch (Exception e) {
132             report_error(std.string.format("Couldn't load texture: ", e.toString));
133         }
134
135         m_default_font = new ASCIIBitmapFont();
136         m_timer = new PerformanceCounter;
137
138         // Register all known size and draw functions.
139         label_register();
140         button_register();
141         checkbox_register();
142         radiobutton_register();
143         textfield_register();
144         optionmenu_register();
145         slider_register();
146         scrollbar_register();
147         panel_register();
148         spinner_register();
149
150         sel_text_color.set(255,255,255);
151         sel_text_bkg_color.set(10,36,106);
152         sel_text_unfocus_bkg_color.set(176,176,176);
153
154     }
155     ~this() {
156         if (m_texid)
157             glDeleteTextures(1, &m_texid);
158     }
159
160     //----THEME DATA--------------------------------------------------------
161     class ThemeData {
162         // per-widget elements needed so we can blend objects independently
163         // a little wasteful, though, since most info does not change per-elem
164         Element[] elements;
165     }
166     ThemeData get_theme_data(Widget w) {
167         Object td;
168         if (w && (td=w.theme_instance_data)!is null) {
169             return cast(ThemeData)td;
170         }
171         else {
172             // This means we haven't made it yet
173             ThemeData td2 = new ThemeData;
174             w.theme_instance_data = td2;
175             auto pElements = w.classinfo in m_elements;
176             if (pElements!=null) {
177                 td2.elements = (*pElements).dup;
178             }
179             else {
180                 // this is an unknown class
181                 // -- find best match up class hierarchy
182                 auto cinfo = w.classinfo;
183                 while (cinfo != Widget.classinfo) {
184                     pElements = cinfo in m_elements;
185                     if (pElements) {
186                         td2.elements = (*pElements).dup;
187                         break;
188                     }
189                     cinfo = cinfo.base;
190                 }
191                 if (cinfo == Widget.classinfo) {
192                     // didn't find it. w is not a subclass of anything
193                     // we know how to draw.
194                     Element[] empty;
195                     m_elements[w.classinfo] = empty;
196                 }
197             }
198             return td2;
199         }
200     }
201     Element[] get_widget_elements(Widget w) {
202         return get_theme_data(w).elements;
203     }
204
205     //----GRPAHICS STATE------------------------------------------------------
206     override void begin_drawing(Rect r) {
207         m_timer.stop();
208         m_elapsedTime = m_timer.milliseconds / 1000.0;
209         m_timer.start();
210        
211         push_graphics_state(r);
212         glPushAttrib(GL_TEXTURE_BIT);
213
214         glEnable(GL_BLEND);
215         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
216         glBindTexture(GL_TEXTURE_2D,m_texid);
217         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
218         //glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
219
220         glMatrixMode(GL_TEXTURE);
221         glPushMatrix();
222         glLoadIdentity();
223         glScalef(1./m_texw, 1./m_texh,1);
224         glMatrixMode(GL_MODELVIEW);
225
226     }
227     override void end_drawing() {
228         glPopAttrib(); // TEXTURE_BIT
229         pop_graphics_state();
230         glMatrixMode(GL_TEXTURE);
231         glPopMatrix();
232         glMatrixMode(GL_MODELVIEW);
233     }
234
235     //============================================================================
236     // Widget drawing and size routines
237
238     //----HELPERS-----------------------------------------------------------------
239     Font get_font() { return m_default_font; }
240     bool set_color(float g)
241     {
242         glColor3f(g,g,g);
243         return true;
244     }
245     bool set_color(Color c)
246     {
247         glColor4ubv(c.ptr);
248         return c.a!=0;
249     }
250
251     enum TextAlign {
252         Center = 0x0,
253         Top = 0x1,
254         Bottom = 0x2,
255         Left = 0x4,
256         Right = 0x8,
257         TopLeft = Top|Left,
258         TopRight = Top|Right,
259         BottomLeft = Bottom|Left,
260         BottomRight = Bottom|Right,
261         LRMask = Right | Left,
262         TBMask = Top | Bottom,
263     }
264     /** Draw text so the upper left corner is at (x,y) */
265     void draw_text(Font font, char[] label, float x, float y)
266     {
267         Point trans = font.origin; trans.x+=x; trans.y+=y;
268         translate(trans);
269         font.draw_string(label);
270         untranslate(trans);
271     }
272     /** Draw text inside the rect with the indicated alignment */
273     void draw_text_rect(Font font, char[] txt, Rect r, TextAlign ta)
274     {
275         Point trans = font.origin; trans.x+=r.x; trans.y+=r.y;
276         TextAlign tbflags = ta & TextAlign.TBMask;
277         TextAlign lrflags = ta & TextAlign.LRMask;
278         if (lrflags != TextAlign.Left) {
279             Rect sr = font.string_rect(txt);
280             if (lrflags == TextAlign.Right) {
281                 trans.x += r.w - sr.w;
282             }
283             else if (lrflags == TextAlign.Center) {
284                 trans.x += (r.w - sr.w)/2;
285             }
286         }
287         if (lrflags != TextAlign.Top) {
288             float txth = font.height;
289             if (lrflags == TextAlign.Bottom) {
290                 trans.y += r.h - txth;
291             }
292             else if (lrflags == TextAlign.Center) {
293                 trans.y += (r.h - txth)/2;
294             }
295         }
296         translate(trans);
297         font.draw_string(txt);
298         untranslate(trans);
299     }
300     /** Draw text centered in the rectangle */
301     void draw_text_centered(Font font, char[] label, Rect rect)
302     {
303         draw_text_rect(font,label,rect,TextAlign.Center);
304     }
305
306     void draw_elem_texture(inout Element elem, inout Rect rect) {
307         if (elem.texColor.alpha == 0)
308             return;
309         glEnable(GL_TEXTURE_2D);
310         set_color(elem.texColor.current);
311         fill_rect(rect, elem.texRect);
312     }
313     void draw_elem_texture(inout Element elem, inout Rect rect, int[4] texRect) {
314         if (elem.texColor.alpha == 0)
315             return;
316         glEnable(GL_TEXTURE_2D);
317         set_color(elem.texColor.current);
318         fill_rect(rect, texRect);
319     }
320     void draw_elem_text(inout Element elem, char[] text, Rect rect,
321                         TextAlign al = TextAlign.Center, bool shadow=false)
322     {
323         if (elem.fontColor.alpha == 0)
324             return;
325         glDisable(GL_TEXTURE_2D);
326         if (shadow) {
327             set_color(0);
328             rect.x+=1; rect.y+=1;
329             draw_text_rect(elem.font, text, rect, al);
330             rect.x-=1; rect.y-=1;
331         }
332         set_color(elem.fontColor.current);
333         draw_text_rect(elem.font, text, rect, al);
334     }
335     //----LABEL---------------------------------------------------------------
336     void label_register() {
337         addHandlers!(Label)(&label_draw, &label_best_size);
338
339         Element[] elems = new Element[1];
340         Element e;
341         e.set_font( m_default_font );
342         e.fontColor.states[ WidgetState.Disabled ] = Color( 200, 200, 200, 200 );
343         elems[0] = e;
344         m_elements[Label.classinfo] = elems;
345     }
346     Size label_best_size(Widget widget, Size bounds) {
347         auto w = cast(Label)widget; assert(w);
348         Element[] elem = get_widget_elements(w);
349         Rect r = elem[0].font.string_rect(w.label);
350         return Size(r.width+2,r.height+2);
351     }
352     void label_draw(Widget widget) {
353         auto w = cast(Label)widget; assert(w);
354         WidgetState state = WidgetState.Normal;
355         if( !w.enabled)
356             state = WidgetState.Disabled;
357         Element[] elem = get_widget_elements(w);
358         elem[0].fontColor.blend( state, m_elapsedTime );
359         draw_elem_text( elem[0], w.label, w.rect, TextAlign.Center, true );
360     }
361
362     //----BUTTON--------------------------------------------------------------
363     void button_register() {
364         addHandlers!(Button)(&button_draw, &button_best_size, &button_min_size);
365         Element[] elems = new Element[2];
366         Element e;
367         // button layer
368         e.set_texture( m_texid, [0,0,136,54] );
369         e.fontColor.states[ WidgetState.Disabled ] = Color(200,200,200,200);
370         e.set_font( m_default_font );
371         e.texColor.states[ WidgetState.Normal ] = Color(255, 255, 255, 150);
372         e.texColor.states[ WidgetState.Depressed ] = Color(255, 255, 255, 200);
373         e.fontColor.states[ WidgetState.Rollover ] = Color(0, 0, 0, 255);
374         elems[0] = e;
375    
376         // fill layer
377         e.set_texture( m_texid, [136, 0, 272, 54], Color(255, 255, 255, 0) );
378         e.texColor.states[ WidgetState.Rollover ] = Color(255, 255, 255, 160);
379         e.texColor.states[ WidgetState.Depressed ] = Color(0, 0, 0, 60);
380         e.texColor.states[ WidgetState.Focus ] = Color(255, 255, 255, 30);
381         elems[1] = e;
382    
383         m_elements[Button.classinfo] = elems;
384     }
385     Size button_min_size(Widget widget, Size bounds) {
386         auto w = cast(Button)widget; assert(w);
387         Element[] elem = get_widget_elements(w);
388         Rect r = elem[0].font.string_rect(w.label);
389         return Size(r.width+20,r.height+8);
390     }
391     Size button_best_size(Widget widget, Size bounds) {
392         auto w = cast(Button)widget; assert(w);
393         Size minsz = button_min_size(w,bounds);
394         return Size(max(50.,minsz.w), max(22.,minsz.h));
395     }
396     void button_draw(Widget widget) {
397         auto w = cast(Button)widget; assert(w);
398         with (w) {
399             int dx = 0;
400             int dy = 0;
401             WidgetState iState = WidgetState.Normal;
402             float blendRate = 0.8f;
403             bool chk = is_toggle && checked;
404             if( !shown ) {
405                 iState = WidgetState.Hidden;
406             }
407             else if( !enabled ) {
408                 iState = WidgetState.Disabled;
409             }
410             else if( depressed )
411             {
412                 iState = WidgetState.Depressed;
413                 blendRate = 0.6;
414             }
415             else if( rollover )
416             {
417                 iState = WidgetState.Rollover;
418             }
419             else if( focused )
420             {
421                 iState = WidgetState.Focus;
422             }
423
424             if ( depressed ^ chk ) {
425                 iState = WidgetState.Depressed;
426                 dx = dy = 1;
427             }
428             if (!depressed && chk && rollover) {
429                 iState = WidgetState.Rollover;
430             }
431             if ( rollover ) {
432                 dx -= 1; dy -= 1;
433             }
434
435             Element[] elem = get_widget_elements(w);
436
437             Rect r = rect;
438             r.inset(1);
439             r.x += dx;
440             r.y += dy;
441
442             foreach(inout e; elem) {
443                 e.texColor.blend( iState, m_elapsedTime, blendRate);
444                 e.fontColor.blend( iState, m_elapsedTime, blendRate);
445                 draw_elem_texture(e, r);
446                 draw_elem_text(e, label, r);
447             }
448         }
449     }
450     //----CHECKBOX----------------------------------------------------------
451     void checkbox_register() {
452         addHandlers!(Checkbox)(&checkbox_draw, &checkbox_best_size, &checkbox_min_size);
453         Element[] elems = new Element[2];
454         Element e;
455
456         e.fontColor.states[ WidgetState.Rollover ] = Color(0, 0, 0, 255);
457         e.texColor.states[ WidgetState.Rollover ] = Color(255, 255, 255, 160);
458    
459         // check box
460         e.set_texture( m_texid, [0, 54, 27, 81] );
461         e.set_font( m_default_font, Color(255, 255, 255, 255) );
462         e.fontColor.states[ WidgetState.Disabled ] = Color( 200, 200, 200, 200 );
463         e.texColor.states[ WidgetState.Normal ] = Color(255, 255, 255, 150);
464         e.texColor.states[ WidgetState.Focus ] = Color(255, 255, 255, 200);
465         e.texColor.states[ WidgetState.Depressed ] = Color(255, 255, 255, 255);
466         elems[0] = e;
467
468         // Check
469         e.set_texture( m_texid, [27, 54, 54, 81] );
470         elems[1] = e;
471
472         m_elements[Checkbox.classinfo] = elems;
473     }
474     Size checkbox_min_size(Widget widget, Size bounds) {
475         auto w = cast(Button)widget; assert(w);
476         Element[] elem = get_widget_elements(w);
477         Point p0 = Point(elem[0].texRect[0], elem[0].texRect[1]);
478         Point p1 = Point(elem[0].texRect[2], elem[0].texRect[3]);
479         p1 -= p0;
480         Size btn = Size(lrint(p1.x/2),lrint(p1.y/2));
481         Size txt = elem[0].font.string_rect(w.label).size;
482
483         return Size(btn.width+3+txt.width+4,max(btn.height,txt.height));
484     }
485     Size checkbox_best_size(Widget widget, Size bounds) {
486         auto w = cast(Button)widget; assert(w);
487         Size minsz = checkbox_min_size(w,bounds);
488         return Size(max(50.,minsz.w), max(22.,minsz.h));
489     }
490     void checkbox_draw(Widget widget) {
491         auto w = cast(Button)widget; assert(w);
492         with (w) {
493             WidgetState iState = WidgetState.Normal;
494             if( !shown )
495                 iState = WidgetState.Hidden;
496             else if( !enabled )
497                 iState = WidgetState.Disabled;
498             else if( depressed )
499                 iState = WidgetState.Depressed;
500             else if( rollover )
501                 iState = WidgetState.Rollover;
502             else if( focused )
503                 iState = WidgetState.Focus;
504
505             float fBlendRate = ( iState == WidgetState.Depressed ) ? 0.0f : 0.8f;
506
507             Element[] elem = get_widget_elements(w);
508             elem[0].texColor.blend( iState, m_elapsedTime, fBlendRate );
509             elem[0].fontColor.blend( iState, m_elapsedTime, fBlendRate );
510
511             Rect