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