@@ -730,26 +730,23 @@ void rg_gui_draw_dialog(const char *title, const rg_gui_option_t *options, size_
730730 y += font_height + 6 ;
731731 }
732732
733- int top_i = 0 ;
734- int end_i = 0 ;
733+ int list_top_i = 0 ;
734+ int list_end_i = 0 ;
735735
736736 // Find top of page that contains selection
737- if ( sel >= 0 && sel < options_count )
737+ for ( int yy = y , i = 0 ; i <= sel && i < options_count ; i ++ )
738738 {
739- for (int yy = y , i = 0 ; i <= sel ; i ++ )
739+ yy += row_height [i ];
740+ if (yy >= box_y + box_height )
740741 {
741- yy += row_height [i ];
742- if (yy >= box_y + box_height )
743- {
744- if (sel < i )
745- break ;
746- yy = y ;
747- top_i = i ;
748- }
742+ if (sel < i )
743+ break ;
744+ yy = y + row_height [i ];
745+ list_top_i = i ;
749746 }
750747 }
751748
752- for (int i = top_i ; i < options_count ; i ++ )
749+ for (int i = list_top_i ; i < options_count ; i ++ )
753750 {
754751 uint16_t color , fg , bg ;
755752 int xx = x + row_padding_x ;
@@ -770,7 +767,7 @@ void rg_gui_draw_dialog(const char *title, const rg_gui_option_t *options, size_
770767 if (y + row_height [i ] >= box_y + box_height )
771768 break ;
772769
773- end_i = i ;
770+ list_end_i = i ;
774771
775772 if (options [i ].flags == RG_DIALOG_FLAG_HIDDEN )
776773 continue ;
@@ -809,7 +806,7 @@ void rg_gui_draw_dialog(const char *title, const rg_gui_option_t *options, size_
809806 rg_gui_draw_rect (box_x - 1 , box_y - 1 , box_width + 2 , box_height + 2 , 1 , gui .style .box_border , C_NONE );
810807
811808 // Basic scroll indicators are overlayed at the end...
812- if (top_i > 0 )
809+ if (list_top_i > 0 )
813810 {
814811 int x = box_x + box_width - 10 ;
815812 int y = box_y + box_padding + 2 ;
@@ -818,7 +815,7 @@ void rg_gui_draw_dialog(const char *title, const rg_gui_option_t *options, size_
818815 rg_gui_draw_rect (x + 2 , y - 4 , 2 , 2 , 0 , 0 , gui .style .scrollbar );
819816 }
820817
821- if (end_i + 1 < options_count )
818+ if (list_end_i + 1 < options_count )
822819 {
823820 int x = box_x + box_width - 10 ;
824821 int y = box_y + box_height - 6 ;
@@ -847,45 +844,55 @@ void rg_gui_draw_message(const char *format, ...)
847844
848845intptr_t rg_gui_dialog (const char * title , const rg_gui_option_t * options_const , int selected_index )
849846{
847+ rg_gui_option_t * options = (rg_gui_option_t * )options_const ;
850848 size_t options_count = get_dialog_items_count (options_const );
851- int sel = selected_index < 0 ? (options_count + selected_index ) : selected_index ;
852- int sel_old = -1 ;
853- bool redraw = false;
854-
855- // Constrain initial cursor and skip FLAG_SKIP items
856- sel = RG_MIN (RG_MAX (0 , sel ), options_count - 1 );
857-
858- // We create a copy of options because the callbacks might modify it (ie option->value)
859- rg_gui_option_t options [options_count + 1 ];
860- // The text_buffer is used for mutable option->values. Because values tend to be small, we can make
861- // some assumptions. This is a terrible system that is prone to corruption. But we're stuck with it.
862- size_t text_buffer_size = RG_MAX (options_count * 32 , 1024 );
863- char * text_buffer = malloc (text_buffer_size );
864- char * text_buffer_ptr = text_buffer ;
865-
866- memcpy (options , options_const , sizeof (options ));
867849
850+ // In many cases we must create a copy of the options array because it can be mutated by the callbacks
851+ // (typically option->value and option->flags). No callback in the array = no way of it being mutable.
852+ // The entire text_buffer system is very brittle and prone to corruption. We get away with it for now
853+ // because most values are less than our assumed 32 bytes...
854+ size_t shadow_options_count = 0 ;
855+ size_t shadow_text_buffer_size = 0 ;
868856 for (size_t i = 0 ; i < options_count ; i ++ )
869857 {
870- rg_gui_option_t * option = & options [i ];
871- if (!option -> label )
872- option -> label = "" ;
873- if (option -> value && text_buffer_ptr )
858+ if (options_const [i ].update_cb )
859+ shadow_options_count = options_count ;
860+ // if (options_const[i].value)
861+ // shadow_text_buffer_size += strlen(options_const[i].value) + 1;
862+ }
863+ rg_gui_option_t shadow_options [shadow_options_count + 1 ];
864+ char * shadow_text_buffer = NULL ;
865+ if (shadow_options_count > 0 )
866+ {
867+ options = memcpy (shadow_options , options_const , sizeof (shadow_options ));
868+ shadow_text_buffer_size = RG_MAX (options_count * 32 , 1024 );
869+ shadow_text_buffer = malloc (shadow_text_buffer_size );
870+ char * text_buffer_ptr = shadow_text_buffer ;
871+ for (size_t i = 0 ; i < shadow_options_count ; i ++ )
872+ {
873+ rg_gui_option_t * option = & shadow_options [i ];
874+ if (!text_buffer_ptr || !option -> value || !option -> update_cb )
875+ continue ;
874876 option -> value = strcpy (text_buffer_ptr , option -> value );
875- if (option -> update_cb )
876877 option -> update_cb (option , RG_DIALOG_INIT );
877- if ( option -> value && text_buffer_ptr )
878- text_buffer_ptr += RG_MAX ( strlen ( option -> value ), 31 ) + 1 ;
878+ text_buffer_ptr += RG_MAX ( strlen ( text_buffer_ptr ), 31 ) + 1 ;
879+ }
879880 }
880881
881- rg_gui_draw_status_bars ();
882- rg_gui_draw_dialog (title , options , options_count , sel );
883- rg_input_wait_for_key (RG_KEY_ALL , false, 1000 );
884- rg_task_delay (80 );
882+ if (selected_index < 0 )
883+ selected_index += options_count ;
885884
886885 rg_gui_event_t event = RG_DIALOG_VOID ;
887886 uint32_t joystick = 0 , joystick_old ;
888887 uint64_t joystick_last = 0 ;
888+ bool redraw = false;
889+ int sel = RG_MIN (RG_MAX (0 , selected_index ), options_count - 1 );
890+ int sel_old = -1 ;
891+
892+ rg_gui_draw_status_bars ();
893+ rg_gui_draw_dialog (title , options , options_count , sel );
894+ rg_input_wait_for_key (RG_KEY_ALL , false, 1000 );
895+ rg_task_delay (80 );
889896
890897 while (event != RG_DIALOG_SELECT && event != RG_DIALOG_CANCEL )
891898 {
@@ -981,7 +988,8 @@ intptr_t rg_gui_dialog(const char *title, const rg_gui_option_t *options_const,
981988
982989 rg_input_wait_for_key (joystick , false, 1000 );
983990 rg_display_force_redraw ();
984- free (text_buffer );
991+ // free(shadow_options);
992+ free (shadow_text_buffer );
985993
986994 if (event == RG_DIALOG_CANCEL || sel < 0 )
987995 return RG_DIALOG_CANCELLED ;
@@ -1014,7 +1022,7 @@ void rg_gui_alert(const char *title, const char *message)
10141022
10151023typedef struct
10161024{
1017- rg_gui_option_t options [ 22 ] ;
1025+ rg_gui_option_t * options ;
10181026 size_t count ;
10191027 bool (* validator )(const char * path );
10201028} file_picker_opts_t ;
@@ -1024,51 +1032,50 @@ static int file_picker_cb(const rg_scandir_t *entry, void *arg)
10241032 file_picker_opts_t * f = arg ;
10251033 if (f -> validator && !(f -> validator )(entry -> path ))
10261034 return RG_SCANDIR_SKIP ;
1027- char * path = strdup (entry -> path );
1028- f -> options [f -> count ].arg = (intptr_t )path ;
1029- f -> options [f -> count ].flags = RG_DIALOG_FLAG_NORMAL ;
1030- f -> options [f -> count ].label = rg_basename (path );
1031- f -> count ++ ;
1032- if (f -> count > 18 )
1035+ rg_gui_option_t * options = realloc (f -> options , (f -> count + 2 ) * sizeof (rg_gui_option_t ));
1036+ if (!options )
10331037 return RG_SCANDIR_STOP ;
1038+ char * name = strdup (entry -> basename );
1039+ f -> options = options ;
1040+ f -> options [f -> count ++ ] = (rg_gui_option_t ){(intptr_t )name , name , NULL , RG_DIALOG_FLAG_NORMAL , NULL };
10341041 return RG_SCANDIR_CONTINUE ;
10351042}
10361043
10371044char * rg_gui_file_picker (const char * title , const char * path , bool (* validator )(const char * path ), bool none_option )
10381045{
10391046 file_picker_opts_t options = {
1040- .options = {} ,
1047+ .options = calloc ( 8 , sizeof ( rg_gui_option_t )) ,
10411048 .count = 0 ,
10421049 .validator = validator ,
10431050 };
1051+ char * filepath = NULL ;
10441052
10451053 if (!title )
10461054 title = _ ("Select file" );
10471055
10481056 if (none_option )
1049- {
10501057 options .options [options .count ++ ] = (rg_gui_option_t ){0 , _ ("<None>" ), NULL , RG_DIALOG_FLAG_NORMAL , NULL };
1051- // options.options[options.count++] = (rg_gui_option_t)RG_DIALOG_SEPARATOR;
1052- }
10531058
10541059 if (!rg_storage_scandir (path , file_picker_cb , & options , 0 ) || options .count < 1 )
10551060 {
10561061 rg_gui_alert (title , _ ("Folder is empty." ));
1057- return NULL ;
1062+ goto cleanup ;
10581063 }
1059-
10601064 options .options [options .count ] = (rg_gui_option_t )RG_DIALOG_END ;
10611065
1062- char * filepath = (char * )rg_gui_dialog (title , options .options , 0 );
1063-
1064- if (filepath != (void * )RG_DIALOG_CANCELLED )
1065- filepath = strdup (filepath ? filepath : "" );
1066- else
1067- filepath = NULL ;
1066+ char * filename = (char * )rg_gui_dialog (title , options .options , 0 );
1067+ if (filename != (void * )RG_DIALOG_CANCELLED )
1068+ {
1069+ char buffer [RG_PATH_MAX ] = "" ;
1070+ if (filename )
1071+ snprintf (buffer , RG_PATH_MAX , "%s/%s" , path , filename );
1072+ filepath = strdup (buffer );
1073+ }
10681074
1075+ cleanup :
10691076 for (size_t i = 0 ; i < options .count ; ++ i )
10701077 free ((void * )(options .options [i ].arg ));
1071-
1078+ free ( options . options );
10721079 return filepath ;
10731080}
10741081
0 commit comments