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 -1
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_FLAGS_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_FLAGS_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. */
1100 /* TODO: Only set out on ENTER/SPACE. */
1101 /* idc_out = ctl->base.idc; */
1102
1103 return idc_out;
1104}
1105
1106static void retrogui_redraw_BUTTON(
1107 struct RETROGUI* gui, union RETROGUI_CTL* ctl
1108) {
1109 retroflat_pxxy_t w = 0,
1110 h = 0,
1111 text_offset = 0;
1112 RETROFLAT_COLOR fg_color = ctl->base.fg_color;
1113 RETROFLAT_COLOR bg_color = ctl->base.bg_color;
1114 RETROFLAT_COLOR push_shadow_color = RETROFLAT_COLOR_DARKGRAY;
1116 MAUG_MHANDLE font_h = (MAUG_MHANDLE)NULL;
1117
1118 if( ctl->base.idc == gui->focus ) {
1119 /* Assign selected color if focused. */
1120 fg_color = ctl->base.sel_fg;
1121 }
1122
1123 if( ctl->base.idc == gui->focus ) {
1124 /* Assign selected color if focused. */
1125 bg_color = ctl->base.sel_bg;
1126 }
1127
1128 /* Figure out push shadow color for current color depth. */
1129 if( 2 >= retroflat_screen_colors() ) {
1130 push_shadow_color = RETROFLAT_COLOR_BLACK;
1131 border_color = RETROFLAT_COLOR_BLACK;
1132 }
1133
1134 retroflat_2d_rect(
1135 gui->draw_bmp, bg_color, gui->x + ctl->base.x, gui->y + ctl->base.y,
1136 ctl->base.w, ctl->base.h, RETROFLAT_FLAGS_FILL );
1137
1138 retroflat_2d_rect( gui->draw_bmp, border_color,
1139 gui->x + ctl->base.x, gui->y + ctl->base.y,
1140 ctl->base.w, ctl->base.h, 0 );
1141
1142 /* Draw the push shadows on top/left or bottom/right, depending on pushed
1143 * status.
1144 */
1145 if( 0 < ctl->BUTTON.push_frames ) {
1146 retroflat_2d_line(
1147 gui->draw_bmp, push_shadow_color,
1148 gui->x + ctl->base.x + 1, gui->y + ctl->base.y + 1,
1149 gui->x + ctl->base.x + ctl->base.w - 2, gui->y + ctl->base.y + 1, 0 );
1150 retroflat_2d_line(
1151 gui->draw_bmp, push_shadow_color,
1152 gui->x + ctl->base.x + 1, gui->y + ctl->base.y + 2,
1153 gui->x + ctl->base.x + 1, gui->y + ctl->base.y + ctl->base.h - 3, 0 );
1154
1155 gui->flags |= RETROGUI_FLAGS_DIRTY; /* Mark dirty for push animation. */
1156 ctl->BUTTON.push_frames--;
1157 text_offset = 1;
1158 } else {
1159 /* Button is not pushed. */
1160 retroflat_2d_line(
1161 gui->draw_bmp, RETROFLAT_COLOR_WHITE,
1162 gui->x + ctl->base.x + 1, gui->y + ctl->base.y + 1,
1163 gui->x + ctl->base.x + ctl->base.w - 2, gui->y + ctl->base.y + 1, 0 );
1164 retroflat_2d_line(
1165 gui->draw_bmp, RETROFLAT_COLOR_WHITE,
1166 gui->x + ctl->base.x + 1, gui->y + ctl->base.y + 2,
1167 gui->x + ctl->base.x + 1, gui->y + ctl->base.y + ctl->base.h - 3, 0 );
1168 }
1169
1170 maug_mlock( ctl->BUTTON.label_h, ctl->BUTTON.label );
1171 if( NULL == ctl->BUTTON.label ) {
1172 error_printf( "could not lock BUTTON label!" );
1173 goto cleanup;
1174 }
1175
1176 /* Grab the string size and use it to center the text in the control. */
1177#ifdef RETROGXC_PRESENT
1178 font_h = retrogxc_get_asset(
1179 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
1180 if( (MAUG_MHANDLE)NULL == font_h ) {
1181 goto cleanup;
1182 }
1183#else
1184 font_h = gui->font.handle;
1185#endif /* RETROGXC_PRESENT */
1186 retrofont_string_sz(
1187 gui->draw_bmp, ctl->BUTTON.label, 0, font_h,
1188 ctl->base.w, ctl->base.h, &w, &h, ctl->BUTTON.font_flags );
1189
1191 gui->draw_bmp, fg_color, ctl->BUTTON.label, 0, font_h,
1192 gui->x + ctl->base.x + ((ctl->base.w >> 1) - (w >> 1)) + text_offset,
1193 gui->y + ctl->base.y + ((ctl->base.h >> 1) - (h >> 1)) + text_offset,
1194 /* TODO: Pad max client area. */
1195 ctl->base.w, ctl->base.h, ctl->BUTTON.font_flags );
1196
1197 maug_munlock( ctl->BUTTON.label_h, ctl->BUTTON.label );
1198
1199cleanup:
1200
1201 return;
1202}
1203
1204static MERROR_RETVAL retrogui_push_BUTTON( union RETROGUI_CTL* ctl ) {
1205 MERROR_RETVAL retval = MERROR_OK;
1206
1207# if defined( RETROGUI_NATIVE_WIN )
1208
1209 ctl->base.hwnd = CreateWindow(
1210 "BUTTON", ctl->BUTTON.label, WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
1211 gui->x + ctl->base.x, gui->y + ctl->base.y, ctl->base.w, ctl->base.h,
1212 g_retroflat_state->window, (HMENU)(ctl->base.idc),
1213 g_retroflat_instance, NULL );
1214 if( (HWND)NULL == ctl->base.hwnd ) {
1216 "Could not create button " RETROGUI_IDC_FMT ": %s",
1217 ctl->base.idc, ctl->BUTTON.label );
1218 retval = MERROR_GUI;
1219 goto cleanup;
1220 }
1221
1222# else
1223 char* label_tmp = NULL;
1224
1225#if RETROGUI_TRACE_LVL > 0
1226 debug_printf( RETROGUI_TRACE_LVL, "pushing BUTTON control..." );
1227#endif /* RETROGUI_TRACE_LVL */
1228
1229 _retrogui_copy_str(
1230 label, ctl->BUTTON.label, ctl->BUTTON, label_tmp, ctl->BUTTON.label_sz );
1231 ctl->BUTTON.label = NULL;
1232# endif
1233
1234cleanup:
1235
1236 return retval;
1237}
1238
1239static MERROR_RETVAL retrogui_sz_BUTTON(
1240 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1243) {
1244 MERROR_RETVAL retval = MERROR_OK;
1245 MAUG_MHANDLE font_h = (MAUG_MHANDLE)NULL;
1246
1247 assert( NULL != ctl );
1248 assert( NULL == ctl->BUTTON.label );
1249 assert( (MAUG_MHANDLE)NULL != ctl->BUTTON.label_h );
1250
1251 maug_mlock( ctl->BUTTON.label_h, ctl->BUTTON.label );
1252 maug_cleanup_if_null_lock( char*, ctl->BUTTON.label );
1253
1254 /* Get the size of the text-based GUI item. */
1255#ifdef RETROGXC_PRESENT
1256 font_h = retrogxc_get_asset(
1257 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
1258 maug_cleanup_if_null_alloc( MAUG_MHANDLE, font_h );
1259#else
1260 font_h = gui->font.handle;
1261#endif /* RETROGXC_PRESENT */
1262 retrofont_string_sz(
1263 NULL, ctl->BUTTON.label, 0, font_h,
1264 max_w - 8, max_h - 8, p_w, p_h, ctl->BUTTON.font_flags );
1265
1266 /* Add space for borders and stuff. */
1267 *p_w += RETROGUI_BTN_LBL_PADDED_X;
1268 *p_h += RETROGUI_BTN_LBL_PADDED_Y;
1269
1270cleanup:
1271
1272 maug_munlock( ctl->BUTTON.label_h, ctl->BUTTON.label );
1273
1274 return retval;
1275}
1276
1277static MERROR_RETVAL retrogui_pos_BUTTON(
1278 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1281) {
1282 MERROR_RETVAL retval = MERROR_OK;
1283
1284# if defined( RETROGUI_NATIVE_WIN )
1285 /* TODO */
1286# else
1287 assert( NULL != ctl );
1288
1289 ctl->base.x = x;
1290 ctl->base.y = y;
1291 if( 0 < w ) {
1292 ctl->base.w = w;
1293 }
1294 if( 0 < h ) {
1295 ctl->base.h = h;
1296 }
1297# endif /* RETROGUI_NATIVE_WIN */
1298
1299 return retval;
1300}
1301
1302static void retrogui_destroy_BUTTON( union RETROGUI_CTL* ctl ) {
1303 if( (MAUG_MHANDLE)NULL != ctl->BUTTON.label_h ) {
1304 maug_mfree( ctl->BUTTON.label_h );
1305 }
1306}
1307
1308static MERROR_RETVAL retrogui_init_BUTTON( union RETROGUI_CTL* ctl ) {
1309 MERROR_RETVAL retval = MERROR_OK;
1310
1311#if RETROGUI_TRACE_LVL > 0
1312 debug_printf( RETROGUI_TRACE_LVL,
1313 "initializing button " RETROGUI_IDC_FMT "...", ctl->base.idc );
1314#endif /* RETROGUI_TRACE_LVL */
1315
1316 if( 2 < retroflat_screen_colors() ) {
1317 ctl->base.fg_color = RETROGUI_COLOR_BORDER;
1318 ctl->base.bg_color = RETROFLAT_COLOR_GRAY;
1319 ctl->base.sel_fg = RETROFLAT_COLOR_BLUE;
1320 ctl->base.sel_bg = RETROFLAT_COLOR_GRAY;
1321 } else {
1322 ctl->base.fg_color = RETROFLAT_COLOR_BLACK;
1323 ctl->base.bg_color = RETROFLAT_COLOR_WHITE;
1324 ctl->base.sel_fg = RETROFLAT_COLOR_WHITE;
1325 ctl->base.sel_bg = RETROFLAT_COLOR_BLACK;
1326 }
1327
1328 return retval;
1329}
1330
1331#ifndef RETROGUI_NO_TEXTBOX
1332
1333/* === Control: TEXTBOX === */
1334
1335static retrogui_idc_t retrogui_click_TEXTBOX(
1336 struct RETROGUI* gui,
1337 union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1338 struct RETROFLAT_INPUT* input_evt
1339) {
1340 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
1341
1342 return idc_out;
1343}
1344
1345static retrogui_idc_t retrogui_key_TEXTBOX(
1346 struct RETROGUI* gui, union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1347 struct RETROFLAT_INPUT* input_evt
1348) {
1349 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
1350 char c = '\0';
1351
1352# if defined( RETROGUI_NATIVE_WIN )
1353 /* Do nothing. */
1354# else
1355
1356 c = retroflat_vk_to_ascii( *p_input, input_evt->key_flags );
1357
1358 /* Ignore non-printable characters. */
1359 if(
1360 0 == c &&
1361 RETROFLAT_KEY_RIGHT != *p_input &&
1362 RETROFLAT_KEY_LEFT != *p_input
1363 ) {
1364 goto cleanup;
1365 }
1366
1367 /* Lock text field. */
1368 assert( NULL == ctl->TEXTBOX.text );
1369 assert( (MAUG_MHANDLE)NULL != ctl->TEXTBOX.text_h );
1370 maug_mlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
1371 if( NULL == ctl->TEXTBOX.text ) {
1372 error_printf( "could not lock TEXTBOX text handle!" );
1373 goto cleanup;
1374 }
1375
1376 switch( *p_input ) {
1377 case RETROFLAT_KEY_BKSP:
1379 ctl->TEXTBOX.text, ctl->TEXTBOX.text_cur, ctl->TEXTBOX.text_sz )
1380 break;
1381
1382 case RETROFLAT_KEY_ENTER:
1383 idc_out = ctl->base.idc;
1384 break;
1385
1386 case RETROFLAT_KEY_LEFT:
1387 if( 0 < ctl->TEXTBOX.text_cur ) {
1388 ctl->TEXTBOX.text_cur--;
1389 }
1390 break;
1391
1392 case RETROFLAT_KEY_RIGHT:
1393 if( ctl->TEXTBOX.text_sz > ctl->TEXTBOX.text_cur ) {
1394 ctl->TEXTBOX.text_cur++;
1395 }
1396 break;
1397
1398 default:
1399 assert( ctl->TEXTBOX.text_sz < ctl->TEXTBOX.text_sz_max );
1400 debug_printf( 1, "cur: %d, sz: %d, sz_max: %d",
1401 ctl->TEXTBOX.text_cur, ctl->TEXTBOX.text_sz, ctl->TEXTBOX.text_sz_max );
1403 ctl->TEXTBOX.text,
1404 ctl->TEXTBOX.text_cur,
1405 ctl->TEXTBOX.text_sz,
1406 ctl->TEXTBOX.text_sz_max );
1407 break;
1408 }
1409
1410 /* TODO: Remove input from queue? */
1411
1412cleanup:
1413
1414 if( NULL != ctl->TEXTBOX.text ) {
1415 maug_munlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
1416 }
1417
1418# endif
1419
1420 return idc_out;
1421}
1422
1423static void retrogui_redraw_TEXTBOX(
1424 struct RETROGUI* gui, union RETROGUI_CTL* ctl
1425) {
1426 RETROFLAT_COLOR shadow_color = RETROFLAT_COLOR_DARKGRAY;
1428 retroflat_pxxy_t cursor_x = 0,
1429 cursor_y = 0;
1430 MAUG_MHANDLE font_h = (MAUG_MHANDLE)NULL;
1431
1432 /* Adjust shadow colors for monochrome. */
1433 if( 2 >= retroflat_screen_colors() ) {
1434 shadow_color = RETROFLAT_COLOR_BLACK;
1435 border_color = RETROFLAT_COLOR_BLACK;
1436 }
1437
1438# if defined( RETROGUI_NATIVE_WIN )
1439 /* Do nothing. */
1440# else
1441
1442 retroflat_2d_rect( gui->draw_bmp, ctl->base.bg_color,
1443 gui->x + ctl->base.x, gui->y + ctl->base.y,
1444 ctl->base.w, ctl->base.h, RETROFLAT_FLAGS_FILL );
1445
1446 /* Draw chiselled inset border. */
1447
1448 retroflat_2d_rect( gui->draw_bmp, border_color,
1449 gui->x + ctl->base.x,
1450 gui->y + ctl->base.y, ctl->base.w, 2,
1452
1453 retroflat_2d_rect( gui->draw_bmp, border_color,
1454 gui->x + ctl->base.x,
1455 gui->y + ctl->base.y, 2, ctl->base.h,
1457
1458 retroflat_2d_rect( gui->draw_bmp, shadow_color,
1459 gui->x + ctl->base.x,
1460 gui->y + ctl->base.y + ctl->base.h - 1,
1461 ctl->base.w, 2,
1463
1464 retroflat_2d_rect( gui->draw_bmp, shadow_color,
1465 gui->x + ctl->base.x + ctl->base.w - 1,
1466 gui->y + ctl->base.y, 2, ctl->base.h,
1468
1469 /* Draw text. */
1470
1471 assert( NULL == ctl->TEXTBOX.text );
1472 maug_mlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
1473 if( NULL == ctl->TEXTBOX.text ) {
1474 goto cleanup;
1475 }
1476
1477#ifdef RETROGXC_PRESENT
1478 font_h = retrogxc_get_asset(
1479 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
1480 if( (MAUG_MHANDLE)NULL == font_h ) {
1481 goto cleanup;
1482 }
1483#else
1484 font_h = gui->font.handle;
1485#endif /* RETROGXC_PRESENT */
1487 gui->draw_bmp, ctl->base.fg_color,
1488 ctl->TEXTBOX.text, ctl->TEXTBOX.text_cur, font_h,
1489 gui->x + ctl->base.x + RETROGUI_PADDING,
1490 gui->y + ctl->base.y + RETROGUI_PADDING, ctl->base.w, ctl->base.h, 0 );
1491
1492 /* Get the line size for cursor placement below. */
1493 retrofont_string_sz(
1494 gui->draw_bmp, ctl->TEXTBOX.text, ctl->TEXTBOX.text_cur, font_h,
1495 ctl->base.w, ctl->base.h, &cursor_x, &cursor_y, RETROFONT_FLAG_SZ_MIN );
1496
1497 if( cursor_x + RETROGUI_CTL_TEXT_CUR_WH >= ctl->base.w ) {
1498 cursor_x = 0;
1499 /* TODO: Use font height. */
1500 cursor_y += RETROGUI_CTL_TEXT_CUR_WH;
1501 }
1502
1503 /* Use same padding as font for cursor. */
1504 retroflat_2d_rect( gui->draw_bmp,
1505 ctl->base.sel_fg,
1506 gui->x + ctl->base.x + RETROGUI_PADDING + cursor_x,
1507 gui->y + ctl->base.y + RETROGUI_PADDING + cursor_y,
1508 RETROGUI_CTL_TEXT_CUR_WH, RETROGUI_CTL_TEXT_CUR_WH,
1509 /* Draw blinking cursor. */
1510 /* TODO: Use a global timer to mark this field dirty. */
1511 gui->focus == ctl->base.idc &&
1512 0 < ctl->TEXTBOX.blink_frames ? RETROFLAT_FLAGS_FILL : 0 );
1513
1514 if( (-1 * RETROGUI_CTL_TEXT_BLINK_FRAMES) > --(ctl->TEXTBOX.blink_frames) ) {
1515 ctl->TEXTBOX.blink_frames = RETROGUI_CTL_TEXT_BLINK_FRAMES;
1516 }
1517
1518 if( NULL != ctl->TEXTBOX.text ) {
1519 /* Draw the text that comes after the cursor. */
1520#ifdef RETROGXC_PRESENT
1521 font_h = retrogxc_get_asset(
1522 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
1523 if( (MAUG_MHANDLE)NULL == font_h ) {
1524 goto cleanup;
1525 }
1526#else
1527 font_h = gui->font.handle;
1528#endif /* RETROGXC_PRESENT */
1529 retrofont_string_indent(
1530 gui->draw_bmp, ctl->base.fg_color,
1531 &(ctl->TEXTBOX.text[ctl->TEXTBOX.text_cur]),
1532 /* Chop off chars from first half. */
1533 strlen( ctl->TEXTBOX.text ) - ctl->TEXTBOX.text_cur, font_h,
1534 gui->x + ctl->base.x + RETROGUI_PADDING,
1535 /* Post-cursor line beings on cursor line. */
1536 gui->y + ctl->base.y + RETROGUI_PADDING + cursor_y,
1537 ctl->base.w, ctl->base.h,
1538 cursor_x + RETROGUI_CTL_TEXT_CUR_WH, 0 );
1539
1540 maug_munlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
1541 }
1542
1543 gui->flags |= RETROGUI_FLAGS_DIRTY; /* Mark dirty for blink animation. */
1544
1545cleanup:
1546
1547# endif
1548
1549 return;
1550}
1551
1552static MERROR_RETVAL retrogui_push_TEXTBOX( union RETROGUI_CTL* ctl ) {
1553 MERROR_RETVAL retval = MERROR_OK;
1554
1555# if defined( RETROGUI_NATIVE_WIN )
1556
1557 ctl->base.hwnd = CreateWindow(
1558 "EDIT", 0, WS_CHILD | WS_VISIBLE | WS_BORDER,
1559 gui->x + ctl->base.x, gui->y + ctl->base.y, ctl->base.w, ctl->base.h,
1560 g_retroflat_state->window, (HMENU)(ctl->base.idc),
1561 g_retroflat_instance, NULL );
1562 if( (HWND)NULL == ctl->base.hwnd ) {
1564 "Could not create textbox: " RETROGUI_IDC_FMT, ctl->base.idc );
1565 retval = MERROR_GUI;
1566 goto cleanup;
1567 }
1568
1569# else
1570
1571#if RETROGUI_TRACE_LVL > 0
1572 debug_printf( RETROGUI_TRACE_LVL,
1573 "clearing textbox " RETROGUI_IDC_FMT " buffer...", ctl->base.idc );
1574#endif /* RETROGUI_TRACE_LVL */
1575 assert( (MAUG_MHANDLE)NULL == ctl->TEXTBOX.text_h );
1576 maug_malloc_test( ctl->TEXTBOX.text_h, RETROGUI_CTL_TEXT_SZ_MAX + 1, 1 );
1577 ctl->TEXTBOX.text_sz_max = RETROGUI_CTL_TEXT_SZ_MAX;
1578
1579 maug_mlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
1580 maug_cleanup_if_null_alloc( char*, ctl->TEXTBOX.text );
1581#if RETROGUI_TRACE_LVL > 0
1582 debug_printf( RETROGUI_TRACE_LVL,
1583 "clearing textbox " RETROGUI_IDC_FMT " buffer...", ctl->base.idc );
1584#endif /* RETROGUI_TRACE_LVL */
1585 maug_mzero( ctl->TEXTBOX.text, RETROGUI_CTL_TEXT_SZ_MAX + 1 );
1586 maug_munlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
1587
1588# endif
1589
1590cleanup:
1591
1592 return retval;
1593}
1594
1595static MERROR_RETVAL retrogui_sz_TEXTBOX(
1596 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1599) {
1600 MERROR_RETVAL retval = MERROR_GUI;
1601 /* TODO */
1602 return retval;
1603}
1604
1605static MERROR_RETVAL retrogui_pos_TEXTBOX(
1606 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1609) {
1610 MERROR_RETVAL retval = MERROR_GUI;
1611 /* TODO */
1612 return retval;
1613}
1614
1615static void retrogui_destroy_TEXTBOX( union RETROGUI_CTL* ctl ) {
1616 if( (MAUG_MHANDLE)NULL != ctl->TEXTBOX.text_h ) {
1617 maug_mfree( ctl->TEXTBOX.text_h );
1618 }
1619}
1620
1621static MERROR_RETVAL retrogui_init_TEXTBOX( union RETROGUI_CTL* ctl ) {
1622 MERROR_RETVAL retval = MERROR_OK;
1623
1624#if RETROGUI_TRACE_LVL > 0
1625 debug_printf( RETROGUI_TRACE_LVL,
1626 "initializing textbox " RETROGUI_IDC_FMT "...", ctl->base.idc );
1627#endif /* RETROGUI_TRACE_LVL */
1628
1629 ctl->base.bg_color = RETROFLAT_COLOR_WHITE;
1630 ctl->base.sel_bg = RETROFLAT_COLOR_WHITE;
1631 if( 2 < retroflat_screen_colors() ) {
1632 ctl->base.sel_fg = RETROFLAT_COLOR_BLUE;
1633 ctl->base.fg_color = RETROGUI_COLOR_BORDER;
1634 } else {
1635 ctl->base.sel_fg = RETROFLAT_COLOR_BLACK;
1636 ctl->base.fg_color = RETROFLAT_COLOR_BLACK;
1637 }
1638
1639 return retval;
1640}
1641
1642#endif /* RETROGUI_NO_TEXTBOX */
1643
1644/* === Control: LABEL === */
1645
1646static retrogui_idc_t retrogui_click_LABEL(
1647 struct RETROGUI* gui,
1648 union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1649 struct RETROFLAT_INPUT* input_evt
1650) {
1651 return RETROGUI_IDC_NONE;
1652}
1653
1654static retrogui_idc_t retrogui_key_LABEL(
1655 struct RETROGUI* gui, union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1656 struct RETROFLAT_INPUT* input_evt
1657) {
1658 return RETROGUI_IDC_NONE;
1659}
1660
1661static void retrogui_redraw_LABEL(
1662 struct RETROGUI* gui, union RETROGUI_CTL* ctl
1663) {
1664 size_t show_sz = 0;
1665 MAUG_MHANDLE font_h = (MAUG_MHANDLE)NULL;
1666
1667# if defined( RETROGUI_NATIVE_WIN )
1668 /* Do nothing. */
1669# else
1670
1671 /* Draw text. */
1672
1673 assert( NULL == ctl->LABEL.label );
1674 maug_mlock( ctl->LABEL.label_h, ctl->LABEL.label );
1675 if( NULL == ctl->LABEL.label ) {
1676 error_printf( "could not lock LABEL text!" );
1677 goto cleanup;
1678 }
1679
1680 if(
1682 (RETROGUI_LABEL_FLAG_SHOWINC & ctl->LABEL.flags)
1683 ) {
1684 if( 0 < ctl->LABEL.show_ticks ) {
1685 /* Increment timer to hold on this much of the label. */
1686 ctl->LABEL.show_ticks--;
1687 show_sz = ctl->LABEL.shown_sz;
1688 if( ctl->LABEL.shown_sz < ctl->LABEL.label_sz ) {
1689 gui->flags |= RETROGUI_FLAGS_DIRTY;
1690 }
1691
1692 } else {
1693 /* Reset ticks counter. */
1694 ctl->LABEL.show_ticks =
1696 (RETROGUI_LABEL_FLAG_SHOWINC_SLOW & ctl->LABEL.flags) ?
1699
1700 /* Work out how much of the label to show this time. */
1701 show_sz = ctl->LABEL.shown_sz + RETROGUI_LABEL_SHOW_INC;
1702 if( show_sz > ctl->LABEL.label_sz ) {
1703 /* Trim down show size to label size. */
1704 show_sz = ctl->LABEL.label_sz;
1705 } else {
1706 /* Not done showing yet! */
1707 gui->flags |= RETROGUI_FLAGS_DIRTY;
1708 }
1709
1710 /* Finalize whatever decision we came to above. */
1711 ctl->LABEL.shown_sz = show_sz;
1712 }
1713
1714 } else {
1715 show_sz = ctl->LABEL.label_sz;
1716 }
1717
1718#ifdef RETROGXC_PRESENT
1719 font_h = retrogxc_get_asset(
1720 gui->font.cache_idx, RETROGXC_ASSET_TYPE_FONT );
1721 if( (MAUG_MHANDLE)NULL == font_h ) {
1722 goto cleanup;
1723 }
1724#else
1725 font_h = gui->font.handle;
1726#endif /* RETROGXC_PRESENT */
1728 gui->draw_bmp, ctl->base.fg_color, ctl->LABEL.label, show_sz, font_h,
1729 gui->x + ctl->base.x + RETROGUI_PADDING,
1730 gui->y + ctl->base.y + RETROGUI_PADDING, ctl->base.w, ctl->base.h,
1731 ctl->LABEL.font_flags );
1732
1733cleanup:
1734
1735 if( NULL != ctl->LABEL.label ) {
1736 maug_munlock( ctl->LABEL.label_h, ctl->LABEL.label );
1737 }
1738
1739# endif
1740
1741 return;
1742}
1743
1744static MERROR_RETVAL retrogui_push_LABEL( union RETROGUI_CTL* ctl ) {
1745 MERROR_RETVAL retval = MERROR_OK;
1746
1747# if defined( RETROGUI_NATIVE_WIN )
1748
1749 /* TODO */
1750
1751# else
1752 char* label_tmp = NULL;
1753
1754#if RETROGUI_TRACE_LVL > 0
1755 debug_printf( RETROGUI_TRACE_LVL, "pushing LABEL control..." );
1756#endif /* RETROGUI_TRACE_LVL */
1757
1758 _retrogui_copy_str(
1759 label, ctl->LABEL.label, ctl->LABEL, label_tmp, ctl->LABEL.label_sz );
1760 ctl->LABEL.label = NULL;
1761 ctl->LABEL.shown_sz = 1;
1762 ctl->LABEL.show_ticks =
1764 (RETROGUI_LABEL_FLAG_SHOWINC_SLOW & ctl->LABEL.flags) ?
1767# endif
1768
1769cleanup:
1770
1771 return retval;
1772}
1773
1774static MERROR_RETVAL retrogui_sz_LABEL(
1775 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1778) {
1779 MERROR_RETVAL retval = MERROR_GUI;
1780 /* TODO */
1781 return retval;
1782}
1783
1784static MERROR_RETVAL retrogui_pos_LABEL(
1785 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1788) {
1789 MERROR_RETVAL retval = MERROR_GUI;
1790 /* TODO */
1791 return retval;
1792}
1793
1794static void retrogui_destroy_LABEL( union RETROGUI_CTL* ctl ) {
1795 if( (MAUG_MHANDLE)NULL != ctl->LABEL.label_h ) {
1796 maug_mfree( ctl->LABEL.label_h );
1797 }
1798}
1799
1800static MERROR_RETVAL retrogui_init_LABEL( union RETROGUI_CTL* ctl ) {
1801 MERROR_RETVAL retval = MERROR_OK;
1802
1803#if RETROGUI_TRACE_LVL > 0
1804 debug_printf( RETROGUI_TRACE_LVL,
1805 "initializing label " RETROGUI_IDC_FMT "...", ctl->base.idc );
1806#endif /* RETROGUI_TRACE_LVL */
1807
1808 if( 2 < retroflat_screen_colors() ) {
1809 ctl->base.fg_color = RETROGUI_COLOR_BORDER;
1810 } else {
1811 ctl->base.fg_color = RETROFLAT_COLOR_BLACK;
1812 }
1813 ctl->base.bg_color = RETROFLAT_COLOR_WHITE;
1814
1815 return retval;
1816}
1817
1818/* === Control: IMAGE === */
1819
1820static retrogui_idc_t retrogui_click_IMAGE(
1821 struct RETROGUI* gui,
1822 union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1823 struct RETROFLAT_INPUT* input_evt
1824) {
1825 return RETROGUI_IDC_NONE;
1826}
1827
1828static retrogui_idc_t retrogui_key_IMAGE(
1829 struct RETROGUI* gui, union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1830 struct RETROFLAT_INPUT* input_evt
1831) {
1832 return RETROGUI_IDC_NONE;
1833}
1834
1835static void retrogui_redraw_IMAGE(
1836 struct RETROGUI* gui, union RETROGUI_CTL* ctl
1837) {
1838# if defined( RETROGUI_NATIVE_WIN )
1839 /* Do nothing. */
1840# else
1841
1842# if defined( RETROGXC_PRESENT )
1843 if( 0 <= ctl->IMAGE.image_cache_id ) {
1844 /* Draw the image from the cache if there is one. */
1845
1846#if RETROGUI_TRACE_LVL > 0
1847 debug_printf( RETROGUI_TRACE_LVL,
1848 "redrawing image ctl " RETROGUI_IDC_FMT
1849 ", cache ID " SSIZE_T_FMT "...",
1850 ctl->base.idc, ctl->IMAGE.image_cache_id );
1851#endif /* RETROGUI_TRACE_LVL */
1852 retrogxc_blit_bitmap(
1853 gui->draw_bmp,
1854 ctl->IMAGE.image_cache_id,
1855 ctl->IMAGE.src_x, ctl->IMAGE.src_y,
1856 gui->x + ctl->base.x, gui->y + ctl->base.y, ctl->base.w, ctl->base.h,
1857 ctl->IMAGE.instance );
1858 } else
1859# endif /* RETROGXC_PRESENT */
1860 if( retroflat_2d_bitmap_ok( &(ctl->IMAGE.image) ) ) {
1861 /* If no cached image (or cache), try to draw the stored image. */
1862 retroflat_2d_blit_bitmap(
1863 gui->draw_bmp,
1864 &(ctl->IMAGE.image),
1865 ctl->IMAGE.src_x, ctl->IMAGE.src_y,
1866 gui->x + ctl->base.x, gui->y + ctl->base.y, ctl->base.w, ctl->base.h,
1867 ctl->IMAGE.instance );
1868 }
1869
1870# endif /* RETROGUI_NATIVE_WIN */
1871
1872 return;
1873}
1874
1875static MERROR_RETVAL retrogui_push_IMAGE( union RETROGUI_CTL* ctl ) {
1876 MERROR_RETVAL retval = MERROR_OK;
1877
1878# if defined( RETROGUI_NATIVE_WIN )
1879
1880 /* TODO */
1881
1882# else
1883
1884#if RETROGUI_TRACE_LVL > 0
1885 debug_printf( RETROGUI_TRACE_LVL, "pushing IMAGE control..." );
1886#endif /* RETROGUI_TRACE_LVL */
1887
1888 assert( !retroflat_2d_bitmap_ok( &(ctl->IMAGE.image) ) );
1889
1890 /* TODO: Copy non-cached image. */
1891
1892 /* ctl->IMAGE.cache_idx = NULL; */
1893# endif
1894
1895 return retval;
1896}
1897
1898static MERROR_RETVAL retrogui_sz_IMAGE(
1899 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1902) {
1903 MERROR_RETVAL retval = MERROR_GUI;
1904
1905 *p_w = 0;
1906 *p_h = 0;
1907
1908# ifdef RETROGXC_PRESENT
1909 retval = retrogxc_bitmap_wh( ctl->IMAGE.image_cache_id, p_w, p_h );
1910 maug_cleanup_if_not_ok();
1911# endif /* RETROGXC_PRESENT */
1912
1913 if(
1914 0 == *p_w && 0 == *p_h &&
1915 !retroflat_2d_bitmap_ok( &(ctl->IMAGE.image) )
1916 ) {
1917 error_printf( "image not assigned!" );
1918 retval = MERROR_GUI;
1919 goto cleanup;
1920 }
1921
1922 *p_w = retroflat_2d_bitmap_w( &(ctl->IMAGE.image) );
1923 *p_h = retroflat_2d_bitmap_h( &(ctl->IMAGE.image) );
1924
1925cleanup:
1926
1927 return retval;
1928}
1929
1930static MERROR_RETVAL retrogui_pos_IMAGE(
1931 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
1934) {
1935 MERROR_RETVAL retval = MERROR_GUI;
1936 /* TODO */
1937 return retval;
1938}
1939
1940static void retrogui_destroy_IMAGE( union RETROGUI_CTL* ctl ) {
1941 if( retroflat_2d_bitmap_ok( &(ctl->IMAGE.image) ) ) {
1942 retroflat_2d_destroy_bitmap( &(ctl->IMAGE.image) );
1943 }
1944}
1945
1946static MERROR_RETVAL retrogui_init_IMAGE( union RETROGUI_CTL* ctl ) {
1947 MERROR_RETVAL retval = MERROR_OK;
1948
1949#if RETROGUI_TRACE_LVL > 0
1950 debug_printf( RETROGUI_TRACE_LVL,
1951 "initializing IMAGE " RETROGUI_IDC_FMT "...", ctl->base.idc );
1952#endif /* RETROGUI_TRACE_LVL */
1953
1954 return retval;
1955}
1956
1957/* === Control: FILLBAR === */
1958
1959static retrogui_idc_t retrogui_click_FILLBAR(
1960 struct RETROGUI* gui,
1961 union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1962 struct RETROFLAT_INPUT* input_evt
1963) {
1964 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
1965
1966 return idc_out;
1967}
1968
1969static retrogui_idc_t retrogui_key_FILLBAR(
1970 struct RETROGUI* gui, union RETROGUI_CTL* ctl, RETROFLAT_IN_KEY* p_input,
1971 struct RETROFLAT_INPUT* input_evt
1972) {
1973 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
1974
1975 return idc_out;
1976}
1977
1978static void retrogui_redraw_FILLBAR(
1979 struct RETROGUI* gui, union RETROGUI_CTL* ctl
1980) {
1981 retroflat_pxxy_t fill_w = 0;
1982
1983 if( 0 == ctl->FILLBAR.cur ) {
1984 fill_w = 0;
1985 } else {
1986 fill_w = ctl->base.w * ctl->FILLBAR.cur / ctl->FILLBAR.max;
1987 }
1988
1989 retroflat_2d_rect(
1990 gui->draw_bmp, ctl->base.bg_color,
1991 gui->x + ctl->base.x + fill_w,
1992 gui->y + ctl->base.y,
1993 ctl->base.w - fill_w, ctl->base.h, RETROFLAT_FLAGS_FILL );
1994
1995 retroflat_2d_rect(
1996 gui->draw_bmp, ctl->base.fg_color,
1997 gui->x + ctl->base.x,
1998 gui->y + ctl->base.y,
1999 fill_w, ctl->base.h, RETROFLAT_FLAGS_FILL );
2000
2001 return;
2002}
2003
2004static MERROR_RETVAL retrogui_push_FILLBAR( union RETROGUI_CTL* ctl ) {
2005 MERROR_RETVAL retval = MERROR_OK;
2006
2007# if defined( RETROGUI_NATIVE_WIN )
2008
2009 /* TODO: Native fillbar implementation. */
2010
2011# endif
2012
2013 return retval;
2014}
2015
2016static MERROR_RETVAL retrogui_sz_FILLBAR(
2017 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
2020) {
2021 MERROR_RETVAL retval = MERROR_OK;
2022
2023 assert( NULL != ctl );
2024
2025 /* TODO? */
2026
2027 return retval;
2028}
2029
2030static MERROR_RETVAL retrogui_pos_FILLBAR(
2031 struct RETROGUI* gui, union RETROGUI_CTL* ctl,
2034) {
2035 MERROR_RETVAL retval = MERROR_OK;
2036
2037# if defined( RETROGUI_NATIVE_WIN )
2038 /* TODO */
2039# else
2040 assert( NULL != ctl );
2041
2042 ctl->base.x = x;
2043 ctl->base.y = y;
2044 if( 0 < w ) {
2045 ctl->base.w = w;
2046 }
2047 if( 0 < h ) {
2048 ctl->base.h = h;
2049 }
2050# endif /* RETROGUI_NATIVE_WIN */
2051
2052 return retval;
2053}
2054
2055static void retrogui_destroy_FILLBAR( union RETROGUI_CTL* ctl ) {
2056}
2057
2058static MERROR_RETVAL retrogui_init_FILLBAR( union RETROGUI_CTL* ctl ) {
2059 MERROR_RETVAL retval = MERROR_OK;
2060
2061#if RETROGUI_TRACE_LVL > 0
2062 debug_printf( RETROGUI_TRACE_LVL,
2063 "initializing fillbar " RETROGUI_IDC_FMT "...", ctl->base.idc );
2064#endif /* RETROGUI_TRACE_LVL */
2065
2066 if( 2 < retroflat_screen_colors() ) {
2067 ctl->base.fg_color = RETROGUI_COLOR_BORDER;
2068 ctl->base.bg_color = RETROFLAT_COLOR_GRAY;
2069 } else {
2070 ctl->base.fg_color = RETROFLAT_COLOR_BLACK;
2071 ctl->base.bg_color = RETROFLAT_COLOR_WHITE;
2072 }
2073
2074 return retval;
2075}
2076
2077/* === Static Internal Functions === */
2078
2079static union RETROGUI_CTL* _retrogui_get_ctl_by_idc(
2080 struct RETROGUI* gui, retrogui_idc_t idc
2081) {
2082 size_t i = 0;
2083 union RETROGUI_CTL* ctl = NULL;
2084
2085 assert( mdata_vector_is_locked( &((gui)->ctls) ) );
2086
2087 for( i = 0 ; mdata_vector_ct( &(gui->ctls) ) > i ; i++ ) {
2088 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
2089 if( idc == ctl->base.idc ) {
2090 break;
2091 }
2092 ctl = NULL;
2093 }
2094
2095 if( NULL == ctl ) {
2096 error_printf( "could not find GUI item IDC " RETROGUI_IDC_FMT, idc );
2097 }
2098
2099 return ctl;
2100}
2101
2102/* === */
2103
2104static MERROR_RETVAL _retrogui_sz_ctl(
2105 struct RETROGUI* gui, retrogui_idc_t idc,
2108) {
2109 MERROR_RETVAL retval = MERROR_OK;
2110 union RETROGUI_CTL* ctl = NULL;
2111
2112 assert( mdata_vector_is_locked( &((gui)->ctls) ) );
2113
2114#if RETROGUI_TRACE_LVL > 0
2115 debug_printf( RETROGUI_TRACE_LVL,
2116 "sizing control " RETROGUI_IDC_FMT " to: " SIZE_T_FMT "x" SIZE_T_FMT,
2117 idc, max_w, max_h );
2118#endif /* RETROGUI_TRACE_LVL */
2119
2120 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2121 if( NULL == ctl ) {
2122 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2123 error_printf( "could not size item!" );
2124 retval = MERROR_GUI;
2125 goto cleanup;
2126 }
2127
2128 #define RETROGUI_CTL_TABLE_SZ( idx, c_name, c_fields ) \
2129 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2130 /* Mark dirty first so redraw can unmark it for animation! */ \
2131 retval = retrogui_sz_ ## c_name( gui, ctl, p_w, p_h, max_w, max_h ); \
2132 maug_cleanup_if_not_ok();
2133
2134 if( 0 ) {
2135 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_SZ )
2136 }
2137
2138#if RETROGUI_TRACE_LVL > 0
2139 debug_printf( RETROGUI_TRACE_LVL,
2140 "sized control " RETROGUI_IDC_FMT " at " SIZE_T_FMT "x" SIZE_T_FMT "...",
2141 ctl->base.idc, ctl->base.w, ctl->base.h );
2142#endif /* RETROGUI_TRACE_LVL */
2143
2144cleanup:
2145
2146 return retval;
2147}
2148
2149/* === Generic Functions === */
2150
2152 struct RETROGUI* gui, RETROFLAT_IN_KEY* p_input,
2153 struct RETROFLAT_INPUT* input_evt
2154) {
2155 size_t i = 0;
2156 retroflat_pxxy_t mouse_x = 0,
2157 mouse_y = 0;
2158 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
2159 union RETROGUI_CTL* ctl = NULL;
2160 MERROR_RETVAL retval = MERROR_OK;
2161
2162 if( 0 == mdata_vector_ct( &(gui->ctls) ) ) {
2163 return RETROGUI_IDC_NONE;
2164 }
2165
2166 assert( !mdata_vector_is_locked( &((gui)->ctls) ) );
2167 mdata_vector_lock( &(gui->ctls) );
2168
2169# if defined( RETROGUI_NATIVE_WIN )
2170
2171 if( 0 == g_retroflat_state->last_idc ) {
2172 /* No WM_COMMAND to process. */
2173 goto cleanup;
2174 }
2175
2176 ctl = retrogui_get_ctl_by_idc( gui, g_retroflat_state->last_idc );
2177 g_retroflat_state->last_idc = 0;
2178 if( NULL == ctl ) {
2179#if RETROGUI_TRACE_LVL > 0
2180 debug_printf( RETROGUI_TRACE_LVL,
2181 "invalid IDC: " RETROGUI_IDC_FMT, gui->focus );
2182#endif /* RETROGUI_TRACE_LVL */
2183 goto cleanup;
2184 }
2185
2186# ifndef RETROGUI_NO_TEXTBOX
2187 if( RETROGUI_CTL_TYPE_TEXTBOX == ctl->base.type ) {
2188 if( SendMessage( ctl->base.hwnd, EM_GETMODIFY, 0, 0 ) ) {
2189 SendMessage( ctl->base.hwnd, EM_SETMODIFY, 0, 0 );
2190#if RETROGUI_TRACE_LVL > 0
2191 debug_printf( RETROGUI_TRACE_LVL, "mod: %d",
2192 SendMessage( ctl->base.hwnd, EM_GETMODIFY, 0, 0 ) );
2193#endif /* RETROGUI_TRACE_LVL */
2194 }
2195 }
2196# endif /* !RETROGUI_NO_TEXTBOX */
2197
2198# else
2199
2200 /* Use our cross-platform controls. */
2201
2202# define RETROGUI_CTL_TABLE_CLICK( idx, c_name, c_fields ) \
2203 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2204 gui->flags |= RETROGUI_FLAGS_DIRTY; \
2205 idc_out = retrogui_click_ ## c_name( gui, ctl, p_input, input_evt );
2206
2207# define RETROGUI_CTL_TABLE_KEY( idx, c_name, c_fields ) \
2208 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2209 gui->flags |= RETROGUI_FLAGS_DIRTY; \
2210 idc_out = retrogui_key_ ## c_name( gui, ctl, p_input, input_evt );
2211
2212 if( 0 != *p_input ) {
2213#if RETROGUI_TRACE_LVL > 0
2214 debug_printf( RETROGUI_TRACE_LVL,
2215 "focus " RETROGUI_IDC_FMT " input: %d", gui->focus, *p_input );
2216#endif /* RETROGUI_TRACE_LVL */
2217 }
2218
2219 /* Don't accept any input until debounce period has expired. */
2220 if( retroflat_get_ms() < gui->debounce_next ) {
2221#if RETROGUI_TRACE_LVL > 0
2222 debug_printf( RETROGUI_TRACE_LVL,
2223 "debo! %d vs %d", retroflat_get_ms(), gui->debounce_next );
2224#endif /* RETROGUI_TRACE_LVL */
2225 goto cleanup;
2226 }
2227
2228 if( 0 == *p_input ) {
2229 /* No input to debounce! */
2230 goto cleanup;
2231
2232 } else if(
2233 retroflat_or_key( *p_input, RETROGUI_KEY_ACTIVATE, RETROGUI_PAD_ACTIVATE )
2234 ) {
2235
2236 if( 0 <= gui->focus ) {
2237 idc_out = gui->focus;
2238 /* gui->focus = -1; */
2239 gui->flags |= RETROGUI_FLAGS_DIRTY;
2240 }
2241
2242 } else if(
2243 retroflat_or_key( *p_input, RETROGUI_KEY_NEXT, RETROGUI_PAD_NEXT )
2244 ) {
2245 retrogui_focus_next( gui );
2246
2247#if RETROGUI_TRACE_LVL > 0
2248 debug_printf( RETROGUI_TRACE_LVL, "next: " RETROGUI_IDC_FMT, gui->focus );
2249#endif /* RETROGUI_TRACE_LVL */
2250
2251 /* Cleanup after the menu. */
2252 *p_input = 0;
2253
2254 } else if(
2255 retroflat_or_key( *p_input, RETROGUI_KEY_PREV, RETROGUI_PAD_PREV )
2256 ) {
2257 retrogui_focus_prev( gui );
2258
2259#if RETROGUI_TRACE_LVL > 0
2260 debug_printf( RETROGUI_TRACE_LVL, "prev: " RETROGUI_IDC_FMT, gui->focus );
2261#endif /* RETROGUI_TRACE_LVL */
2262
2263 /* Cleanup after the menu. */
2264 *p_input = 0;
2265
2266# ifndef RETROGUI_NO_MOUSE
2267 } else if(
2268 RETROFLAT_MOUSE_B_LEFT == *p_input ||
2269 RETROFLAT_MOUSE_B_RIGHT == *p_input
2270 ) {
2271 /* Handle mouse input. */
2272
2273 /* Remove all focus before testing if a new control has focus. */
2274#if RETROGUI_TRACE_LVL > 0
2275 debug_printf( RETROGUI_TRACE_LVL, "resetting focus for mouse click..." );
2276#endif /* RETROGUI_TRACE_LVL */
2277 gui->focus = RETROGUI_IDC_NONE;
2278
2279 mouse_x = input_evt->mouse_x - gui->x;
2280 mouse_y = input_evt->mouse_y - gui->y;
2281
2282 for( i = 0 ; mdata_vector_ct( &(gui->ctls) ) > i ; i++ ) {
2283 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
2284 if(
2285 mouse_x < ctl->base.x ||
2286 mouse_y < ctl->base.y ||
2287 mouse_x > ctl->base.x + ctl->base.w ||
2288 mouse_y > ctl->base.y + ctl->base.h
2289 ) {
2290 continue;
2291 }
2292
2293 if( gui->idc_prev == ctl->base.idc ) {
2294 /* No repeated clicks! */
2295 /* TODO: Allow exceptions for e.g. scrollbars. */
2296 idc_out = RETROGUI_IDC_NONE;
2297 goto cleanup;
2298 }
2299
2300 gui->idc_prev = ctl->base.idc;
2301
2302#if RETROGUI_TRACE_LVL > 0
2303 debug_printf( RETROGUI_TRACE_LVL,
2304 "setting focus to clicked control: " RETROGUI_IDC_FMT,
2305 ctl->base.idc );
2306#endif /* RETROGUI_TRACE_LVL */
2307 gui->focus = ctl->base.idc;
2308
2309 if( 0 ) {
2310 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_CLICK )
2311 }
2312 break;
2313 }
2314# endif /* !RETROGUI_NO_MOUSE */
2315
2316 } else {
2317
2318 if( RETROGUI_IDC_NONE == gui->focus ) {
2319 goto cleanup;
2320 }
2321
2322 /* Send keystrokes to control that has focus. */
2323
2324 ctl = _retrogui_get_ctl_by_idc( gui, gui->focus );
2325 if( NULL == ctl ) {
2326 /* This is a debug message because an invalid focus isn't necessarily
2327 * a game-over situation...
2328 */
2329#if RETROGUI_TRACE_LVL > 0
2330 debug_printf( RETROGUI_TRACE_LVL,
2331 "invalid focus IDC: " RETROGUI_IDC_FMT, gui->focus );
2332#endif /* RETROGUI_TRACE_LVL */
2333 goto cleanup;
2334 }
2335
2336 if( 0 ) {
2337 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_KEY )
2338 }
2339 }
2340
2341 /* Input must have been valid, so set a debounce backoff time. */
2342 gui->debounce_next = retroflat_get_ms() + gui->debounce_max;
2343
2344# endif
2345
2346cleanup:
2347
2348 if( MERROR_OK != retval ) {
2349 idc_out = RETROGUI_IDC_NONE;
2350 }
2351
2352 mdata_vector_unlock( &(gui->ctls) );
2353
2354 return idc_out;
2355}
2356
2357/* === */
2358
2359MERROR_RETVAL retrogui_redraw_ctls( struct RETROGUI* gui ) {
2360 size_t i = 0;
2361 union RETROGUI_CTL* ctl = NULL;
2362 MERROR_RETVAL retval = MERROR_OK;
2363 int autolock = 0;
2364
2365#if RETROGUI_TRACE_LVL > 0
2366 debug_printf( RETROGUI_TRACE_LVL, "redrawing controls..." );
2367#endif /* RETROGUI_TRACE_LVL */
2368
2369#ifndef RETROWIN_NO_BITMAP
2370 if( RETROGUI_FLAGS_DIRTY != (RETROGUI_FLAGS_DIRTY & gui->flags) ) {
2371 /* Shortcut! */
2372 return MERROR_OK;
2373 }
2374#endif /* !RETROWIN_NO_BITMAP */
2375
2376 if( 0 == mdata_vector_ct( &(gui->ctls) ) ) {
2377 return MERROR_OK;
2378 }
2379
2380 if( !retrogxc_cachable_is_loaded( &(gui->font) ) ) {
2381 error_printf( "no font has been defined for GUI %p!", gui );
2382 return MERROR_GUI;
2383 }
2384
2385 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
2386 mdata_vector_lock( &(gui->ctls) );
2387 autolock = 1;
2388 }
2389
2390 if(
2391 RETROFLAT_COLOR_BLACK != gui->bg_color &&
2392 0 < gui->w && 0 < gui->h
2393 ) {
2394 retroflat_2d_rect( gui->draw_bmp,
2395 gui->bg_color, gui->x, gui->y, gui->w, gui->h, RETROFLAT_FLAGS_FILL );
2396 }
2397
2398 /* Mark the GUI dirty first so redraw can unmark it for animation! */
2399 gui->flags &= ~RETROGUI_FLAGS_DIRTY;
2400
2401 #define RETROGUI_CTL_TABLE_REDRAW( idx, c_name, c_fields ) \
2402 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2403 retrogui_redraw_ ## c_name( gui, ctl );
2404
2405 /* Iterate and redraw all controls. */
2406 for( i = 0 ; mdata_vector_ct( &(gui->ctls) ) > i ; i++ ) {
2407 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
2408 if( 0 ) {
2409 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_REDRAW )
2410 }
2411 }
2412
2413#if RETROGUI_TRACE_LVL > 0
2414 debug_printf( RETROGUI_TRACE_LVL, "redrawing controls complete!" );
2415#endif /* RETROGUI_TRACE_LVL */
2416
2417
2418cleanup:
2419
2420 if( autolock ) {
2421 mdata_vector_unlock( &(gui->ctls) );
2422 }
2423
2424 return retval;
2425}
2426
2427/* === */
2428
2429MERROR_RETVAL retrogui_pos_ctl(
2430 struct RETROGUI* gui, retrogui_idc_t idc,
2433) {
2434 MERROR_RETVAL retval = MERROR_OK;
2435 union RETROGUI_CTL* ctl = NULL;
2436 int autolock = 0;
2437
2438 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
2439 mdata_vector_lock( &(gui->ctls) );
2440 autolock = 1;
2441 }
2442
2443#if RETROGUI_TRACE_LVL > 0
2444 debug_printf( RETROGUI_TRACE_LVL,
2445 "moving control " RETROGUI_IDC_FMT " to: " SIZE_T_FMT ", " SIZE_T_FMT,
2446 idc, x, y );
2447#endif /* RETROGUI_TRACE_LVL */
2448
2449 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2450 if( NULL == ctl ) {
2451 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2452 error_printf( "could not position control!" );
2453 retval = MERROR_GUI;
2454 goto cleanup;
2455 }
2456
2457 #define RETROGUI_CTL_TABLE_POS( idx, c_name, c_fields ) \
2458 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2459 /* Mark dirty first so redraw can unmark it for animation! */ \
2460 retval = retrogui_pos_ ## c_name( gui, ctl, x, y, w, h ); \
2461 maug_cleanup_if_not_ok();
2462
2463 if( 0 ) {
2464 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_POS )
2465 }
2466
2467#if RETROGUI_TRACE_LVL > 0
2468 debug_printf( RETROGUI_TRACE_LVL,
2469 "moved control " RETROGUI_IDC_FMT " to " SIZE_T_FMT ", " SIZE_T_FMT
2470 " and sized to " SIZE_T_FMT "x" SIZE_T_FMT "...",
2471 ctl->base.idc, gui->x + ctl->base.x, gui->y + ctl->base.y,
2472 ctl->base.w, ctl->base.h );
2473#endif /* RETROGUI_TRACE_LVL */
2474
2475 /* New position! Redraw! */
2476 gui->flags |= RETROGUI_FLAGS_DIRTY;
2477
2478cleanup:
2479
2480 if( autolock ) {
2481 mdata_vector_unlock( &(gui->ctls) );
2482 }
2483
2484 return retval;
2485
2486}
2487
2488/* === */
2489
2490MERROR_RETVAL retrogui_push_ctl(
2491 struct RETROGUI* gui, union RETROGUI_CTL* ctl
2492) {
2493 MERROR_RETVAL retval = MERROR_OK;
2494 int autolock = 0;
2495
2496 assert( 0 < ctl->base.idc );
2497
2498 /* TODO: Hunt for control IDC and fail if duplicate found! */
2499
2500#if RETROGUI_TRACE_LVL > 0
2501 debug_printf( RETROGUI_TRACE_LVL,
2502 "gui->ctls_ct: " SIZE_T_FMT, mdata_vector_ct( &(gui->ctls) ) );
2503#endif /* RETROGUI_TRACE_LVL */
2504
2505 if(
2506 RETROGUI_CTL_TYPE_IMAGE != ctl->base.type &&
2507 RETROFLAT_COLOR_NULL == ctl->base.bg_color
2508 ) {
2510 "invalid background color specified for control " RETROGUI_IDC_FMT "!",
2511 ctl->base.idc );
2512 retval = MERROR_GUI;
2513 goto cleanup;
2514
2515 }
2516
2517 if(
2518 RETROGUI_CTL_TYPE_IMAGE != ctl->base.type &&
2519 RETROFLAT_COLOR_NULL == ctl->base.fg_color
2520 ) {
2522 "invalid foreground color specified for control " RETROGUI_IDC_FMT "!",
2523 ctl->base.idc );
2524 retval = MERROR_GUI;
2525 goto cleanup;
2526 }
2527
2528 /* Perform the actual push. */
2529
2530#ifdef RETROGUI_TRACE_TOKENS
2531 debug_printf( RETROGUI_TRACE_LVL,
2532 "pushing %s " RETROGUI_IDC_FMT " to slot " SIZE_T_FMT "...",
2533 gc_retrogui_ctl_names[ctl->base.type], ctl->base.idc,
2534 mdata_vector_ct( &(gui->ctls) ) );
2535#elif RETROGUI_TRACE_LVL > 0
2536 debug_printf( RETROGUI_TRACE_LVL,
2537 "pushing control type %d, " RETROGUI_IDC_FMT " to slot " SIZE_T_FMT "...",
2538 ctl->base.type, ctl->base.idc, mdata_vector_ct( &(gui->ctls) ) );
2539#endif /* RETROGUI_TRACE_TOKENS */
2540
2541 mdata_vector_append( &(gui->ctls), ctl, sizeof( union RETROGUI_CTL ) );
2542
2543 gui->flags |= RETROGUI_FLAGS_DIRTY;
2544
2545 /* Now that append is done, lock the vector and grab a pointer to our
2546 * newly-pushed control to run some fixups on.
2547 */
2548 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
2549 mdata_vector_lock( &(gui->ctls) );
2550 autolock = 1;
2551 }
2552
2553 /* TODO: More elegant way to grab index. */
2554 ctl = mdata_vector_get_last( &(gui->ctls),
2555 union RETROGUI_CTL );
2556 assert( NULL != ctl );
2557
2558#if RETROGUI_TRACE_LVL > 0
2559# define RETROGUI_CTL_TABLE_PUSH( idx, c_name, c_fields ) \
2560 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2561 debug_printf( RETROGUI_TRACE_LVL, \
2562 "running " #c_name " push hook..." ); \
2563 retval = retrogui_push_ ## c_name( ctl ); \
2564 maug_cleanup_if_not_ok();
2565#else
2566# define RETROGUI_CTL_TABLE_PUSH( idx, c_name, c_fields ) \
2567 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2568 retval = retrogui_push_ ## c_name( ctl ); \
2569 maug_cleanup_if_not_ok();
2570#endif /* RETROGUI_TRACE_LVL */
2571
2572 if( 0 ) {
2573 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_PUSH )
2574 }
2575
2576 /* Try to auto-size the control now that the push-hook as set its text
2577 * or whatever else might be needed to determine an automatic size.
2578 */
2579 if( 0 == ctl->base.w || 0 == ctl->base.h ) {
2580#ifdef RETROGUI_TRACE_TOKENS
2581 debug_printf( RETROGUI_TRACE_LVL,
2582 "determining size for new %s control " RETROGUI_IDC_FMT "...",
2583 gc_retrogui_ctl_names[ctl->base.type], ctl->base.idc );
2584#elif RETROGUI_TRACE_LVL > 0
2585 debug_printf( RETROGUI_TRACE_LVL,
2586 "determining size for new control type %d, " RETROGUI_IDC_FMT "...",
2587 ctl->base.type, ctl->base.idc );
2588#endif /* RETROGUI_TRACE_TOKENS */
2589 retval = _retrogui_sz_ctl(
2590 gui, ctl->base.idc, &(ctl->base.w), &(ctl->base.h), 0, 0 );
2591 maug_cleanup_if_not_ok();
2592 }
2593
2594 if( 0 > gui->focus && retrogui_can_focus_ctl( ctl ) ) {
2595#if RETROGUI_TRACE_LVL > 0
2596 debug_printf( RETROGUI_TRACE_LVL,
2597 "setting focus to control: " RETROGUI_IDC_FMT, ctl->base.idc );
2598#endif /* RETROGUI_TRACE_LVL */
2599 gui->focus = ctl->base.idc;
2600 }
2601
2602cleanup:
2603
2604 if( autolock ) {
2605 mdata_vector_unlock( &(gui->ctls) );
2606 }
2607
2608 return retval;
2609}
2610
2611/* === */
2612
2613MERROR_RETVAL retrogui_remove_ctl( struct RETROGUI* gui, retrogui_idc_t idc ) {
2614 size_t i = 0;
2615 union RETROGUI_CTL* ctl = NULL;
2616 MERROR_RETVAL retval = MERROR_OK;
2617
2618 if( mdata_vector_is_locked( &((gui)->ctls) ) ) {
2619 error_printf( "GUI is locked!" );
2620 goto cleanup;
2621 }
2622
2623 assert( !mdata_vector_is_locked( &((gui)->ctls) ) );
2624 mdata_vector_lock( &(gui->ctls) );
2625
2626 #define RETROGUI_CTL_TABLE_FREE_CTL( idx, c_name, c_fields ) \
2627 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
2628 retrogui_destroy_ ## c_name( ctl );
2629
2630 for( i = 0 ; mdata_vector_ct( &(gui->ctls) ) > i ; i++ ) {
2631 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
2632 if( idc != ctl->base.idc ) {
2633 continue;
2634 }
2635
2636 /* Free the control data. */
2637 if( 0 ) {
2638 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_FREE_CTL )
2639 }
2640
2641 /* Remove the control. */
2642 mdata_vector_unlock( &(gui->ctls) );
2643 mdata_vector_remove( &(gui->ctls), i );
2644 mdata_vector_lock( &(gui->ctls) );
2645 break;
2646 }
2647
2648 mdata_vector_unlock( &(gui->ctls) );
2649
2650cleanup:
2651
2652 return retval;
2653}
2654
2655/* === */
2656
2658 struct RETROGUI* gui, const maug_path font_path
2659) {
2660 MERROR_RETVAL retval = MERROR_OK;
2661
2662#ifdef RETROGXC_PRESENT
2663 if(
2664 RETROFLAT_FLAGS_USE_GXC ==
2665 (RETROFLAT_FLAGS_USE_GXC & g_retroflat_state->retroflat_flags)
2666 ) {
2667 gui->font.cache_idx = retrogxc_load_font( font_path, 0, 33, 93 );
2668 maug_cleanup_if_lt(
2669 gui->font.cache_idx, (ssize_t)0, SSIZE_T_FMT, MERROR_GUI );
2670 } else {
2671#endif /* RETROGXC_PRESENT */
2672 if(
2673 RETROGUI_FLAGS_FONT_OWNED == (RETROGUI_FLAGS_FONT_OWNED & gui->flags) &&
2674 (MAUG_MHANDLE)NULL != gui->font.handle
2675 ) {
2676 debug_printf( RETROGUI_TRACE_LVL, "freeing existing GUI font..." );
2677 maug_mfree( gui->font.handle );
2678 }
2679 retval = retrofont_load( font_path, &(gui->font.handle), 0, 33, 93 );
2680 maug_cleanup_if_not_ok();
2681#ifdef RETROGXC_PRESENT
2682 }
2683#endif /* RETROGXC_PRESENT */
2684
2685 gui->flags |= RETROGUI_FLAGS_FONT_OWNED;
2686
2687cleanup:
2688
2689 return retval;
2690}
2691
2692#ifndef RETROGUI_NO_TEXTBOX
2693
2694MERROR_RETVAL retrogui_get_ctl_text(
2695 struct RETROGUI* gui, retrogui_idc_t idc, char* buffer, size_t buffer_sz
2696) {
2697 MERROR_RETVAL retval = MERROR_OK;
2698 union RETROGUI_CTL* ctl = NULL;
2699 int autolock = 0;
2700
2701 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
2702 mdata_vector_lock( &(gui->ctls) );
2703 autolock = 1;
2704 }
2705
2706 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2707 if( NULL == ctl ) {
2708 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2709 error_printf( "could not get control text!" );
2710 retval = MERROR_GUI;
2711 goto cleanup;
2712 }
2713
2714 if( RETROGUI_CTL_TYPE_TEXTBOX == ctl->base.type ) {
2715# if defined( RETROGUI_NATIVE_WIN )
2716 /* TODO */
2717#else
2718 maug_mlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
2719 maug_cleanup_if_null_lock( char*, ctl->TEXTBOX.text );
2720
2721 maug_strncpy( buffer, ctl->TEXTBOX.text, buffer_sz );
2722# endif
2723 } else if( RETROGUI_CTL_TYPE_LABEL == ctl->base.type ) {
2724# if defined( RETROGUI_NATIVE_WIN )
2725 /* TODO */
2726#else
2727 maug_mlock( ctl->LABEL.label_h, ctl->LABEL.label );
2728 maug_cleanup_if_null_lock( char*, ctl->LABEL.label );
2729
2730 maug_strncpy( buffer, ctl->LABEL.label, buffer_sz );
2731# endif
2732
2733 }
2734
2735cleanup:
2736
2737 if( RETROGUI_CTL_TYPE_TEXTBOX == ctl->base.type ) {
2738 if( NULL != ctl->TEXTBOX.text ) {
2739 maug_munlock( ctl->TEXTBOX.text_h, ctl->TEXTBOX.text );
2740 }
2741
2742 } else if( RETROGUI_CTL_TYPE_LABEL == ctl->base.type ) {
2743 if( NULL != ctl->LABEL.label ) {
2744 maug_munlock( ctl->LABEL.label_h, ctl->LABEL.label );
2745 }
2746 }
2747
2748 if( autolock ) {
2749 mdata_vector_unlock( &(gui->ctls) );
2750 }
2751
2752 return retval;
2753}
2754
2755#endif /* !RETROGUI_NO_TEXTBOX */
2756
2757/* === */
2758
2759ssize_t retrogui_get_ctl_sel_idx( struct RETROGUI* gui, retrogui_idc_t idc ) {
2760 ssize_t idx = -1;
2761 union RETROGUI_CTL* ctl = NULL;
2762 MERROR_RETVAL retval = MERROR_OK;
2763 int autolock = 0;
2764
2765 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
2766 mdata_vector_lock( &(gui->ctls) );
2767 autolock = 1;
2768 }
2769
2770 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2771 if( NULL == ctl ) {
2772 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2773 error_printf( "could not get control selection!" );
2774 retval = MERROR_GUI;
2775 goto cleanup;
2776 }
2777
2778 assert( RETROGUI_CTL_TYPE_LISTBOX == ctl->base.type );
2779
2780# if defined( RETROGUI_NATIVE_WIN )
2781 idx = SendMessage( ctl->base.hwnd, LB_GETCARETINDEX, 0, 0 );
2782# else
2783 idx = ctl->LISTBOX.sel_idx;
2784# endif
2785
2786cleanup:
2787
2788 if( autolock ) {
2789 mdata_vector_unlock( &(gui->ctls) );
2790 }
2791
2792 if( MERROR_OK != retval ) {
2793 idx = -1 * retval;
2794 }
2795
2796 return idx;
2797}
2798
2799/* === */
2800
2801MERROR_RETVAL retrogui_set_ctl_color(
2802 struct RETROGUI* gui, retrogui_idc_t idc, uint8_t color_key,
2803 RETROFLAT_COLOR color_val
2804) {
2805 MERROR_RETVAL retval = MERROR_OK;
2806 union RETROGUI_CTL* ctl = NULL;
2807
2808 assert( !mdata_vector_is_locked( &((gui)->ctls) ) );
2809 mdata_vector_lock( &(gui->ctls) );
2810
2811#if RETROGUI_TRACE_LVL > 0
2812 debug_printf( RETROGUI_TRACE_LVL,
2813 "setting control " RETROGUI_IDC_FMT " color %u to: %d",
2814 idc, color_key, color_val );
2815#endif /* RETROGUI_TRACE_LVL */
2816
2817 /* Figure out the control to update. */
2818 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2819 if( NULL == ctl ) {
2820 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2821 error_printf( "could not set control color!" );
2822 retval = MERROR_GUI;
2823 goto cleanup;
2824 }
2825
2826 switch( color_key ) {
2827 case RETROGUI_COLOR_BG: ctl->base.bg_color = color_val; break;
2828 case RETROGUI_COLOR_FG: ctl->base.fg_color = color_val; break;
2829 case RETROGUI_COLOR_SEL_BG: ctl->base.sel_bg = color_val; break;
2830 case RETROGUI_COLOR_SEL_FG: ctl->base.sel_fg = color_val; break;
2831
2832 default:
2833 error_printf( "invalid color key specified: %u", color_key );
2834 break;
2835 }
2836
2837cleanup:
2838
2839 mdata_vector_unlock( &(gui->ctls) );
2840
2841 return retval;
2842}
2843
2844/* === */
2845
2846MERROR_RETVAL retrogui_set_ctl_text(
2847 struct RETROGUI* gui, retrogui_idc_t idc, size_t buffer_sz,
2848 const char* fmt, ...
2849) {
2850 MERROR_RETVAL retval = MERROR_OK;
2851 char* label_tmp = NULL;
2852 char* buffer = NULL;
2853 union RETROGUI_CTL* ctl = NULL;
2854 MAUG_MHANDLE buffer_h = (MAUG_MHANDLE)NULL;
2855 va_list args;
2856
2857 assert( !mdata_vector_is_locked( &((gui)->ctls) ) );
2858 mdata_vector_lock( &(gui->ctls) );
2859
2860#if RETROGUI_TRACE_LVL > 0
2861 debug_printf( RETROGUI_TRACE_LVL,
2862 "setting control " RETROGUI_IDC_FMT " text to: %s", idc, fmt );
2863#endif /* RETROGUI_TRACE_LVL */
2864
2865 /* Figure out the control to update. */
2866 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2867 if( NULL == ctl ) {
2868 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2869 error_printf( "could not set control text!" );
2870 retval = MERROR_GUI;
2871 goto cleanup;
2872 }
2873
2874 /* Perform the buffer substitutions. */
2875 maug_malloc_test( buffer_h, 1, buffer_sz + 1 );
2876
2877 /* Buffer is always at least 1 for \0. */
2878 maug_mlock( buffer_h, buffer );
2879 maug_cleanup_if_null_lock( char*, buffer );
2880 maug_mzero( buffer, buffer_sz + 1 );
2881
2882 if( NULL == fmt ) {
2883 /* Zero the buffer. */
2884 maug_mzero( buffer, buffer_sz + 1);
2885
2886 } else {
2887 /* Format the buffer. */
2888 va_start( args, fmt );
2889 maug_vsnprintf( buffer, buffer_sz, fmt, args );
2890 va_end( args );
2891 }
2892
2893 /* Perform the actual update. */
2894 if( RETROGUI_CTL_TYPE_BUTTON == ctl->base.type ) {
2895 assert( NULL == ctl->BUTTON.label );
2896 _retrogui_copy_str( label, buffer, ctl->BUTTON, label_tmp, buffer_sz );
2897 } else if( RETROGUI_CTL_TYPE_LABEL == ctl->base.type ) {
2898 assert( NULL == ctl->LABEL.label );
2899 _retrogui_copy_str(
2900 label, buffer, ctl->LABEL, label_tmp, buffer_sz );
2901 ctl->LABEL.shown_sz = 1;
2902 ctl->LABEL.show_ticks =
2904 (RETROGUI_LABEL_FLAG_SHOWINC_SLOW & ctl->LABEL.flags) ?
2907
2908#ifndef RETROGUI_NO_TEXTBOX
2909 } else if( RETROGUI_CTL_TYPE_TEXTBOX == ctl->base.type ) {
2910 /* This is slightly different from _retrogui_copy_str, as it handles
2911 * the sz_max and sz_ fields independently for the TEXTBOX and allocates
2912 * an extra byte for NULL at the end for safety.
2913 */
2914 if( buffer_sz > ctl->TEXTBOX.text_sz_max ) {
2915#if RETROGUI_TRACE_LVL > 0
2916 debug_printf( RETROGUI_TRACE_LVL,
2917 "string size different; creating new buffer..." );
2918#endif /* RETROGUI_TRACE_LVL */
2919 if( (MAUG_MHANDLE)NULL != ctl->TEXTBOX.text_h ) {
2920 /* Free the existing string. */
2921 maug_mfree( ctl->TEXTBOX.text_h );
2922 }
2923
2924 /* Allocate new string space. */
2925 maug_malloc_test( ctl->TEXTBOX.text_h, buffer_sz + 1, 1 );
2926
2927 ctl->TEXTBOX.text_sz_max = buffer_sz;
2928 }
2929 ctl->TEXTBOX.text_sz = buffer_sz;
2930 ctl->TEXTBOX.text_cur = 0;
2931 maug_mlock( ctl->TEXTBOX.text_h, label_tmp );
2932 maug_cleanup_if_null_lock( char*, label_tmp );
2933
2934 /* Copy the string over. */
2935 maug_mzero( label_tmp, buffer_sz + 1 );
2936#if RETROGUI_TRACE_LVL > 0
2937 debug_printf( RETROGUI_TRACE_LVL,
2938 "zeroed str sz for \"%s\": " SIZE_T_FMT, buffer, buffer_sz + 1 );
2939#endif /* RETROGUI_TRACE_LVL */
2940 maug_strncpy( label_tmp, buffer, buffer_sz );
2941#if RETROGUI_TRACE_LVL > 0
2942 debug_printf( RETROGUI_TRACE_LVL, "copied str as: \"%s\"", label_tmp );
2943#endif /* RETROGUI_TRACE_LVL */
2944 maug_munlock( ctl->TEXTBOX.text_h, label_tmp );
2945
2946#endif /* !RETROGUI_NO_TEXTBOX */
2947 } else {
2948 error_printf( "invalid control type! no label!" );
2949 goto cleanup;
2950 }
2951
2952 /* New text! Redraw! */
2953 gui->flags |= RETROGUI_FLAGS_DIRTY;
2954
2955cleanup:
2956
2957 if( NULL != buffer ) {
2958 maug_munlock( buffer_h, buffer );
2959 }
2960
2961 if( (MAUG_MHANDLE)NULL != buffer_h ) {
2962 maug_mfree( buffer_h );
2963 }
2964
2965 mdata_vector_unlock( &(gui->ctls) );
2966
2967 return retval;
2968}
2969
2970/* === */
2971
2973 struct RETROGUI* gui, retrogui_idc_t idc, const maug_path path, uint8_t flags
2974) {
2975 MERROR_RETVAL retval = MERROR_OK;
2976 union RETROGUI_CTL* ctl = NULL;
2977 int autolock = 0;
2978
2979 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
2980 mdata_vector_lock( &(gui->ctls) );
2981 autolock = 1;
2982 }
2983
2984#if RETROGUI_TRACE_LVL > 0
2985 debug_printf( RETROGUI_TRACE_LVL,
2986 "setting control " RETROGUI_IDC_FMT " image to: %s", idc, path );
2987#endif /* RETROGUI_TRACE_LVL */
2988
2989 /* Figure out the control to update. */
2990 ctl = _retrogui_get_ctl_by_idc( gui, idc );
2991 if( NULL == ctl ) {
2992 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
2993 error_printf( "could not set control image!" );
2994 retval = MERROR_GUI;
2995 goto cleanup;
2996 }
2997
2998 /* Perform the actual update. */
2999 if( RETROGUI_CTL_TYPE_IMAGE == ctl->base.type ) {
3000 /* Cleanup existing image. */
3001# ifdef RETROGXC_PRESENT
3002 ctl->IMAGE.image_cache_id = -1;
3003# endif /* RETROGXC_PRESENT */
3004 if( retroflat_2d_bitmap_ok( &(ctl->IMAGE.image) ) ) {
3005 retroflat_2d_destroy_bitmap( &(ctl->IMAGE.image) );
3006 }
3007
3008 /* Load the replacement image. */
3009 if( NULL != path && '\0' != path[0] ) {
3010# if defined( RETROGXC_PRESENT )
3011 ctl->IMAGE.image_cache_id = retrogxc_load_bitmap( path, flags );
3012# else /* Only use a normal image if cache not present! */
3013 retroflat_2d_load_bitmap( path, &(ctl->IMAGE.image), flags );
3014# endif /* RETROGXC_PRESENT */
3015 }
3016 } else {
3017 error_printf( "invalid control type! no image!" );
3018 goto cleanup;
3019 }
3020
3021 /* New text! Redraw! */
3022 gui->flags |= RETROGUI_FLAGS_DIRTY;
3023
3024cleanup:
3025
3026 if( autolock ) {
3027 mdata_vector_unlock( &(gui->ctls) );
3028 }
3029
3030 return retval;
3031}
3032
3033/* === */
3034
3036 struct RETROGUI* gui, retrogui_idc_t idc, retroflat_blit_t* blit,
3037 uint8_t flags
3038) {
3039 MERROR_RETVAL retval = MERROR_OK;
3040 union RETROGUI_CTL* ctl = NULL;
3041 int autolock = 0;
3042 retroflat_pxxy_t ctl_img_w, ctl_img_h, blit_w, blit_h;
3043
3044 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
3045 mdata_vector_lock( &(gui->ctls) );
3046 autolock = 1;
3047 }
3048
3049#if RETROGUI_TRACE_LVL > 0
3050 debug_printf( RETROGUI_TRACE_LVL,
3051 "setting control " RETROGUI_IDC_FMT " image to: %p", idc, blit );
3052#endif /* RETROGUI_TRACE_LVL */
3053
3054 /* Figure out the control to update. */
3055 ctl = _retrogui_get_ctl_by_idc( gui, idc );
3056 if( NULL == ctl ) {
3057 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
3058 error_printf( "could not set control image!" );
3059 retval = MERROR_GUI;
3060 goto cleanup;
3061 }
3062
3063 /* Perform the actual update. */
3064 if( RETROGUI_CTL_TYPE_IMAGE == ctl->base.type ) {
3065 if( NULL != blit ) {
3066 /* Cleanup any existing image. */
3067# if defined( RETROGXC_PRESENT )
3068 ctl->IMAGE.image_cache_id = -1;
3069# endif /* RETROGXC_PRESENT */
3070
3071 ctl_img_w = retroflat_2d_bitmap_w( &(ctl->IMAGE.image) );
3072 ctl_img_h = retroflat_2d_bitmap_h( &(ctl->IMAGE.image) );
3073 blit_w = retroflat_2d_bitmap_w( blit );
3074 blit_h = retroflat_2d_bitmap_h( blit );
3075
3076 /* Only cleanup a normal image if it's smaller than the one to blit. */
3077 if(
3078 retroflat_2d_bitmap_ok( &(ctl->IMAGE.image) ) &&
3079 (blit_w < ctl_img_w || blit_h < ctl_img_h)
3080 ) {
3081 retroflat_2d_destroy_bitmap( &(ctl->IMAGE.image) );
3082 }
3083
3084#if RETROGUI_TRACE_LVL > 0
3085 debug_printf( RETROGUI_TRACE_LVL,
3086 "creating control " RETROGUI_IDC_FMT " image: %u, %u",
3087 idc, blit_w, blit_h );
3088#endif /* RETROGUI_TRACE_LVL */
3089
3090 /* Create the new image to blit to. */
3091 retval = retroflat_2d_create_bitmap(
3092 blit_w, blit_h, &(ctl->IMAGE.image), 0 );
3093 maug_cleanup_if_not_ok();
3094
3095 retroflat_2d_lock_bitmap( &(ctl->IMAGE.image) );
3096
3097#if RETROGUI_TRACE_LVL > 0
3098 debug_printf( RETROGUI_TRACE_LVL,
3099 "blitting control " RETROGUI_IDC_FMT " image from: %p", idc, blit );
3100#endif /* RETROGUI_TRACE_LVL */
3101
3102 /* Blit the new image over the old one. */
3103 retroflat_2d_blit_bitmap(
3104 &(ctl->IMAGE.image),
3105 blit,
3106 0, 0, 0, 0, blit_w, blit_h,
3107 ctl->IMAGE.instance );
3108 retroflat_2d_release_bitmap( &(ctl->IMAGE.image) );
3109 } else {
3110
3111 }
3112 } else {
3113 error_printf( "invalid control type! no image!" );
3114 goto cleanup;
3115 }
3116
3117 /* New text! Redraw! */
3118 gui->flags |= RETROGUI_FLAGS_DIRTY;
3119
3120cleanup:
3121
3122 if( autolock ) {
3123 mdata_vector_unlock( &(gui->ctls) );
3124 }
3125
3126 return retval;
3127}
3128
3129/* === */
3130
3132 struct RETROGUI* gui, retrogui_idc_t idc, uint16_t level, uint16_t max,
3133 uint8_t flags
3134) {
3135 MERROR_RETVAL retval = MERROR_OK;
3136 union RETROGUI_CTL* ctl = NULL;
3137 int autolock = 0;
3138
3139 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
3140 mdata_vector_lock( &(gui->ctls) );
3141 autolock = 1;
3142 }
3143
3144#if RETROGUI_TRACE_LVL > 0
3145 debug_printf( RETROGUI_TRACE_LVL,
3146 "setting control " RETROGUI_IDC_FMT " level to: %u", idc, level );
3147#endif /* RETROGUI_TRACE_LVL */
3148
3149 /* Figure out the control to update. */
3150 ctl = _retrogui_get_ctl_by_idc( gui, idc );
3151 if( NULL == ctl ) {
3152 /* Elaborate on error from _retrogui_get_ctl_by_idc(). */
3153 error_printf( "could not set control level!" );
3154 retval = MERROR_GUI;
3155 goto cleanup;
3156 }
3157
3158 /* Perform the actual update. */
3159 if( RETROGUI_CTL_TYPE_FILLBAR == ctl->base.type ) {
3160 ctl->FILLBAR.cur = level;
3161 if( 0 < max ) {
3162 ctl->FILLBAR.max = max;
3163 }
3164 } else {
3165 error_printf( "invalid control type! no level!" );
3166 goto cleanup;
3167 }
3168
3169 /* New level! Redraw! */
3170 gui->flags |= RETROGUI_FLAGS_DIRTY;
3171
3172cleanup:
3173
3174 if( autolock ) {
3175 mdata_vector_unlock( &(gui->ctls) );
3176 }
3177
3178 return retval;
3179}
3180
3181/* === */
3182
3183MERROR_RETVAL retrogui_init_ctl(
3184 union RETROGUI_CTL* ctl, uint8_t type, retrogui_idc_t idc
3185) {
3186 MERROR_RETVAL retval = MERROR_OK;
3187
3188#if RETROGUI_TRACE_LVL > 0
3189 debug_printf( RETROGUI_TRACE_LVL,
3190 "initializing control base " RETROGUI_IDC_FMT "...", idc );
3191#endif /* RETROGUI_TRACE_LVL */
3192
3193 maug_mzero( ctl, sizeof( union RETROGUI_CTL ) );
3194
3195 ctl->base.type = type;
3196 ctl->base.idc = idc;
3197 ctl->base.fg_color = RETROFLAT_COLOR_NULL;
3198 ctl->base.bg_color = RETROFLAT_COLOR_NULL;
3199 ctl->base.sel_fg = RETROFLAT_COLOR_NULL;
3200 ctl->base.sel_bg = RETROFLAT_COLOR_NULL;
3201
3202 #define RETROGUI_CTL_TABLE_INITS( idx, c_name, c_fields ) \
3203 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
3204 retrogui_init_ ## c_name( ctl );
3205
3206 if( 0 ) {
3207 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_INITS )
3208 }
3209
3210# ifdef RETROGXC_PRESENT
3211 if( RETROGUI_CTL_TYPE_IMAGE == type ) {
3212 ctl->IMAGE.image_cache_id = -1;
3213 }
3214# endif /* !RETROGXC_PRESENT */
3215
3216 return retval;
3217}
3218
3219/* === */
3220
3221MERROR_RETVAL retrogui_destroy( struct RETROGUI* gui ) {
3222 size_t i = 0;
3223 union RETROGUI_CTL* ctl = NULL;
3224 MERROR_RETVAL retval = MERROR_OK;
3225
3226 if( mdata_vector_is_locked( &((gui)->ctls) ) ) {
3227 error_printf( "GUI is locked!" );
3228 goto cleanup;
3229 }
3230
3231 if( 0 == mdata_vector_ct( &(gui->ctls) ) ) {
3232 goto cleanup;
3233 }
3234
3235 assert( !mdata_vector_is_locked( &((gui)->ctls) ) );
3236 mdata_vector_lock( &(gui->ctls) );
3237
3238 #define RETROGUI_CTL_TABLE_FREE( idx, c_name, c_fields ) \
3239 } else if( RETROGUI_CTL_TYPE_ ## c_name == ctl->base.type ) { \
3240 retrogui_destroy_ ## c_name( ctl );
3241
3242 for( i = 0 ; mdata_vector_ct( &(gui->ctls) ) > i ; i++ ) {
3243 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
3244 if( 0 ) {
3245 RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_FREE )
3246 }
3247 }
3248
3249 mdata_vector_unlock( &(gui->ctls) );
3250
3251# ifndef RETROGXC_PRESENT
3252 if( RETROGUI_FLAGS_FONT_OWNED == (RETROGUI_FLAGS_FONT_OWNED & gui->flags) ) {
3253 maug_mfree( gui->font.handle );
3254 }
3255# endif /* !RETROGXC_PRESENT */
3256
3257cleanup:
3258
3259 mdata_vector_free( &(gui->ctls) );
3260
3261 return retval;
3262}
3263
3264/* === */
3265
3266retrogui_idc_t retrogui_focus_iter(
3267 struct RETROGUI* gui, size_t start, ssize_t incr
3268) {
3269 retrogui_idc_t idc_out = RETROGUI_IDC_NONE;
3270 union RETROGUI_CTL* ctl = NULL;
3271 MERROR_RETVAL retval = MERROR_OK;
3272 retrogui_idc_t i = 0;
3273 ssize_t i_before = -1; /* Index of the current selected IDC. */
3274 int autolock = 0;
3275
3276 if( 0 == mdata_vector_ct( &(gui->ctls) ) ) {
3277 goto cleanup;
3278 }
3279
3280 if( !mdata_vector_is_locked( &((gui)->ctls) ) ) {
3281 mdata_vector_lock( &(gui->ctls) );
3282 autolock = 1;
3283 }
3284
3285 /* Find the currently selected IDC. */
3286 for(
3287 i = start ; mdata_vector_ct( &(gui->ctls) ) > i && 0 <= i ; i += incr
3288 ) {
3289 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
3290 if( !retrogui_can_focus_ctl( ctl ) ) {
3291 continue;
3292 } else if( RETROGUI_IDC_NONE == gui->focus || 0 <= i_before ) {
3293 /* We're primed to set the new focus, so do that and finish. */
3294 idc_out = ctl->base.idc;
3295#if RETROGUI_TRACE_LVL > 0
3296 debug_printf( RETROGUI_TRACE_LVL,
3297 "moving focus to control: " RETROGUI_IDC_FMT, idc_out );
3298#endif /* RETROGUI_TRACE_LVL */
3299 gui->focus = idc_out;
3300 goto cleanup;
3301
3302 } else if( ctl->base.idc == gui->focus ) {
3303 /* We've found the current focus, so prime to select the new focus. */
3304 i_before = i;
3305 }
3306 }
3307
3308 /* We didn't select a focus in the loop above, so we must be wrapping around!
3309 */
3310
3311 /* Select the next IDC. */
3312 if( 0 > i ) {
3313 /* Wrap around to last SELECTABLE item. */
3314 for( i = mdata_vector_ct( &(gui->ctls) ) - 1 ; 0 <= i ; i-- ) {
3315 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
3316 if( !retrogui_can_focus_ctl( ctl ) ) {
3317 /* Skip NON-SELECTABLE items! */
3318#if RETROGUI_TRACE_LVL > 0
3319 debug_printf(
3320 RETROGUI_TRACE_LVL, "skipping: " RETROGUI_IDC_FMT, i );
3321#endif /* RETROGUI_TRACE_LVL */
3322 continue;
3323 } else {
3324 idc_out = ctl->base.idc;
3325#if RETROGUI_TRACE_LVL > 0
3326 debug_printf( RETROGUI_TRACE_LVL,
3327 "moving focus to control: " RETROGUI_IDC_FMT, idc_out );
3328#endif /* RETROGUI_TRACE_LVL */
3329 gui->focus = idc_out;
3330 break;
3331 }
3332 }
3333
3334 } else if( mdata_vector_ct( &(gui->ctls) ) <= i ) {
3335 /* Wrap around to first SELECTABLE item. */
3336 for( i = 0 ; mdata_vector_ct( &(gui->ctls) ) > i ; i++ ) {
3337 ctl = mdata_vector_get( &(gui->ctls), i, union RETROGUI_CTL );
3338 if( !retrogui_can_focus_ctl( ctl ) ) {
3339 /* Skip NON-SELECTABLE items! */
3340#if RETROGUI_TRACE_LVL > 0
3341 debug_printf(
3342 RETROGUI_TRACE_LVL, "skipping: " RETROGUI_IDC_FMT, i );
3343#endif /* RETROGUI_TRACE_LVL */
3344 continue;
3345 } else {
3346 idc_out = ctl->base.idc;
3347#if RETROGUI_TRACE_LVL > 0
3348 debug_printf( RETROGUI_TRACE_LVL,
3349 "moving focus to control: " RETROGUI_IDC_FMT, idc_out );
3350#endif /* RETROGUI_TRACE_LVL */
3351 gui->focus = idc_out;
3352 break;
3353 }
3354 }
3355
3356 } else {
3357 error_printf( "invalid focus: " RETROGUI_IDC_FMT, i );
3358
3359 }
3360
3361cleanup:
3362
3363 /* New focus! Dirty! */
3364 gui->flags |= RETROGUI_FLAGS_DIRTY;
3365
3366 if( MERROR_OK != retval ) {
3367 idc_out = merror_retval_to_sz( retval );
3368 }
3369
3370 if( autolock ) {
3371 mdata_vector_unlock( &(gui->ctls) );
3372 }
3373
3374#if RETROGUI_TRACE_LVL > 0
3375 debug_printf(
3376 RETROGUI_TRACE_LVL, "selected IDC: " RETROGUI_IDC_FMT, idc_out );
3377#endif /* RETROGUI_TRACE_LVL */
3378
3379 return idc_out;
3380}
3381
3382/* === */
3383
3384MERROR_RETVAL retrogui_init( struct RETROGUI* gui ) {
3385 MERROR_RETVAL retval = MERROR_OK;
3386
3387 maug_mzero( gui, sizeof( struct RETROGUI ) );
3388
3389 gui->bg_color = RETROFLAT_COLOR_BLACK;
3390 gui->focus = RETROGUI_IDC_NONE;
3391 gui->debounce_max = RETROGUI_DEBOUNCE_MAX_DEFAULT;
3392
3393#if RETROGUI_TRACE_LVL > 0
3394 debug_printf( RETROGUI_TRACE_LVL, "initialized GUI" );
3395#endif /* RETROGUI_TRACE_LVL */
3396
3397 return retval;
3398}
3399
3400#else
3401
3402#define RETROGUI_CTL_TABLE_CONSTS( idx, c_name, c_fields ) \
3403 extern MAUG_CONST uint8_t SEG_MCONST RETROGUI_CTL_TYPE_ ## c_name;
3404
3405RETROGUI_CTL_TABLE( RETROGUI_CTL_TABLE_CONSTS )
3406
3407#ifdef RETROGUI_TRACE_TOKENS
3408extern MAUG_CONST char* gc_retrogui_ctl_names[];
3409#endif /* RETROGUI_TRACE_TOKENS */
3410
3411#endif /* RETROGUI_C */
3412 /* maug_retrogui */
3414 /* maug_retroflt */
3416
3417#endif /* !RETROGUI_H */
3418
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:141
int8_t RETROFLAT_COLOR
Defines an index in the platform-specific color-table.
Definition retroflt.h:326
#define RETROFLAT_FLAGS_FILL
Flag for retroflat_rect() or retroflat_ellipse(), indicating drawn shape should be filled.
Definition retroflt.h:374
#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:831
#define retroflat_buffer_bksp(buffer, buffer_cur, buffer_sz)
Remove a character from a text buffer before cursor position.
Definition retroflt.h:815
#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:1066
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:474
int16_t retroflat_pxxy_t
Type used for surface pixel coordinates.
Definition retroflt.h:910
#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:854
int mouse_y
Y-coordinate of the mouse pointer in pixels if the returned event is a mouse click.
Definition retroflt.h:864
int mouse_x
X-coordinate of the mouse pointer in pixels if the returned event is a mouse click.
Definition retroflt.h:859
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
Unique identifying index for current highlighted RETROGUI_CTL.
Definition retrogui.h:478
Definition retrogui.h:450