maug
Quick and dirty C mini-augmentation library.
Loading...
Searching...
No Matches
retrogui.h
Go to the documentation of this file.
1
2#ifndef RETROGUI_H
3#define RETROGUI_H
4
76
77#ifndef RETROFONT_PRESENT
78# error "retrofont not present!"
79#endif /* !RETROFONT_PRESENT */
80
81/* TODO: Maug log unified API to reference. */
82
83#ifndef RETROGUI_TRACE_LVL
84# define RETROGUI_TRACE_LVL 0
85#endif /* !RETROGUI_TRACE_LVL */
86
87#ifndef RETROGUI_COLOR_BORDER
93# define RETROGUI_COLOR_BORDER RETROFLAT_COLOR_DARKBLUE
94#endif /* !RETROGUI_COLOR_BORDER */
95
96#ifndef RETROGUI_CTL_TEXT_SZ_MAX
102
103#ifndef RETROGUI_KEY_ACTIVATE
109# define RETROGUI_KEY_ACTIVATE RETROFLAT_KEY_SPACE
110#endif /* !RETROGUI_KEY_ACTIVATE */
111
112#ifndef RETROGUI_KEY_NEXT
118# define RETROGUI_KEY_NEXT RETROFLAT_KEY_DOWN
119#endif /* !RETROGUI_KEY_NEXT */
120
121#ifndef RETROGUI_KEY_PREV
127# define RETROGUI_KEY_PREV RETROFLAT_KEY_UP
128#endif /* !RETROGUI_KEY_PREV */
129
130#ifndef RETROGUI_KEY_SEL_NEXT
136# define RETROGUI_KEY_SEL_NEXT RETROFLAT_KEY_RIGHT
137#endif /* !RETROGUI_KEY_SEL_NEXT */
138
139#ifndef RETROGUI_KEY_SEL_PREV
145# define RETROGUI_KEY_SEL_PREV RETROFLAT_KEY_LEFT
146#endif /* !RETROGUI_KEY_SEL_PREV */
147
148#ifndef RETROGUI_PAD_ACTIVATE
154# define RETROGUI_PAD_ACTIVATE RETROFLAT_PAD_A
155#endif /* !RETROGUI_PAD_ACTIVATE */
156
157#ifndef RETROGUI_PAD_NEXT
163# define RETROGUI_PAD_NEXT RETROFLAT_PAD_DOWN
164#endif /* !RETROGUI_PAD_NEXT */
165
166#ifndef RETROGUI_PAD_PREV
172# define RETROGUI_PAD_PREV RETROFLAT_PAD_UP
173#endif /* !RETROGUI_PAD_PREV */
174
175#ifndef RETROGUI_PAD_SEL_NEXT
181# define RETROGUI_PAD_SEL_NEXT RETROFLAT_PAD_RIGHT
182#endif /* !RETROGUI_PAD_SEL_NEXT */
183
184#ifndef RETROGUI_PAD_SEL_PREV
190# define RETROGUI_PAD_SEL_PREV RETROFLAT_PAD_LEFT
191#endif /* !RETROGUI_PAD_SEL_PREV */
192
193# define RETROGUI_CTL_TEXT_SZ_MAX 128
194#endif /* !RETROGUI_CTL_TEXT_SZ_MAX */
195
196#ifndef RETROGUI_CTL_SZ_MAX_INIT
197# define RETROGUI_CTL_SZ_MAX_INIT 20
198#endif /* !RETROGUI_CTL_SZ_MAX_INIT */
199
200#ifndef RETROGUI_PADDING
205# define RETROGUI_PADDING 5
206#endif /* !RETROGUI_PADDING */
207
208#ifndef RETROGUI_BTN_LBL_SZ_MAX
209# define RETROGUI_BTN_LBL_SZ_MAX 64
210#endif /* !RETROGUI_BTN_LBL_SZ_MAX */
211
212#ifndef RETROGUI_BTN_LBL_PADDED_X
213# define RETROGUI_BTN_LBL_PADDED_X 8
214#endif /* !RETROGUI_BTN_LBL_PADDED_X */
215
216#ifndef RETROGUI_BTN_LBL_PADDED_Y
217# define RETROGUI_BTN_LBL_PADDED_Y 8
218#endif /* !RETROGUI_BTN_LBL_PADDED_Y */
219
220#ifndef RETROGUI_CTL_TEXT_BLINK_FRAMES
221# define RETROGUI_CTL_TEXT_BLINK_FRAMES 15
222#endif /* !RETROGUI_CTL_TEXT_BLINK_FRAMES */
223
224#ifndef RETROGUI_CTL_LISTBOX_CURSOR_RADIUS
225# define RETROGUI_CTL_LISTBOX_CURSOR_RADIUS 8
226#endif /* !RETROGUI_CTL_LISTBOX_CURSOR_RADIUS */
227
228#ifndef RETROGUI_CTL_LISTBOX_STR_SZ_MAX
229# define RETROGUI_CTL_LISTBOX_STR_SZ_MAX 255
230#endif /* !RETROGUI_CTL_LISTBOX_STR_SZ_MAX */
231
232#ifndef RETROGUI_CTL_TEXT_CUR_WH
233# define RETROGUI_CTL_TEXT_CUR_WH 8
234#endif /* !RETROGUI_CTL_LISTBOX_STR_SZ_MAX */
235
236#ifndef RETROGUI_DEBOUNCE_MAX_DEFAULT
237# define RETROGUI_DEBOUNCE_MAX_DEFAULT 100
238#endif /* !RETROGUI_DEBOUNCE_MAX_DEFAULT */
239
240#ifndef RETROGUI_LABEL_SHOW_TICKS_MAX
246# define RETROGUI_LABEL_SHOW_TICKS_MAX 2
247#endif /* !RETROGUI_LABEL_SHOW_TICKS_MAX */
248
249#ifndef RETROGUI_LABEL_SHOW_INC
254# define RETROGUI_LABEL_SHOW_INC 3
255#endif /* !RETROGUI_LABEL_SHOW_INC */
256 /* maug_retrogui_cfg */
258
263#define RETROGUI_FLAGS_DIRTY 0x01
264
270#define RETROGUI_FLAGS_FONT_OWNED 0x02
271
277#define RETROGUI_LABEL_FLAG_SHOWINC 0x02
278
288#define RETROGUI_LABEL_FLAG_SHOWINC_SLOW 0x06
289
290#define RETROGUI_FILLBAR_FLAG_SHOWNUM 0x02
291
292#define _retrogui_copy_str( field, src_str, dest_ctl, str_tmp, str_sz ) \
293 /* Sanity checking. */ \
294 assert( NULL != src_str ); \
295 debug_printf( RETROGUI_TRACE_LVL, \
296 "copying string \"%s\" to " #dest_ctl, src_str ); \
297 if( 0 == str_sz ) { \
298 str_sz = maug_strlen( src_str ); \
299 debug_printf( RETROGUI_TRACE_LVL, \
300 "determined str sz of \"%s\": " SIZE_T_FMT, src_str, str_sz ); \
301 } \
302 if( \
303 str_sz != dest_ctl. field ## _sz && \
304 (MAUG_MHANDLE)NULL != dest_ctl. field ## _h \
305 ) { \
306 debug_printf( RETROGUI_TRACE_LVL, \
307 "string size different; creating new buffer..." ); \
308 /* Free the existing string. */ \
309 maug_mfree( dest_ctl. field ## _h ); \
310 dest_ctl. field ## _h = (MAUG_MHANDLE)NULL; \
311 } \
312 if( (MAUG_MHANDLE)NULL == dest_ctl. field ## _h ) { \
313 /* Allocate new string space if not allocated. */ \
314 maug_malloc_test( dest_ctl. field ## _h, str_sz + 1, 1 ); \
315 } \
316 maug_mlock( dest_ctl. field ## _h, str_tmp ); \
317 maug_cleanup_if_null_lock( char*, str_tmp ); \
318 \
319 /* Copy the string over. */ \
320 assert( NULL != str_tmp ); \
321 maug_mzero( str_tmp, str_sz + 1 ); \
322 debug_printf( RETROGUI_TRACE_LVL, \
323 "zeroed str sz for \"%s\": " SIZE_T_FMT, src_str, str_sz + 1 ); \
324 maug_strncpy( str_tmp, src_str, str_sz ); \
325 dest_ctl. field ## _sz = str_sz; \
326 debug_printf( RETROGUI_TRACE_LVL, "copied str as: \"%s\"", str_tmp ); \
327 maug_munlock( dest_ctl. field ## _h, str_tmp );
328
330typedef int16_t retrogui_idc_t;
331
332#define RETROGUI_IDC_FMT "%d"
333
334#define RETROGUI_IDC_NONE 0
335
339#define RETROGUI_COLOR_BG 1
340
344#define RETROGUI_COLOR_FG 2
345
350#define RETROGUI_COLOR_SEL_BG 3
351
356#define RETROGUI_COLOR_SEL_FG 4
357
362
380/* TODO: USe MDATA_VECTOR for LISTBOX items. */
381#define RETROGUI_CTL_TABLE_BASE( f ) \
382 f( 0, NONE, void* none; ) \
383 f( 1, LISTBOX, struct MDATA_VECTOR list; size_t sel_idx; ) \
384 f( 2, BUTTON, MAUG_MHANDLE label_h; char* label; size_t label_sz; int16_t push_frames; uint8_t font_flags; ) \
385 f( 3, LABEL, uint8_t flags; MAUG_MHANDLE label_h; char* label; size_t label_sz; uint8_t font_flags; size_t shown_sz; int show_ticks; ) \
386 f( 4, IMAGE, retroflat_blit_t image; ssize_t image_cache_id; int16_t instance; retroflat_pxxy_t src_x; retroflat_pxxy_t src_y; ) \
387 f( 5, FILLBAR, uint8_t flags; uint16_t cur; uint16_t max; )
388
389#ifdef RETROGUI_NO_TEXTBOX
390# define RETROGUI_CTL_TABLE( f ) RETROGUI_CTL_TABLE_BASE( f )
391#else
392# define RETROGUI_CTL_TABLE( f ) RETROGUI_CTL_TABLE_BASE( f ) \
393 f( 6, TEXTBOX, MAUG_MHANDLE text_h; char* text; size_t text_sz; size_t text_sz_max; size_t text_cur; int16_t blink_frames; )
394#endif /* RETROGUI_NO_TEXTBOX */
395
396#if 0
397 f( 6, SCROLLBAR, size_t min; size_t max; size_t value; )
398#endif
399
400#ifdef RETROGUI_NO_TEXTBOX
401# define retrogui_can_focus_ctl( ctl ) \
402 (RETROGUI_CTL_TYPE_BUTTON == (ctl)->base.type || \
403 RETROGUI_CTL_TYPE_LISTBOX == (ctl)->base.type)
404#else
408# define retrogui_can_focus_ctl( ctl ) \
409 (RETROGUI_CTL_TYPE_BUTTON == (ctl)->base.type || \
410 RETROGUI_CTL_TYPE_TEXTBOX == (ctl)->base.type || \
411 RETROGUI_CTL_TYPE_LISTBOX == (ctl)->base.type)
412#endif /* RETROGUI_NO_TEXTBOX */
413
416 uint8_t type;
417 retrogui_idc_t idc;
422 RETROFLAT_COLOR bg_color;
423 RETROFLAT_COLOR fg_color;
424 RETROFLAT_COLOR sel_fg;
425 RETROFLAT_COLOR sel_bg;
426#if defined( RETROGUI_NATIVE_WIN )
427 HWND hwnd;
428#endif
429};
430
435#define RETROGUI_CTL_TABLE_FIELDS( idx, c_name, c_fields ) \
436 struct RETROGUI_CTL_ ## c_name { \
437 struct RETROGUI_CTL_BASE base; \
438 c_fields \
439 };
440
441RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_FIELDS )
442
443
447#define RETROGUI_CTL_TABLE_TYPES( idx, c_name, c_fields ) \
448 struct RETROGUI_CTL_ ## c_name c_name;
449
451 struct RETROGUI_CTL_BASE base;
452 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_TYPES )
453};
454 /* maug_retrogui_ctl */
456
457typedef void (*retrogui_xy_cb)(
458 retroflat_pxxy_t* x, retroflat_pxxy_t* y, void* data );
459
460typedef char retrogui_list_t[RETROGUI_CTL_LISTBOX_STR_SZ_MAX + 1];
461
462/*
463 * \note It is possible to have multiple GUI controllers in a program. For
464 * example, a web browser might have a controller for its address bar and
465 * a controller for form elements on pages.
466 *
467 */
468struct RETROGUI {
469 uint8_t flags;
474 RETROFLAT_COLOR bg_color;
475 retrogui_idc_t idc_prev;
476 struct MDATA_VECTOR ctls;
479 retroflat_blit_t* draw_bmp;
480 retroflat_ms_t debounce_next;
481 retroflat_ms_t debounce_max;
489 union RETROGXC_CACHABLE font;
490};
491
492MERROR_RETVAL retrogui_push_listbox_item(
493 struct RETROGUI* gui, retrogui_idc_t idc, const char* item, size_t item_sz );
494
504 struct RETROGUI* gui, RETROFLAT_IN_KEY* p_input,
505 struct RETROFLAT_INPUT* input_evt );
506
507MERROR_RETVAL retrogui_redraw_ctls( struct RETROGUI* gui );
508
509MERROR_RETVAL retrogui_sz_ctl(
510 struct RETROGUI* gui, retrogui_idc_t idc,
512 retroflat_pxxy_t max_w, retroflat_pxxy_t max_h );
513
514MERROR_RETVAL retrogui_pos_ctl(
515 struct RETROGUI* gui, retrogui_idc_t idc,
518
519MERROR_RETVAL retrogui_push_ctl(
520 struct RETROGUI* gui, union RETROGUI_CTL* ctl );
521
530 struct RETROGUI* gui, const maug_path font_path );
531
532#ifndef RETROGUI_NO_TEXTBOX
533
534MERROR_RETVAL retrogui_get_ctl_text(
535 struct RETROGUI* gui, retrogui_idc_t idc, char* buffer, size_t buffer_sz );
536
537#endif /* !RETROGUI_NO_TEXTBOX */
538
539ssize_t retrogui_get_ctl_sel_idx( struct RETROGUI* gui, retrogui_idc_t idc );
540
541MERROR_RETVAL retrogui_set_ctl_color(
542 struct RETROGUI* gui, retrogui_idc_t idc, uint8_t color_key,
543 RETROFLAT_COLOR color_val );
544
545MERROR_RETVAL retrogui_set_ctl_text(
546 struct RETROGUI* gui, retrogui_idc_t idc, size_t buffer_sz,
547 const char* fmt, ... );
548
558 struct RETROGUI* gui, retrogui_idc_t idc, const maug_path path,
559 uint8_t flags );
560
566 struct RETROGUI* gui, retrogui_idc_t idc, retroflat_blit_t* blit,
567 uint8_t flags );
568
579 struct RETROGUI* gui, retrogui_idc_t idc, uint16_t level, uint16_t max,
580 uint8_t flags );
581
582MERROR_RETVAL retrogui_init_ctl(
583 union RETROGUI_CTL* ctl, uint8_t type, retrogui_idc_t idc );
584
597 struct RETROGUI* gui, size_t start, ssize_t incr );
598
612
622
634
635#define retrogui_focus_next( gui ) \
636 retrogui_focus_iter( gui, 0, 1 )
637
638#define retrogui_focus_prev( gui ) \
639 retrogui_focus_iter( gui, mdata_vector_ct( &((gui)->ctls) ) - 1, -1 )
640
641#ifdef RETROGUI_C
642
643#define RETROGUI_CTL_TABLE_CONSTS( idx, c_name, c_fields ) \
644 MAUG_CONST uint8_t SEG_MCONST RETROGUI_CTL_TYPE_ ## c_name = idx;
645
646RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_CONSTS )
647
648#ifdef RETROGUI_TRACE_TOKENS
649
650#define RETROGUI_CTL_TABLE_NAMES( idx, c_name, f_fields ) \
651 #c_name,
652
653MAUG_CONST char* SEG_MCONST gc_retrogui_ctl_names[] = {
654 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_NAMES )
655 ""
656};
657
658#endif /* RETROGUI_TRACE_TOKENS */
659
660static union RETROGUI_CTL* _retrogui_get_ctl_by_idc(
661 struct RETROGUI* gui, retrogui_idc_t idc );
662
663/* === Control: NONE === */
664
665static retrogui_idc_t retrogui_click_NONE(
666 struct RETROGUI* gui,
667 union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
668 struct RETROFLAT_INPUT* input_evt
669) {
670 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
671
672 return idc_out;
673}
674
675static retrogui_idc_t retrogui_key_NONE(
676 struct RETROGUI* gui, union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
677 struct RETROFLAT_INPUT* input_evt
678) {
679 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
680
681 return idc_out;
682}
683
684void retrogui_redraw_NONE( struct RETROGUI* gui, union RETROGUI_CTL* ctl ) {
685}
686
687static MERROR_RETVAL retrogui_push_NONE( union RETROGUI_CTL* ctl ) {
688 MERROR_RETVAL retval = MERROR_GUI;
689
690 return retval;
691}
692
693static MERROR_RETVAL retrogui_sz_NONE(
694 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
697) {
698 return MERROR_OK;
699}
700
701static MERROR_RETVAL retrogui_pos_NONE(
702 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
705) {
706 return MERROR_OK;
707}
708
709static void retrogui_destroy_NONE( union RETROGUI_CTL* ctl ) {
710}
711
712static MERROR_RETVAL retrogui_init_NONE( union RETROGUI_CTL* ctl ) {
713 MERROR_RETVAL retval = MERROR_GUI;
714
715 return retval;
716}
717
718/* === Control: LISTBOX === */
719
720static retrogui_idc_t retrogui_click_LISTBOX(
721 struct RETROGUI* gui,
722 union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
723 struct RETROFLAT_INPUT* input_evt
724) {
725 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
726 MERROR_RETVAL retval = MERROR_OK;
727 size_t i = 0;
728 retroflat_pxxy_t w = 0,
729 h = 0;
730 int autolock = 0;
731 char* list_i = NULL;
732 MAUG_MHANDLE font_h = (MAUG_MHANDLE)NULL;
733
734# if defined( RETROGUI_NATIVE_WIN )
735 /* Do nothing. */
736# else
737
738 if( !mdata_vector_is_locked( &(ctl->LISTBOX.list) ) ) {
739 mdata_vector_lock( &(ctl->LISTBOX.list) );
740 autolock = 1;
741 }
742
743 /* Figure out the item clicked. */
744 while( i < mdata_vector_ct( &(ctl->LISTBOX.list) ) ) {
745 list_i =
746 (char*)mdata_vector_get( &(ctl->LISTBOX.list), i, retrogui_list_t );
747
748 /* TODO: Use retrogui_sz_LISTBOX() instead? */
749#ifdef RETROGXC_PRESENT
750 font_h = retrogxc_get_asset(
751 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
752 maug_cleanup_if_null_alloc( MAUG_MHANDLE, font_h );
753#else
754 font_h = gui->font.handle;
755#endif /* RETROGXC_PRESENT */
756 retrofont_string_sz(
757 gui->draw_bmp, list_i, 0, font_h,
758 ctl->base.w, ctl->base.h, &w, &h, 0 );
759
760 if(
761 (retroflat_pxxy_t)(input_evt->mouse_y) <
762 ctl->base.y + ((i + 1) * (h + RETROGUI_PADDING))
763 ) {
764 ctl->LISTBOX.sel_idx = i;
765 break;
766 }
767
768 i++;
769 }
770
771cleanup:
772
773 if( autolock ) {
774 mdata_vector_unlock( &(ctl->LISTBOX.list) );
775 }
776
777 if( MERROR_OK != retval ) {
778 idc_out = RETROGUI_IDC_NONE;
779 }
780
781#endif
782
783 return idc_out;
784}
785
786static retrogui_idc_t retrogui_key_LISTBOX(
787 struct RETROGUI* gui, union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
788 struct RETROFLAT_INPUT* input_evt
789) {
790 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
791
792 switch( *p_input ) {
794 ctl->LISTBOX.sel_idx++;
795 if( ctl->LISTBOX.sel_idx >= mdata_vector_ct( &(ctl->LISTBOX.list) ) ) {
796 ctl->LISTBOX.sel_idx = 0;
797 }
798
799 /* Redraw and preempt further processing of input. */
800 gui->flags |= RETROGUI_FLAGS_DIRTY;
801 *p_input = 0;
802 break;
803
805 ctl->LISTBOX.sel_idx--;
806 /* sel_idx is unsigned, so overflow is always positive! */
807 if( ctl->LISTBOX.sel_idx >= mdata_vector_ct( &(ctl->LISTBOX.list) ) ) {
808 ctl->LISTBOX.sel_idx = mdata_vector_ct( &(ctl->LISTBOX.list) ) - 1;
809 }
810
811 /* Redraw and preempt further processing of input. */
812 gui->flags |= RETROGUI_FLAGS_DIRTY;
813 *p_input = 0;
814 break;
815 }
816
817 return idc_out;
818}
819
820static void retrogui_redraw_LISTBOX(
821 struct RETROGUI* gui, union RETROGUI_CTL* ctl
822) {
823 size_t i = 0;
824 retroflat_pxxy_t w = 0,
825 h = 0,
826 item_y = 0;
827 RETROFLAT_COLOR fg_color;
828 int autolock = 0;
829 char* list_i = NULL;
830 MERROR_RETVAL retval = MERROR_OK;
831 MAUG_MHANDLE font_h = (MAUG_MHANDLE)NULL;
832
833# if defined( RETROGUI_NATIVE_WIN )
834 /* TODO: InvalidateRect()? */
835# else
836
837 if( !mdata_vector_is_locked( &(ctl->LISTBOX.list) ) ) {
838 mdata_vector_lock( &(ctl->LISTBOX.list) );
839 autolock = 1;
840 }
841
842 if( RETROFLAT_COLOR_BLACK != ctl->base.bg_color ) {
843 retroflat_2d_rect( gui->draw_bmp, ctl->base.bg_color,
844 gui->x + ctl->base.x, gui->y + ctl->base.y,
845 ctl->base.w, ctl->base.h, RETROFLAT_DRAW_FLAG_FILL );
846 }
847
848 while( i < mdata_vector_ct( &(ctl->LISTBOX.list) ) ) {
849 list_i =
850 (char*)mdata_vector_get( &(ctl->LISTBOX.list), i, retrogui_list_t );
851#ifdef RETROGXC_PRESENT
852 font_h = retrogxc_get_asset(
853 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
854 maug_cleanup_if_null_alloc( MAUG_MHANDLE, font_h );
855#else
856 font_h = gui->font.handle;
857#endif /* RETROGXC_PRESENT */
858 retrofont_string_sz(
859 gui->draw_bmp, list_i, 0, font_h,
860 ctl->base.w, ctl->base.h, &w, &h, 0 );
861#if RETROGUI_TRACE_LVL > 0
862 debug_printf( RETROGUI_TRACE_LVL,
863 "str height for \"%s\": " SIZE_T_FMT, list_i, h );
864#endif /* RETROGUI_TRACE_LVL */
865 if( i == ctl->LISTBOX.sel_idx ) {
866 /* Draw selection colors. */
867 retroflat_2d_rect( gui->draw_bmp, ctl->base.sel_bg,
868 gui->x + ctl->base.x,
869 gui->y + ctl->base.y + item_y,
870 ctl->base.w, h, RETROFLAT_DRAW_FLAG_FILL );
871 fg_color = ctl->base.sel_fg;
872
873 retroflat_2d_ellipse(
874 gui->draw_bmp, ctl->base.sel_fg,
875 gui->x + ctl->base.x,
876 gui->y + ctl->base.y + item_y,
877 RETROGUI_CTL_LISTBOX_CURSOR_RADIUS,
878 RETROGUI_CTL_LISTBOX_CURSOR_RADIUS, 0 );
879
880 } else {
881 fg_color = ctl->base.fg_color;
882 }
883
884#ifdef RETROGXC_PRESENT
885 font_h = retrogxc_get_asset(
886 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
887 maug_cleanup_if_null_alloc( MAUG_MHANDLE, font_h );
888#else
889 font_h = gui->font.handle;
890#endif /* RETROGXC_PRESENT */
892 gui->draw_bmp, fg_color, list_i, 0, font_h,
893 gui->x + ctl->base.x +
894 RETROGUI_CTL_LISTBOX_CURSOR_RADIUS + RETROGUI_PADDING,
895 gui->y + ctl->base.y + item_y,
896 0, 0, 0 );
897
898 i++;
899 /* Track the item height separately in case the item is multi-line. */
900 item_y += h + RETROGUI_PADDING;
901 }
902
903cleanup:
904
905 if( autolock ) {
906 mdata_vector_unlock( &(ctl->LISTBOX.list) );
907 }
908
909 if( MERROR_OK != retval ) {
910 error_printf( "error drawing LISTBOX: %d", retval );
911 }
912
913# endif
914
915}
916
917MERROR_RETVAL retrogui_select_listbox_item(
918 union RETROGUI_CTL* ctl, size_t item_idx
919) {
920 MERROR_RETVAL retval = MERROR_OK;
921
922# if defined( RETROGUI_NATIVE_WIN )
923
924 /* Select sel_idx. */
925 SendMessage( ctl->base.hwnd, LB_SETCURSEL, item_idx, 0 );
926
927# else
928
929 ctl->LISTBOX.sel_idx = item_idx;
930
931# endif
932
933 return retval;
934}
935
936MERROR_RETVAL retrogui_push_listbox_item(
937 struct RETROGUI* gui, retrogui_idc_t idc, const char* item, size_t item_sz
938) {
939 MERROR_RETVAL retval = MERROR_OK;
940 union RETROGUI_CTL* ctl = NULL;
941 int autolock = 0;
942 retrogui_list_t item_stage;
943 ssize_t i = 0;
944
945 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
946 mdata_vector_lock( &(gui->ctls) );
947 autolock = 1;
948 }
949
950#if RETROGUI_TRACE_LVL > 0
951 debug_printf( RETROGUI_TRACE_LVL,
952 "pushing item \"%s\" to listbox " RETROGUI_IDC_FMT "...", item, idc );
953#endif /* RETROGUI_TRACE_LVL */
954
955 ctl = _retrogui_get_ctl_by_idc( gui, idc );
956 if( NULL == ctl ) {
957 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
958 error_printf( "could not add item: %s", item );
959 retval = MERROR_GUI;
960 goto cleanup;
961 }
962
963# if defined( RETROGUI_NATIVE_WIN )
964
965 SendMessage( ctl->LISTBOX.base.hwnd, LB_ADDSTRING, 0, (LPARAM)item );
966
967# else
968
969 maug_mzero( item_stage, RETROGUI_CTL_LISTBOX_STR_SZ_MAX + 1 );
970 maug_strncpy( item_stage, item, RETROGUI_CTL_LISTBOX_STR_SZ_MAX );
971 i = mdata_vector_append(
972 &(ctl->LISTBOX.list), item_stage, RETROGUI_CTL_LISTBOX_STR_SZ_MAX + 1 );
973 if( 0 > i ) {
974 retval = merror_sz_to_retval( i );
975 }
976
977#endif
978
979 gui->flags |= RETROGUI_FLAGS_DIRTY;
980
981cleanup:
982
983 if( autolock ) {
984 mdata_vector_unlock( &(gui->ctls) );
985 }
986
987 return retval;
988}
989
990static MERROR_RETVAL retrogui_push_LISTBOX( union RETROGUI_CTL* ctl ) {
991 MERROR_RETVAL retval = MERROR_OK;
992
993# if defined( RETROGUI_NATIVE_WIN )
994
995 ctl->base.hwnd = CreateWindow(
996 "LISTBOX", NULL, WS_CHILD | WS_VISIBLE | LBS_STANDARD,
997 gui->x + ctl->base.x, gui->y + ctl->base.y, ctl->base.w, ctl->base.h,
998 g_retroflat_state->window, (HMENU)(ctl->base.idc),
999 g_retroflat_instance, NULL );
1000#if RETROGUI_TRACE_LVL > 0
1001 debug_printf( RETROGUI_TRACE_LVL,
1002 "listbox hwnd: %p", ctl->LISTBOX.base.hwnd );
1003#endif /* RETROGUI_TRACE_LVL */
1004 if( (HWND)NULL == ctl->base.hwnd ) {
1005 error_printf( "could not create listbox" );
1006 retval = MERROR_GUI;
1007 goto cleanup;
1008 }
1009
1010 gui->flags |= RETROGUI_FLAGS_DIRTY;
1011
1012cleanup:
1013
1014# else
1015
1016 /* TODO? */
1017
1018# endif
1019
1020 return retval;
1021}
1022
1023static MERROR_RETVAL retrogui_sz_LISTBOX(
1024 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1027) {
1028 MERROR_RETVAL retval = MERROR_GUI;
1029 /* TODO */
1030 return retval;
1031}
1032
1033static MERROR_RETVAL retrogui_pos_LISTBOX(
1034 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1037) {
1038 MERROR_RETVAL retval = MERROR_GUI;
1039 /* TODO */
1040 return retval;
1041}
1042
1043static void retrogui_destroy_LISTBOX( union RETROGUI_CTL* ctl ) {
1044 mdata_vector_free( &(ctl->LISTBOX.list) );
1045}
1046
1047static MERROR_RETVAL retrogui_init_LISTBOX( union RETROGUI_CTL* ctl ) {
1048 MERROR_RETVAL retval = MERROR_OK;
1049
1050#if RETROGUI_TRACE_LVL > 0
1051 debug_printf( RETROGUI_TRACE_LVL,
1052 "initializing listbox " RETROGUI_IDC_FMT "...", ctl->base.idc );
1053#endif /* RETROGUI_IDC_FMT */
1054
1055 ctl->base.bg_color = RETROFLAT_COLOR_WHITE;
1056 ctl->base.sel_fg = RETROFLAT_COLOR_WHITE;
1057 if( 2 < retroflat_screen_colors() ) {
1058 ctl->base.sel_bg = RETROFLAT_COLOR_BLUE;
1059 ctl->base.fg_color = RETROGUI_COLOR_BORDER;
1060 } else {
1061 ctl->base.sel_bg = RETROFLAT_COLOR_BLACK;
1062 ctl->base.fg_color = RETROFLAT_COLOR_BLACK;
1063 }
1064
1065 return retval;
1066}
1067
1068/* === Control: BUTTON === */
1069
1070static retrogui_idc_t retrogui_click_BUTTON(
1071 struct RETROGUI* gui,
1072 union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1073 struct RETROFLAT_INPUT* input_evt
1074) {
1075 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
1076
1077 if( 0 < ctl->BUTTON.push_frames ) {
1078 goto cleanup;
1079 }
1080
1081 /* Set the last button clicked. */
1082 idc_out = ctl->base.idc;
1083
1084 /* Set the frames to show the pushed-in view. */
1085 /* TODO: Use a constant, here. */
1086 ctl->BUTTON.push_frames = 3;
1087
1088cleanup:
1089
1090 return idc_out;
1091}
1092
1093static retrogui_idc_t retrogui_key_BUTTON(
1094 struct RETROGUI* gui, union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1095 struct RETROFLAT_INPUT* input_evt
1096) {
1097 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
1098
1099 /* Set the last button clicked. Only set out on ENTER/SPACE. */
1100 if(
1101 retroflat_or_key( *p_input, RETROGUI_KEY_ACTIVATE, RETROGUI_PAD_ACTIVATE )
1102 ) {
1103#if RETROGUI_TRACE_LVL > 0
1104 debug_printf( RETROGUI_TRACE_LVL, "pushing BUTTON control..." );
1105#endif /* RETROGUI_TRACE_LVL */
1106 idc_out = ctl->base.idc;
1107 }
1108
1109 return idc_out;
1110}
1111
1112static void retrogui_redraw_BUTTON(
1113 struct RETROGUI* gui, union RETROGUI_CTL* ctl
1114) {
1115 retroflat_pxxy_t w = 0,
1116 h = 0,
1117 text_offset = 0;
1118 RETROFLAT_COLOR fg_color = ctl->base.fg_color;
1119 RETROFLAT_COLOR bg_color = ctl->base.bg_color;
1120 RETROFLAT_COLOR push_shadow_color = RETROFLAT_COLOR_DARKGRAY;
1122 MAUG_MHANDLE font_h = (MAUG_MHANDLE)NULL;
1123
1124 if( ctl->base.idc == gui->focus_idc ) {
1125 /* Assign selected color if focused. */
1126 fg_color = ctl->base.sel_fg;
1127 }
1128
1129 if( ctl->base.idc == gui->focus_idc ) {
1130 /* Assign selected color if focused. */
1131 bg_color = ctl->base.sel_bg;
1132 }
1133
1134 /* Figure out push shadow color for current color depth. */
1135 if( 2 >= retroflat_screen_colors() ) {
1136 push_shadow_color = RETROFLAT_COLOR_BLACK;
1137 border_color = RETROFLAT_COLOR_BLACK;
1138 }
1139
1140 retroflat_2d_rect(
1141 gui->draw_bmp, bg_color, gui->x + ctl->base.x, gui->y + ctl->base.y,
1142 ctl->base.w, ctl->base.h, RETROFLAT_DRAW_FLAG_FILL );
1143
1144 retroflat_2d_rect( gui->draw_bmp, border_color,
1145 gui->x + ctl->base.x, gui->y + ctl->base.y,
1146 ctl->base.w, ctl->base.h, 0 );
1147
1148 /* Draw the push shadows on top/left or bottom/right, depending on pushed
1149 * status.
1150 */
1151 if( 0 < ctl->BUTTON.push_frames ) {
1152 retroflat_2d_line(
1153 gui->draw_bmp, push_shadow_color,
1154 gui->x + ctl->base.x + 1, gui->y + ctl->base.y + 1,
1155 gui->x + ctl->base.x + ctl->base.w - 2, gui->y + ctl->base.y + 1, 0 );
1156 retroflat_2d_line(
1157 gui->draw_bmp, push_shadow_color,
1158 gui->x + ctl->base.x + 1, gui->y + ctl->base.y + 2,
1159 gui->x + ctl->base.x + 1, gui->y + ctl->base.y + ctl->base.h - 3, 0 );
1160
1161 gui->flags |= RETROGUI_FLAGS_DIRTY; /* Mark dirty for push animation. */
1162 ctl->BUTTON.push_frames--;
1163 text_offset = 1;
1164 } else {
1165 /* Button is not pushed. */
1166 retroflat_2d_line(
1167 gui->draw_bmp, RETROFLAT_COLOR_WHITE,
1168 gui->x + ctl->base.x + 1, gui->y + ctl->base.y + 1,
1169 gui->x + ctl->base.x + ctl->base.w - 2, gui->y + ctl->base.y + 1, 0 );
1170 retroflat_2d_line(
1171 gui->draw_bmp, RETROFLAT_COLOR_WHITE,
1172 gui->x + ctl->base.x + 1, gui->y + ctl->base.y + 2,
1173 gui->x + ctl->base.x + 1, gui->y + ctl->base.y + ctl->base.h - 3, 0 );
1174 }
1175
1176 maug_mlock( ctl->BUTTON.label_h, ctl->BUTTON.label );
1177 if( NULL == ctl->BUTTON.label ) {
1178 error_printf( "could not lock BUTTON label!" );
1179 goto cleanup;
1180 }
1181
1182 /* Grab the string size and use it to center the text in the control. */
1183#ifdef RETROGXC_PRESENT
1184 font_h = retrogxc_get_asset(
1185 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
1186 if( (MAUG_MHANDLE)NULL == font_h ) {
1187 goto cleanup;
1188 }
1189#else
1190 font_h = gui->font.handle;
1191#endif /* RETROGXC_PRESENT */
1192 retrofont_string_sz(
1193 gui->draw_bmp, ctl->BUTTON.label, 0, font_h,
1194 ctl->base.w, ctl->base.h, &w, &h, ctl->BUTTON.font_flags );
1195
1197 gui->draw_bmp, fg_color, ctl->BUTTON.label, 0, font_h,
1198 gui->x + ctl->base.x + ((ctl->base.w >> 1) - (w >> 1)) + text_offset,
1199 gui->y + ctl->base.y + ((ctl->base.h >> 1) - (h >> 1)) + text_offset,
1200 /* TODO: Pad max client area. */
1201 ctl->base.w, ctl->base.h, ctl->BUTTON.font_flags );
1202
1203 maug_munlock( ctl->BUTTON.label_h, ctl->BUTTON.label );
1204
1205cleanup:
1206
1207 return;
1208}
1209
1210static MERROR_RETVAL retrogui_push_BUTTON( union RETROGUI_CTL* ctl ) {
1211 MERROR_RETVAL retval = MERROR_OK;
1212
1213# if defined( RETROGUI_NATIVE_WIN )
1214
1215 ctl->base.hwnd = CreateWindow(
1216 "BUTTON", ctl->BUTTON.label, WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
1217 gui->x + ctl->base.x, gui->y + ctl->base.y, ctl->base.w, ctl->base.h,
1218 g_retroflat_state->window, (HMENU)(ctl->base.idc),
1219 g_retroflat_instance, NULL );
1220 if( (HWND)NULL == ctl->base.hwnd ) {
1222 "Could not create button " RETROGUI_IDC_FMT ": %s",
1223 ctl->base.idc, ctl->BUTTON.label );
1224 retval = MERROR_GUI;
1225 goto cleanup;
1226 }
1227
1228# else
1229 char* label_tmp = NULL;
1230
1231#if RETROGUI_TRACE_LVL > 0
1232 debug_printf( RETROGUI_TRACE_LVL, "pushing BUTTON control..." );
1233#endif /* RETROGUI_TRACE_LVL */
1234
1235 _retrogui_copy_str(
1236 label, ctl->BUTTON.label, ctl->BUTTON, label_tmp, ctl->BUTTON.label_sz );
1237 ctl->BUTTON.label = NULL;
1238# endif
1239
1240cleanup:
1241
1242 return retval;
1243}
1244
1245static MERROR_RETVAL retrogui_sz_BUTTON(
1246 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1249) {
1250 MERROR_RETVAL retval = MERROR_OK;
1251 MAUG_MHANDLE font_h = (MAUG_MHANDLE)NULL;
1252
1253 assert( NULL != ctl );
1254 assert( NULL == ctl->BUTTON.label );
1255 assert( (MAUG_MHANDLE)NULL != ctl->BUTTON.label_h );
1256
1257 maug_mlock( ctl->BUTTON.label_h, ctl->BUTTON.label );
1258 maug_cleanup_if_null_lock( char*, ctl->BUTTON.label );
1259
1260 /* Get the size of the text-based GUI item. */
1261#ifdef RETROGXC_PRESENT
1262 font_h = retrogxc_get_asset(
1263 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
1264 maug_cleanup_if_null_alloc( MAUG_MHANDLE, font_h );
1265#else
1266 font_h = gui->font.handle;
1267#endif /* RETROGXC_PRESENT */
1268 retrofont_string_sz(
1269 NULL, ctl->BUTTON.label, 0, font_h,
1270 max_w - 8, max_h - 8, p_w, p_h, ctl->BUTTON.font_flags );
1271
1272 /* Add space for borders and stuff. */
1273 *p_w += RETROGUI_BTN_LBL_PADDED_X;
1274 *p_h += RETROGUI_BTN_LBL_PADDED_Y;
1275
1276cleanup:
1277
1278 maug_munlock( ctl->BUTTON.label_h, ctl->BUTTON.label );
1279
1280 return retval;
1281}
1282
1283static MERROR_RETVAL retrogui_pos_BUTTON(
1284 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1287) {
1288 MERROR_RETVAL retval = MERROR_OK;
1289
1290# if defined( RETROGUI_NATIVE_WIN )
1291 /* TODO */
1292# else
1293 assert( NULL != ctl );
1294
1295 ctl->base.x = x;
1296 ctl->base.y = y;
1297 if( 0 < w ) {
1298 ctl->base.w = w;
1299 }
1300 if( 0 < h ) {
1301 ctl->base.h = h;
1302 }
1303# endif /* RETROGUI_NATIVE_WIN */
1304
1305 return retval;
1306}
1307
1308static void retrogui_destroy_BUTTON( union RETROGUI_CTL* ctl ) {
1309 if( (MAUG_MHANDLE)NULL != ctl->BUTTON.label_h ) {
1310 maug_mfree( ctl->BUTTON.label_h );
1311 }
1312}
1313
1314static MERROR_RETVAL retrogui_init_BUTTON( union RETROGUI_CTL* ctl ) {
1315 MERROR_RETVAL retval = MERROR_OK;
1316
1317#if RETROGUI_TRACE_LVL > 0
1318 debug_printf( RETROGUI_TRACE_LVL,
1319 "initializing button " RETROGUI_IDC_FMT "...", ctl->base.idc );
1320#endif /* RETROGUI_TRACE_LVL */
1321
1322 if( 2 < retroflat_screen_colors() ) {
1323 ctl->base.fg_color = RETROGUI_COLOR_BORDER;
1324 ctl->base.bg_color = RETROFLAT_COLOR_GRAY;
1325 ctl->base.sel_fg = RETROFLAT_COLOR_BLUE;
1326 ctl->base.sel_bg = RETROFLAT_COLOR_GRAY;
1327 } else {
1328 ctl->base.fg_color = RETROFLAT_COLOR_BLACK;
1329 ctl->base.bg_color = RETROFLAT_COLOR_WHITE;
1330 ctl->base.sel_fg = RETROFLAT_COLOR_WHITE;
1331 ctl->base.sel_bg = RETROFLAT_COLOR_BLACK;
1332 }
1333
1334 return retval;
1335}
1336
1337#ifndef RETROGUI_NO_TEXTBOX
1338
1339/* === Control: TEXTBOX === */
1340
1341static retrogui_idc_t retrogui_click_TEXTBOX(
1342 struct RETROGUI* gui,
1343 union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1344 struct RETROFLAT_INPUT* input_evt
1345) {
1346 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
1347
1348 return idc_out;
1349}
1350
1351static retrogui_idc_t retrogui_key_TEXTBOX(
1352 struct RETROGUI* gui, union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1353 struct RETROFLAT_INPUT* input_evt
1354) {
1355 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
1356 char c = '\0';
1357
1358# if defined( RETROGUI_NATIVE_WIN )
1359 /* Do nothing. */
1360# else
1361
1362 c = retroflat_vk_to_ascii( *p_input, input_evt->key_flags );
1363
1364 /* Ignore non-printable characters. */
1365 if(
1366 0 == c &&
1367 RETROFLAT_KEY_RIGHT != *p_input &&
1368 RETROFLAT_KEY_LEFT != *p_input
1369 ) {
1370 goto cleanup;
1371 }
1372
1373 /* Lock text field. */
1374 assert( NULL == ctl->TEXTBOX.text );
1375 assert( (MAUG_MHANDLE)NULL != ctl->TEXTBOX.text_h );
1376 maug_mlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
1377 if( NULL == ctl->TEXTBOX.text ) {
1378 error_printf( "could not lock TEXTBOX text handle!" );
1379 goto cleanup;
1380 }
1381
1382 switch( *p_input ) {
1383 case RETROFLAT_KEY_BKSP:
1385 ctl->TEXTBOX.text, ctl->TEXTBOX.text_cur, ctl->TEXTBOX.text_sz )
1386 break;
1387
1388 case RETROFLAT_KEY_ENTER:
1389 idc_out = ctl->base.idc;
1390 break;
1391
1392 case RETROFLAT_KEY_LEFT:
1393 if( 0 < ctl->TEXTBOX.text_cur ) {
1394 ctl->TEXTBOX.text_cur--;
1395 }
1396 break;
1397
1398 case RETROFLAT_KEY_RIGHT:
1399 if( ctl->TEXTBOX.text_sz > ctl->TEXTBOX.text_cur ) {
1400 ctl->TEXTBOX.text_cur++;
1401 }
1402 break;
1403
1404 default:
1405 assert( ctl->TEXTBOX.text_sz < ctl->TEXTBOX.text_sz_max );
1406 debug_printf( 1, "cur: %d, sz: %d, sz_max: %d",
1407 ctl->TEXTBOX.text_cur, ctl->TEXTBOX.text_sz, ctl->TEXTBOX.text_sz_max );
1409 ctl->TEXTBOX.text,
1410 ctl->TEXTBOX.text_cur,
1411 ctl->TEXTBOX.text_sz,
1412 ctl->TEXTBOX.text_sz_max );
1413 break;
1414 }
1415
1416 /* TODO: Remove input from queue? */
1417
1418cleanup:
1419
1420 if( NULL != ctl->TEXTBOX.text ) {
1421 maug_munlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
1422 }
1423
1424# endif
1425
1426 return idc_out;
1427}
1428
1429static void retrogui_redraw_TEXTBOX(
1430 struct RETROGUI* gui, union RETROGUI_CTL* ctl
1431) {
1432 RETROFLAT_COLOR shadow_color = RETROFLAT_COLOR_DARKGRAY;
1434 retroflat_pxxy_t cursor_x = 0,
1435 cursor_y = 0;
1436 MAUG_MHANDLE font_h = (MAUG_MHANDLE)NULL;
1437
1438 /* Adjust shadow colors for monochrome. */
1439 if( 2 >= retroflat_screen_colors() ) {
1440 shadow_color = RETROFLAT_COLOR_BLACK;
1441 border_color = RETROFLAT_COLOR_BLACK;
1442 }
1443
1444# if defined( RETROGUI_NATIVE_WIN )
1445 /* Do nothing. */
1446# else
1447
1448 retroflat_2d_rect( gui->draw_bmp, ctl->base.bg_color,
1449 gui->x + ctl->base.x, gui->y + ctl->base.y,
1450 ctl->base.w, ctl->base.h, RETROFLAT_DRAW_FLAG_FILL );
1451
1452 /* Draw chiselled inset border. */
1453
1454 retroflat_2d_rect( gui->draw_bmp, border_color,
1455 gui->x + ctl->base.x,
1456 gui->y + ctl->base.y, ctl->base.w, 2,
1458
1459 retroflat_2d_rect( gui->draw_bmp, border_color,
1460 gui->x + ctl->base.x,
1461 gui->y + ctl->base.y, 2, ctl->base.h,
1463
1464 retroflat_2d_rect( gui->draw_bmp, shadow_color,
1465 gui->x + ctl->base.x,
1466 gui->y + ctl->base.y + ctl->base.h - 1,
1467 ctl->base.w, 2,
1469
1470 retroflat_2d_rect( gui->draw_bmp, shadow_color,
1471 gui->x + ctl->base.x + ctl->base.w - 1,
1472 gui->y + ctl->base.y, 2, ctl->base.h,
1474
1475 /* Draw text. */
1476
1477 assert( NULL == ctl->TEXTBOX.text );
1478 maug_mlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
1479 if( NULL == ctl->TEXTBOX.text ) {
1480 goto cleanup;
1481 }
1482
1483#ifdef RETROGXC_PRESENT
1484 font_h = retrogxc_get_asset(
1485 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
1486 if( (MAUG_MHANDLE)NULL == font_h ) {
1487 goto cleanup;
1488 }
1489#else
1490 font_h = gui->font.handle;
1491#endif /* RETROGXC_PRESENT */
1493 gui->draw_bmp, ctl->base.fg_color,
1494 ctl->TEXTBOX.text, ctl->TEXTBOX.text_cur, font_h,
1495 gui->x + ctl->base.x + RETROGUI_PADDING,
1496 gui->y + ctl->base.y + RETROGUI_PADDING, ctl->base.w, ctl->base.h, 0 );
1497
1498 /* Get the line size for cursor placement below. */
1499 retrofont_string_sz(
1500 gui->draw_bmp, ctl->TEXTBOX.text, ctl->TEXTBOX.text_cur, font_h,
1501 ctl->base.w, ctl->base.h, &cursor_x, &cursor_y, RETROFONT_FLAG_SZ_MIN );
1502
1503 if( cursor_x + RETROGUI_CTL_TEXT_CUR_WH >= ctl->base.w ) {
1504 cursor_x = 0;
1505 /* TODO: Use font height. */
1506 cursor_y += RETROGUI_CTL_TEXT_CUR_WH;
1507 }
1508
1509 /* Use same padding as font for cursor. */
1510 retroflat_2d_rect( gui->draw_bmp,
1511 ctl->base.sel_fg,
1512 gui->x + ctl->base.x + RETROGUI_PADDING + cursor_x,
1513 gui->y + ctl->base.y + RETROGUI_PADDING + cursor_y,
1514 RETROGUI_CTL_TEXT_CUR_WH, RETROGUI_CTL_TEXT_CUR_WH,
1515 /* Draw blinking cursor. */
1516 /* TODO: Use a global timer to mark this field dirty. */
1517 gui->focus_idc == ctl->base.idc &&
1518 0 < ctl->TEXTBOX.blink_frames ? RETROFLAT_DRAW_FLAG_FILL : 0 );
1519
1520 if( (-1 * RETROGUI_CTL_TEXT_BLINK_FRAMES) > --(ctl->TEXTBOX.blink_frames) ) {
1521 ctl->TEXTBOX.blink_frames = RETROGUI_CTL_TEXT_BLINK_FRAMES;
1522 }
1523
1524 if( NULL != ctl->TEXTBOX.text ) {
1525 /* Draw the text that comes after the cursor. */
1526#ifdef RETROGXC_PRESENT
1527 font_h = retrogxc_get_asset(
1528 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
1529 if( (MAUG_MHANDLE)NULL == font_h ) {
1530 goto cleanup;
1531 }
1532#else
1533 font_h = gui->font.handle;
1534#endif /* RETROGXC_PRESENT */
1535 retrofont_string_indent(
1536 gui->draw_bmp, ctl->base.fg_color,
1537 &(ctl->TEXTBOX.text[ctl->TEXTBOX.text_cur]),
1538 /* Chop off chars from first half. */
1539 strlen( ctl->TEXTBOX.text ) - ctl->TEXTBOX.text_cur, font_h,
1540 gui->x + ctl->base.x + RETROGUI_PADDING,
1541 /* Post-cursor line beings on cursor line. */
1542 gui->y + ctl->base.y + RETROGUI_PADDING + cursor_y,
1543 ctl->base.w, ctl->base.h,
1544 cursor_x + RETROGUI_CTL_TEXT_CUR_WH, 0 );
1545
1546 maug_munlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
1547 }
1548
1549 gui->flags |= RETROGUI_FLAGS_DIRTY; /* Mark dirty for blink animation. */
1550
1551cleanup:
1552
1553# endif
1554
1555 return;
1556}
1557
1558static MERROR_RETVAL retrogui_push_TEXTBOX( union RETROGUI_CTL* ctl ) {
1559 MERROR_RETVAL retval = MERROR_OK;
1560
1561# if defined( RETROGUI_NATIVE_WIN )
1562
1563 ctl->base.hwnd = CreateWindow(
1564 "EDIT", 0, WS_CHILD | WS_VISIBLE | WS_BORDER,
1565 gui->x + ctl->base.x, gui->y + ctl->base.y, ctl->base.w, ctl->base.h,
1566 g_retroflat_state->window, (HMENU)(ctl->base.idc),
1567 g_retroflat_instance, NULL );
1568 if( (HWND)NULL == ctl->base.hwnd ) {
1570 "Could not create textbox: " RETROGUI_IDC_FMT, ctl->base.idc );
1571 retval = MERROR_GUI;
1572 goto cleanup;
1573 }
1574
1575# else
1576
1577#if RETROGUI_TRACE_LVL > 0
1578 debug_printf( RETROGUI_TRACE_LVL,
1579 "clearing textbox " RETROGUI_IDC_FMT " buffer...", ctl->base.idc );
1580#endif /* RETROGUI_TRACE_LVL */
1581 assert( (MAUG_MHANDLE)NULL == ctl->TEXTBOX.text_h );
1582 maug_malloc_test( ctl->TEXTBOX.text_h, RETROGUI_CTL_TEXT_SZ_MAX + 1, 1 );
1583 ctl->TEXTBOX.text_sz_max = RETROGUI_CTL_TEXT_SZ_MAX;
1584
1585 maug_mlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
1586 maug_cleanup_if_null_alloc( char*, ctl->TEXTBOX.text );
1587#if RETROGUI_TRACE_LVL > 0
1588 debug_printf( RETROGUI_TRACE_LVL,
1589 "clearing textbox " RETROGUI_IDC_FMT " buffer...", ctl->base.idc );
1590#endif /* RETROGUI_TRACE_LVL */
1591 maug_mzero( ctl->TEXTBOX.text, RETROGUI_CTL_TEXT_SZ_MAX + 1 );
1592 maug_munlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
1593
1594# endif
1595
1596cleanup:
1597
1598 return retval;
1599}
1600
1601static MERROR_RETVAL retrogui_sz_TEXTBOX(
1602 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1605) {
1606 MERROR_RETVAL retval = MERROR_GUI;
1607 /* TODO */
1608 return retval;
1609}
1610
1611static MERROR_RETVAL retrogui_pos_TEXTBOX(
1612 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1615) {
1616 MERROR_RETVAL retval = MERROR_GUI;
1617 /* TODO */
1618 return retval;
1619}
1620
1621static void retrogui_destroy_TEXTBOX( union RETROGUI_CTL* ctl ) {
1622 if( (MAUG_MHANDLE)NULL != ctl->TEXTBOX.text_h ) {
1623 maug_mfree( ctl->TEXTBOX.text_h );
1624 }
1625}
1626
1627static MERROR_RETVAL retrogui_init_TEXTBOX( union RETROGUI_CTL* ctl ) {
1628 MERROR_RETVAL retval = MERROR_OK;
1629
1630#if RETROGUI_TRACE_LVL > 0
1631 debug_printf( RETROGUI_TRACE_LVL,
1632 "initializing textbox " RETROGUI_IDC_FMT "...", ctl->base.idc );
1633#endif /* RETROGUI_TRACE_LVL */
1634
1635 ctl->base.bg_color = RETROFLAT_COLOR_WHITE;
1636 ctl->base.sel_bg = RETROFLAT_COLOR_WHITE;
1637 if( 2 < retroflat_screen_colors() ) {
1638 ctl->base.sel_fg = RETROFLAT_COLOR_BLUE;
1639 ctl->base.fg_color = RETROGUI_COLOR_BORDER;
1640 } else {
1641 ctl->base.sel_fg = RETROFLAT_COLOR_BLACK;
1642 ctl->base.fg_color = RETROFLAT_COLOR_BLACK;
1643 }
1644
1645 return retval;
1646}
1647
1648#endif /* RETROGUI_NO_TEXTBOX */
1649
1650/* === Control: LABEL === */
1651
1652static retrogui_idc_t retrogui_click_LABEL(
1653 struct RETROGUI* gui,
1654 union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1655 struct RETROFLAT_INPUT* input_evt
1656) {
1657 return RETROGUI_IDC_NONE;
1658}
1659
1660static retrogui_idc_t retrogui_key_LABEL(
1661 struct RETROGUI* gui, union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1662 struct RETROFLAT_INPUT* input_evt
1663) {
1664 return RETROGUI_IDC_NONE;
1665}
1666
1667static void retrogui_redraw_LABEL(
1668 struct RETROGUI* gui, union RETROGUI_CTL* ctl
1669) {
1670 size_t show_sz = 0;
1671 MAUG_MHANDLE font_h = (MAUG_MHANDLE)NULL;
1672
1673# if defined( RETROGUI_NATIVE_WIN )
1674 /* Do nothing. */
1675# else
1676
1677 /* Draw text. */
1678
1679 assert( NULL == ctl->LABEL.label );
1680 maug_mlock( ctl->LABEL.label_h, ctl->LABEL.label );
1681 if( NULL == ctl->LABEL.label ) {
1682 error_printf( "could not lock LABEL text!" );
1683 goto cleanup;
1684 }
1685
1686 if(
1688 (RETROGUI_LABEL_FLAG_SHOWINC & ctl->LABEL.flags)
1689 ) {
1690 if( 0 < ctl->LABEL.show_ticks ) {
1691 /* Increment timer to hold on this much of the label. */
1692 ctl->LABEL.show_ticks--;
1693 show_sz = ctl->LABEL.shown_sz;
1694 if( ctl->LABEL.shown_sz < ctl->LABEL.label_sz ) {
1695 gui->flags |= RETROGUI_FLAGS_DIRTY;
1696 }
1697
1698 } else {
1699 /* Reset ticks counter. */
1700 ctl->LABEL.show_ticks =
1702 (RETROGUI_LABEL_FLAG_SHOWINC_SLOW & ctl->LABEL.flags) ?
1705
1706 /* Work out how much of the label to show this time. */
1707 show_sz = ctl->LABEL.shown_sz + RETROGUI_LABEL_SHOW_INC;
1708 if( show_sz > ctl->LABEL.label_sz ) {
1709 /* Trim down show size to label size. */
1710 show_sz = ctl->LABEL.label_sz;
1711 } else {
1712 /* Not done showing yet! */
1713 gui->flags |= RETROGUI_FLAGS_DIRTY;
1714 }
1715
1716 /* Finalize whatever decision we came to above. */
1717 ctl->LABEL.shown_sz = show_sz;
1718 }
1719
1720 } else {
1721 show_sz = ctl->LABEL.label_sz;
1722 }
1723
1724#ifdef RETROGXC_PRESENT
1725 font_h = retrogxc_get_asset(
1726 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
1727 if( (MAUG_MHANDLE)NULL == font_h ) {
1728 goto cleanup;
1729 }
1730#else
1731 font_h = gui->font.handle;
1732#endif /* RETROGXC_PRESENT */
1734 gui->draw_bmp, ctl->base.fg_color, ctl->LABEL.label, show_sz, font_h,
1735 gui->x + ctl->base.x + RETROGUI_PADDING,
1736 gui->y + ctl->base.y + RETROGUI_PADDING, ctl->base.w, ctl->base.h,
1737 ctl->LABEL.font_flags );
1738
1739cleanup:
1740
1741 if( NULL != ctl->LABEL.label ) {
1742 maug_munlock( ctl->LABEL.label_h, ctl->LABEL.label );
1743 }
1744
1745# endif
1746
1747 return;
1748}
1749
1750static MERROR_RETVAL retrogui_push_LABEL( union RETROGUI_CTL* ctl ) {
1751 MERROR_RETVAL retval = MERROR_OK;
1752
1753# if defined( RETROGUI_NATIVE_WIN )
1754
1755 /* TODO */
1756
1757# else
1758 char* label_tmp = NULL;
1759
1760#if RETROGUI_TRACE_LVL > 0
1761 debug_printf( RETROGUI_TRACE_LVL, "pushing LABEL control..." );
1762#endif /* RETROGUI_TRACE_LVL */
1763
1764 _retrogui_copy_str(
1765 label, ctl->LABEL.label, ctl->LABEL, label_tmp, ctl->LABEL.label_sz );
1766 ctl->LABEL.label = NULL;
1767 ctl->LABEL.shown_sz = 1;
1768 ctl->LABEL.show_ticks =
1770 (RETROGUI_LABEL_FLAG_SHOWINC_SLOW & ctl->LABEL.flags) ?
1773# endif
1774
1775cleanup:
1776
1777 return retval;
1778}
1779
1780static MERROR_RETVAL retrogui_sz_LABEL(
1781 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1784) {
1785 MERROR_RETVAL retval = MERROR_GUI;
1786 /* TODO */
1787 return retval;
1788}
1789
1790static MERROR_RETVAL retrogui_pos_LABEL(
1791 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1794) {
1795 MERROR_RETVAL retval = MERROR_GUI;
1796 /* TODO */
1797 return retval;
1798}
1799
1800static void retrogui_destroy_LABEL( union RETROGUI_CTL* ctl ) {
1801 if( (MAUG_MHANDLE)NULL != ctl->LABEL.label_h ) {
1802 maug_mfree( ctl->LABEL.label_h );
1803 }
1804}
1805
1806static MERROR_RETVAL retrogui_init_LABEL( union RETROGUI_CTL* ctl ) {
1807 MERROR_RETVAL retval = MERROR_OK;
1808
1809#if RETROGUI_TRACE_LVL > 0
1810 debug_printf( RETROGUI_TRACE_LVL,
1811 "initializing label " RETROGUI_IDC_FMT "...", ctl->base.idc );
1812#endif /* RETROGUI_TRACE_LVL */
1813
1814 if( 2 < retroflat_screen_colors() ) {
1815 ctl->base.fg_color = RETROGUI_COLOR_BORDER;
1816 } else {
1817 ctl->base.fg_color = RETROFLAT_COLOR_BLACK;
1818 }
1819 ctl->base.bg_color = RETROFLAT_COLOR_WHITE;
1820
1821 return retval;
1822}
1823
1824/* === Control: IMAGE === */
1825
1826static retrogui_idc_t retrogui_click_IMAGE(
1827 struct RETROGUI* gui,
1828 union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1829 struct RETROFLAT_INPUT* input_evt
1830) {
1831 return RETROGUI_IDC_NONE;
1832}
1833
1834static retrogui_idc_t retrogui_key_IMAGE(
1835 struct RETROGUI* gui, union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1836 struct RETROFLAT_INPUT* input_evt
1837) {
1838 return RETROGUI_IDC_NONE;
1839}
1840
1841static void retrogui_redraw_IMAGE(
1842 struct RETROGUI* gui, union RETROGUI_CTL* ctl
1843) {
1844# if defined( RETROGUI_NATIVE_WIN )
1845 /* Do nothing. */
1846# else
1847
1848# if defined( RETROGXC_PRESENT )
1849 if( 0 <= ctl->IMAGE.image_cache_id ) {
1850 /* Draw the image from the cache if there is one. */
1851
1852#if RETROGUI_TRACE_LVL > 0
1853 debug_printf( RETROGUI_TRACE_LVL,
1854 "redrawing image ctl " RETROGUI_IDC_FMT
1855 ", cache ID " SSIZE_T_FMT "...",
1856 ctl->base.idc, ctl->IMAGE.image_cache_id );
1857#endif /* RETROGUI_TRACE_LVL */
1858 retrogxc_blit_bitmap(
1859 gui->draw_bmp,
1860 ctl->IMAGE.image_cache_id,
1861 ctl->IMAGE.src_x, ctl->IMAGE.src_y,
1862 gui->x + ctl->base.x, gui->y + ctl->base.y, ctl->base.w, ctl->base.h,
1863 ctl->IMAGE.instance );
1864 } else
1865# endif /* RETROGXC_PRESENT */
1866 if( retroflat_2d_bitmap_ok( &(ctl->IMAGE.image) ) ) {
1867 /* If no cached image (or cache), try to draw the stored image. */
1868 retroflat_2d_blit_bitmap(
1869 gui->draw_bmp,
1870 &(ctl->IMAGE.image),
1871 ctl->IMAGE.src_x, ctl->IMAGE.src_y,
1872 gui->x + ctl->base.x, gui->y + ctl->base.y, ctl->base.w, ctl->base.h,
1873 ctl->IMAGE.instance );
1874 }
1875
1876# endif /* RETROGUI_NATIVE_WIN */
1877
1878 return;
1879}
1880
1881static MERROR_RETVAL retrogui_push_IMAGE( union RETROGUI_CTL* ctl ) {
1882 MERROR_RETVAL retval = MERROR_OK;
1883
1884# if defined( RETROGUI_NATIVE_WIN )
1885
1886 /* TODO */
1887
1888# else
1889
1890#if RETROGUI_TRACE_LVL > 0
1891 debug_printf( RETROGUI_TRACE_LVL, "pushing IMAGE control..." );
1892#endif /* RETROGUI_TRACE_LVL */
1893
1894 assert( !retroflat_2d_bitmap_ok( &(ctl->IMAGE.image) ) );
1895
1896 /* TODO: Copy non-cached image. */
1897
1898 /* ctl->IMAGE.cache_idx = NULL; */
1899# endif
1900
1901 return retval;
1902}
1903
1904static MERROR_RETVAL retrogui_sz_IMAGE(
1905 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1908) {
1909 MERROR_RETVAL retval = MERROR_GUI;
1910
1911 *p_w = 0;
1912 *p_h = 0;
1913
1914# ifdef RETROGXC_PRESENT
1915 retval = retrogxc_bitmap_wh( ctl->IMAGE.image_cache_id, p_w, p_h );
1916 maug_cleanup_if_not_ok();
1917# endif /* RETROGXC_PRESENT */
1918
1919 if(
1920 0 == *p_w && 0 == *p_h &&
1921 !retroflat_2d_bitmap_ok( &(ctl->IMAGE.image) )
1922 ) {
1923 error_printf( "image not assigned!" );
1924 retval = MERROR_GUI;
1925 goto cleanup;
1926 }
1927
1928 *p_w = retroflat_2d_bitmap_w( &(ctl->IMAGE.image) );
1929 *p_h = retroflat_2d_bitmap_h( &(ctl->IMAGE.image) );
1930
1931cleanup:
1932
1933 return retval;
1934}
1935
1936static MERROR_RETVAL retrogui_pos_IMAGE(
1937 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1940) {
1941 MERROR_RETVAL retval = MERROR_GUI;
1942 /* TODO */
1943 return retval;
1944}
1945
1946static void retrogui_destroy_IMAGE( union RETROGUI_CTL* ctl ) {
1947 if( retroflat_2d_bitmap_ok( &(ctl->IMAGE.image) ) ) {
1948 retroflat_2d_destroy_bitmap( &(ctl->IMAGE.image) );
1949 }
1950}
1951
1952static MERROR_RETVAL retrogui_init_IMAGE( union RETROGUI_CTL* ctl ) {
1953 MERROR_RETVAL retval = MERROR_OK;
1954
1955#if RETROGUI_TRACE_LVL > 0
1956 debug_printf( RETROGUI_TRACE_LVL,
1957 "initializing IMAGE " RETROGUI_IDC_FMT "...", ctl->base.idc );
1958#endif /* RETROGUI_TRACE_LVL */
1959
1960 return retval;
1961}
1962
1963/* === Control: FILLBAR === */
1964
1965static retrogui_idc_t retrogui_click_FILLBAR(
1966 struct RETROGUI* gui,
1967 union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1968 struct RETROFLAT_INPUT* input_evt
1969) {
1970 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
1971
1972 return idc_out;
1973}
1974
1975static retrogui_idc_t retrogui_key_FILLBAR(
1976 struct RETROGUI* gui, union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1977 struct RETROFLAT_INPUT* input_evt
1978) {
1979 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
1980
1981 return idc_out;
1982}
1983
1984static void retrogui_redraw_FILLBAR(
1985 struct RETROGUI* gui, union RETROGUI_CTL* ctl
1986) {
1987 retroflat_pxxy_t fill_w = 0;
1988
1989 if( 0 == ctl->FILLBAR.cur ) {
1990 fill_w = 0;
1991 } else {
1992 fill_w = ctl->base.w * ctl->FILLBAR.cur / ctl->FILLBAR.max;
1993 }
1994
1995 retroflat_2d_rect(
1996 gui->draw_bmp, ctl->base.bg_color,
1997 gui->x + ctl->base.x + fill_w,
1998 gui->y + ctl->base.y,
1999 ctl->base.w - fill_w, ctl->base.h, RETROFLAT_DRAW_FLAG_FILL );
2000
2001 retroflat_2d_rect(
2002 gui->draw_bmp, ctl->base.fg_color,
2003 gui->x + ctl->base.x,
2004 gui->y + ctl->base.y,
2005 fill_w, ctl->base.h, RETROFLAT_DRAW_FLAG_FILL );
2006
2007 return;
2008}
2009
2010static MERROR_RETVAL retrogui_push_FILLBAR( union RETROGUI_CTL* ctl ) {
2011 MERROR_RETVAL retval = MERROR_OK;
2012
2013# if defined( RETROGUI_NATIVE_WIN )
2014
2015 /* TODO: Native fillbar implementation. */
2016
2017# endif
2018
2019 return retval;
2020}
2021
2022static MERROR_RETVAL retrogui_sz_FILLBAR(
2023 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
2026) {
2027 MERROR_RETVAL retval = MERROR_OK;
2028
2029 assert( NULL != ctl );
2030
2031 /* TODO? */
2032
2033 return retval;
2034}
2035
2036static MERROR_RETVAL retrogui_pos_FILLBAR(
2037 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
2040) {
2041 MERROR_RETVAL retval = MERROR_OK;
2042
2043# if defined( RETROGUI_NATIVE_WIN )
2044 /* TODO */
2045# else
2046 assert( NULL != ctl );
2047
2048 ctl->base.x = x;
2049 ctl->base.y = y;
2050 if( 0 < w ) {
2051 ctl->base.w = w;
2052 }
2053 if( 0 < h ) {
2054 ctl->base.h = h;
2055 }
2056# endif /* RETROGUI_NATIVE_WIN */
2057
2058 return retval;
2059}
2060
2061static void retrogui_destroy_FILLBAR( union RETROGUI_CTL* ctl ) {
2062}
2063
2064static MERROR_RETVAL retrogui_init_FILLBAR( union RETROGUI_CTL* ctl ) {
2065 MERROR_RETVAL retval = MERROR_OK;
2066
2067#if RETROGUI_TRACE_LVL > 0
2068 debug_printf( RETROGUI_TRACE_LVL,
2069 "initializing fillbar " RETROGUI_IDC_FMT "...", ctl->base.idc );
2070#endif /* RETROGUI_TRACE_LVL */
2071
2072 if( 2 < retroflat_screen_colors() ) {
2073 ctl->base.fg_color = RETROGUI_COLOR_BORDER;
2074 ctl->base.bg_color = RETROFLAT_COLOR_GRAY;
2075 } else {
2076 ctl->base.fg_color = RETROFLAT_COLOR_BLACK;
2077 ctl->base.bg_color = RETROFLAT_COLOR_WHITE;
2078 }
2079
2080 return retval;
2081}
2082
2083/* === Static Internal Functions === */
2084
2085static union RETROGUI_CTL* _retrogui_get_ctl_by_idc(
2086 struct RETROGUI* gui, retrogui_idc_t idc
2087) {
2088 size_t i = 0;
2089 union RETROGUI_CTL* ctl = NULL;
2090
2091 assert( mdata_vector_is_locked( &((gui)->ctls) ) );
2092
2093 for( i = 0 ; mdata_vector_ct( &(gui->ctls) ) > i ; i++ ) {
2094 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
2095 if( idc == ctl->base.idc ) {
2096 break;
2097 }
2098 ctl = NULL;
2099 }
2100
2101 if( NULL == ctl ) {
2102 error_printf( "could not find GUI item IDC " RETROGUI_IDC_FMT, idc );
2103 }
2104
2105 return ctl;
2106}
2107
2108/* === */
2109
2110static MERROR_RETVAL _retrogui_sz_ctl(
2111 struct RETROGUI* gui, retrogui_idc_t idc,
2114) {
2115 MERROR_RETVAL retval = MERROR_OK;
2116 union RETROGUI_CTL* ctl = NULL;
2117
2118 assert( mdata_vector_is_locked( &((gui)->ctls) ) );
2119
2120#if RETROGUI_TRACE_LVL > 0
2121 debug_printf( RETROGUI_TRACE_LVL,
2122 "sizing control " RETROGUI_IDC_FMT " to: " SIZE_T_FMT "x" SIZE_T_FMT,
2123 idc, max_w, max_h );
2124#endif /* RETROGUI_TRACE_LVL */
2125
2126 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2127 if( NULL == ctl ) {
2128 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2129 error_printf( "could not size item!" );
2130 retval = MERROR_GUI;
2131 goto cleanup;
2132 }
2133
2134 #define RETROGUI_CTL_TABLE_SZ( idx, c_name, c_fields ) \
2135 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2136 /* Mark dirty first so redraw can unmark it for animation! */ \
2137 retval = retrogui_sz_ ## c_name( gui, ctl, p_w, p_h, max_w, max_h ); \
2138 maug_cleanup_if_not_ok();
2139
2140 if( 0 ) {
2141 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_SZ )
2142 }
2143
2144#if RETROGUI_TRACE_LVL > 0
2145 debug_printf( RETROGUI_TRACE_LVL,
2146 "sized control " RETROGUI_IDC_FMT " at " SIZE_T_FMT "x" SIZE_T_FMT "...",
2147 ctl->base.idc, ctl->base.w, ctl->base.h );
2148#endif /* RETROGUI_TRACE_LVL */
2149
2150cleanup:
2151
2152 return retval;
2153}
2154
2155/* === Generic Functions === */
2156
2158 struct RETROGUI* gui, RETROFLAT_IN_KEY* p_input,
2159 struct RETROFLAT_INPUT* input_evt
2160) {
2161 size_t i = 0;
2162 retroflat_pxxy_t mouse_x = 0,
2163 mouse_y = 0;
2164 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
2165 union RETROGUI_CTL* ctl = NULL;
2166 MERROR_RETVAL retval = MERROR_OK;
2167
2168 if( 0 == mdata_vector_ct( &(gui->ctls) ) ) {
2169 return RETROGUI_IDC_NONE;
2170 }
2171
2172 assert( !mdata_vector_is_locked( &((gui)->ctls) ) );
2173 mdata_vector_lock( &(gui->ctls) );
2174
2175# if defined( RETROGUI_NATIVE_WIN )
2176
2177 if( 0 == g_retroflat_state->last_idc ) {
2178 /* No WM_COMMAND to process. */
2179 goto cleanup;
2180 }
2181
2182 ctl = retrogui_get_ctl_by_idc( gui, g_retroflat_state->last_idc );
2183 g_retroflat_state->last_idc = 0;
2184 if( NULL == ctl ) {
2185#if RETROGUI_TRACE_LVL > 0
2186 debug_printf( RETROGUI_TRACE_LVL,
2187 "invalid IDC: " RETROGUI_IDC_FMT, gui->focus_idc );
2188#endif /* RETROGUI_TRACE_LVL */
2189 goto cleanup;
2190 }
2191
2192# ifndef RETROGUI_NO_TEXTBOX
2193 if( RETROGUI_CTL_TYPE_TEXTBOX == ctl->base.type ) {
2194 if( SendMessage( ctl->base.hwnd, EM_GETMODIFY, 0, 0 ) ) {
2195 SendMessage( ctl->base.hwnd, EM_SETMODIFY, 0, 0 );
2196#if RETROGUI_TRACE_LVL > 0
2197 debug_printf( RETROGUI_TRACE_LVL, "mod: %d",
2198 SendMessage( ctl->base.hwnd, EM_GETMODIFY, 0, 0 ) );
2199#endif /* RETROGUI_TRACE_LVL */
2200 }
2201 }
2202# endif /* !RETROGUI_NO_TEXTBOX */
2203
2204# else
2205
2206 /* Use our cross-platform controls. */
2207
2208# define RETROGUI_CTL_TABLE_CLICK( idx, c_name, c_fields ) \
2209 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2210 gui->flags |= RETROGUI_FLAGS_DIRTY; \
2211 idc_out = retrogui_click_ ## c_name( gui, ctl, p_input, input_evt );
2212
2213# define RETROGUI_CTL_TABLE_KEY( idx, c_name, c_fields ) \
2214 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2215 gui->flags |= RETROGUI_FLAGS_DIRTY; \
2216 idc_out = retrogui_key_ ## c_name( gui, ctl, p_input, input_evt );
2217
2218 if( 0 != *p_input ) {
2219#if RETROGUI_TRACE_LVL > 0
2220 debug_printf( RETROGUI_TRACE_LVL,
2221 "focus " RETROGUI_IDC_FMT " input: %d", gui->focus_idc, *p_input );
2222#endif /* RETROGUI_TRACE_LVL */
2223 }
2224
2225 /* Don't accept any input until debounce period has expired. */
2226 if( retroflat_get_ms() < gui->debounce_next ) {
2227#if RETROGUI_TRACE_LVL > 0
2228 debug_printf( RETROGUI_TRACE_LVL,
2229 "debo! %d vs %d", retroflat_get_ms(), gui->debounce_next );
2230#endif /* RETROGUI_TRACE_LVL */
2231 goto cleanup;
2232#if RETROGUI_TRACE_LVL > 0
2233 } else if( 0 != *p_input ) {
2234 debug_printf( RETROGUI_TRACE_LVL, "no debo!" );
2235#endif /* RETROGUI_TRACE_LVL */
2236 }
2237
2238 if( 0 == *p_input ) {
2239 /* No input to debounce! */
2240 goto cleanup;
2241
2242 } else if(
2243 retroflat_or_key( *p_input, RETROGUI_KEY_ACTIVATE, RETROGUI_PAD_ACTIVATE )
2244 ) {
2245
2246 if( RETROGUI_IDC_NONE < gui->focus_idc ) {
2247#if RETROGUI_TRACE_LVL > 0
2248 debug_printf( RETROGUI_TRACE_LVL,
2249 "activate on focus IDC: " RETROGUI_IDC_FMT, gui->focus_idc );
2250#endif /* RETROGUI_TRACE_LVL */
2251 idc_out = gui->focus_idc;
2252 /* gui->focus_idc = -1; */
2253 gui->flags |= RETROGUI_FLAGS_DIRTY;
2254 }
2255
2256 } else if(
2257 retroflat_or_key( *p_input, RETROGUI_KEY_NEXT, RETROGUI_PAD_NEXT )
2258 ) {
2259 retrogui_focus_next( gui );
2260
2261#if RETROGUI_TRACE_LVL > 0
2262 debug_printf( RETROGUI_TRACE_LVL, "next: " RETROGUI_IDC_FMT, gui->focus_idc );
2263#endif /* RETROGUI_TRACE_LVL */
2264
2265 /* Cleanup after the menu. */
2266 *p_input = 0;
2267
2268 } else if(
2269 retroflat_or_key( *p_input, RETROGUI_KEY_PREV, RETROGUI_PAD_PREV )
2270 ) {
2271 retrogui_focus_prev( gui );
2272
2273#if RETROGUI_TRACE_LVL > 0
2274 debug_printf( RETROGUI_TRACE_LVL, "prev: " RETROGUI_IDC_FMT, gui->focus_idc );
2275#endif /* RETROGUI_TRACE_LVL */
2276
2277 /* Cleanup after the menu. */
2278 *p_input = 0;
2279
2280# ifndef RETROGUI_NO_MOUSE
2281 } else if(
2282 RETROFLAT_MOUSE_B_LEFT == *p_input ||
2283 RETROFLAT_MOUSE_B_RIGHT == *p_input
2284 ) {
2285 /* Handle mouse input. */
2286
2287 /* Remove all focus before testing if a new control has focus. */
2288#if RETROGUI_TRACE_LVL > 0
2289 debug_printf( RETROGUI_TRACE_LVL, "resetting focus for mouse click..." );
2290#endif /* RETROGUI_TRACE_LVL */
2291 gui->focus_idc = RETROGUI_IDC_NONE;
2292
2293 mouse_x = input_evt->mouse_x - gui->x;
2294 mouse_y = input_evt->mouse_y - gui->y;
2295
2296 for( i = 0 ; mdata_vector_ct( &(gui->ctls) ) > i ; i++ ) {
2297 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
2298 if(
2299 mouse_x < ctl->base.x ||
2300 mouse_y < ctl->base.y ||
2301 mouse_x > ctl->base.x + ctl->base.w ||
2302 mouse_y > ctl->base.y + ctl->base.h
2303 ) {
2304 continue;
2305 }
2306
2307 if( gui->idc_prev == ctl->base.idc ) {
2308 /* No repeated clicks! */
2309 /* TODO: Allow exceptions for e.g. scrollbars. */
2310 idc_out = RETROGUI_IDC_NONE;
2311 goto cleanup;
2312 }
2313
2314 gui->idc_prev = ctl->base.idc;
2315
2316#if RETROGUI_TRACE_LVL > 0
2317 debug_printf( RETROGUI_TRACE_LVL,
2318 "setting focus to clicked control: " RETROGUI_IDC_FMT,
2319 ctl->base.idc );
2320#endif /* RETROGUI_TRACE_LVL */
2321 gui->focus_idc = ctl->base.idc;
2322
2323 if( 0 ) {
2324 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_CLICK )
2325 }
2326 break;
2327 }
2328# endif /* !RETROGUI_NO_MOUSE */
2329
2330 } else {
2331
2332 if( RETROGUI_IDC_NONE == gui->focus_idc ) {
2333 error_printf( "no control has focus!" );
2334 goto cleanup;
2335 }
2336
2337 /* Send keystrokes to control that has focus. */
2338
2339 ctl = _retrogui_get_ctl_by_idc( gui, gui->focus_idc );
2340 if( NULL == ctl ) {
2341 /* This is a debug message because an invalid focus isn't necessarily
2342 * a game-over situation...
2343 */
2344#if RETROGUI_TRACE_LVL > 0
2345 debug_printf( RETROGUI_TRACE_LVL,
2346 "invalid focus IDC: " RETROGUI_IDC_FMT, gui->focus_idc );
2347#endif /* RETROGUI_TRACE_LVL */
2348 goto cleanup;
2349 }
2350
2351 if( 0 ) {
2352 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_KEY )
2353 }
2354 }
2355
2356 /* Input must have been valid, so set a debounce backoff time. */
2357 gui->debounce_next = retroflat_get_ms() + gui->debounce_max;
2358
2359# endif
2360
2361cleanup:
2362
2363 if( MERROR_OK != retval ) {
2364 idc_out = merror_retval_to_sz( retval );
2365 }
2366
2367 mdata_vector_unlock( &(gui->ctls) );
2368
2369 return idc_out;
2370}
2371
2372/* === */
2373
2374MERROR_RETVAL retrogui_redraw_ctls( struct RETROGUI* gui ) {
2375 size_t i = 0;
2376 union RETROGUI_CTL* ctl = NULL;
2377 MERROR_RETVAL retval = MERROR_OK;
2378 int autolock = 0;
2379
2380#if RETROGUI_TRACE_LVL > 0
2381 debug_printf( RETROGUI_TRACE_LVL, "redrawing controls..." );
2382#endif /* RETROGUI_TRACE_LVL */
2383
2384#ifndef RETROWIN_NO_BITMAP
2385 if( RETROGUI_FLAGS_DIRTY != (RETROGUI_FLAGS_DIRTY & gui->flags) ) {
2386 /* Shortcut! */
2387 return MERROR_OK;
2388 }
2389#endif /* !RETROWIN_NO_BITMAP */
2390
2391 if( 0 == mdata_vector_ct( &(gui->ctls) ) ) {
2392 return MERROR_OK;
2393 }
2394
2395 if( !retrogxc_cachable_is_loaded( &(gui->font) ) ) {
2396 error_printf( "no font has been defined for GUI %p!", gui );
2397 return MERROR_GUI;
2398 }
2399
2400 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
2401 mdata_vector_lock( &(gui->ctls) );
2402 autolock = 1;
2403 }
2404
2405 if(
2406 RETROFLAT_COLOR_BLACK != gui->bg_color &&
2407 0 < gui->w && 0 < gui->h
2408 ) {
2409 retroflat_2d_rect( gui->draw_bmp,
2410 gui->bg_color, gui->x, gui->y, gui->w, gui->h, RETROFLAT_DRAW_FLAG_FILL );
2411 }
2412
2413 /* Mark the GUI dirty first so redraw can unmark it for animation! */
2414 gui->flags &= ~RETROGUI_FLAGS_DIRTY;
2415
2416 #define RETROGUI_CTL_TABLE_REDRAW( idx, c_name, c_fields ) \
2417 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2418 retrogui_redraw_ ## c_name( gui, ctl );
2419
2420 /* Iterate and redraw all controls. */
2421 for( i = 0 ; mdata_vector_ct( &(gui->ctls) ) > i ; i++ ) {
2422 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
2423 if( 0 ) {
2424 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_REDRAW )
2425 }
2426 }
2427
2428#if RETROGUI_TRACE_LVL > 0
2429 debug_printf( RETROGUI_TRACE_LVL, "redrawing controls complete!" );
2430#endif /* RETROGUI_TRACE_LVL */
2431
2432
2433cleanup:
2434
2435 if( autolock ) {
2436 mdata_vector_unlock( &(gui->ctls) );
2437 }
2438
2439 return retval;
2440}
2441
2442/* === */
2443
2444MERROR_RETVAL retrogui_pos_ctl(
2445 struct RETROGUI* gui, retrogui_idc_t idc,
2448) {
2449 MERROR_RETVAL retval = MERROR_OK;
2450 union RETROGUI_CTL* ctl = NULL;
2451 int autolock = 0;
2452
2453 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
2454 mdata_vector_lock( &(gui->ctls) );
2455 autolock = 1;
2456 }
2457
2458#if RETROGUI_TRACE_LVL > 0
2459 debug_printf( RETROGUI_TRACE_LVL,
2460 "moving control " RETROGUI_IDC_FMT " to: " SIZE_T_FMT ", " SIZE_T_FMT,
2461 idc, x, y );
2462#endif /* RETROGUI_TRACE_LVL */
2463
2464 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2465 if( NULL == ctl ) {
2466 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2467 error_printf( "could not position control!" );
2468 retval = MERROR_GUI;
2469 goto cleanup;
2470 }
2471
2472 #define RETROGUI_CTL_TABLE_POS( idx, c_name, c_fields ) \
2473 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2474 /* Mark dirty first so redraw can unmark it for animation! */ \
2475 retval = retrogui_pos_ ## c_name( gui, ctl, x, y, w, h ); \
2476 maug_cleanup_if_not_ok();
2477
2478 if( 0 ) {
2479 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_POS )
2480 }
2481
2482#if RETROGUI_TRACE_LVL > 0
2483 debug_printf( RETROGUI_TRACE_LVL,
2484 "moved control " RETROGUI_IDC_FMT " to " SIZE_T_FMT ", " SIZE_T_FMT
2485 " and sized to " SIZE_T_FMT "x" SIZE_T_FMT "...",
2486 ctl->base.idc, gui->x + ctl->base.x, gui->y + ctl->base.y,
2487 ctl->base.w, ctl->base.h );
2488#endif /* RETROGUI_TRACE_LVL */
2489
2490 /* New position! Redraw! */
2491 gui->flags |= RETROGUI_FLAGS_DIRTY;
2492
2493cleanup:
2494
2495 if( autolock ) {
2496 mdata_vector_unlock( &(gui->ctls) );
2497 }
2498
2499 return retval;
2500
2501}
2502
2503/* === */
2504
2505MERROR_RETVAL retrogui_push_ctl(
2506 struct RETROGUI* gui, union RETROGUI_CTL* ctl
2507) {
2508 MERROR_RETVAL retval = MERROR_OK;
2509 int autolock = 0;
2510
2511 assert( 0 < ctl->base.idc );
2512
2513 /* TODO: Hunt for control IDC and fail if duplicate found! */
2514
2515#if RETROGUI_TRACE_LVL > 0
2516 debug_printf( RETROGUI_TRACE_LVL,
2517 "gui->ctls_ct: " SIZE_T_FMT, mdata_vector_ct( &(gui->ctls) ) );
2518#endif /* RETROGUI_TRACE_LVL */
2519
2520 if(
2521 RETROGUI_CTL_TYPE_IMAGE != ctl->base.type &&
2522 RETROFLAT_COLOR_NULL == ctl->base.bg_color
2523 ) {
2525 "invalid background color specified for control " RETROGUI_IDC_FMT "!",
2526 ctl->base.idc );
2527 retval = MERROR_GUI;
2528 goto cleanup;
2529
2530 }
2531
2532 if(
2533 RETROGUI_CTL_TYPE_IMAGE != ctl->base.type &&
2534 RETROFLAT_COLOR_NULL == ctl->base.fg_color
2535 ) {
2537 "invalid foreground color specified for control " RETROGUI_IDC_FMT "!",
2538 ctl->base.idc );
2539 retval = MERROR_GUI;
2540 goto cleanup;
2541 }
2542
2543 /* Perform the actual push. */
2544
2545#ifdef RETROGUI_TRACE_TOKENS
2546 debug_printf( RETROGUI_TRACE_LVL,
2547 "pushing %s " RETROGUI_IDC_FMT " to slot " SIZE_T_FMT "...",
2548 gc_retrogui_ctl_names[ctl->base.type], ctl->base.idc,
2549 mdata_vector_ct( &(gui->ctls) ) );
2550#elif RETROGUI_TRACE_LVL > 0
2551 debug_printf( RETROGUI_TRACE_LVL,
2552 "pushing control type %d, " RETROGUI_IDC_FMT " to slot " SIZE_T_FMT "...",
2553 ctl->base.type, ctl->base.idc, mdata_vector_ct( &(gui->ctls) ) );
2554#endif /* RETROGUI_TRACE_TOKENS */
2555
2556 mdata_vector_append( &(gui->ctls), ctl, sizeof( union RETROGUI_CTL ) );
2557
2558 gui->flags |= RETROGUI_FLAGS_DIRTY;
2559
2560 /* Now that append is done, lock the vector and grab a pointer to our
2561 * newly-pushed control to run some fixups on.
2562 */
2563 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
2564 mdata_vector_lock( &(gui->ctls) );
2565 autolock = 1;
2566 }
2567
2568 /* TODO: More elegant way to grab index. */
2569 ctl = mdata_vector_get_last( &(gui->ctls),
2570 union RETROGUI_CTL );
2571 assert( NULL != ctl );
2572
2573#if RETROGUI_TRACE_LVL > 0
2574# define RETROGUI_CTL_TABLE_PUSH( idx, c_name, c_fields ) \
2575 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2576 debug_printf( RETROGUI_TRACE_LVL, \
2577 "running " #c_name " push hook..." ); \
2578 retval = retrogui_push_ ## c_name( ctl ); \
2579 maug_cleanup_if_not_ok();
2580#else
2581# define RETROGUI_CTL_TABLE_PUSH( idx, c_name, c_fields ) \
2582 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2583 retval = retrogui_push_ ## c_name( ctl ); \
2584 maug_cleanup_if_not_ok();
2585#endif /* RETROGUI_TRACE_LVL */
2586
2587 if( 0 ) {
2588 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_PUSH )
2589 }
2590
2591 /* Try to auto-size the control now that the push-hook as set its text
2592 * or whatever else might be needed to determine an automatic size.
2593 */
2594 if( 0 == ctl->base.w || 0 == ctl->base.h ) {
2595#ifdef RETROGUI_TRACE_TOKENS
2596 debug_printf( RETROGUI_TRACE_LVL,
2597 "determining size for new %s control " RETROGUI_IDC_FMT "...",
2598 gc_retrogui_ctl_names[ctl->base.type], ctl->base.idc );
2599#elif RETROGUI_TRACE_LVL > 0
2600 debug_printf( RETROGUI_TRACE_LVL,
2601 "determining size for new control type %d, " RETROGUI_IDC_FMT "...",
2602 ctl->base.type, ctl->base.idc );
2603#endif /* RETROGUI_TRACE_TOKENS */
2604 retval = _retrogui_sz_ctl(
2605 gui, ctl->base.idc, &(ctl->base.w), &(ctl->base.h), 0, 0 );
2606 maug_cleanup_if_not_ok();
2607 }
2608
2609 if( RETROGUI_IDC_NONE >= gui->focus_idc && retrogui_can_focus_ctl( ctl ) ) {
2610#if RETROGUI_TRACE_LVL > 0
2611 debug_printf( RETROGUI_TRACE_LVL,
2612 "setting focus to control: " RETROGUI_IDC_FMT, ctl->base.idc );
2613#endif /* RETROGUI_TRACE_LVL */
2614 gui->focus_idc = ctl->base.idc;
2615 }
2616
2617cleanup:
2618
2619 if( autolock ) {
2620 mdata_vector_unlock( &(gui->ctls) );
2621 }
2622
2623 return retval;
2624}
2625
2626/* === */
2627
2628MERROR_RETVAL retrogui_remove_ctl( struct RETROGUI* gui, retrogui_idc_t idc ) {
2629 size_t i = 0;
2630 union RETROGUI_CTL* ctl = NULL;
2631 MERROR_RETVAL retval = MERROR_OK;
2632
2633 if( mdata_vector_is_locked( &((gui)->ctls) ) ) {
2634 error_printf( "GUI is locked!" );
2635 goto cleanup;
2636 }
2637
2638 assert( !mdata_vector_is_locked( &((gui)->ctls) ) );
2639 mdata_vector_lock( &(gui->ctls) );
2640
2641 #define RETROGUI_CTL_TABLE_FREE_CTL( idx, c_name, c_fields ) \
2642 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2643 retrogui_destroy_ ## c_name( ctl );
2644
2645 for( i = 0 ; mdata_vector_ct( &(gui->ctls) ) > i ; i++ ) {
2646 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
2647 if( idc != ctl->base.idc ) {
2648 continue;
2649 }
2650
2651 /* Free the control data. */
2652 if( 0 ) {
2653 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_FREE_CTL )
2654 }
2655
2656 /* Remove the control. */
2657 mdata_vector_unlock( &(gui->ctls) );
2658 mdata_vector_remove( &(gui->ctls), i );
2659 mdata_vector_lock( &(gui->ctls) );
2660 break;
2661 }
2662
2663 mdata_vector_unlock( &(gui->ctls) );
2664
2665cleanup:
2666
2667 return retval;
2668}
2669
2670/* === */
2671
2673 struct RETROGUI* gui, const maug_path font_path
2674) {
2675 MERROR_RETVAL retval = MERROR_OK;
2676
2677#ifdef RETROGXC_PRESENT
2678 if(
2679 RETROFLAT_STATE_FLAG_USE_GXC ==
2680 (RETROFLAT_STATE_FLAG_USE_GXC & g_retroflat_state->retroflat_flags)
2681 ) {
2682 gui->font.cache_idx = retrogxc_load_font( font_path, 0, 33, 93 );
2683 maug_cleanup_if_lt(
2684 gui->font.cache_idx, (ssize_t)0, SSIZE_T_FMT, MERROR_GUI );
2685 } else {
2686#endif /* RETROGXC_PRESENT */
2687 if(
2688 RETROGUI_FLAGS_FONT_OWNED == (RETROGUI_FLAGS_FONT_OWNED & gui->flags) &&
2689 (MAUG_MHANDLE)NULL != gui->font.handle
2690 ) {
2691 debug_printf( RETROGUI_TRACE_LVL, "freeing existing GUI font..." );
2692 maug_mfree( gui->font.handle );
2693 }
2694 retval = retrofont_load( font_path, &(gui->font.handle), 0, 33, 93 );
2695 maug_cleanup_if_not_ok();
2696#ifdef RETROGXC_PRESENT
2697 }
2698#endif /* RETROGXC_PRESENT */
2699
2700 gui->flags |= RETROGUI_FLAGS_FONT_OWNED;
2701
2702cleanup:
2703
2704 return retval;
2705}
2706
2707#ifndef RETROGUI_NO_TEXTBOX
2708
2709MERROR_RETVAL retrogui_get_ctl_text(
2710 struct RETROGUI* gui, retrogui_idc_t idc, char* buffer, size_t buffer_sz
2711) {
2712 MERROR_RETVAL retval = MERROR_OK;
2713 union RETROGUI_CTL* ctl = NULL;
2714 int autolock = 0;
2715
2716 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
2717 mdata_vector_lock( &(gui->ctls) );
2718 autolock = 1;
2719 }
2720
2721 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2722 if( NULL == ctl ) {
2723 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2724 error_printf( "could not get control text!" );
2725 retval = MERROR_GUI;
2726 goto cleanup;
2727 }
2728
2729 if( RETROGUI_CTL_TYPE_TEXTBOX == ctl->base.type ) {
2730# if defined( RETROGUI_NATIVE_WIN )
2731 /* TODO */
2732#else
2733 maug_mlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
2734 maug_cleanup_if_null_lock( char*, ctl->TEXTBOX.text );
2735
2736 maug_strncpy( buffer, ctl->TEXTBOX.text, buffer_sz );
2737# endif
2738 } else if( RETROGUI_CTL_TYPE_LABEL == ctl->base.type ) {
2739# if defined( RETROGUI_NATIVE_WIN )
2740 /* TODO */
2741#else
2742 maug_mlock( ctl->LABEL.label_h, ctl->LABEL.label );
2743 maug_cleanup_if_null_lock( char*, ctl->LABEL.label );
2744
2745 maug_strncpy( buffer, ctl->LABEL.label, buffer_sz );
2746# endif
2747
2748 }
2749
2750cleanup:
2751
2752 if( RETROGUI_CTL_TYPE_TEXTBOX == ctl->base.type ) {
2753 if( NULL != ctl->TEXTBOX.text ) {
2754 maug_munlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
2755 }
2756
2757 } else if( RETROGUI_CTL_TYPE_LABEL == ctl->base.type ) {
2758 if( NULL != ctl->LABEL.label ) {
2759 maug_munlock( ctl->LABEL.label_h, ctl->LABEL.label );
2760 }
2761 }
2762
2763 if( autolock ) {
2764 mdata_vector_unlock( &(gui->ctls) );
2765 }
2766
2767 return retval;
2768}
2769
2770#endif /* !RETROGUI_NO_TEXTBOX */
2771
2772/* === */
2773
2774ssize_t retrogui_get_ctl_sel_idx( struct RETROGUI* gui, retrogui_idc_t idc ) {
2775 ssize_t idx = -1;
2776 union RETROGUI_CTL* ctl = NULL;
2777 MERROR_RETVAL retval = MERROR_OK;
2778 int autolock = 0;
2779
2780 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
2781 mdata_vector_lock( &(gui->ctls) );
2782 autolock = 1;
2783 }
2784
2785 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2786 if( NULL == ctl ) {
2787 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2788 error_printf( "could not get control selection!" );
2789 retval = MERROR_GUI;
2790 goto cleanup;
2791 }
2792
2793 assert( RETROGUI_CTL_TYPE_LISTBOX == ctl->base.type );
2794
2795# if defined( RETROGUI_NATIVE_WIN )
2796 idx = SendMessage( ctl->base.hwnd, LB_GETCARETINDEX, 0, 0 );
2797# else
2798 idx = ctl->LISTBOX.sel_idx;
2799# endif
2800
2801cleanup:
2802
2803 if( autolock ) {
2804 mdata_vector_unlock( &(gui->ctls) );
2805 }
2806
2807 if( MERROR_OK != retval ) {
2808 idx = -1 * retval;
2809 }
2810
2811 return idx;
2812}
2813
2814/* === */
2815
2816MERROR_RETVAL retrogui_set_ctl_color(
2817 struct RETROGUI* gui, retrogui_idc_t idc, uint8_t color_key,
2818 RETROFLAT_COLOR color_val
2819) {
2820 MERROR_RETVAL retval = MERROR_OK;
2821 union RETROGUI_CTL* ctl = NULL;
2822
2823 assert( !mdata_vector_is_locked( &((gui)->ctls) ) );
2824 mdata_vector_lock( &(gui->ctls) );
2825
2826#if RETROGUI_TRACE_LVL > 0
2827 debug_printf( RETROGUI_TRACE_LVL,
2828 "setting control " RETROGUI_IDC_FMT " color %u to: %d",
2829 idc, color_key, color_val );
2830#endif /* RETROGUI_TRACE_LVL */
2831
2832 /* Figure out the control to update. */
2833 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2834 if( NULL == ctl ) {
2835 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2836 error_printf( "could not set control color!" );
2837 retval = MERROR_GUI;
2838 goto cleanup;
2839 }
2840
2841 switch( color_key ) {
2842 case RETROGUI_COLOR_BG: ctl->base.bg_color = color_val; break;
2843 case RETROGUI_COLOR_FG: ctl->base.fg_color = color_val; break;
2844 case RETROGUI_COLOR_SEL_BG: ctl->base.sel_bg = color_val; break;
2845 case RETROGUI_COLOR_SEL_FG: ctl->base.sel_fg = color_val; break;
2846
2847 default:
2848 error_printf( "invalid color key specified: %u", color_key );
2849 break;
2850 }
2851
2852cleanup:
2853
2854 mdata_vector_unlock( &(gui->ctls) );
2855
2856 return retval;
2857}
2858
2859/* === */
2860
2861MERROR_RETVAL retrogui_set_ctl_text(
2862 struct RETROGUI* gui, retrogui_idc_t idc, size_t buffer_sz,
2863 const char* fmt, ...
2864) {
2865 MERROR_RETVAL retval = MERROR_OK;
2866 char* label_tmp = NULL;
2867 char* buffer = NULL;
2868 union RETROGUI_CTL* ctl = NULL;
2869 MAUG_MHANDLE buffer_h = (MAUG_MHANDLE)NULL;
2870 va_list args;
2871
2872 assert( !mdata_vector_is_locked( &((gui)->ctls) ) );
2873 mdata_vector_lock( &(gui->ctls) );
2874
2875#if RETROGUI_TRACE_LVL > 0
2876 debug_printf( RETROGUI_TRACE_LVL,
2877 "setting control " RETROGUI_IDC_FMT " text to: %s", idc, fmt );
2878#endif /* RETROGUI_TRACE_LVL */
2879
2880 /* Figure out the control to update. */
2881 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2882 if( NULL == ctl ) {
2883 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2884 error_printf( "could not set control text!" );
2885 retval = MERROR_GUI;
2886 goto cleanup;
2887 }
2888
2889 /* Perform the buffer substitutions. */
2890 maug_malloc_test( buffer_h, 1, buffer_sz + 1 );
2891
2892 /* Buffer is always at least 1 for \0. */
2893 maug_mlock( buffer_h, buffer );
2894 maug_cleanup_if_null_lock( char*, buffer );
2895 maug_mzero( buffer, buffer_sz + 1 );
2896
2897 if( NULL == fmt ) {
2898 /* Zero the buffer. */
2899 maug_mzero( buffer, buffer_sz + 1);
2900
2901 } else {
2902 /* Format the buffer. */
2903 va_start( args, fmt );
2904 maug_vsnprintf( buffer, buffer_sz, fmt, args );
2905 va_end( args );
2906 }
2907
2908 /* Perform the actual update. */
2909 if( RETROGUI_CTL_TYPE_BUTTON == ctl->base.type ) {
2910 assert( NULL == ctl->BUTTON.label );
2911 _retrogui_copy_str( label, buffer, ctl->BUTTON, label_tmp, buffer_sz );
2912 } else if( RETROGUI_CTL_TYPE_LABEL == ctl->base.type ) {
2913 assert( NULL == ctl->LABEL.label );
2914 _retrogui_copy_str(
2915 label, buffer, ctl->LABEL, label_tmp, buffer_sz );
2916 ctl->LABEL.shown_sz = 1;
2917 ctl->LABEL.show_ticks =
2919 (RETROGUI_LABEL_FLAG_SHOWINC_SLOW & ctl->LABEL.flags) ?
2922
2923#ifndef RETROGUI_NO_TEXTBOX
2924 } else if( RETROGUI_CTL_TYPE_TEXTBOX == ctl->base.type ) {
2925 /* This is slightly different from _retrogui_copy_str, as it handles
2926 * the sz_max and sz_ fields independently for the TEXTBOX and allocates
2927 * an extra byte for NULL at the end for safety.
2928 */
2929 if( buffer_sz > ctl->TEXTBOX.text_sz_max ) {
2930#if RETROGUI_TRACE_LVL > 0
2931 debug_printf( RETROGUI_TRACE_LVL,
2932 "string size different; creating new buffer..." );
2933#endif /* RETROGUI_TRACE_LVL */
2934 if( (MAUG_MHANDLE)NULL != ctl->TEXTBOX.text_h ) {
2935 /* Free the existing string. */
2936 maug_mfree( ctl->TEXTBOX.text_h );
2937 }
2938
2939 /* Allocate new string space. */
2940 maug_malloc_test( ctl->TEXTBOX.text_h, buffer_sz + 1, 1 );
2941
2942 ctl->TEXTBOX.text_sz_max = buffer_sz;
2943 }
2944 ctl->TEXTBOX.text_sz = buffer_sz;
2945 ctl->TEXTBOX.text_cur = 0;
2946 maug_mlock( ctl->TEXTBOX.text_h, label_tmp );
2947 maug_cleanup_if_null_lock( char*, label_tmp );
2948
2949 /* Copy the string over. */
2950 maug_mzero( label_tmp, buffer_sz + 1 );
2951#if RETROGUI_TRACE_LVL > 0
2952 debug_printf( RETROGUI_TRACE_LVL,
2953 "zeroed str sz for \"%s\": " SIZE_T_FMT, buffer, buffer_sz + 1 );
2954#endif /* RETROGUI_TRACE_LVL */
2955 maug_strncpy( label_tmp, buffer, buffer_sz );
2956#if RETROGUI_TRACE_LVL > 0
2957 debug_printf( RETROGUI_TRACE_LVL, "copied str as: \"%s\"", label_tmp );
2958#endif /* RETROGUI_TRACE_LVL */
2959 maug_munlock( ctl->TEXTBOX.text_h, label_tmp );
2960
2961#endif /* !RETROGUI_NO_TEXTBOX */
2962 } else {
2963 error_printf( "invalid control type! no label!" );
2964 goto cleanup;
2965 }
2966
2967 /* New text! Redraw! */
2968 gui->flags |= RETROGUI_FLAGS_DIRTY;
2969
2970cleanup:
2971
2972 if( NULL != buffer ) {
2973 maug_munlock( buffer_h, buffer );
2974 }
2975
2976 if( (MAUG_MHANDLE)NULL != buffer_h ) {
2977 maug_mfree( buffer_h );
2978 }
2979
2980 mdata_vector_unlock( &(gui->ctls) );
2981
2982 return retval;
2983}
2984
2985/* === */
2986
2988 struct RETROGUI* gui, retrogui_idc_t idc, const maug_path path, uint8_t flags
2989) {
2990 MERROR_RETVAL retval = MERROR_OK;
2991 union RETROGUI_CTL* ctl = NULL;
2992 int autolock = 0;
2993
2994 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
2995 mdata_vector_lock( &(gui->ctls) );
2996 autolock = 1;
2997 }
2998
2999#if RETROGUI_TRACE_LVL > 0
3000 debug_printf( RETROGUI_TRACE_LVL,
3001 "setting control " RETROGUI_IDC_FMT " image to: %s", idc, path );
3002#endif /* RETROGUI_TRACE_LVL */
3003
3004 /* Figure out the control to update. */
3005 ctl = _retrogui_get_ctl_by_idc( gui, idc );
3006 if( NULL == ctl ) {
3007 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
3008 error_printf( "could not set control image!" );
3009 retval = MERROR_GUI;
3010 goto cleanup;
3011 }
3012
3013 /* Perform the actual update. */
3014 if( RETROGUI_CTL_TYPE_IMAGE == ctl->base.type ) {
3015 /* Cleanup existing image. */
3016# ifdef RETROGXC_PRESENT
3017 ctl->IMAGE.image_cache_id = -1;
3018# endif /* RETROGXC_PRESENT */
3019 if( retroflat_2d_bitmap_ok( &(ctl->IMAGE.image) ) ) {
3020 retroflat_2d_destroy_bitmap( &(ctl->IMAGE.image) );
3021 }
3022
3023 /* Load the replacement image. */
3024 if( NULL != path && '\0' != path[0] ) {
3025# if defined( RETROGXC_PRESENT )
3026 ctl->IMAGE.image_cache_id = retrogxc_load_bitmap( path, flags );
3027# else /* Only use a normal image if cache not present! */
3028 retroflat_2d_load_bitmap( path, &(ctl->IMAGE.image), flags );
3029# endif /* RETROGXC_PRESENT */
3030 }
3031 } else {
3032 error_printf( "invalid control type! no image!" );
3033 goto cleanup;
3034 }
3035
3036 /* New text! Redraw! */
3037 gui->flags |= RETROGUI_FLAGS_DIRTY;
3038
3039cleanup:
3040
3041 if( autolock ) {
3042 mdata_vector_unlock( &(gui->ctls) );
3043 }
3044
3045 return retval;
3046}
3047
3048/* === */
3049
3051 struct RETROGUI* gui, retrogui_idc_t idc, retroflat_blit_t* blit,
3052 uint8_t flags
3053) {
3054 MERROR_RETVAL retval = MERROR_OK;
3055 union RETROGUI_CTL* ctl = NULL;
3056 int autolock = 0;
3057 retroflat_pxxy_t ctl_img_w, ctl_img_h, blit_w, blit_h;
3058
3059 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
3060 mdata_vector_lock( &(gui->ctls) );
3061 autolock = 1;
3062 }
3063
3064#if RETROGUI_TRACE_LVL > 0
3065 debug_printf( RETROGUI_TRACE_LVL,
3066 "setting control " RETROGUI_IDC_FMT " image to: %p", idc, blit );
3067#endif /* RETROGUI_TRACE_LVL */
3068
3069 /* Figure out the control to update. */
3070 ctl = _retrogui_get_ctl_by_idc( gui, idc );
3071 if( NULL == ctl ) {
3072 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
3073 error_printf( "could not set control image!" );
3074 retval = MERROR_GUI;
3075 goto cleanup;
3076 }
3077
3078 /* Perform the actual update. */
3079 if( RETROGUI_CTL_TYPE_IMAGE == ctl->base.type ) {
3080 if( NULL != blit ) {
3081 /* Cleanup any existing image. */
3082# if defined( RETROGXC_PRESENT )
3083 ctl->IMAGE.image_cache_id = -1;
3084# endif /* RETROGXC_PRESENT */
3085
3086 ctl_img_w = retroflat_2d_bitmap_w( &(ctl->IMAGE.image) );
3087 ctl_img_h = retroflat_2d_bitmap_h( &(ctl->IMAGE.image) );
3088 blit_w = retroflat_2d_bitmap_w( blit );
3089 blit_h = retroflat_2d_bitmap_h( blit );
3090
3091 /* Only cleanup a normal image if it's smaller than the one to blit. */
3092 if(
3093 retroflat_2d_bitmap_ok( &(ctl->IMAGE.image) ) &&
3094 (blit_w < ctl_img_w || blit_h < ctl_img_h)
3095 ) {
3096 retroflat_2d_destroy_bitmap( &(ctl->IMAGE.image) );
3097 }
3098
3099#if RETROGUI_TRACE_LVL > 0
3100 debug_printf( RETROGUI_TRACE_LVL,
3101 "creating control " RETROGUI_IDC_FMT " image: %u, %u",
3102 idc, blit_w, blit_h );
3103#endif /* RETROGUI_TRACE_LVL */
3104
3105 /* Create the new image to blit to. */
3106 retval = retroflat_2d_create_bitmap(
3107 blit_w, blit_h, &(ctl->IMAGE.image), 0 );
3108 maug_cleanup_if_not_ok();
3109
3110 retroflat_2d_lock_bitmap( &(ctl->IMAGE.image) );
3111
3112#if RETROGUI_TRACE_LVL > 0
3113 debug_printf( RETROGUI_TRACE_LVL,
3114 "blitting control " RETROGUI_IDC_FMT " image from: %p", idc, blit );
3115#endif /* RETROGUI_TRACE_LVL */
3116
3117 /* Blit the new image over the old one. */
3118 retroflat_2d_blit_bitmap(
3119 &(ctl->IMAGE.image),
3120 blit,
3121 0, 0, 0, 0, blit_w, blit_h,
3122 ctl->IMAGE.instance );
3123 retroflat_2d_release_bitmap( &(ctl->IMAGE.image) );
3124 } else {
3125
3126 }
3127 } else {
3128 error_printf( "invalid control type! no image!" );
3129 goto cleanup;
3130 }
3131
3132 /* New text! Redraw! */
3133 gui->flags |= RETROGUI_FLAGS_DIRTY;
3134
3135cleanup:
3136
3137 if( autolock ) {
3138 mdata_vector_unlock( &(gui->ctls) );
3139 }
3140
3141 return retval;
3142}
3143
3144/* === */
3145
3147 struct RETROGUI* gui, retrogui_idc_t idc, uint16_t level, uint16_t max,
3148 uint8_t flags
3149) {
3150 MERROR_RETVAL retval = MERROR_OK;
3151 union RETROGUI_CTL* ctl = NULL;
3152 int autolock = 0;
3153
3154 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
3155 mdata_vector_lock( &(gui->ctls) );
3156 autolock = 1;
3157 }
3158
3159#if RETROGUI_TRACE_LVL > 0
3160 debug_printf( RETROGUI_TRACE_LVL,
3161 "setting control " RETROGUI_IDC_FMT " level to: %u", idc, level );
3162#endif /* RETROGUI_TRACE_LVL */
3163
3164 /* Figure out the control to update. */
3165 ctl = _retrogui_get_ctl_by_idc( gui, idc );
3166 if( NULL == ctl ) {
3167 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
3168 error_printf( "could not set control level!" );
3169 retval = MERROR_GUI;
3170 goto cleanup;
3171 }
3172
3173 /* Perform the actual update. */
3174 if( RETROGUI_CTL_TYPE_FILLBAR == ctl->base.type ) {
3175 ctl->FILLBAR.cur = level;
3176 if( 0 < max ) {
3177 ctl->FILLBAR.max = max;
3178 }
3179 } else {
3180 error_printf( "invalid control type! no level!" );
3181 goto cleanup;
3182 }
3183
3184 /* New level! Redraw! */
3185 gui->flags |= RETROGUI_FLAGS_DIRTY;
3186
3187cleanup:
3188
3189 if( autolock ) {
3190 mdata_vector_unlock( &(gui->ctls) );
3191 }
3192
3193 return retval;
3194}
3195
3196/* === */
3197
3198MERROR_RETVAL retrogui_init_ctl(
3199 union RETROGUI_CTL* ctl, uint8_t type, retrogui_idc_t idc
3200) {
3201 MERROR_RETVAL retval = MERROR_OK;
3202
3203 if( RETROGUI_IDC_NONE >= idc ) {
3204 error_printf( "invalid IDC: %d", idc );
3205 retval = MERROR_GUI;
3206 goto cleanup;
3207 }
3208
3209#if RETROGUI_TRACE_LVL > 0
3210 debug_printf( RETROGUI_TRACE_LVL,
3211 "initializing control base " RETROGUI_IDC_FMT "...", idc );
3212#endif /* RETROGUI_TRACE_LVL */
3213
3214 maug_mzero( ctl, sizeof( union RETROGUI_CTL ) );
3215
3216 ctl->base.type = type;
3217 ctl->base.idc = idc;
3218 ctl->base.fg_color = RETROFLAT_COLOR_NULL;
3219 ctl->base.bg_color = RETROFLAT_COLOR_NULL;
3220 ctl->base.sel_fg = RETROFLAT_COLOR_NULL;
3221 ctl->base.sel_bg = RETROFLAT_COLOR_NULL;
3222
3223 #define RETROGUI_CTL_TABLE_INITS( idx, c_name, c_fields ) \
3224 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
3225 retrogui_init_ ## c_name( ctl );
3226
3227 if( 0 ) {
3228 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_INITS )
3229 }
3230
3231# ifdef RETROGXC_PRESENT
3232 if( RETROGUI_CTL_TYPE_IMAGE == type ) {
3233 ctl->IMAGE.image_cache_id = -1;
3234 }
3235# endif /* !RETROGXC_PRESENT */
3236
3237cleanup:
3238
3239 return retval;
3240}
3241
3242/* === */
3243
3244MERROR_RETVAL retrogui_destroy( struct RETROGUI* gui ) {
3245 size_t i = 0;
3246 union RETROGUI_CTL* ctl = NULL;
3247 MERROR_RETVAL retval = MERROR_OK;
3248
3249 if( mdata_vector_is_locked( &((gui)->ctls) ) ) {
3250 error_printf( "GUI is locked!" );
3251 goto cleanup;
3252 }
3253
3254 if( 0 == mdata_vector_ct( &(gui->ctls) ) ) {
3255 goto cleanup;
3256 }
3257
3258 assert( !mdata_vector_is_locked( &((gui)->ctls) ) );
3259 mdata_vector_lock( &(gui->ctls) );
3260
3261 #define RETROGUI_CTL_TABLE_FREE( idx, c_name, c_fields ) \
3262 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
3263 retrogui_destroy_ ## c_name( ctl );
3264
3265 for( i = 0 ; mdata_vector_ct( &(gui->ctls) ) > i ; i++ ) {
3266 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
3267 if( 0 ) {
3268 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_FREE )
3269 }
3270 }
3271
3272 mdata_vector_unlock( &(gui->ctls) );
3273
3274# ifndef RETROGXC_PRESENT
3275 if( RETROGUI_FLAGS_FONT_OWNED == (RETROGUI_FLAGS_FONT_OWNED & gui->flags) ) {
3276 maug_mfree( gui->font.handle );
3277 }
3278# endif /* !RETROGXC_PRESENT */
3279
3280cleanup:
3281
3282 mdata_vector_free( &(gui->ctls) );
3283
3284 return retval;
3285}
3286
3287/* === */
3288
3289retrogui_idc_t retrogui_focus_iter(
3290 struct RETROGUI* gui, size_t start, ssize_t incr
3291) {
3292 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
3293 union RETROGUI_CTL* ctl = NULL;
3294 MERROR_RETVAL retval = MERROR_OK;
3295 retrogui_idc_t i = 0;
3296 ssize_t i_before = -1; /* Index of the current selected IDC. */
3297 int autolock = 0;
3298
3299 if( 0 == mdata_vector_ct( &(gui->ctls) ) ) {
3300 goto cleanup;
3301 }
3302
3303 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
3304 mdata_vector_lock( &(gui->ctls) );
3305 autolock = 1;
3306 }
3307
3308 /* Find the currently selected IDC. */
3309 for(
3310 i = start ; mdata_vector_ct( &(gui->ctls) ) > i && 0 <= i ; i += incr
3311 ) {
3312 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
3313 if( !retrogui_can_focus_ctl( ctl ) ) {
3314 continue;
3315 } else if( RETROGUI_IDC_NONE == gui->focus_idc || 0 <= i_before ) {
3316 /* We're primed to set the new focus, so do that and finish. */
3317 idc_out = ctl->base.idc;
3318#if RETROGUI_TRACE_LVL > 0
3319 debug_printf( RETROGUI_TRACE_LVL,
3320 "moving focus to control: " RETROGUI_IDC_FMT, idc_out );
3321#endif /* RETROGUI_TRACE_LVL */
3322 gui->focus_idc = idc_out;
3323 goto cleanup;
3324
3325 } else if( ctl->base.idc == gui->focus_idc ) {
3326 /* We've found the current focus, so prime to select the new focus. */
3327 i_before = i;
3328 }
3329 }
3330
3331 /* We didn't select a focus in the loop above, so we must be wrapping around!
3332 */
3333
3334 /* Select the next IDC. */
3335 if( 0 > i ) {
3336 /* Wrap around to last SELECTABLE item. */
3337 for( i = mdata_vector_ct( &(gui->ctls) ) - 1 ; 0 <= i ; i-- ) {
3338 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
3339 if( !retrogui_can_focus_ctl( ctl ) ) {
3340 /* Skip NON-SELECTABLE items! */
3341#if RETROGUI_TRACE_LVL > 0
3342 debug_printf(
3343 RETROGUI_TRACE_LVL, "skipping: " RETROGUI_IDC_FMT, i );
3344#endif /* RETROGUI_TRACE_LVL */
3345 continue;
3346 } else {
3347 idc_out = ctl->base.idc;
3348#if RETROGUI_TRACE_LVL > 0
3349 debug_printf( RETROGUI_TRACE_LVL,
3350 "moving focus to control: " RETROGUI_IDC_FMT, idc_out );
3351#endif /* RETROGUI_TRACE_LVL */
3352 gui->focus_idc = idc_out;
3353 break;
3354 }
3355 }
3356
3357 } else if( mdata_vector_ct( &(gui->ctls) ) <= i ) {
3358 /* Wrap around to first SELECTABLE item. */
3359 for( i = 0 ; mdata_vector_ct( &(gui->ctls) ) > i ; i++ ) {
3360 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
3361 if( !retrogui_can_focus_ctl( ctl ) ) {
3362 /* Skip NON-SELECTABLE items! */
3363#if RETROGUI_TRACE_LVL > 0
3364 debug_printf(
3365 RETROGUI_TRACE_LVL, "skipping: " RETROGUI_IDC_FMT, i );
3366#endif /* RETROGUI_TRACE_LVL */
3367 continue;
3368 } else {
3369 idc_out = ctl->base.idc;
3370#if RETROGUI_TRACE_LVL > 0
3371 debug_printf( RETROGUI_TRACE_LVL,
3372 "moving focus to control: " RETROGUI_IDC_FMT, idc_out );
3373#endif /* RETROGUI_TRACE_LVL */
3374 gui->focus_idc = idc_out;
3375 break;
3376 }
3377 }
3378
3379 } else {
3380 error_printf( "invalid focus: " RETROGUI_IDC_FMT, i );
3381
3382 }
3383
3384cleanup:
3385
3386 /* New focus! Dirty! */
3387 gui->flags |= RETROGUI_FLAGS_DIRTY;
3388
3389 if( MERROR_OK != retval ) {
3390 idc_out = merror_retval_to_sz( retval );
3391 }
3392
3393 if( autolock ) {
3394 mdata_vector_unlock( &(gui->ctls) );
3395 }
3396
3397#if RETROGUI_TRACE_LVL > 0
3398 debug_printf(
3399 RETROGUI_TRACE_LVL, "selected IDC: " RETROGUI_IDC_FMT, idc_out );
3400#endif /* RETROGUI_TRACE_LVL */
3401
3402 return idc_out;
3403}
3404
3405/* === */
3406
3407MERROR_RETVAL retrogui_init( struct RETROGUI* gui ) {
3408 MERROR_RETVAL retval = MERROR_OK;
3409
3410 maug_mzero( gui, sizeof( struct RETROGUI ) );
3411
3412 gui->bg_color = RETROFLAT_COLOR_BLACK;
3413 gui->focus_idc = RETROGUI_IDC_NONE;
3414 gui->debounce_max = RETROGUI_DEBOUNCE_MAX_DEFAULT;
3415
3416#if RETROGUI_TRACE_LVL > 0
3417 debug_printf( RETROGUI_TRACE_LVL, "initialized GUI" );
3418#endif /* RETROGUI_TRACE_LVL */
3419
3420 return retval;
3421}
3422
3423#else
3424
3425#define RETROGUI_CTL_TABLE_CONSTS( idx, c_name, c_fields ) \
3426 extern MAUG_CONST uint8_t SEG_MCONST RETROGUI_CTL_TYPE_ ## c_name;
3427
3428RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_CONSTS )
3429
3430#ifdef RETROGUI_TRACE_TOKENS
3431extern MAUG_CONST char* gc_retrogui_ctl_names[];
3432#endif /* RETROGUI_TRACE_TOKENS */
3433
3434#endif /* RETROGUI_C */
3435 /* maug_retrogui */
3437 /* maug_retroflt */
3439
3440#endif /* !RETROGUI_H */
3441
uint16_t MERROR_RETVAL
Return type indicating function returns a value from this list.
Definition merror.h:28
char maug_path[MAUG_PATH_SZ_MAX]
Path/name used to load an asset from disk or access other files.
Definition mfile.h:138
int8_t RETROFLAT_COLOR
Defines an index in the platform-specific color-table.
Definition retroflt.h:328
#define RETROFLAT_DRAW_FLAG_FILL
Flag for retroflat_rect() or retroflat_ellipse(), indicating drawn shape should be filled.
Definition retroflt.h:376
#define retroflat_buffer_insert(c, buffer, buffer_cur, buffer_sz, buffer_mx)
Insert a character into a text buffer at cursor position.
Definition retroflt.h:842
#define retroflat_buffer_bksp(buffer, buffer_cur, buffer_sz)
Remove a character from a text buffer before cursor position.
Definition retroflt.h:826
#define retroflat_case_key(key, pad)
Specify cases for a select() on the result of retroflat_poll_input() for keyboard or game pad as avai...
Definition retroflt.h:984
void retroflat_message(uint8_t flags, const char *title, const char *format,...)
Display a message in a dialog box and/or on stderr.
#define RETROFLAT_MSG_FLAG_ERROR
This icon/type flag indicates an error. It will try to display messages in an urgent way with a red i...
Definition retroflt.h:456
int16_t retroflat_pxxy_t
Type used for surface pixel coordinates.
Definition retroflt.h:925
#define RETROGUI_KEY_SEL_PREV
Overrideable constant defining the keyboard key (RETROFLAT_KEY_*) that will select the previous sub-i...
Definition retrogui.h:145
#define RETROGUI_PAD_SEL_NEXT
Overrideable constant defining the gamepad button (RETROGUI_PAD_*) that will select the next sub-item...
Definition retrogui.h:181
#define RETROGUI_KEY_NEXT
Overrideable constant defining the keyboard key (RETROFLAT_KEY_*) that will select the next activatea...
Definition retrogui.h:118
#define RETROGUI_LABEL_SHOW_TICKS_MAX
The number of ticks (frames) to count down before incrementing the amount of text shown in a label if...
Definition retrogui.h:246
#define RETROGUI_KEY_ACTIVATE
Overrideable constant defining the keyboard key (RETROFLAT_KEY_*) that will activate the RETROGUI_CTL...
Definition retrogui.h:109
#define RETROGUI_PAD_ACTIVATE
Overrideable constant defining the gamepad button (RETROFLAT_PAD_*) that will activate the RETROGUI_C...
Definition retrogui.h:154
#define RETROGUI_PAD_SEL_PREV
Overrideable constant defining the gamepad button (RETROGUI_PAD_*) that will select the previous sub-...
Definition retrogui.h:190
#define RETROGUI_PAD_NEXT
Overrideable constant defining the gamepad button (RETROFLAT_PAD_*) that will select the next activat...
Definition retrogui.h:163
#define RETROGUI_LABEL_SHOW_INC
The number of characters to increment the amount of text shown in a label by if RETROGUI_LABEL_FLAG_S...
Definition retrogui.h:254
#define RETROGUI_KEY_PREV
Overrideable constant defining the keyboard key (RETROFLAT_KEY_*) that will select the previous activ...
Definition retrogui.h:127
#define RETROGUI_KEY_SEL_NEXT
Overrideable constant defining the keyboard key (RETROFLAT_KEY_*) that will select the next sub-item ...
Definition retrogui.h:136
#define RETROGUI_PAD_PREV
Overrideable constant defining the gamepad button (RETROFLAT_PAD_*) that will select the previous act...
Definition retrogui.h:172
#define RETROGUI_PADDING
Overrideable constant defining the padding for text inside of controls in pixels.
Definition retrogui.h:205
#define RETROGUI_CTL_TABLE_FIELDS(idx, c_name, c_fields)
Creates the corresponding RETROGUI_* structs from RETROGUI_CTL_TABLE that populate union RETROGUI_CTL...
Definition retrogui.h:435
#define retrogui_can_focus_ctl(ctl)
Determine if a RETROGUI_CTL can hold RETROGUI::focus.
Definition retrogui.h:408
#define RETROGUI_CTL_TABLE_TYPES(idx, c_name, c_fields)
Adds the structs created by RETROGUI_CTL_TABLE_FIELDS to union RETROGUI_CTL.
Definition retrogui.h:447
MERROR_RETVAL retrogui_set_ctl_level(struct RETROGUI *gui, retrogui_idc_t idc, uint16_t level, uint16_t max, uint8_t flags)
Set the current progress level displayed by a FILLBAR-type RETROGUI_CTL.
#define RETROGUI_COLOR_SEL_BG
Value for retrogui_set_ctl_color() color_key indicating selection background.
Definition retrogui.h:350
retrogui_idc_t retrogui_poll_ctls(struct RETROGUI *gui, RETROFLAT_IN_KEY *p_input, struct RETROFLAT_INPUT *input_evt)
Poll for the last clicked control and maintain listboxes and menus.
MERROR_RETVAL retrogui_remove_ctl(struct RETROGUI *gui, retrogui_idc_t idc)
Remove a control with the given unique identifier index from the given RETROGUI controller.
MERROR_RETVAL retrogui_set_font(struct RETROGUI *gui, const maug_path font_path)
Load the RetroFont API for the given RETROGUI to draw its controls with. Use RetroGXCache API if avai...
#define RETROGUI_COLOR_SEL_FG
Value for retrogui_set_ctl_color() color_key indicating selection foreground.
Definition retrogui.h:356
#define RETROGUI_LABEL_FLAG_SHOWINC
Flag for flags field in RETROGUI_CTL LABEL type indicating that the label should be shown incremental...
Definition retrogui.h:277
#define RETROGUI_COLOR_BG
Value for retrogui_set_ctl_color() color_key indicating background.
Definition retrogui.h:339
MERROR_RETVAL retrogui_destroy(struct RETROGUI *gui)
Free memory held by a RETROGUI controller internally and clean up any subordinate controls.
MERROR_RETVAL retrogui_set_ctl_image_blit(struct RETROGUI *gui, retrogui_idc_t idc, retroflat_blit_t *blit, uint8_t flags)
Blit the given image onto the control, ensuring that the size is sufficient to hold it.
retrogui_idc_t retrogui_focus_iter(struct RETROGUI *gui, size_t start, ssize_t incr)
Increment RETROGUI::focus, skipping elements that cannot hold focus.
#define RETROGUI_LABEL_FLAG_SHOWINC_SLOW
Flag for flags field in RETROGUI_CTL LABEL type indicating that the label should be shown incremental...
Definition retrogui.h:288
MERROR_RETVAL retrogui_set_ctl_image(struct RETROGUI *gui, retrogui_idc_t idc, const maug_path path, uint8_t flags)
Set the image displayed by an IMAGE-type RETROGUI_CTL.
MERROR_RETVAL retrogui_init(struct RETROGUI *gui)
Prepare a RETROGUI controller for use.
int16_t retrogui_idc_t
Unique identifying constant number for controls.
Definition retrogui.h:330
#define RETROGUI_COLOR_BORDER
RetroGUI will try to use this color on non-monochrome systems instead of black to draw things like bo...
Definition retrogui.h:93
#define RETROGUI_COLOR_FG
Value for retrogui_set_ctl_color() color_key indicating foreground.
Definition retrogui.h:344
MAUG_MHANDLE retrogxc_get_asset(size_t asset_idx, retrogxc_asset_type_t asset_type)
Retrive an asset for which we have a prior cached index.
#define RETROFONT_FLAG_SZ_MIN
Flag for retroflat_string_sz() to return the size of the shortest line in a multi-line string.
Definition retrofnt.h:46
MERROR_RETVAL retrofont_load(const char *font_name, MAUG_MHANDLE *p_font_h, uint8_t glyph_h, uint16_t first_glyph, uint16_t glyphs_count)
Load a font for drawing.
void retrofont_string(retroflat_blit_t *target, RETROFLAT_COLOR color, const char *str, size_t str_sz, MAUG_MHANDLE font_h, retroflat_pxxy_t x, retroflat_pxxy_t y, retroflat_pxxy_t max_w, retroflat_pxxy_t max_h, uint8_t flags)
Draw a string with the given font.
A vector of uniformly-sized objects, stored contiguously.
Definition mdata.h:108
Struct passed to retroflat_poll_input() to hold return data.
Definition retroflt.h:865
int mouse_y
Y-coordinate of the mouse pointer in pixels if the returned event is a mouse click.
Definition retroflt.h:875
int mouse_x
X-coordinate of the mouse pointer in pixels if the returned event is a mouse click.
Definition retroflt.h:870
Fields common to ALL RETROGUI_CTL types.
Definition retrogui.h:415
Definition retrogui.h:468
union RETROGXC_CACHABLE font
Font used to draw any attached RETROGUI_CTL.
Definition retrogui.h:489
retrogui_idc_t focus_idc
Unique identifying index for current highlighted RETROGUI_CTL.
Definition retrogui.h:478
Definition retrogui.h:450