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