diff --git a/4coder_custom.cpp b/4coder_custom.cpp index 91a498be..68bfdea9 100644 --- a/4coder_custom.cpp +++ b/4coder_custom.cpp @@ -139,7 +139,7 @@ CUSTOM_COMMAND_SIG(build_search){ // Step 3: If the batch file did not exist try to move to the parent directory using // app->directory_cd. The cd function can also be used to navigate to subdirectories. // It returns true if it can actually move in the specified direction, and false otherwise. - + int keep_going = 1; String dir = push_directory(app, cmd_context); while (keep_going){ @@ -147,7 +147,7 @@ CUSTOM_COMMAND_SIG(build_search){ push_parameter(app, cmd_context, par_cli_overlap_with_conflict, 0); push_parameter(app, cmd_context, par_target_buffer_name, literal("*compilation*")); push_parameter(app, cmd_context, par_cli_path, dir.str, dir.size); - + if (append(&dir, "build")){ app->push_parameter(cmd_context, dynamic_int(par_cli_command), @@ -157,7 +157,7 @@ CUSTOM_COMMAND_SIG(build_search){ else{ clear_parameters(cmd_context); } - + return; } diff --git a/4ed.cpp b/4ed.cpp index 91d8d17b..2b9d4251 100644 --- a/4ed.cpp +++ b/4ed.cpp @@ -3038,7 +3038,7 @@ App_Step_Sig(app_step){ // NOTE(allen): OS clipboard event handling if (clipboard.str){ String *dest = working_set_next_clipboard_string(&vars->mem.general, &vars->working_set, clipboard.size); - copy(dest, make_string((char*)clipboard.str, clipboard.size)); + dest->size = eol_convert_in(dest->str, clipboard.str, clipboard.size); } // TODO(allen): profile this make sure it's not costing me too much power. diff --git a/4ed.h b/4ed.h index 5edf9bc4..8590881c 100644 --- a/4ed.h +++ b/4ed.h @@ -84,12 +84,6 @@ struct Input_Summary{ Key_Codes *codes; }; -// TODO(allen): This can go, and we can just use a String for it. -struct Clipboard_Contents{ - u8 *str; - i32 size; -}; - struct Command_Line_Parameters{ char **argv; int argc; @@ -122,7 +116,7 @@ typedef App_Read_Command_Line_Sig(App_Read_Command_Line); Application_Memory *memory, \ Exchange *exchange, \ Key_Codes *codes, \ - Clipboard_Contents clipboard, \ + String clipboard, \ String current_directory, \ Custom_API api) @@ -152,7 +146,7 @@ struct Application_Step_Result{ Render_Target *target, \ Application_Memory *memory, \ Exchange *exchange, \ - Clipboard_Contents clipboard, \ + String clipboard, \ b32 time_step, b32 first_step, b32 force_redraw, \ Application_Step_Result *result) diff --git a/4ed_app_target.cpp b/4ed_app_target.cpp index 819365b8..1058a1e2 100644 --- a/4ed_app_target.cpp +++ b/4ed_app_target.cpp @@ -40,6 +40,8 @@ #include "4ed_command.cpp" #include "4ed_layout.cpp" #include "4ed_style.cpp" +#include "4ed_file.cpp" +#include "4ed_gui.cpp" #include "4ed_file_view.cpp" #include "4ed_color_view.cpp" #include "4ed_interactive_view.cpp" diff --git a/4ed_file.cpp b/4ed_file.cpp new file mode 100644 index 00000000..d4efe7fc --- /dev/null +++ b/4ed_file.cpp @@ -0,0 +1,376 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 20.02.2016 + * + * File editing view for 4coder + * + */ + +// TOP + +#include "buffer/4coder_shared.cpp" + +#if BUFFER_EXPERIMENT_SCALPEL == 0 +#include "buffer/4coder_golden_array.cpp" +#define Buffer_Type Buffer +#elif BUFFER_EXPERIMENT_SCALPEL == 1 +#include "buffer/4coder_gap_buffer.cpp" +#define Buffer_Type Gap_Buffer +#elif BUFFER_EXPERIMENT_SCALPEL == 2 +#include "buffer/4coder_multi_gap_buffer.cpp" +#define Buffer_Type Multi_Gap_Buffer +#else +#include "buffer/4coder_rope_buffer.cpp" +#define Buffer_Type Rope_Buffer +#endif + +#include "buffer/4coder_buffer_abstract.cpp" + +enum Edit_Type{ + ED_NORMAL, + ED_REVERSE_NORMAL, + ED_UNDO, + ED_REDO, +}; + +struct Edit_Step{ + Edit_Type type; + union{ + struct{ + b32 can_merge; + Buffer_Edit edit; + i32 pre_pos; + i32 post_pos; + i32 next_block, prev_block; + }; + struct{ + i32 first_child; + i32 inverse_first_child; + i32 inverse_child_count; + i32 special_type; + }; + }; + i32 child_count; +}; + +struct Edit_Stack{ + u8 *strings; + i32 size, max; + + Edit_Step *edits; + i32 edit_count, edit_max; +}; + +struct Small_Edit_Stack{ + u8 *strings; + i32 size, max; + + Buffer_Edit *edits; + i32 edit_count, edit_max; +}; + +struct Undo_Data{ + Edit_Stack undo; + Edit_Stack redo; + Edit_Stack history; + Small_Edit_Stack children; + + i32 history_block_count, history_head_block; + i32 edit_history_cursor; + b32 current_block_normal; +}; + +struct Text_Effect{ + i32 start, end; + u32 color; + i32 tick_down, tick_max; +}; + +// NOTE(allen): The Editing_File struct is now divided into two +// parts. Variables in the Settings part can be set even when the +// file is still streaming in, and all operations except for the +// initial allocation of the file. +struct Editing_File_Settings{ + Font_Set *set; + i32 base_map_id; + i32 dos_write_mode; + b32 unwrapped_lines; + b8 tokens_exist; + b8 super_locked; + b8 is_initialized; + b8 unimportant; +}; + +// NOTE(allen): This part of the Editing_File is cleared whenever +// the contents of the file is set. +struct Editing_File_State{ + b32 is_dummy; + b32 is_loading; + + i16 font_id; + Buffer_Type buffer; + + i32 cursor_pos; + + Undo_Data undo; + + Cpp_Token_Stack token_stack; + Cpp_Token_Stack swap_stack; + u32 lex_job; + b32 tokens_complete; + b32 still_lexing; + + Text_Effect paste_effect; + + u64 last_4ed_write_time; + u64 last_4ed_edit_time; + u64 last_sys_write_time; +}; + +struct Editing_File_Preload{ + i32 start_line; +}; + +struct Editing_File_Name{ + char live_name_[256]; + String live_name; + + char source_path_[256]; + char extension_[16]; + String source_path; + String extension; +}; + +struct Editing_File{ + Editing_File_Settings settings; + union{ + Editing_File_State state; + Editing_File_Preload preload; + }; + Editing_File_Name name; +}; + +struct File_Table_Entry{ + String name; + u32 hash; + i32 id; +}; + +struct File_Table{ + File_Table_Entry *table; + i32 count, max; +}; + +internal u32 +get_file_hash(String name){ + u32 x = 5381; + int i = 0; + char c; + while (i < name.size){ + c = name.str[i++]; + x = ((x << 5) + x) + c; + } + return x; +} + +internal b32 +table_add(File_Table *table, String name, i32 id){ + Assert(table->count * 3 < table->max * 2); + + File_Table_Entry entry, e; + i32 i; + + entry.name = name; + entry.id = id; + entry.hash = get_file_hash(name); + i = entry.hash % table->max; + while ((e = table->table[i]).name.str){ + if (e.hash == entry.hash && match(e.name, entry.name)){ + return 1; + } + i = (i + 1) % table->max; + } + table->table[i] = entry; + ++table->count; + + return 0; +} + +internal bool32 +table_find_pos(File_Table *table, String name, i32 *index){ + File_Table_Entry e; + i32 i; + u32 hash; + + hash = get_file_hash(name); + i = hash % table->max; + while ((e = table->table[i]).name.size){ + if (e.name.str && e.hash == hash && match(e.name, name)){ + *index = i; + return 1; + } + i = (i + 1) % table->max; + } + + return 0; +} + +inline b32 +table_find(File_Table *table, String name, i32 *id){ + i32 pos; + b32 r = table_find_pos(table, name, &pos); + if (r) *id = table->table[pos].id; + return r; +} + +inline b32 +table_remove(File_Table *table, String name){ + i32 pos; + b32 r = table_find_pos(table, name, &pos); + if (r){ + table->table[pos].name.str = 0; + --table->count; + } + return r; +} + +struct Working_Set{ + Editing_File *files; + i32 file_index_count, file_max_count; + + File_Table table; + + String clipboards[64]; + i32 clipboard_size, clipboard_max_size; + i32 clipboard_current, clipboard_rolling; +}; + +// Hot Directory + +struct Hot_Directory{ + String string; + File_List file_list; +}; + +internal void +hot_directory_init(Hot_Directory *hot_directory, String base, String dir){ + hot_directory->string = base; + hot_directory->string.str[255] = 0; + hot_directory->string.size = 0; + copy(&hot_directory->string, dir); + append(&hot_directory->string, "\\"); +} + +internal void +hot_directory_clean_end(Hot_Directory *hot_directory){ + String *str = &hot_directory->string; + if (str->size != 0 && str->str[str->size-1] != '\\'){ + str->size = reverse_seek_slash(*str) + 1; + str->str[str->size] = 0; + } +} + +internal i32 +hot_directory_quick_partition(File_Info *infos, i32 start, i32 pivot){ + File_Info *p = infos + pivot; + File_Info *a = infos + start; + for (i32 i = start; i < pivot; ++i, ++a){ + i32 comp = 0; + comp = p->folder - a->folder; + if (comp == 0) comp = compare(a->filename, p->filename); + if (comp < 0){ + Swap(*a, infos[start]); + ++start; + } + } + Swap(*p, infos[start]); + return start; +} + +internal void +hot_directory_quick_sort(File_Info *infos, i32 start, i32 pivot){ + i32 mid = hot_directory_quick_partition(infos, start, pivot); + if (start < mid-1) hot_directory_quick_sort(infos, start, mid-1); + if (mid+1 < pivot) hot_directory_quick_sort(infos, mid+1, pivot); +} + +inline void +hot_directory_fixup(Hot_Directory *hot_directory, Working_Set *working_set){ + File_List *files = &hot_directory->file_list; + if (files->count >= 2) + hot_directory_quick_sort(files->infos, 0, files->count - 1); +} + +inline void +hot_directory_set(System_Functions *system, Hot_Directory *hot_directory, + String str, Working_Set *working_set){ + b32 success = copy_checked(&hot_directory->string, str); + terminate_with_null(&hot_directory->string); + if (success){ + system->set_file_list(&hot_directory->file_list, str); + } + hot_directory_fixup(hot_directory, working_set); +} + +inline void +hot_directory_reload(System_Functions *system, Hot_Directory *hot_directory, Working_Set *working_set){ + system->set_file_list(&hot_directory->file_list, hot_directory->string); + hot_directory_fixup(hot_directory, working_set); +} + +struct Hot_Directory_Match{ + String filename; + b32 is_folder; +}; + +internal b32 +filename_match(String query, Absolutes *absolutes, String filename, b32 case_sensitive){ + b32 result; + result = (query.size == 0); + if (!result) result = wildcard_match(absolutes, filename, case_sensitive); + return result; +} + +internal Hot_Directory_Match +hot_directory_first_match(Hot_Directory *hot_directory, + String str, + b32 include_files, + b32 exact_match, + b32 case_sensitive){ + Hot_Directory_Match result = {}; + + Absolutes absolutes; + if (!exact_match) + get_absolutes(str, &absolutes, 1, 1); + + File_List *files = &hot_directory->file_list; + File_Info *info, *end; + end = files->infos + files->count; + for (info = files->infos; info != end; ++info){ + String filename = info->filename; + b32 is_match = 0; + if (exact_match){ + if (case_sensitive){ + if (match(filename, str)) is_match = 1; + } + else{ + if (match_unsensitive(filename, str)) is_match = 1; + } + } + else{ + if (filename_match(str, &absolutes, filename, case_sensitive)) is_match = 1; + } + + if (is_match){ + result.is_folder = info->folder; + result.filename = filename; + break; + } + } + + return result; +} + +// BOTTOM + diff --git a/4ed_file_view.cpp b/4ed_file_view.cpp index aca25ace..53eb2077 100644 --- a/4ed_file_view.cpp +++ b/4ed_file_view.cpp @@ -9,247 +9,10 @@ // TOP -#include "buffer/4coder_shared.cpp" - -#if BUFFER_EXPERIMENT_SCALPEL == 0 -#include "buffer/4coder_golden_array.cpp" -#define Buffer_Type Buffer -#elif BUFFER_EXPERIMENT_SCALPEL == 1 -#include "buffer/4coder_gap_buffer.cpp" -#define Buffer_Type Gap_Buffer -#elif BUFFER_EXPERIMENT_SCALPEL == 2 -#include "buffer/4coder_multi_gap_buffer.cpp" -#define Buffer_Type Multi_Gap_Buffer -#else -#include "buffer/4coder_rope_buffer.cpp" -#define Buffer_Type Rope_Buffer -#endif - -#include "buffer/4coder_buffer_abstract.cpp" - struct Range{ i32 start, end; }; -enum Edit_Type{ - ED_NORMAL, - ED_REVERSE_NORMAL, - ED_UNDO, - ED_REDO, -}; - -struct Edit_Step{ - Edit_Type type; - union{ - struct{ - b32 can_merge; - Buffer_Edit edit; - i32 pre_pos; - i32 post_pos; - i32 next_block, prev_block; - }; - struct{ - i32 first_child; - i32 inverse_first_child; - i32 inverse_child_count; - i32 special_type; - }; - }; - i32 child_count; -}; - -struct Edit_Stack{ - u8 *strings; - i32 size, max; - - Edit_Step *edits; - i32 edit_count, edit_max; -}; - -struct Small_Edit_Stack{ - u8 *strings; - i32 size, max; - - Buffer_Edit *edits; - i32 edit_count, edit_max; -}; - -struct Undo_Data{ - Edit_Stack undo; - Edit_Stack redo; - Edit_Stack history; - Small_Edit_Stack children; - - i32 history_block_count, history_head_block; - i32 edit_history_cursor; - b32 current_block_normal; -}; - -struct Text_Effect{ - i32 start, end; - u32 color; - i32 tick_down, tick_max; -}; - -// NOTE(allen): The Editing_File struct is now divided into two -// parts. Variables in the Settings part can be set even when the -// file is still streaming in, and all operations except for the -// initial allocation of the file. -struct Editing_File_Settings{ - Font_Set *set; - i32 base_map_id; - i32 dos_write_mode; - b32 unwrapped_lines; - b8 tokens_exist; - b8 super_locked; - b8 is_initialized; - b8 unimportant; -}; - -// NOTE(allen): This part of the Editing_File is cleared whenever -// the contents of the file is set. -struct Editing_File_State{ - b32 is_dummy; - b32 is_loading; - - i16 font_id; - Buffer_Type buffer; - - i32 cursor_pos; - - Undo_Data undo; - - Cpp_Token_Stack token_stack; - Cpp_Token_Stack swap_stack; - u32 lex_job; - b32 tokens_complete; - b32 still_lexing; - - Text_Effect paste_effect; - - u64 last_4ed_write_time; - u64 last_4ed_edit_time; - u64 last_sys_write_time; -}; - -struct Editing_File_Preload{ - i32 start_line; -}; - -struct Editing_File_Name{ - char live_name_[256]; - String live_name; - - char source_path_[256]; - char extension_[16]; - String source_path; - String extension; -}; - -struct Editing_File{ - Editing_File_Settings settings; - union{ - Editing_File_State state; - Editing_File_Preload preload; - }; - Editing_File_Name name; -}; - -struct File_Table_Entry{ - String name; - u32 hash; - i32 id; -}; - -struct File_Table{ - File_Table_Entry *table; - i32 count, max; -}; - -internal u32 -get_file_hash(String name){ - u32 x = 5381; - int i = 0; - char c; - while (i < name.size){ - c = name.str[i++]; - x = ((x << 5) + x) + c; - } - return x; -} - -internal b32 -table_add(File_Table *table, String name, i32 id){ - Assert(table->count * 3 < table->max * 2); - - File_Table_Entry entry, e; - i32 i; - - entry.name = name; - entry.id = id; - entry.hash = get_file_hash(name); - i = entry.hash % table->max; - while ((e = table->table[i]).name.str){ - if (e.hash == entry.hash && match(e.name, entry.name)){ - return 1; - } - i = (i + 1) % table->max; - } - table->table[i] = entry; - ++table->count; - - return 0; -} - -internal bool32 -table_find_pos(File_Table *table, String name, i32 *index){ - File_Table_Entry e; - i32 i; - u32 hash; - - hash = get_file_hash(name); - i = hash % table->max; - while ((e = table->table[i]).name.size){ - if (e.name.str && e.hash == hash && match(e.name, name)){ - *index = i; - return 1; - } - i = (i + 1) % table->max; - } - - return 0; -} - -inline b32 -table_find(File_Table *table, String name, i32 *id){ - i32 pos; - b32 r = table_find_pos(table, name, &pos); - if (r) *id = table->table[pos].id; - return r; -} - -inline b32 -table_remove(File_Table *table, String name){ - i32 pos; - b32 r = table_find_pos(table, name, &pos); - if (r){ - table->table[pos].name.str = 0; - --table->count; - } - return r; -} - -struct Working_Set{ - Editing_File *files; - i32 file_index_count, file_max_count; - - File_Table table; - - String clipboards[64]; - i32 clipboard_size, clipboard_max_size; - i32 clipboard_current, clipboard_rolling; -}; - struct File_View_Mode{ b8 rewrite; }; @@ -296,784 +59,6 @@ delayed_action(Delay *delay, Action_Type type, delay->acts[delay->count++] = action; } -// Hot Directory - -struct Hot_Directory{ - String string; - File_List file_list; -}; - -internal void -hot_directory_init(Hot_Directory *hot_directory, String base, String dir){ - hot_directory->string = base; - hot_directory->string.str[255] = 0; - hot_directory->string.size = 0; - copy(&hot_directory->string, dir); - append(&hot_directory->string, "\\"); -} - -internal void -hot_directory_clean_end(Hot_Directory *hot_directory){ - String *str = &hot_directory->string; - if (str->size != 0 && str->str[str->size-1] != '\\'){ - str->size = reverse_seek_slash(*str) + 1; - str->str[str->size] = 0; - } -} - -internal i32 -hot_directory_quick_partition(File_Info *infos, i32 start, i32 pivot){ - File_Info *p = infos + pivot; - File_Info *a = infos + start; - for (i32 i = start; i < pivot; ++i, ++a){ - i32 comp = 0; - comp = p->folder - a->folder; - if (comp == 0) comp = compare(a->filename, p->filename); - if (comp < 0){ - Swap(*a, infos[start]); - ++start; - } - } - Swap(*p, infos[start]); - return start; -} - -internal void -hot_directory_quick_sort(File_Info *infos, i32 start, i32 pivot){ - i32 mid = hot_directory_quick_partition(infos, start, pivot); - if (start < mid-1) hot_directory_quick_sort(infos, start, mid-1); - if (mid+1 < pivot) hot_directory_quick_sort(infos, mid+1, pivot); -} - -inline void -hot_directory_fixup(Hot_Directory *hot_directory, Working_Set *working_set){ - File_List *files = &hot_directory->file_list; - if (files->count >= 2) - hot_directory_quick_sort(files->infos, 0, files->count - 1); -} - -inline void -hot_directory_set(System_Functions *system, Hot_Directory *hot_directory, - String str, Working_Set *working_set){ - b32 success = copy_checked(&hot_directory->string, str); - terminate_with_null(&hot_directory->string); - if (success){ - system->set_file_list(&hot_directory->file_list, str); - } - hot_directory_fixup(hot_directory, working_set); -} - -inline void -hot_directory_reload(System_Functions *system, Hot_Directory *hot_directory, Working_Set *working_set){ - system->set_file_list(&hot_directory->file_list, hot_directory->string); - hot_directory_fixup(hot_directory, working_set); -} - -struct Hot_Directory_Match{ - String filename; - b32 is_folder; -}; - -internal b32 -filename_match(String query, Absolutes *absolutes, String filename, b32 case_sensitive){ - b32 result; - result = (query.size == 0); - if (!result) result = wildcard_match(absolutes, filename, case_sensitive); - return result; -} - -internal Hot_Directory_Match -hot_directory_first_match(Hot_Directory *hot_directory, - String str, - b32 include_files, - b32 exact_match, - b32 case_sensitive){ - Hot_Directory_Match result = {}; - - Absolutes absolutes; - if (!exact_match) - get_absolutes(str, &absolutes, 1, 1); - - File_List *files = &hot_directory->file_list; - File_Info *info, *end; - end = files->infos + files->count; - for (info = files->infos; info != end; ++info){ - String filename = info->filename; - b32 is_match = 0; - if (exact_match){ - if (case_sensitive){ - if (match(filename, str)) is_match = 1; - } - else{ - if (match_unsensitive(filename, str)) is_match = 1; - } - } - else{ - if (filename_match(str, &absolutes, filename, case_sensitive)) is_match = 1; - } - - if (is_match){ - result.is_folder = info->folder; - result.filename = filename; - break; - } - } - - return result; -} - -// Single line input - -struct Single_Line_Input_Step{ - b8 hit_newline; - b8 hit_ctrl_newline; - b8 hit_a_character; - b8 hit_backspace; - b8 hit_esc; - b8 made_a_change; - b8 did_command; - b8 no_file_match; -}; - -enum Single_Line_Input_Type{ - SINGLE_LINE_STRING, - SINGLE_LINE_FILE -}; - -struct Single_Line_Mode{ - Single_Line_Input_Type type; - String *string; - Hot_Directory *hot_directory; - b32 fast_folder_select; - b32 try_to_match; - b32 case_sensitive; -}; - -internal Single_Line_Input_Step -app_single_line_input_core(System_Functions *system, - Key_Codes *codes, Working_Set *working_set, - Key_Event_Data key, Single_Line_Mode mode){ - Single_Line_Input_Step result = {}; - - if (key.keycode == codes->back){ - result.hit_backspace = 1; - if (mode.string->size > 0){ - result.made_a_change = 1; - --mode.string->size; - switch (mode.type){ - case SINGLE_LINE_STRING: - mode.string->str[mode.string->size] = 0; break; - - case SINGLE_LINE_FILE: - { - char end_character = mode.string->str[mode.string->size]; - if (char_is_slash(end_character)){ - mode.string->size = reverse_seek_slash(*mode.string) + 1; - mode.string->str[mode.string->size] = 0; - hot_directory_set(system, mode.hot_directory, *mode.string, working_set); - } - else{ - mode.string->str[mode.string->size] = 0; - } - }break; - } - } - } - - else if (key.character == '\n' || key.character == '\t'){ - result.made_a_change = 1; - if (key.modifiers[CONTROL_KEY_CONTROL] || - key.modifiers[CONTROL_KEY_ALT]){ - result.hit_ctrl_newline = 1; - } - else{ - result.hit_newline = 1; - if (mode.fast_folder_select){ - Hot_Directory_Match match; - char front_name_space[256]; - String front_name = make_fixed_width_string(front_name_space); - get_front_of_directory(&front_name, *mode.string); - - match = - hot_directory_first_match(mode.hot_directory, front_name, 1, 1, mode.case_sensitive); - - if (mode.try_to_match && !match.filename.str){ - match = hot_directory_first_match(mode.hot_directory, front_name, 1, 0, mode.case_sensitive); - } - if (match.filename.str){ - if (match.is_folder){ - set_last_folder(mode.string, match.filename); - hot_directory_set(system, mode.hot_directory, *mode.string, working_set); - result.hit_newline = 0; - } - else{ - if (mode.try_to_match){ - mode.string->size = reverse_seek_slash(*mode.string) + 1; - append(mode.string, match.filename); - } - } - } - else{ - if (mode.try_to_match){ - result.no_file_match = 1; - } - } - } - } - } - - else if (key.keycode == codes->esc){ - result.hit_esc = 1; - result.made_a_change = 1; - } - - else if (key.character){ - result.hit_a_character = 1; - if (!key.modifiers[CONTROL_KEY_CONTROL] && - !key.modifiers[CONTROL_KEY_ALT]){ - if (mode.string->size+1 < mode.string->memory_size){ - u8 new_character = (u8)key.character; - mode.string->str[mode.string->size] = new_character; - mode.string->size++; - mode.string->str[mode.string->size] = 0; - if (mode.type == SINGLE_LINE_FILE && char_is_slash(new_character)){ - hot_directory_set(system, mode.hot_directory, *mode.string, working_set); - } - result.made_a_change = 1; - } - } - else{ - result.did_command = 1; - result.made_a_change = 1; - } - } - - return result; -} - -inline Single_Line_Input_Step -app_single_line_input_step(System_Functions *system, - Key_Codes *codes, Key_Event_Data key, String *string){ - Single_Line_Mode mode = {}; - mode.type = SINGLE_LINE_STRING; - mode.string = string; - return app_single_line_input_core(system, codes, 0, key, mode); -} - -inline Single_Line_Input_Step -app_single_file_input_step(System_Functions *system, Key_Codes *codes, - Working_Set *working_set, Key_Event_Data key, - String *string, Hot_Directory *hot_directory, - b32 fast_folder_select, b32 try_to_match, b32 case_sensitive){ - Single_Line_Mode mode = {}; - mode.type = SINGLE_LINE_FILE; - mode.string = string; - mode.hot_directory = hot_directory; - mode.fast_folder_select = fast_folder_select; - mode.try_to_match = try_to_match; - mode.case_sensitive = case_sensitive; - return app_single_line_input_core(system, codes, working_set, key, mode); -} - -inline Single_Line_Input_Step -app_single_number_input_step(System_Functions *system, Key_Codes *codes, - Key_Event_Data key, String *string){ - Single_Line_Input_Step result = {}; - Single_Line_Mode mode = {}; - mode.type = SINGLE_LINE_STRING; - mode.string = string; - - char c = (char)key.character; - if (c == 0 || c == '\n' || char_is_numeric(c)) - result = app_single_line_input_core(system, codes, 0, key, mode); - return result; -} - -// UI stuff (this shouldn't be here!) - -struct Widget_ID{ - i32 id; - i32 sub_id0; - i32 sub_id1; - i32 sub_id2; -}; - -inline b32 -widget_match(Widget_ID s1, Widget_ID s2){ - return (s1.id == s2.id && s1.sub_id0 == s2.sub_id0 && - s1.sub_id1 == s2.sub_id1 && s1.sub_id2 == s2.sub_id2); -} - -struct UI_State{ - Render_Target *target; - Style *style; - Font_Set *font_set; - Mouse_Summary *mouse; - Key_Summary *keys; - Key_Codes *codes; - Working_Set *working_set; - i16 font_id; - - Widget_ID selected, hover, hot; - b32 activate_me; - b32 redraw; - b32 input_stage; - i32 sub_id1_change; - - real32 height, view_y; -}; - -inline Widget_ID -make_id(UI_State *state, i32 id){ - Widget_ID r = state->selected; - r.id = id; - return r; -} - -inline Widget_ID -make_sub0(UI_State *state, i32 id){ - Widget_ID r = state->selected; - r.sub_id0 = id; - return r; -} - -inline Widget_ID -make_sub1(UI_State *state, i32 id){ - Widget_ID r = state->selected; - r.sub_id1 = id; - return r; -} - -inline Widget_ID -make_sub2(UI_State *state, i32 id){ - Widget_ID r = state->selected; - r.sub_id2 = id; - return r; -} - -inline b32 -is_selected(UI_State *state, Widget_ID id){ - return widget_match(state->selected, id); -} - -inline b32 -is_hot(UI_State *state, Widget_ID id){ - return widget_match(state->hot, id); -} - -inline b32 -is_hover(UI_State *state, Widget_ID id){ - return widget_match(state->hover, id); -} - -struct UI_Layout{ - i32 row_count; - i32 row_item_width; - i32 row_max_item_height; - - i32_Rect rect; - i32 x, y; -}; - -struct UI_Layout_Restore{ - UI_Layout layout; - UI_Layout *dest; -}; - -inline void -begin_layout(UI_Layout *layout, i32_Rect rect){ - layout->rect = rect; - layout->x = rect.x0; - layout->y = rect.y0; - layout->row_count = 0; - layout->row_max_item_height = 0; -} - -inline void -begin_row(UI_Layout *layout, i32 count){ - layout->row_count = count; - layout->row_item_width = (layout->rect.x1 - layout->x) / count; -} - -inline i32_Rect -layout_rect(UI_Layout *layout, i32 height){ - i32_Rect rect; - rect.x0 = layout->x; - rect.y0 = layout->y; - rect.x1 = rect.x0; - rect.y1 = rect.y0 + height; - if (layout->row_count > 0){ - --layout->row_count; - rect.x1 = rect.x0 + layout->row_item_width; - layout->x += layout->row_item_width; - layout->row_max_item_height = Max(height, layout->row_max_item_height); - } - if (layout->row_count == 0){ - rect.x1 = layout->rect.x1; - layout->row_max_item_height = Max(height, layout->row_max_item_height); - layout->y += layout->row_max_item_height; - layout->x = layout->rect.x0; - layout->row_max_item_height = 0; - } - return rect; -} - -inline UI_Layout_Restore -begin_sub_layout(UI_Layout *layout, i32_Rect area){ - UI_Layout_Restore restore; - restore.layout = *layout; - restore.dest = layout; - begin_layout(layout, area); - return restore; -} - -inline void -end_sub_layout(UI_Layout_Restore restore){ - *restore.dest = restore.layout; -} - -struct UI_Style{ - u32 dark, dim, bright; - u32 base, pop1, pop2; -}; - -internal UI_Style -get_ui_style(Style *style){ - UI_Style ui_style; - ui_style.dark = style->main.back_color; - ui_style.dim = style->main.margin_color; - ui_style.bright = style->main.margin_active_color; - ui_style.base = style->main.default_color; - ui_style.pop1 = style->main.file_info_style.pop1_color; - ui_style.pop2 = style->main.file_info_style.pop2_color; - return ui_style; -} - -internal UI_Style -get_ui_style_upper(Style *style){ - UI_Style ui_style; - ui_style.dark = style->main.margin_color; - ui_style.dim = style->main.margin_hover_color; - ui_style.bright = style->main.margin_active_color; - ui_style.base = style->main.default_color; - ui_style.pop1 = style->main.file_info_style.pop1_color; - ui_style.pop2 = style->main.file_info_style.pop2_color; - return ui_style; -} - -inline void -get_colors(UI_State *state, u32 *back, u32 *fore, Widget_ID wid, UI_Style style){ - bool32 hover = is_hover(state, wid); - bool32 hot = is_hot(state, wid); - i32 level = hot + hover; - switch (level){ - case 2: - *back = style.bright; - *fore = style.dark; - break; - case 1: - *back = style.dim; - *fore = style.bright; - break; - case 0: - *back = style.dark; - *fore = style.bright; - break; - } -} - -inline void -get_pop_color(UI_State *state, u32 *pop, Widget_ID wid, UI_Style style){ - bool32 hover = is_hover(state, wid); - bool32 hot = is_hot(state, wid); - i32 level = hot + hover; - switch (level){ - case 2: - *pop = style.pop1; - break; - case 1: - *pop = style.pop1; - break; - case 0: - *pop = style.pop1; - break; - } -} - -internal UI_State -ui_state_init(UI_State *state_in, Render_Target *target, Input_Summary *user_input, - Style *style, Font_Set *font_set, Working_Set *working_set, b32 input_stage){ - UI_State state = {}; - state.target = target; - state.style = style; - state.font_set = font_set; - state.font_id = style->font_id; - state.working_set = working_set; - if (user_input){ - state.mouse = &user_input->mouse; - state.keys = &user_input->keys; - state.codes = user_input->codes; - } - state.selected = state_in->selected; - state.hot = state_in->hot; - if (input_stage) state.hover = {}; - else state.hover = state_in->hover; - state.redraw = 0; - state.activate_me = 0; - state.input_stage = input_stage; - state.height = state_in->height; - state.view_y = state_in->view_y; - return state; -} - -inline bool32 -ui_state_match(UI_State a, UI_State b){ - return (widget_match(a.selected, b.selected) && - widget_match(a.hot, b.hot) && - widget_match(a.hover, b.hover)); -} - -internal b32 -ui_finish_frame(UI_State *persist_state, UI_State *state, UI_Layout *layout, i32_Rect rect, - b32 do_wheel, b32 *did_activation){ - b32 result = 0; - f32 h = layout->y + persist_state->view_y - rect.y0; - f32 max_y = h - (rect.y1 - rect.y0); - - persist_state->height = h; - persist_state->view_y = state->view_y; - - if (state->input_stage){ - Mouse_Summary *mouse = state->mouse; - Font_Set *font_set = state->font_set; - - if (mouse->wheel_used && do_wheel){ - i32 height = get_font_info(font_set, state->font_id)->height; - persist_state->view_y += mouse->wheel_amount*height; - result = 1; - } - if (mouse->release_l && widget_match(state->hot, state->hover)){ - if (did_activation) *did_activation = 1; - if (state->activate_me){ - state->selected = state->hot; - } - } - if (!mouse->l && !mouse->r){ - state->hot = {}; - } - - if (!ui_state_match(*persist_state, *state) || state->redraw){ - result = 1; - } - - *persist_state = *state; - } - - if (persist_state->view_y >= max_y) persist_state->view_y = max_y; - if (persist_state->view_y < 0) persist_state->view_y = 0; - - return result; -} - -internal bool32 -ui_do_button_input(UI_State *state, i32_Rect rect, Widget_ID id, bool32 activate, bool32 *right = 0){ - bool32 result = 0; - Mouse_Summary *mouse = state->mouse; - bool32 hover = hit_check(mouse->mx, mouse->my, rect); - if (hover){ - state->hover = id; - if (activate) state->activate_me = 1; - if (mouse->press_l || (mouse->press_r && right)) state->hot = id; - if (mouse->l && mouse->r) state->hot = {}; - } - bool32 is_match = is_hot(state, id); - if (mouse->release_l && is_match){ - if (hover) result = 1; - state->redraw = 1; - } - if (right && mouse->release_r && is_match){ - if (hover) *right = 1; - state->redraw = 1; - } - return result; -} - -internal bool32 -ui_do_subdivided_button_input(UI_State *state, i32_Rect rect, i32 parts, Widget_ID id, bool32 activate, i32 *indx_out, bool32 *right = 0){ - bool32 result = 0; - real32 x0, x1; - i32_Rect sub_rect; - Widget_ID sub_widg = id; - real32 sub_width = (rect.x1 - rect.x0) / (real32)parts; - sub_rect.y0 = rect.y0; - sub_rect.y1 = rect.y1; - x1 = (real32)rect.x0; - - for (i32 i = 0; i < parts; ++i){ - x0 = x1; - x1 = x1 + sub_width; - sub_rect.x0 = TRUNC32(x0); - sub_rect.x1 = TRUNC32(x1); - sub_widg.sub_id2 = i; - if (ui_do_button_input(state, sub_rect, sub_widg, activate, right)){ - *indx_out = i; - break; - } - } - - return result; -} - -internal real32 -ui_do_vscroll_input(UI_State *state, i32_Rect top, i32_Rect bottom, i32_Rect slider, - Widget_ID id, real32 val, real32 step_amount, - real32 smin, real32 smax, real32 vmin, real32 vmax){ - Mouse_Summary *mouse = state->mouse; - i32 mx = mouse->mx; - i32 my = mouse->my; - if (hit_check(mx, my, top)){ - state->hover = id; - state->hover.sub_id2 = 1; - } - if (hit_check(mx, my, bottom)){ - state->hover = id; - state->hover.sub_id2 = 2; - } - if (hit_check(mx, my, slider)){ - state->hover = id; - state->hover.sub_id2 = 3; - } - if (mouse->press_l) state->hot = state->hover; - if (id.id == state->hot.id){ - if (mouse->release_l){ - Widget_ID wid1, wid2; - wid1 = wid2 = id; - wid1.sub_id2 = 1; - wid2.sub_id2 = 2; - if (state->hot.sub_id2 == 1 && is_hover(state, wid1)) val -= step_amount; - if (state->hot.sub_id2 == 2 && is_hover(state, wid2)) val += step_amount; - state->redraw = 1; - } - if (state->hot.sub_id2 == 3){ - real32 S, L; - S = (real32)mouse->my - (slider.y1 - slider.y0) / 2; - if (S < smin) S = smin; - if (S > smax) S = smax; - L = unlerp(smin, S, smax); - val = lerp(vmin, L, vmax); - state->redraw = 1; - } - } - return val; -} - -internal b32 -ui_do_text_field_input(UI_State *state, String *str){ - b32 result = 0; - Key_Summary *keys = state->keys; - for (i32 key_i = 0; key_i < keys->count; ++key_i){ - Key_Event_Data key = get_single_key(keys, key_i); - char c = (char)key.character; - if (char_is_basic(c) && str->size < str->memory_size-1){ - str->str[str->size++] = c; - str->str[str->size] = 0; - } - else if (c == '\n'){ - result = 1; - } - else if (key.keycode == state->codes->back && str->size > 0){ - str->str[--str->size] = 0; - } - } - return result; -} - -internal b32 -ui_do_file_field_input(System_Functions *system, UI_State *state, - Hot_Directory *hot_dir, b32 try_to_match, b32 case_sensitive){ - Key_Event_Data key; - Single_Line_Input_Step step; - String *str = &hot_dir->string; - Key_Summary *keys = state->keys; - i32 key_i; - b32 result = 0; - - terminate_with_null(str); - - for (key_i = 0; key_i < keys->count; ++key_i){ - key = get_single_key(keys, key_i); - step = - app_single_file_input_step(system, state->codes, - state->working_set, key, str, - hot_dir, 1, try_to_match, case_sensitive); - if ((step.hit_newline || step.hit_ctrl_newline) && !step.no_file_match) result = 1; - } - return result; -} - -internal b32 -ui_do_line_field_input(System_Functions *system, - UI_State *state, String *string){ - b32 result = 0; - Key_Summary *keys = state->keys; - for (i32 key_i = 0; key_i < keys->count; ++key_i){ - Key_Event_Data key = get_single_key(keys, key_i); - terminate_with_null(string); - Single_Line_Input_Step step = - app_single_line_input_step(system, state->codes, key, string); - if (step.hit_newline || step.hit_ctrl_newline) result = 1; - } - return result; -} - -internal bool32 -ui_do_slider_input(UI_State *state, i32_Rect rect, Widget_ID wid, - real32 min, real32 max, real32 *v){ - bool32 result = 0; - ui_do_button_input(state, rect, wid, 0); - Mouse_Summary *mouse = state->mouse; - if (is_hot(state, wid)){ - result = 1; - *v = unlerp(min, (real32)mouse->mx, max); - state->redraw = 1; - } - return result; -} - -internal bool32 -do_text_field(Widget_ID wid, UI_State *state, UI_Layout *layout, - String prompt, String dest){ - b32 result = 0; - i32 character_h = get_font_info(state->font_set, state->font_id)->height; - - i32_Rect rect = layout_rect(layout, character_h); - - if (state->input_stage){ - ui_do_button_input(state, rect, wid, 1); - if (is_selected(state, wid)){ - if (ui_do_text_field_input(state, &dest)){ - result = 1; - } - } - } - else{ - Render_Target *target = state->target; - UI_Style ui_style = get_ui_style_upper(state->style); - u32 back, fore, prompt_pop; - get_colors(state, &back, &fore, wid, ui_style); - get_pop_color(state, &prompt_pop, wid, ui_style); - - draw_rectangle(target, rect, back); - i32 x = rect.x0; - x = draw_string(target, state->font_id, prompt, x, rect.y0, prompt_pop); - draw_string(target, state->font_id, dest, x, rect.y0, ui_style.base); - } - - return result; -} - -// Widget - enum File_View_Widget_Type{ FWIDG_NONE, FWIDG_SEARCH, @@ -3775,200 +2760,6 @@ remeasure_file_view(System_Functions *system, View *view_, i32_Rect rect){ } } -// MORE UI STUFF GOD DAMN IT!!! -internal b32 -do_button(i32 id, UI_State *state, UI_Layout *layout, char *text, i32 height_mult, - b32 is_toggle = 0, b32 on = 0){ - b32 result = 0; - i16 font_id = state->style->font_id; - i32 character_h = get_font_info(state->font_set, font_id)->height; - - i32_Rect btn_rect = layout_rect(layout, character_h * height_mult); - if (height_mult > 1) btn_rect = get_inner_rect(btn_rect, 2); - else{ - btn_rect.x0 += 2; - btn_rect.x1 -= 2; - } - - Widget_ID wid = make_id(state, id); - - if (state->input_stage){ - if (ui_do_button_input(state, btn_rect, wid, 0)){ - result = 1; - } - } - else{ - Render_Target *target = state->target; - UI_Style ui_style = get_ui_style(state->style); - u32 back, fore, outline; - outline = ui_style.bright; - get_colors(state, &back, &fore, wid, ui_style); - - draw_rectangle(target, btn_rect, back); - draw_rectangle_outline(target, btn_rect, outline); - real32 text_width = font_string_width(target, font_id, text); - i32 box_width = btn_rect.x1 - btn_rect.x0; - i32 box_height = btn_rect.y1 - btn_rect.y0; - i32 x_pos = TRUNC32(btn_rect.x0 + (box_width - text_width)*.5f); - draw_string(target, font_id, text, x_pos, btn_rect.y0 + (box_height - character_h) / 2, fore); - - if (is_toggle){ - i32_Rect on_box = get_inner_rect(btn_rect, character_h/2); - on_box.x1 = on_box.x0 + (on_box.y1 - on_box.y0); - - if (on) draw_rectangle(target, on_box, fore); - else draw_rectangle(target, on_box, back); - draw_rectangle_outline(target, on_box, fore); - } - } - - return result; -} - -internal b32 -do_undo_slider(Widget_ID wid, UI_State *state, UI_Layout *layout, i32 max, i32 v, Undo_Data *undo, i32 *out){ - b32 result = 0; - i16 font_id = state->style->font_id; - i32 character_h = get_font_info(state->font_set, font_id)->height; - - i32_Rect containing_rect = layout_rect(layout, character_h); - - i32_Rect click_rect; - click_rect.x0 = containing_rect.x0 + character_h - 1; - click_rect.x1 = containing_rect.x1 - character_h + 1; - click_rect.y0 = containing_rect.y0 + 2; - click_rect.y1 = containing_rect.y1 - 2; - - if (state->input_stage){ - real32 l; - if (ui_do_slider_input(state, click_rect, wid, (real32)click_rect.x0, (real32)click_rect.x1, &l)){ - real32 v_new = lerp(0.f, l, (real32)max); - v = ROUND32(v_new); - result = 1; - if (out) *out = v; - } - } - else{ - Render_Target *target = state->target; - if (max > 0){ - UI_Style ui_style = get_ui_style_upper(state->style); - - real32 L = unlerp(0.f, (real32)v, (real32)max); - i32 x = FLOOR32(lerp((real32)click_rect.x0, L, (real32)click_rect.x1)); - - i32 bar_top = ((click_rect.y0 + click_rect.y1) >> 1) - 1; - i32 bar_bottom = bar_top + 2; - - bool32 show_bar = 1; - real32 tick_step = (click_rect.x1 - click_rect.x0) / (real32)max; - bool32 show_ticks = 1; - if (tick_step <= 5.f) show_ticks = 0; - - if (undo == 0){ - if (show_bar){ - i32_Rect slider_rect; - slider_rect.x0 = click_rect.x0; - slider_rect.x1 = x; - slider_rect.y0 = bar_top; - slider_rect.y1 = bar_bottom; - - draw_rectangle(target, slider_rect, ui_style.dim); - - slider_rect.x0 = x; - slider_rect.x1 = click_rect.x1; - draw_rectangle(target, slider_rect, ui_style.pop1); - } - - if (show_ticks){ - f32_Rect tick; - tick.x0 = (real32)click_rect.x0 - 1; - tick.x1 = (real32)click_rect.x0 + 1; - tick.y0 = (real32)bar_top - 3; - tick.y1 = (real32)bar_bottom + 3; - - for (i32 i = 0; i < v; ++i){ - draw_rectangle(target, tick, ui_style.dim); - tick.x0 += tick_step; - tick.x1 += tick_step; - } - - for (i32 i = v; i <= max; ++i){ - draw_rectangle(target, tick, ui_style.pop1); - tick.x0 += tick_step; - tick.x1 += tick_step; - } - } - } - else{ - if (show_bar){ - i32_Rect slider_rect; - slider_rect.x0 = click_rect.x0; - slider_rect.y0 = bar_top; - slider_rect.y1 = bar_bottom; - - Edit_Step *history = undo->history.edits; - i32 block_count = undo->history_block_count; - Edit_Step *step = history; - for (i32 i = 0; i < block_count; ++i){ - u32 color; - if (step->type == ED_REDO || - step->type == ED_UNDO) color = ui_style.pop1; - else color = ui_style.dim; - - real32 L; - if (i + 1 == block_count){ - L = 1.f; - }else{ - step = history + step->next_block; - L = unlerp(0.f, (real32)(step - history), (real32)max); - } - if (L > 1.f) L = 1.f; - i32 x = FLOOR32(lerp((real32)click_rect.x0, L, (real32)click_rect.x1)); - - slider_rect.x1 = x; - draw_rectangle(target, slider_rect, color); - slider_rect.x0 = slider_rect.x1; - - if (L == 1.f) break; - } - } - - if (show_ticks){ - f32_Rect tick; - tick.x0 = (real32)click_rect.x0 - 1; - tick.x1 = (real32)click_rect.x0 + 1; - tick.y0 = (real32)bar_top - 3; - tick.y1 = (real32)bar_bottom + 3; - - Edit_Step *history = undo->history.edits; - u32 color = ui_style.dim; - for (i32 i = 0; i <= max; ++i){ - if (i != max){ - if (history[i].type == ED_REDO) color = ui_style.pop1; - else if (history[i].type == ED_UNDO || - history[i].type == ED_NORMAL) color = ui_style.pop2; - else color = ui_style.dim; - } - draw_rectangle(target, tick, color); - tick.x0 += tick_step; - tick.x1 += tick_step; - } - } - } - - i32_Rect slider_handle; - slider_handle.x0 = x - 2; - slider_handle.x1 = x + 2; - slider_handle.y0 = click_rect.y0; - slider_handle.y1 = click_rect.y1; - - draw_rectangle(target, slider_handle, ui_style.bright); - } - } - - return result; -} - internal i32 step_file_view(System_Functions *system, View *view_, i32_Rect rect, b32 is_active, Input_Summary *user_input){ diff --git a/4ed_gui.cpp b/4ed_gui.cpp new file mode 100644 index 00000000..13bf6cf0 --- /dev/null +++ b/4ed_gui.cpp @@ -0,0 +1,854 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 20.02.2016 + * + * GUI system for 4coder + * + */ + +// TOP + +struct Single_Line_Input_Step{ + b8 hit_newline; + b8 hit_ctrl_newline; + b8 hit_a_character; + b8 hit_backspace; + b8 hit_esc; + b8 made_a_change; + b8 did_command; + b8 no_file_match; +}; + +enum Single_Line_Input_Type{ + SINGLE_LINE_STRING, + SINGLE_LINE_FILE +}; + +struct Single_Line_Mode{ + Single_Line_Input_Type type; + String *string; + Hot_Directory *hot_directory; + b32 fast_folder_select; + b32 try_to_match; + b32 case_sensitive; +}; + +internal Single_Line_Input_Step +app_single_line_input_core(System_Functions *system, + Key_Codes *codes, Working_Set *working_set, + Key_Event_Data key, Single_Line_Mode mode){ + Single_Line_Input_Step result = {}; + + if (key.keycode == codes->back){ + result.hit_backspace = 1; + if (mode.string->size > 0){ + result.made_a_change = 1; + --mode.string->size; + switch (mode.type){ + case SINGLE_LINE_STRING: + mode.string->str[mode.string->size] = 0; break; + + case SINGLE_LINE_FILE: + { + char end_character = mode.string->str[mode.string->size]; + if (char_is_slash(end_character)){ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + mode.string->str[mode.string->size] = 0; + hot_directory_set(system, mode.hot_directory, *mode.string, working_set); + } + else{ + mode.string->str[mode.string->size] = 0; + } + }break; + } + } + } + + else if (key.character == '\n' || key.character == '\t'){ + result.made_a_change = 1; + if (key.modifiers[CONTROL_KEY_CONTROL] || + key.modifiers[CONTROL_KEY_ALT]){ + result.hit_ctrl_newline = 1; + } + else{ + result.hit_newline = 1; + if (mode.fast_folder_select){ + Hot_Directory_Match match; + char front_name_space[256]; + String front_name = make_fixed_width_string(front_name_space); + get_front_of_directory(&front_name, *mode.string); + + match = + hot_directory_first_match(mode.hot_directory, front_name, 1, 1, mode.case_sensitive); + + if (mode.try_to_match && !match.filename.str){ + match = hot_directory_first_match(mode.hot_directory, front_name, 1, 0, mode.case_sensitive); + } + if (match.filename.str){ + if (match.is_folder){ + set_last_folder(mode.string, match.filename); + hot_directory_set(system, mode.hot_directory, *mode.string, working_set); + result.hit_newline = 0; + } + else{ + if (mode.try_to_match){ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + append(mode.string, match.filename); + } + } + } + else{ + if (mode.try_to_match){ + result.no_file_match = 1; + } + } + } + } + } + + else if (key.keycode == codes->esc){ + result.hit_esc = 1; + result.made_a_change = 1; + } + + else if (key.character){ + result.hit_a_character = 1; + if (!key.modifiers[CONTROL_KEY_CONTROL] && + !key.modifiers[CONTROL_KEY_ALT]){ + if (mode.string->size+1 < mode.string->memory_size){ + u8 new_character = (u8)key.character; + mode.string->str[mode.string->size] = new_character; + mode.string->size++; + mode.string->str[mode.string->size] = 0; + if (mode.type == SINGLE_LINE_FILE && char_is_slash(new_character)){ + hot_directory_set(system, mode.hot_directory, *mode.string, working_set); + } + result.made_a_change = 1; + } + } + else{ + result.did_command = 1; + result.made_a_change = 1; + } + } + + return result; +} + +inline Single_Line_Input_Step +app_single_line_input_step(System_Functions *system, + Key_Codes *codes, Key_Event_Data key, String *string){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_STRING; + mode.string = string; + return app_single_line_input_core(system, codes, 0, key, mode); +} + +inline Single_Line_Input_Step +app_single_file_input_step(System_Functions *system, Key_Codes *codes, + Working_Set *working_set, Key_Event_Data key, + String *string, Hot_Directory *hot_directory, + b32 fast_folder_select, b32 try_to_match, b32 case_sensitive){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_FILE; + mode.string = string; + mode.hot_directory = hot_directory; + mode.fast_folder_select = fast_folder_select; + mode.try_to_match = try_to_match; + mode.case_sensitive = case_sensitive; + return app_single_line_input_core(system, codes, working_set, key, mode); +} + +inline Single_Line_Input_Step +app_single_number_input_step(System_Functions *system, Key_Codes *codes, + Key_Event_Data key, String *string){ + Single_Line_Input_Step result = {}; + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_STRING; + mode.string = string; + + char c = (char)key.character; + if (c == 0 || c == '\n' || char_is_numeric(c)) + result = app_single_line_input_core(system, codes, 0, key, mode); + return result; +} + +struct Widget_ID{ + i32 id; + i32 sub_id0; + i32 sub_id1; + i32 sub_id2; +}; + +inline b32 +widget_match(Widget_ID s1, Widget_ID s2){ + return (s1.id == s2.id && s1.sub_id0 == s2.sub_id0 && + s1.sub_id1 == s2.sub_id1 && s1.sub_id2 == s2.sub_id2); +} + +struct UI_State{ + Render_Target *target; + Style *style; + Font_Set *font_set; + Mouse_Summary *mouse; + Key_Summary *keys; + Key_Codes *codes; + Working_Set *working_set; + i16 font_id; + + Widget_ID selected, hover, hot; + b32 activate_me; + b32 redraw; + b32 input_stage; + i32 sub_id1_change; + + real32 height, view_y; +}; + +inline Widget_ID +make_id(UI_State *state, i32 id){ + Widget_ID r = state->selected; + r.id = id; + return r; +} + +inline Widget_ID +make_sub0(UI_State *state, i32 id){ + Widget_ID r = state->selected; + r.sub_id0 = id; + return r; +} + +inline Widget_ID +make_sub1(UI_State *state, i32 id){ + Widget_ID r = state->selected; + r.sub_id1 = id; + return r; +} + +inline Widget_ID +make_sub2(UI_State *state, i32 id){ + Widget_ID r = state->selected; + r.sub_id2 = id; + return r; +} + +inline b32 +is_selected(UI_State *state, Widget_ID id){ + return widget_match(state->selected, id); +} + +inline b32 +is_hot(UI_State *state, Widget_ID id){ + return widget_match(state->hot, id); +} + +inline b32 +is_hover(UI_State *state, Widget_ID id){ + return widget_match(state->hover, id); +} + +struct UI_Layout{ + i32 row_count; + i32 row_item_width; + i32 row_max_item_height; + + i32_Rect rect; + i32 x, y; +}; + +struct UI_Layout_Restore{ + UI_Layout layout; + UI_Layout *dest; +}; + +inline void +begin_layout(UI_Layout *layout, i32_Rect rect){ + layout->rect = rect; + layout->x = rect.x0; + layout->y = rect.y0; + layout->row_count = 0; + layout->row_max_item_height = 0; +} + +inline void +begin_row(UI_Layout *layout, i32 count){ + layout->row_count = count; + layout->row_item_width = (layout->rect.x1 - layout->x) / count; +} + +inline i32_Rect +layout_rect(UI_Layout *layout, i32 height){ + i32_Rect rect; + rect.x0 = layout->x; + rect.y0 = layout->y; + rect.x1 = rect.x0; + rect.y1 = rect.y0 + height; + if (layout->row_count > 0){ + --layout->row_count; + rect.x1 = rect.x0 + layout->row_item_width; + layout->x += layout->row_item_width; + layout->row_max_item_height = Max(height, layout->row_max_item_height); + } + if (layout->row_count == 0){ + rect.x1 = layout->rect.x1; + layout->row_max_item_height = Max(height, layout->row_max_item_height); + layout->y += layout->row_max_item_height; + layout->x = layout->rect.x0; + layout->row_max_item_height = 0; + } + return rect; +} + +inline UI_Layout_Restore +begin_sub_layout(UI_Layout *layout, i32_Rect area){ + UI_Layout_Restore restore; + restore.layout = *layout; + restore.dest = layout; + begin_layout(layout, area); + return restore; +} + +inline void +end_sub_layout(UI_Layout_Restore restore){ + *restore.dest = restore.layout; +} + +struct UI_Style{ + u32 dark, dim, bright; + u32 base, pop1, pop2; +}; + +internal UI_Style +get_ui_style(Style *style){ + UI_Style ui_style; + ui_style.dark = style->main.back_color; + ui_style.dim = style->main.margin_color; + ui_style.bright = style->main.margin_active_color; + ui_style.base = style->main.default_color; + ui_style.pop1 = style->main.file_info_style.pop1_color; + ui_style.pop2 = style->main.file_info_style.pop2_color; + return ui_style; +} + +internal UI_Style +get_ui_style_upper(Style *style){ + UI_Style ui_style; + ui_style.dark = style->main.margin_color; + ui_style.dim = style->main.margin_hover_color; + ui_style.bright = style->main.margin_active_color; + ui_style.base = style->main.default_color; + ui_style.pop1 = style->main.file_info_style.pop1_color; + ui_style.pop2 = style->main.file_info_style.pop2_color; + return ui_style; +} + +inline void +get_colors(UI_State *state, u32 *back, u32 *fore, Widget_ID wid, UI_Style style){ + bool32 hover = is_hover(state, wid); + bool32 hot = is_hot(state, wid); + i32 level = hot + hover; + switch (level){ + case 2: + *back = style.bright; + *fore = style.dark; + break; + case 1: + *back = style.dim; + *fore = style.bright; + break; + case 0: + *back = style.dark; + *fore = style.bright; + break; + } +} + +inline void +get_pop_color(UI_State *state, u32 *pop, Widget_ID wid, UI_Style style){ + bool32 hover = is_hover(state, wid); + bool32 hot = is_hot(state, wid); + i32 level = hot + hover; + switch (level){ + case 2: + *pop = style.pop1; + break; + case 1: + *pop = style.pop1; + break; + case 0: + *pop = style.pop1; + break; + } +} + +internal UI_State +ui_state_init(UI_State *state_in, Render_Target *target, Input_Summary *user_input, + Style *style, Font_Set *font_set, Working_Set *working_set, b32 input_stage){ + UI_State state = {}; + state.target = target; + state.style = style; + state.font_set = font_set; + state.font_id = style->font_id; + state.working_set = working_set; + if (user_input){ + state.mouse = &user_input->mouse; + state.keys = &user_input->keys; + state.codes = user_input->codes; + } + state.selected = state_in->selected; + state.hot = state_in->hot; + if (input_stage) state.hover = {}; + else state.hover = state_in->hover; + state.redraw = 0; + state.activate_me = 0; + state.input_stage = input_stage; + state.height = state_in->height; + state.view_y = state_in->view_y; + return state; +} + +inline bool32 +ui_state_match(UI_State a, UI_State b){ + return (widget_match(a.selected, b.selected) && + widget_match(a.hot, b.hot) && + widget_match(a.hover, b.hover)); +} + +internal b32 +ui_finish_frame(UI_State *persist_state, UI_State *state, UI_Layout *layout, i32_Rect rect, + b32 do_wheel, b32 *did_activation){ + b32 result = 0; + f32 h = layout->y + persist_state->view_y - rect.y0; + f32 max_y = h - (rect.y1 - rect.y0); + + persist_state->height = h; + persist_state->view_y = state->view_y; + + if (state->input_stage){ + Mouse_Summary *mouse = state->mouse; + Font_Set *font_set = state->font_set; + + if (mouse->wheel_used && do_wheel){ + i32 height = get_font_info(font_set, state->font_id)->height; + persist_state->view_y += mouse->wheel_amount*height; + result = 1; + } + if (mouse->release_l && widget_match(state->hot, state->hover)){ + if (did_activation) *did_activation = 1; + if (state->activate_me){ + state->selected = state->hot; + } + } + if (!mouse->l && !mouse->r){ + state->hot = {}; + } + + if (!ui_state_match(*persist_state, *state) || state->redraw){ + result = 1; + } + + *persist_state = *state; + } + + if (persist_state->view_y >= max_y) persist_state->view_y = max_y; + if (persist_state->view_y < 0) persist_state->view_y = 0; + + return result; +} + +internal bool32 +ui_do_button_input(UI_State *state, i32_Rect rect, Widget_ID id, bool32 activate, bool32 *right = 0){ + bool32 result = 0; + Mouse_Summary *mouse = state->mouse; + bool32 hover = hit_check(mouse->mx, mouse->my, rect); + if (hover){ + state->hover = id; + if (activate) state->activate_me = 1; + if (mouse->press_l || (mouse->press_r && right)) state->hot = id; + if (mouse->l && mouse->r) state->hot = {}; + } + bool32 is_match = is_hot(state, id); + if (mouse->release_l && is_match){ + if (hover) result = 1; + state->redraw = 1; + } + if (right && mouse->release_r && is_match){ + if (hover) *right = 1; + state->redraw = 1; + } + return result; +} + +internal bool32 +ui_do_subdivided_button_input(UI_State *state, i32_Rect rect, i32 parts, Widget_ID id, bool32 activate, i32 *indx_out, bool32 *right = 0){ + bool32 result = 0; + real32 x0, x1; + i32_Rect sub_rect; + Widget_ID sub_widg = id; + real32 sub_width = (rect.x1 - rect.x0) / (real32)parts; + sub_rect.y0 = rect.y0; + sub_rect.y1 = rect.y1; + x1 = (real32)rect.x0; + + for (i32 i = 0; i < parts; ++i){ + x0 = x1; + x1 = x1 + sub_width; + sub_rect.x0 = TRUNC32(x0); + sub_rect.x1 = TRUNC32(x1); + sub_widg.sub_id2 = i; + if (ui_do_button_input(state, sub_rect, sub_widg, activate, right)){ + *indx_out = i; + break; + } + } + + return result; +} + +internal real32 +ui_do_vscroll_input(UI_State *state, i32_Rect top, i32_Rect bottom, i32_Rect slider, + Widget_ID id, real32 val, real32 step_amount, + real32 smin, real32 smax, real32 vmin, real32 vmax){ + Mouse_Summary *mouse = state->mouse; + i32 mx = mouse->mx; + i32 my = mouse->my; + if (hit_check(mx, my, top)){ + state->hover = id; + state->hover.sub_id2 = 1; + } + if (hit_check(mx, my, bottom)){ + state->hover = id; + state->hover.sub_id2 = 2; + } + if (hit_check(mx, my, slider)){ + state->hover = id; + state->hover.sub_id2 = 3; + } + if (mouse->press_l) state->hot = state->hover; + if (id.id == state->hot.id){ + if (mouse->release_l){ + Widget_ID wid1, wid2; + wid1 = wid2 = id; + wid1.sub_id2 = 1; + wid2.sub_id2 = 2; + if (state->hot.sub_id2 == 1 && is_hover(state, wid1)) val -= step_amount; + if (state->hot.sub_id2 == 2 && is_hover(state, wid2)) val += step_amount; + state->redraw = 1; + } + if (state->hot.sub_id2 == 3){ + real32 S, L; + S = (real32)mouse->my - (slider.y1 - slider.y0) / 2; + if (S < smin) S = smin; + if (S > smax) S = smax; + L = unlerp(smin, S, smax); + val = lerp(vmin, L, vmax); + state->redraw = 1; + } + } + return val; +} + +internal b32 +ui_do_text_field_input(UI_State *state, String *str){ + b32 result = 0; + Key_Summary *keys = state->keys; + for (i32 key_i = 0; key_i < keys->count; ++key_i){ + Key_Event_Data key = get_single_key(keys, key_i); + char c = (char)key.character; + if (char_is_basic(c) && str->size < str->memory_size-1){ + str->str[str->size++] = c; + str->str[str->size] = 0; + } + else if (c == '\n'){ + result = 1; + } + else if (key.keycode == state->codes->back && str->size > 0){ + str->str[--str->size] = 0; + } + } + return result; +} + +internal b32 +ui_do_file_field_input(System_Functions *system, UI_State *state, + Hot_Directory *hot_dir, b32 try_to_match, b32 case_sensitive){ + Key_Event_Data key; + Single_Line_Input_Step step; + String *str = &hot_dir->string; + Key_Summary *keys = state->keys; + i32 key_i; + b32 result = 0; + + terminate_with_null(str); + + for (key_i = 0; key_i < keys->count; ++key_i){ + key = get_single_key(keys, key_i); + step = + app_single_file_input_step(system, state->codes, + state->working_set, key, str, + hot_dir, 1, try_to_match, case_sensitive); + if ((step.hit_newline || step.hit_ctrl_newline) && !step.no_file_match) result = 1; + } + return result; +} + +internal b32 +ui_do_line_field_input(System_Functions *system, + UI_State *state, String *string){ + b32 result = 0; + Key_Summary *keys = state->keys; + for (i32 key_i = 0; key_i < keys->count; ++key_i){ + Key_Event_Data key = get_single_key(keys, key_i); + terminate_with_null(string); + Single_Line_Input_Step step = + app_single_line_input_step(system, state->codes, key, string); + if (step.hit_newline || step.hit_ctrl_newline) result = 1; + } + return result; +} + +internal bool32 +ui_do_slider_input(UI_State *state, i32_Rect rect, Widget_ID wid, + real32 min, real32 max, real32 *v){ + bool32 result = 0; + ui_do_button_input(state, rect, wid, 0); + Mouse_Summary *mouse = state->mouse; + if (is_hot(state, wid)){ + result = 1; + *v = unlerp(min, (real32)mouse->mx, max); + state->redraw = 1; + } + return result; +} + +internal bool32 +do_text_field(Widget_ID wid, UI_State *state, UI_Layout *layout, + String prompt, String dest){ + b32 result = 0; + i32 character_h = get_font_info(state->font_set, state->font_id)->height; + + i32_Rect rect = layout_rect(layout, character_h); + + if (state->input_stage){ + ui_do_button_input(state, rect, wid, 1); + if (is_selected(state, wid)){ + if (ui_do_text_field_input(state, &dest)){ + result = 1; + } + } + } + else{ + Render_Target *target = state->target; + UI_Style ui_style = get_ui_style_upper(state->style); + u32 back, fore, prompt_pop; + get_colors(state, &back, &fore, wid, ui_style); + get_pop_color(state, &prompt_pop, wid, ui_style); + + draw_rectangle(target, rect, back); + i32 x = rect.x0; + x = draw_string(target, state->font_id, prompt, x, rect.y0, prompt_pop); + draw_string(target, state->font_id, dest, x, rect.y0, ui_style.base); + } + + return result; +} + +internal b32 +do_button(i32 id, UI_State *state, UI_Layout *layout, char *text, i32 height_mult, + b32 is_toggle = 0, b32 on = 0){ + b32 result = 0; + i16 font_id = state->style->font_id; + i32 character_h = get_font_info(state->font_set, font_id)->height; + + i32_Rect btn_rect = layout_rect(layout, character_h * height_mult); + if (height_mult > 1) btn_rect = get_inner_rect(btn_rect, 2); + else{ + btn_rect.x0 += 2; + btn_rect.x1 -= 2; + } + + Widget_ID wid = make_id(state, id); + + if (state->input_stage){ + if (ui_do_button_input(state, btn_rect, wid, 0)){ + result = 1; + } + } + else{ + Render_Target *target = state->target; + UI_Style ui_style = get_ui_style(state->style); + u32 back, fore, outline; + outline = ui_style.bright; + get_colors(state, &back, &fore, wid, ui_style); + + draw_rectangle(target, btn_rect, back); + draw_rectangle_outline(target, btn_rect, outline); + real32 text_width = font_string_width(target, font_id, text); + i32 box_width = btn_rect.x1 - btn_rect.x0; + i32 box_height = btn_rect.y1 - btn_rect.y0; + i32 x_pos = TRUNC32(btn_rect.x0 + (box_width - text_width)*.5f); + draw_string(target, font_id, text, x_pos, btn_rect.y0 + (box_height - character_h) / 2, fore); + + if (is_toggle){ + i32_Rect on_box = get_inner_rect(btn_rect, character_h/2); + on_box.x1 = on_box.x0 + (on_box.y1 - on_box.y0); + + if (on) draw_rectangle(target, on_box, fore); + else draw_rectangle(target, on_box, back); + draw_rectangle_outline(target, on_box, fore); + } + } + + return result; +} + +internal b32 +do_undo_slider(Widget_ID wid, UI_State *state, UI_Layout *layout, i32 max, i32 v, Undo_Data *undo, i32 *out){ + b32 result = 0; + i16 font_id = state->style->font_id; + i32 character_h = get_font_info(state->font_set, font_id)->height; + + i32_Rect containing_rect = layout_rect(layout, character_h); + + i32_Rect click_rect; + click_rect.x0 = containing_rect.x0 + character_h - 1; + click_rect.x1 = containing_rect.x1 - character_h + 1; + click_rect.y0 = containing_rect.y0 + 2; + click_rect.y1 = containing_rect.y1 - 2; + + if (state->input_stage){ + real32 l; + if (ui_do_slider_input(state, click_rect, wid, (real32)click_rect.x0, (real32)click_rect.x1, &l)){ + real32 v_new = lerp(0.f, l, (real32)max); + v = ROUND32(v_new); + result = 1; + if (out) *out = v; + } + } + else{ + Render_Target *target = state->target; + if (max > 0){ + UI_Style ui_style = get_ui_style_upper(state->style); + + real32 L = unlerp(0.f, (real32)v, (real32)max); + i32 x = FLOOR32(lerp((real32)click_rect.x0, L, (real32)click_rect.x1)); + + i32 bar_top = ((click_rect.y0 + click_rect.y1) >> 1) - 1; + i32 bar_bottom = bar_top + 2; + + bool32 show_bar = 1; + real32 tick_step = (click_rect.x1 - click_rect.x0) / (real32)max; + bool32 show_ticks = 1; + if (tick_step <= 5.f) show_ticks = 0; + + if (undo == 0){ + if (show_bar){ + i32_Rect slider_rect; + slider_rect.x0 = click_rect.x0; + slider_rect.x1 = x; + slider_rect.y0 = bar_top; + slider_rect.y1 = bar_bottom; + + draw_rectangle(target, slider_rect, ui_style.dim); + + slider_rect.x0 = x; + slider_rect.x1 = click_rect.x1; + draw_rectangle(target, slider_rect, ui_style.pop1); + } + + if (show_ticks){ + f32_Rect tick; + tick.x0 = (real32)click_rect.x0 - 1; + tick.x1 = (real32)click_rect.x0 + 1; + tick.y0 = (real32)bar_top - 3; + tick.y1 = (real32)bar_bottom + 3; + + for (i32 i = 0; i < v; ++i){ + draw_rectangle(target, tick, ui_style.dim); + tick.x0 += tick_step; + tick.x1 += tick_step; + } + + for (i32 i = v; i <= max; ++i){ + draw_rectangle(target, tick, ui_style.pop1); + tick.x0 += tick_step; + tick.x1 += tick_step; + } + } + } + else{ + if (show_bar){ + i32_Rect slider_rect; + slider_rect.x0 = click_rect.x0; + slider_rect.y0 = bar_top; + slider_rect.y1 = bar_bottom; + + Edit_Step *history = undo->history.edits; + i32 block_count = undo->history_block_count; + Edit_Step *step = history; + for (i32 i = 0; i < block_count; ++i){ + u32 color; + if (step->type == ED_REDO || + step->type == ED_UNDO) color = ui_style.pop1; + else color = ui_style.dim; + + real32 L; + if (i + 1 == block_count){ + L = 1.f; + }else{ + step = history + step->next_block; + L = unlerp(0.f, (real32)(step - history), (real32)max); + } + if (L > 1.f) L = 1.f; + i32 x = FLOOR32(lerp((real32)click_rect.x0, L, (real32)click_rect.x1)); + + slider_rect.x1 = x; + draw_rectangle(target, slider_rect, color); + slider_rect.x0 = slider_rect.x1; + + if (L == 1.f) break; + } + } + + if (show_ticks){ + f32_Rect tick; + tick.x0 = (real32)click_rect.x0 - 1; + tick.x1 = (real32)click_rect.x0 + 1; + tick.y0 = (real32)bar_top - 3; + tick.y1 = (real32)bar_bottom + 3; + + Edit_Step *history = undo->history.edits; + u32 color = ui_style.dim; + for (i32 i = 0; i <= max; ++i){ + if (i != max){ + if (history[i].type == ED_REDO) color = ui_style.pop1; + else if (history[i].type == ED_UNDO || + history[i].type == ED_NORMAL) color = ui_style.pop2; + else color = ui_style.dim; + } + draw_rectangle(target, tick, color); + tick.x0 += tick_step; + tick.x1 += tick_step; + } + } + } + + i32_Rect slider_handle; + slider_handle.x0 = x - 2; + slider_handle.x1 = x + 2; + slider_handle.y0 = click_rect.y0; + slider_handle.y1 = click_rect.y1; + + draw_rectangle(target, slider_handle, ui_style.bright); + } + } + + return result; +} + + + +// BOTTOM + diff --git a/vc120.pdb b/vc120.pdb index 4e2ab7e7..b1ebf2e0 100644 Binary files a/vc120.pdb and b/vc120.pdb differ diff --git a/win32_4ed.cpp b/win32_4ed.cpp index a7951358..550bfd47 100644 --- a/win32_4ed.cpp +++ b/win32_4ed.cpp @@ -111,7 +111,7 @@ struct Win32_Vars{ HCURSOR cursor_leftright; HCURSOR cursor_updown; Application_Mouse_Cursor prev_mouse_cursor; - Clipboard_Contents clipboard_contents; + String clipboard_contents; b32 next_clipboard_is_self; DWORD clipboard_sequence; @@ -1326,7 +1326,7 @@ UpdateLoop(LPVOID param){ HANDLE clip_data; clip_data = GetClipboardData(CF_TEXT); if (clip_data){ - win32vars.clipboard_contents.str = (u8*)GlobalLock(clip_data); + win32vars.clipboard_contents.str = (char*)GlobalLock(clip_data); if (win32vars.clipboard_contents.str){ win32vars.clipboard_contents.size = str_size((char*)win32vars.clipboard_contents.str); GlobalUnlock(clip_data); @@ -1337,12 +1337,11 @@ UpdateLoop(LPVOID param){ } } } - + u32 redraw = exchange_vars.thread.force_redraw; if (redraw) exchange_vars.thread.force_redraw = 0; redraw = redraw || input_chunk.trans.redraw; - Key_Input_Data input_data; Mouse_State mouse; Application_Step_Result result; @@ -1749,7 +1748,7 @@ main(int argc, char **argv){ HANDLE clip_data; clip_data = GetClipboardData(CF_TEXT); if (clip_data){ - win32vars.clipboard_contents.str = (u8*)GlobalLock(clip_data); + win32vars.clipboard_contents.str = (char*)GlobalLock(clip_data); if (win32vars.clipboard_contents.str){ win32vars.clipboard_contents.size = str_size((char*)win32vars.clipboard_contents.str); GlobalUnlock(clip_data);