1 //********************************************************************************************
2 //*
3 //*    This file is part of Egoboo.
4 //*
5 //*    Egoboo is free software: you can redistribute it and/or modify it
6 //*    under the terms of the GNU General Public License as published by
7 //*    the Free Software Foundation, either version 3 of the License, or
8 //*    (at your option) any later version.
9 //*
10 //*    Egoboo is distributed in the hope that it will be useful, but
11 //*    WITHOUT ANY WARRANTY; without even the implied warranty of
12 //*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 //*    General Public License for more details.
14 //*
15 //*    You should have received a copy of the GNU General Public License
16 //*    along with Egoboo.  If not, see <http://www.gnu.org/licenses/>.
17 //*
18 //********************************************************************************************
19 
20 /// @file ui.c
21 /// @brief The Egoboo GUI
22 /// @details A basic library for implementing user interfaces, based off of Casey Muratori's
23 /// IMGUI.  (https://mollyrocket.com/forums/viewtopic.php?t=134)
24 
25 #include "ui.h"
26 #include "graphic.h"
27 
28 #include "ogl_debug.h"
29 #include "SDL_extensions.h"
30 
31 #include <string.h>
32 #include <SDL_opengl.h>
33 
34 //--------------------------------------------------------------------------------------------
35 //--------------------------------------------------------------------------------------------
36 
37 /// The data to describe the UI state
38 struct UiContext
39 {
40     // Tracking control focus stuff
41     ui_id_t active;
42     ui_id_t hot;
43 
44     // Basic mouse state
45     float mouseX, mouseY;
46     int   mouseReleased;
47     int   mousePressed;
48 
49     STRING defaultFontName;
50     float  defaultFontSize;
51     Font  *defaultFont;
52     Font  *activeFont;
53 
54     // virtual window
55     float vw, vh, ww, wh;
56 
57     // define the forward transform
58     float aw, ah, bw, bh;
59 
60     // define the inverse transform
61     float iaw, iah, ibw, ibh;
62 };
63 
64 static struct UiContext ui_context;
65 
66 GLfloat ui_white_color[]  = {1.00f, 1.00f, 1.00f, 1.00f};
67 
68 GLfloat ui_active_color[]  = {0.00f, 0.00f, 0.90f, 0.60f};
69 GLfloat ui_hot_color[]     = {0.54f, 0.00f, 0.00f, 1.00f};
70 GLfloat ui_normal_color[]  = {0.66f, 0.00f, 0.00f, 0.60f};
71 
72 GLfloat ui_active_color2[] = {0.00f, 0.45f, 0.45f, 0.60f};
73 GLfloat ui_hot_color2[]    = {0.00f, 0.28f, 0.28f, 1.00f};
74 GLfloat ui_normal_color2[] = {0.33f, 0.00f, 0.33f, 0.60f};
75 
76 static void ui_virtual_to_screen( float vx, float vy, float *rx, float *ry );
77 static void ui_screen_to_virtual( float rx, float ry, float *vx, float *vy );
78 
79 //--------------------------------------------------------------------------------------------
80 //--------------------------------------------------------------------------------------------
81 // Core functions
ui_begin(const char * default_font,int default_font_size)82 int ui_begin( const char *default_font, int default_font_size )
83 {
84     // initialize the font handler
85     fnt_init();
86 
87     memset( &ui_context, 0, sizeof( ui_context ) );
88 
89     ui_context.active = ui_context.hot = UI_Nothing;
90 
91     ui_context.defaultFontSize = default_font_size;
92     strncpy( ui_context.defaultFontName, default_font, SDL_arraysize( ui_context.defaultFontName ) );
93 
94     ui_set_virtual_screen( sdl_scr.x, sdl_scr.y, sdl_scr.x, sdl_scr.y );
95 
96     return 1;
97 }
98 
99 //--------------------------------------------------------------------------------------------
ui_end()100 void ui_end()
101 {
102     // clear out the default font
103     if ( NULL != ui_context.defaultFont )
104     {
105         fnt_freeFont( ui_context.defaultFont );
106         ui_context.defaultFont = NULL;
107     }
108 
109     // clear out the active font
110     ui_context.activeFont = NULL;
111 
112     memset( &ui_context, 0, sizeof( ui_context ) );
113 }
114 
115 //--------------------------------------------------------------------------------------------
ui_Reset()116 void ui_Reset()
117 {
118     ui_context.active = ui_context.hot = UI_Nothing;
119 }
120 
121 //--------------------------------------------------------------------------------------------
ui_handleSDLEvent(SDL_Event * evt)122 bool_t ui_handleSDLEvent( SDL_Event *evt )
123 {
124     bool_t handled;
125 
126     if ( NULL == evt ) return bfalse;
127 
128     handled = btrue;
129     switch ( evt->type )
130     {
131         case SDL_MOUSEBUTTONDOWN:
132             ui_context.mouseReleased = 0;
133             ui_context.mousePressed = 1;
134 
135             break;
136 
137         case SDL_MOUSEBUTTONUP:
138             ui_context.mousePressed = 0;
139             ui_context.mouseReleased = 1;
140 
141             break;
142 
143         case SDL_MOUSEMOTION:
144             // convert the screen coordinates to our "virtual coordinates"
145             ui_screen_to_virtual( evt->motion.x, evt->motion.y, &( ui_context.mouseX ), &( ui_context.mouseY ) );
146             break;
147 
148         case SDL_VIDEORESIZE:
149             if ( SDL_VIDEORESIZE == evt->resize.type )
150             {
151                 // the video has been resized, if the game is active, the
152                 // view matrix needs to be recalculated and possibly the
153                 // auto-formatting for the menu system and the ui system must be
154                 // recalculated
155 
156                 // grab all the new SDL screen info
157                 SDLX_Get_Screen_Info( &sdl_scr, SDL_FALSE );
158 
159                 // set the ui's virtual screen size based on the graphic system's
160                 // configuration
161                 gfx_set_virtual_screen( &gfx );
162             }
163             break;
164 
165         default:
166             handled = bfalse;
167     }
168 
169     return handled;
170 }
171 
172 //--------------------------------------------------------------------------------------------
ui_beginFrame(float deltaTime)173 void ui_beginFrame( float deltaTime )
174 {
175     // do not use the ATTRIB_PUSH macro, since the glPopAttrib() is in a different function
176     GL_DEBUG( glPushAttrib )( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT );
177 
178     // don't worry about hidden surfaces
179     GL_DEBUG( glDisable )( GL_DEPTH_TEST );                                    // GL_ENABLE_BIT
180 
181     // draw draw front and back faces of polygons
182     GL_DEBUG( glDisable )( GL_CULL_FACE );                                     // GL_ENABLE_BIT
183 
184     GL_DEBUG( glEnable )( GL_TEXTURE_2D );                                     // GL_ENABLE_BIT
185 
186     // use normal alpha blending
187     GL_DEBUG( glEnable )( GL_BLEND );                                          // GL_ENABLE_BIT
188     GL_DEBUG( glBlendFunc )( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );           // GL_COLOR_BUFFER_BIT
189 
190     // do not display the completely transparent portion
191     GL_DEBUG( glEnable )( GL_ALPHA_TEST );                                     // GL_ENABLE_BIT
192     GL_DEBUG( glAlphaFunc )( GL_GREATER, 0.0f );                               // GL_COLOR_BUFFER_BIT
193 
194     GL_DEBUG( glViewport )( 0, 0, sdl_scr.x, sdl_scr.y );                      // GL_VIEWPORT_BIT
195 
196     // Set up an ortho projection for the gui to use.  Controls are free to modify this
197     // later, but most of them will need this, so it's done by default at the beginning
198     // of a frame
199 
200     // store the GL_PROJECTION matrix (this stack has a finite depth, minimum of 32)
201     GL_DEBUG( glMatrixMode )( GL_PROJECTION );
202     GL_DEBUG( glPushMatrix )();
203     GL_DEBUG( glLoadIdentity )();
204     GL_DEBUG( glOrtho )( 0, sdl_scr.x, sdl_scr.y, 0, -1, 1 );
205 
206     // store the GL_MODELVIEW matrix (this stack has a finite depth, minimum of 32)
207     GL_DEBUG( glMatrixMode )( GL_MODELVIEW );
208     GL_DEBUG( glLoadIdentity )();
209 
210     // hotness gets reset at the start of each frame
211     ui_context.hot = UI_Nothing;
212 }
213 
214 //--------------------------------------------------------------------------------------------
ui_endFrame()215 void ui_endFrame()
216 {
217     // Restore the GL_PROJECTION matrix
218     GL_DEBUG( glMatrixMode )( GL_PROJECTION );
219     GL_DEBUG( glPopMatrix )();
220 
221     // Restore the GL_MODELVIEW matrix
222     GL_DEBUG( glMatrixMode )( GL_MODELVIEW );
223     GL_DEBUG( glLoadIdentity )();
224 
225     // Re-enable any states disabled by gui_beginFrame
226     // do not use the ATTRIB_POP macro, since the glPushAttrib() is in a different function
227     GL_DEBUG( glPopAttrib )();
228 
229     // Clear input states at the end of the frame
230     ui_context.mousePressed = ui_context.mouseReleased = 0;
231 }
232 
233 //--------------------------------------------------------------------------------------------
234 // Utility functions
ui_mouseInside(float vx,float vy,float vwidth,float vheight)235 int ui_mouseInside( float vx, float vy, float vwidth, float vheight )
236 {
237     float vright, vbottom;
238 
239     vright  = vx + vwidth;
240     vbottom = vy + vheight;
241     if ( vx <= ui_context.mouseX && vy <= ui_context.mouseY && ui_context.mouseX <= vright && ui_context.mouseY <= vbottom )
242     {
243         return 1;
244     }
245 
246     return 0;
247 }
248 
249 //--------------------------------------------------------------------------------------------
ui_setactive(ui_id_t id)250 void ui_setactive( ui_id_t id )
251 {
252     ui_context.active = id;
253 }
254 
255 //--------------------------------------------------------------------------------------------
ui_sethot(ui_id_t id)256 void ui_sethot( ui_id_t id )
257 {
258     ui_context.hot = id;
259 }
260 
261 //--------------------------------------------------------------------------------------------
ui_setWidgetactive(ui_Widget_t * pw)262 void ui_setWidgetactive( ui_Widget_t * pw )
263 {
264     if ( NULL == pw )
265     {
266         ui_context.active = UI_Nothing;
267     }
268     else
269     {
270         ui_context.active = pw->id;
271 
272         pw->timeout = egoboo_get_ticks() + 100;
273         if ( HAS_SOME_BITS( pw->mask, UI_BITS_CLICKED ) )
274         {
275             // use exclusive or to flip the bit
276             pw->state ^= UI_BITS_CLICKED;
277         };
278     };
279 }
280 
281 //--------------------------------------------------------------------------------------------
ui_setWidgethot(ui_Widget_t * pw)282 void ui_setWidgethot( ui_Widget_t * pw )
283 {
284     if ( NULL == pw )
285     {
286         ui_context.hot = UI_Nothing;
287     }
288     else if (( ui_context.active == pw->id || ui_context.active == UI_Nothing ) )
289     {
290         if ( pw->timeout < egoboo_get_ticks() )
291         {
292             pw->timeout = egoboo_get_ticks() + 100;
293 
294             if ( HAS_SOME_BITS( pw->mask, UI_BITS_MOUSEOVER ) && ui_context.hot != pw->id )
295             {
296                 // use exclusive or to flip the bit
297                 pw->state ^= UI_BITS_MOUSEOVER;
298             };
299         };
300 
301         // Only allow hotness to be set if this control, or no control is active
302         ui_context.hot = pw->id;
303     }
304 }
305 
306 //--------------------------------------------------------------------------------------------
ui_getFont()307 Font* ui_getFont()
308 {
309     return ( NULL != ui_context.activeFont ) ? ui_context.activeFont : ui_context.defaultFont;
310 }
311 
312 //--------------------------------------------------------------------------------------------
ui_setFont(Font * font)313 Font* ui_setFont( Font * font )
314 {
315     ui_context.activeFont = font;
316 
317     return ui_context.activeFont;
318 }
319 
320 //--------------------------------------------------------------------------------------------
321 // Behaviors
ui_buttonBehavior(ui_id_t id,float vx,float vy,float vwidth,float vheight)322 ui_buttonValues ui_buttonBehavior( ui_id_t id, float vx, float vy, float vwidth, float vheight )
323 {
324     ui_buttonValues result = BUTTON_NOCHANGE;
325 
326     // If the mouse is over the button, try and set hotness so that it can be cursor_clicked
327     if ( ui_mouseInside( vx, vy, vwidth, vheight ) )
328     {
329         ui_sethot( id );
330     }
331 
332     // Check to see if the button gets cursor_clicked on
333     if ( ui_context.active == id )
334     {
335         if ( ui_context.mouseReleased == 1 )
336         {
337             if ( ui_context.hot == id ) result = BUTTON_UP;
338 
339             ui_setactive( UI_Nothing );
340         }
341     }
342     else if ( ui_context.hot == id )
343     {
344         if ( ui_context.mousePressed == 1 )
345         {
346             if ( ui_context.hot == id ) result = BUTTON_DOWN;
347 
348             ui_setactive( id );
349         }
350     }
351 
352     return result;
353 }
354 
355 //--------------------------------------------------------------------------------------------
ui_WidgetBehavior(ui_Widget_t * pWidget)356 ui_buttonValues ui_WidgetBehavior( ui_Widget_t * pWidget )
357 {
358     ui_buttonValues result = BUTTON_NOCHANGE;
359 
360     // If the mouse is over the button, try and set hotness so that it can be cursor_clicked
361     if ( ui_mouseInside( pWidget->vx, pWidget->vy, pWidget->vwidth, pWidget->vheight ) )
362     {
363         ui_setWidgethot( pWidget );
364     }
365 
366     // Check to see if the button gets cursor_clicked on
367     if ( ui_context.active == pWidget->id )
368     {
369         if ( ui_context.mouseReleased == 1 )
370         {
371             // mouse button up
372             if ( ui_context.active == pWidget->id ) result = BUTTON_UP;
373 
374             ui_setWidgetactive( NULL );
375         }
376     }
377     else if ( ui_context.hot == pWidget->id )
378     {
379         if ( ui_context.mousePressed == 1 )
380         {
381             // mouse button down
382             if ( ui_context.hot == pWidget->id ) result = BUTTON_DOWN;
383 
384             ui_setWidgetactive( pWidget );
385         }
386     }
387 
388     return result;
389 }
390 
391 //--------------------------------------------------------------------------------------------
392 // Drawing
ui_drawButton(ui_id_t id,float vx,float vy,float vwidth,float vheight,GLXvector4f pcolor)393 void ui_drawButton( ui_id_t id, float vx, float vy, float vwidth, float vheight, GLXvector4f pcolor )
394 {
395     float x1, x2, y1, y2;
396 
397     GLXvector4f color_1 = { 0.0f, 0.0f, 0.9f, 0.6f };
398     GLXvector4f color_2 = { 0.54f, 0.0f, 0.0f, 1.0f };
399     GLXvector4f color_3 = { 0.66f, 0.0f, 0.0f, 0.6f };
400 
401     // Draw the button
402     GL_DEBUG( glDisable )( GL_TEXTURE_2D );
403 
404     if ( NULL == pcolor )
405     {
406         if ( ui_context.active != UI_Nothing && ui_context.active == id && ui_context.hot == id )
407         {
408             pcolor = color_1;
409         }
410         else if ( ui_context.hot != UI_Nothing && ui_context.hot == id )
411         {
412             pcolor = color_2;
413         }
414         else
415         {
416             pcolor = color_3;
417         }
418     }
419 
420     // convert the virtual coordinates to screen coordinates
421     ui_virtual_to_screen( vx, vy, &x1, &y1 );
422     ui_virtual_to_screen( vx + vwidth, vy + vheight, &x2, &y2 );
423 
424     GL_DEBUG( glColor4fv )( pcolor );
425     GL_DEBUG( glBegin )( GL_QUADS );
426     {
427         GL_DEBUG( glVertex2f )( x1, y1 );
428         GL_DEBUG( glVertex2f )( x1, y2 );
429         GL_DEBUG( glVertex2f )( x2, y2 );
430         GL_DEBUG( glVertex2f )( x2, y1 );
431     }
432     GL_DEBUG_END();
433 
434     GL_DEBUG( glEnable )( GL_TEXTURE_2D );
435 }
436 
437 //--------------------------------------------------------------------------------------------
ui_drawImage(ui_id_t id,oglx_texture_t * img,float vx,float vy,float vwidth,float vheight,GLXvector4f image_tint)438 void ui_drawImage( ui_id_t id, oglx_texture_t *img, float vx, float vy, float vwidth, float vheight, GLXvector4f image_tint )
439 {
440     GLXvector4f tmp_tint = {1, 1, 1, 1};
441 
442     float vw, vh;
443     float tx, ty;
444     float x1, x2, y1, y2;
445 
446     // handle optional parameters
447     if ( NULL == image_tint ) image_tint = tmp_tint;
448 
449     if ( img )
450     {
451         if ( 0 == vwidth || 0 == vheight )
452         {
453             vw = img->imgW;
454             vh = img->imgH;
455         }
456         else
457         {
458             vw = vwidth;
459             vh = vheight;
460         }
461 
462         tx = ( float ) oglx_texture_GetImageWidth( img )  / ( float ) oglx_texture_GetTextureWidth( img );
463         ty = ( float ) oglx_texture_GetImageHeight( img ) / ( float ) oglx_texture_GetTextureHeight( img );
464 
465         // convert the virtual coordinates to screen coordinates
466         ui_virtual_to_screen( vx, vy, &x1, &y1 );
467         ui_virtual_to_screen( vx + vw, vy + vh, &x2, &y2 );
468 
469         // Draw the image
470         oglx_texture_Bind( img );
471 
472         GL_DEBUG( glColor4fv )( image_tint );
473 
474         GL_DEBUG( glBegin )( GL_QUADS );
475         {
476             GL_DEBUG( glTexCoord2f )( 0,  0 );  GL_DEBUG( glVertex2f )( x1, y1 );
477             GL_DEBUG( glTexCoord2f )( tx,  0 );  GL_DEBUG( glVertex2f )( x2, y1 );
478             GL_DEBUG( glTexCoord2f )( tx, ty );  GL_DEBUG( glVertex2f )( x2, y2 );
479             GL_DEBUG( glTexCoord2f )( 0, ty );  GL_DEBUG( glVertex2f )( x1, y2 );
480         }
481         GL_DEBUG_END();
482     }
483 }
484 
485 //--------------------------------------------------------------------------------------------
ui_drawBar(ui_id_t id,float vx,float vy,int current,int max,Uint8 bar_type)486 int ui_drawBar( ui_id_t id, float vx, float vy, int current, int max, Uint8 bar_type )
487 {
488     float x1, y1;
489 
490     // convert the virtual coordinates to screen coordinates
491     ui_virtual_to_screen( vx, vy, &x1, &y1 );
492 
493     //Draw the bar
494     y1 = draw_one_bar( bar_type, x1, y1, current, max );
495     ui_virtual_to_screen( vx, y1, &x1, &y1 );
496     return y1;
497 }
498 
499 //--------------------------------------------------------------------------------------------
ui_drawWidgetButton(ui_Widget_t * pw)500 void ui_drawWidgetButton( ui_Widget_t * pw )
501 {
502     GLfloat * pcolor = NULL;
503     bool_t bactive, bhot;
504 
505     bactive = ui_context.active == pw->id && ui_context.hot == pw->id;
506     bactive = bactive || 0 != ( pw->mask & pw->state & UI_BITS_CLICKED );
507     bhot    = ui_context.hot == pw->id;
508     bhot    = bhot || 0 != ( pw->mask & pw->state & UI_BITS_MOUSEOVER );
509 
510     if ( 0 != pw->mask )
511     {
512         if ( bactive )
513         {
514             pcolor = ui_normal_color2;
515         }
516         else if ( bhot )
517         {
518             pcolor = ui_hot_color;
519         }
520         else
521         {
522             pcolor = ui_normal_color;
523         }
524     }
525     else
526     {
527         if ( bactive )
528         {
529             pcolor = ui_active_color;
530         }
531         else if ( bhot )
532         {
533             pcolor = ui_hot_color;
534         }
535         else
536         {
537             pcolor = ui_normal_color;
538         }
539     }
540 
541     ui_drawButton( pw->id, pw->vx, pw->vy, pw->vwidth, pw->vheight, pcolor );
542 }
543 
544 //--------------------------------------------------------------------------------------------
ui_drawWidgetImage(ui_Widget_t * pw)545 void ui_drawWidgetImage( ui_Widget_t * pw )
546 {
547     if ( NULL != pw && NULL != pw->img )
548     {
549         ui_drawImage( pw->id, pw->img, pw->vx, pw->vy, pw->vwidth, pw->vheight, NULL );
550     }
551 }
552 
553 //--------------------------------------------------------------------------------------------
554 /** ui_drawTextBox
555  * Draws a text string into a box, splitting it into lines according to newlines in the string.
556  * @warning Doesn't pay attention to the width/height arguments yet.
557  *
558  * text    - The text to draw
559  * x       - The x position to start drawing at
560  * y       - The y position to start drawing at
561  * width   - Maximum width of the box (not implemented)
562  * height  - Maximum height of the box (not implemented)
563  * spacing - Amount of space to move down between lines. (usually close to your font size)
564  */
ui_drawTextBox(Font * font,const char * text,float vx,float vy,float vwidth,float vheight,float vspacing)565 void ui_drawTextBox( Font * font, const char *text, float vx, float vy, float vwidth, float vheight, float vspacing )
566 {
567     float x1, x2, y1, y2;
568     float spacing;
569 
570     if ( NULL == font ) font = ui_getFont();
571 
572     // convert the virtual coordinates to screen coordinates
573     ui_virtual_to_screen( vx, vy, &x1, &y1 );
574     ui_virtual_to_screen( vx + vwidth, vy + vheight, &x2, &y2 );
575     spacing = ui_context.ah * vspacing;
576 
577     // draw using screen coordinates
578     fnt_drawTextBox( font, NULL, x1, y1, x2 - x1, y2 - y1, spacing, text );
579 }
580 
581 //--------------------------------------------------------------------------------------------
582 // Controls
ui_doButton(ui_id_t id,const char * text,Font * font,float vx,float vy,float vwidth,float vheight)583 ui_buttonValues ui_doButton( ui_id_t id, const char *text, Font * font, float vx, float vy, float vwidth, float vheight )
584 {
585     ui_buttonValues result;
586     int text_w, text_h;
587     int text_x, text_y;
588 
589     // Do all the logic type work for the button
590     result = ui_buttonBehavior( id, vx, vy, vwidth, vheight );
591 
592     // Draw the button part of the button
593     ui_drawButton( id, vx, vy, vwidth, vheight, NULL );
594 
595     // And then draw the text that goes on top of the button
596     if ( NULL == font ) font = ui_getFont();
597     if ( NULL != font && NULL != text && '\0' != text[0] )
598     {
599         float x1, x2, y1, y2;
600 
601         // convert the virtual coordinates to screen coordinates
602         ui_virtual_to_screen( vx, vy, &x1, &y1 );
603         ui_virtual_to_screen( vx + vwidth, vy + vheight, &x2, &y2 );
604 
605         // find the vwidth & vheight of the text to be drawn, so that it can be centered inside
606         // the button
607         fnt_getTextSize( font, text, &text_w, &text_h );
608 
609         text_x = (( x2 - x1 ) - text_w ) / 2 + x1;
610         text_y = (( y2 - y1 ) - text_h ) / 2 + y1;
611 
612         GL_DEBUG( glColor3f )( 1, 1, 1 );
613         fnt_drawText( font, NULL, text_x, text_y, text );
614     }
615 
616     return result;
617 }
618 
619 //--------------------------------------------------------------------------------------------
ui_doImageButton(ui_id_t id,oglx_texture_t * img,float vx,float vy,float vwidth,float vheight,GLXvector3f image_tint)620 ui_buttonValues ui_doImageButton( ui_id_t id, oglx_texture_t *img, float vx, float vy, float vwidth, float vheight, GLXvector3f image_tint )
621 {
622     ui_buttonValues result;
623 
624     // Do all the logic type work for the button
625     result = ui_buttonBehavior( id, vx, vy, vwidth, vheight );
626 
627     // Draw the button part of the button
628     ui_drawButton( id, vx, vy, vwidth, vheight, NULL );
629 
630     // And then draw the image on top of it
631     ui_drawImage( id, img, vx + 5, vy + 5, vwidth - 10, vheight - 10, image_tint );
632 
633     return result;
634 }
635 
636 //--------------------------------------------------------------------------------------------
ui_doImageButtonWithText(ui_id_t id,oglx_texture_t * img,const char * text,Font * font,float vx,float vy,float vwidth,float vheight)637 ui_buttonValues ui_doImageButtonWithText( ui_id_t id, oglx_texture_t *img, const char *text, Font * font, float vx, float vy, float vwidth, float vheight )
638 {
639     ui_buttonValues result;
640 
641     float text_x, text_y;
642     int   text_w, text_h;
643 
644     // Do all the logic type work for the button
645     result = ui_buttonBehavior( id, vx, vy, vwidth, vheight );
646 
647     // Draw the button part of the button
648     ui_drawButton( id, vx, vy, vwidth, vheight, NULL );
649 
650     // Draw the image part
651     ui_drawImage( id, img, vx + 5, vy + 5, 0, 0, NULL );
652 
653     // And draw the text next to the image
654     // And then draw the text that goes on top of the button
655     if ( NULL == font ) font = ui_getFont();
656     if ( NULL != font )
657     {
658         float x1, x2, y1, y2;
659 
660         // convert the virtual coordinates to screen coordinates
661         ui_virtual_to_screen( vx, vy, &x1, &y1 );
662         ui_virtual_to_screen( vx + vwidth, vy + vheight, &x2, &y2 );
663 
664         // find the vwidth & vheight of the text to be drawn, so that it can be centered inside
665         // the button
666         fnt_getTextSize( font, text, &text_w, &text_h );
667 
668         text_x = ( img->imgW + 10 ) * ui_context.aw + x1;
669         text_y = (( y2 - y1 ) - text_h ) / 2         + y1;
670 
671         GL_DEBUG( glColor3f )( 1, 1, 1 );
672         fnt_drawText( font, NULL, text_x, text_y, text );
673     }
674 
675     return result;
676 }
677 
678 //--------------------------------------------------------------------------------------------
679 //--------------------------------------------------------------------------------------------
ui_doWidget(ui_Widget_t * pw)680 ui_buttonValues ui_doWidget( ui_Widget_t * pw )
681 {
682     ui_buttonValues result;
683 
684     float text_x, text_y;
685     int   text_w, text_h;
686     float img_w;
687 
688     // Do all the logic type work for the button
689     result = ui_WidgetBehavior( pw );
690 
691     // Draw the button part of the button
692     ui_drawWidgetButton( pw );
693 
694     // draw any image on the left hand side of the button
695     img_w = 0;
696     if ( NULL != pw->img )
697     {
698         ui_Widget_t wtmp;
699 
700         // Draw the image part
701         GL_DEBUG( glColor3f )( 1, 1, 1 );
702 
703         ui_shrinkWidget( &wtmp, pw, 5 );
704         wtmp.vwidth = wtmp.vheight;
705 
706         ui_drawWidgetImage( &wtmp );
707 
708         // get the non-virtual image width
709         img_w = pw->img->imgW * ui_context.aw;
710     }
711 
712     // And draw the text on the right hand side of any image
713     if ( NULL != pw->pfont && NULL != pw->text && '\0' != pw->text[0] )
714     {
715         float x1, x2, y1, y2;
716 
717         // convert the virtual coordinates to screen coordinates
718         ui_virtual_to_screen( pw->vx, pw->vy, &x1, &y1 );
719         ui_virtual_to_screen( pw->vx + pw->vwidth, pw->vy + pw->vheight, &x2, &y2 );
720 
721         GL_DEBUG( glColor3f )( 1, 1, 1 );
722 
723         // find the (x2-x1) & (y2-y1) of the pw->text to be drawn, so that it can be centered inside
724         // the button
725         fnt_getTextSize( pw->pfont, pw->text, &text_w, &text_h );
726 
727         text_w = MIN( text_w, ( x2 - x1 ) );
728         text_h = MIN( text_h, ( y2 - y1 ) );
729 
730         text_x = (( x2 - x1 ) - text_w ) / 2 + x1;
731         text_y = (( y2 - y1 ) - text_h ) / 2 + y1;
732 
733         text_x = img_w + (( x2 - x1 ) - img_w - text_w ) / 2 + x1;
734         text_y = (( y2 - y1 ) - text_h ) / 2                + y1;
735 
736         GL_DEBUG( glColor3f )( 1, 1, 1 );
737         fnt_drawText( pw->pfont, &( pw->text_surf ), text_x, text_y, pw->text );
738     }
739 
740     return result;
741 }
742 
743 //--------------------------------------------------------------------------------------------
ui_copyWidget(ui_Widget_t * pw2,ui_Widget_t * pw1)744 bool_t ui_copyWidget( ui_Widget_t * pw2, ui_Widget_t * pw1 )
745 {
746     if ( NULL == pw2 || NULL == pw1 ) return bfalse;
747     return NULL != memcpy( pw2, pw1, sizeof( ui_Widget_t ) );
748 }
749 
750 //--------------------------------------------------------------------------------------------
ui_shrinkWidget(ui_Widget_t * pw2,ui_Widget_t * pw1,float pixels)751 bool_t ui_shrinkWidget( ui_Widget_t * pw2, ui_Widget_t * pw1, float pixels )
752 {
753     if ( NULL == pw2 || NULL == pw1 ) return bfalse;
754 
755     if ( !ui_copyWidget( pw2, pw1 ) ) return bfalse;
756 
757     pw2->vx += pixels;
758     pw2->vy += pixels;
759     pw2->vwidth  -= 2 * pixels;
760     pw2->vheight -= 2 * pixels;
761 
762     if ( pw2->vwidth < 0 )  pw2->vwidth   = 0;
763     if ( pw2->vheight < 0 ) pw2->vheight = 0;
764 
765     return pw2->vwidth > 0 && pw2->vheight > 0;
766 }
767 
768 //--------------------------------------------------------------------------------------------
ui_initWidget(ui_Widget_t * pw,ui_id_t id,Font * pfont,const char * text,oglx_texture_t * img,float vx,float vy,float vwidth,float vheight)769 bool_t ui_initWidget( ui_Widget_t * pw, ui_id_t id, Font * pfont, const char *text, oglx_texture_t *img, float vx, float vy, float vwidth, float vheight )
770 {
771     if ( NULL == pw ) return bfalse;
772 
773     if ( NULL == pfont ) pfont = ui_getFont();
774 
775     pw->id      = id;
776     pw->pfont   = pfont;
777     pw->text    = text;
778     pw->img     = img;
779     pw->vx      = vx;
780     pw->vy      = vy;
781     pw->vwidth  = vwidth;
782     pw->vheight = vheight;
783     pw->state   = 0;
784     pw->mask    = 0;
785     pw->timeout = 0;
786 
787     return btrue;
788 }
789 
790 //--------------------------------------------------------------------------------------------
ui_widgetAddMask(ui_Widget_t * pw,BIT_FIELD mbits)791 bool_t ui_widgetAddMask( ui_Widget_t * pw, BIT_FIELD mbits )
792 {
793     if ( NULL == pw ) return bfalse;
794 
795     SET_BIT( pw->mask, mbits );
796     UNSET_BIT( pw->state, mbits );
797 
798     return btrue;
799 }
800 
801 //--------------------------------------------------------------------------------------------
ui_widgetRemoveMask(ui_Widget_t * pw,BIT_FIELD mbits)802 bool_t ui_widgetRemoveMask( ui_Widget_t * pw, BIT_FIELD mbits )
803 {
804     if ( NULL == pw ) return bfalse;
805 
806     UNSET_BIT( pw->mask, mbits );
807     UNSET_BIT( pw->state, mbits );
808 
809     return btrue;
810 }
811 
812 //--------------------------------------------------------------------------------------------
ui_widgetSetMask(ui_Widget_t * pw,BIT_FIELD mbits)813 bool_t ui_widgetSetMask( ui_Widget_t * pw, BIT_FIELD mbits )
814 {
815     if ( NULL == pw ) return bfalse;
816 
817     pw->mask  = mbits;
818     UNSET_BIT( pw->state, mbits );
819 
820     return btrue;
821 }
822 
823 //--------------------------------------------------------------------------------------------
ui_virtual_to_screen(float vx,float vy,float * rx,float * ry)824 void ui_virtual_to_screen( float vx, float vy, float * rx, float * ry )
825 {
826     /// @details BB@> convert "virtual" screen positions into "real" space
827 
828     *rx = ui_context.aw * vx + ui_context.bw;
829     *ry = ui_context.ah * vy + ui_context.bh;
830 }
831 
832 //--------------------------------------------------------------------------------------------
ui_screen_to_virtual(float rx,float ry,float * vx,float * vy)833 void ui_screen_to_virtual( float rx, float ry, float *vx, float *vy )
834 {
835     /// @details BB@> convert "real" mouse positions into "virtual" space
836 
837     *vx = ui_context.iaw * rx + ui_context.ibw;
838     *vy = ui_context.iah * ry + ui_context.ibh;
839 }
840 
841 //--------------------------------------------------------------------------------------------
ui_set_virtual_screen(float vw,float vh,float ww,float wh)842 void ui_set_virtual_screen( float vw, float vh, float ww, float wh )
843 {
844     /// @details BB@> set up the ui's virtual screen
845 
846     float k;
847     Font * old_defaultFont;
848 
849     // define the virtual screen
850     ui_context.vw = vw;
851     ui_context.vh = vh;
852     ui_context.ww = ww;
853     ui_context.wh = wh;
854 
855     // define the forward transform
856     k = MIN( sdl_scr.x / ww, sdl_scr.y / wh );
857     ui_context.aw = k;
858     ui_context.ah = k;
859     ui_context.bw = ( sdl_scr.x - k * ww ) * 0.5f;
860     ui_context.bh = ( sdl_scr.y - k * wh ) * 0.5f;
861 
862     // define the inverse transform
863     ui_context.iaw = 1.0f / ui_context.aw;
864     ui_context.iah = 1.0f / ui_context.ah;
865     ui_context.ibw = -ui_context.bw * ui_context.iaw;
866     ui_context.ibh = -ui_context.bh * ui_context.iah;
867 
868     // make sure the font is sized right for the virtual screen
869     old_defaultFont = ui_context.defaultFont;
870     if ( NULL != ui_context.defaultFont )
871     {
872         fnt_freeFont( ui_context.defaultFont );
873     }
874     ui_context.defaultFont = ui_loadFont( ui_context.defaultFontName, ui_context.defaultFontSize );
875 
876     // fix the active font. in general, we do not own it, so do not delete
877     if ( NULL == ui_context.activeFont || old_defaultFont == ui_context.activeFont )
878     {
879         ui_context.activeFont = ui_context.defaultFont;
880     }
881 }
882 
883 //--------------------------------------------------------------------------------------------
ui_loadFont(const char * font_name,float vpointSize)884 Font * ui_loadFont( const char * font_name, float vpointSize )
885 {
886     float pointSize;
887 
888     pointSize = vpointSize * ui_context.aw;
889 
890     return fnt_loadFont( font_name, pointSize );
891 }
892