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