diff options
author | Leo Tenenbaum <pommicket@gmail.com> | 2021-01-24 14:56:00 -0500 |
---|---|---|
committer | Leo Tenenbaum <pommicket@gmail.com> | 2021-01-24 14:56:00 -0500 |
commit | 8c5eb397121c7fb3fa7feb2391d7c9b674b4190b (patch) | |
tree | cee9002f9e748eb4ca1861dd137abf366ac0ecba | |
parent | b9079377328e9abeb20950ac144a7ebd98fde88e (diff) |
file selector much nicer interface
-rw-r--r-- | base.h | 4 | ||||
-rw-r--r-- | command.c | 7 | ||||
-rw-r--r-- | math.c | 6 | ||||
-rw-r--r-- | ted.cfg | 2 | ||||
-rw-r--r-- | ted.h | 2 | ||||
-rw-r--r-- | ui.c | 67 | ||||
-rw-r--r-- | util.c | 12 |
7 files changed, 76 insertions, 24 deletions
@@ -5,6 +5,10 @@ #define NDEBUG 1 #endif +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #if _WIN32 #include <windows.h> #include <shlobj.h> @@ -19,6 +19,7 @@ char const *command_to_str(Command c) { void command_execute(Ted *ted, Command c, i64 argument) { TextBuffer *buffer = ted->active_buffer; + FileSelector *file_selector = &ted->file_selector; Settings *settings = &ted->settings; @@ -37,10 +38,12 @@ void command_execute(Ted *ted, Command c, i64 argument) { if (buffer) buffer_cursor_move_right(buffer, argument); break; case CMD_UP: - if (buffer) buffer_cursor_move_up(buffer, argument); + if (file_selector->open) file_selector_up(file_selector, argument); + else if (buffer) buffer_cursor_move_up(buffer, argument); break; case CMD_DOWN: - if (buffer) buffer_cursor_move_down(buffer, argument); + if (file_selector->open) file_selector_down(file_selector, argument); + else if (buffer) buffer_cursor_move_down(buffer, argument); break; case CMD_SELECT_LEFT: if (buffer) buffer_select_left(buffer, argument); @@ -82,6 +82,12 @@ static u32 maxu32(u32 a, u32 b) { return a > b ? a : b; } +static i64 mod_i64(i64 a, i64 b) { + i64 ret = a % b; + if (ret < 0) ret += b; + return ret; +} + static float sgnf(float x) { if (x < 0) return -1; if (x > 0) return +1; @@ -81,4 +81,4 @@ bg = #001 # By making it transparent, we can dim everything else while the menu is open. menu-backdrop = #0004 menu-bg = #222 -menu-hl = #666 +menu-hl = #afa2 @@ -86,9 +86,11 @@ typedef struct { typedef struct { Rect bounds; u32 n_entries; + u32 selected; // which FileEntry is currently selected float scroll; FileEntry *entries; char cwd[TED_PATH_MAX]; + bool open; // is the file selector on screen? bool submitted; // set to true if the line buffer was just submitted this frame. } FileSelector; @@ -41,7 +41,20 @@ static void file_selector_free(FileSelector *fs) { memset(fs, 0, sizeof *fs); } -static int qsort_file_entry_cmp(void const *av, void const *bv) { +static void file_selector_up(FileSelector *fs, i64 n) { + i64 selected = fs->selected - n; + selected = mod_i64(selected, fs->n_entries); + fs->selected = (u32)selected; +} + +static void file_selector_down(FileSelector *fs, i64 n) { + i64 selected = fs->selected + n; + selected = mod_i64(selected, fs->n_entries); + fs->selected = (u32)selected; +} + +static int qsort_file_entry_cmp(void const *av, void const *bv, void *search_termv) { + char const *search_term = search_termv; FileEntry const *a = av, *b = bv; // put directories first if (a->type == FS_DIRECTORY && b->type != FS_DIRECTORY) { @@ -50,6 +63,17 @@ static int qsort_file_entry_cmp(void const *av, void const *bv) { if (a->type != FS_DIRECTORY && b->type == FS_DIRECTORY) { return +1; } + if (search_term) { + bool a_prefix = str_is_prefix(a->name, search_term); + bool b_prefix = str_is_prefix(b->name, search_term); + if (a_prefix && !b_prefix) { + return -1; + } + if (b_prefix && !a_prefix) { + return +1; + } + } + return strcmp_case_insensitive(a->name, b->name); } @@ -166,12 +190,15 @@ static Status file_selector_cd_(FileSelector *fs, char const *path, int symlink_ // other members of ALL_PATH_SEPARATORS // returns false if this path doesn't exist or isn't a directory static bool file_selector_cd(FileSelector *fs, char const *path) { + fs->selected = 0; return file_selector_cd_(fs, path, 0); } // returns the name of the selected file, or NULL // if none was selected. the returned pointer should be freed. static char *file_selector_update(Ted *ted, FileSelector *fs) { + fs->open = true; + TextBuffer *line_buffer = &ted->line_buffer; String32 search_term32 = buffer_get_line(line_buffer, 0); char *const cwd = fs->cwd; @@ -246,25 +273,22 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) { } } else on_screen = false; - // check if we submitted this entry - if (submitted && streq(search_term, name)) { - switch (type) { - case FS_FILE: - free(search_term); - if (path) return str_dup(path); - break; - case FS_DIRECTORY: - file_selector_cd(fs, name); - buffer_clear(line_buffer); // clear search term - break; - default: break; - } - } } - // user pressed enter after typing a non-existent file into the search bar - if (submitted) { - // don't do anything for now + // user pressed enter in search bar + if (submitted && fs->selected < fs->n_entries) { + FileEntry *entry = &fs->entries[fs->selected]; + switch (entry->type) { + case FS_FILE: + free(search_term); + if (entry->path) return str_dup(entry->path); + break; + case FS_DIRECTORY: + file_selector_cd(fs, entry->name); + buffer_clear(line_buffer); // clear search term + break; + default: break; + } } // free previous entries @@ -297,6 +321,7 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) { FileEntry *entries = ted_calloc(ted, nfiles, sizeof *entries); if (entries) { fs->n_entries = nfiles; + if (fs->selected >= fs->n_entries) fs->selected = nfiles - 1; fs->entries = entries; for (u32 i = 0; i < nfiles; ++i) { char *name = files[i]; @@ -314,7 +339,7 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) { } } } - qsort(entries, nfiles, sizeof *entries, qsort_file_entry_cmp); + qsort_with_context(entries, nfiles, sizeof *entries, qsort_file_entry_cmp, search_term); } free(files); @@ -363,10 +388,10 @@ static void file_selector_render(Ted *ted, FileSelector *fs) { for (u32 i = 0; i < n_entries; ++i) { - // highlight entry user is mousing over + // highlight entry user is hovering over/selecting Rect r; if (!file_selector_entry_pos(ted, fs, i, &r)) break; - if (rect_contains_point(r, ted->mouse_pos)) { + if (rect_contains_point(r, ted->mouse_pos) || fs->selected == i) { glBegin(GL_QUADS); gl_color_rgba(colors[COLOR_MENU_HL]); rect_render(r); @@ -191,3 +191,15 @@ static int str_qsort_case_insensitive_cmp(const void *av, const void *bv) { char const *const *a = av, *const *b = bv; return strcmp_case_insensitive(*a, *b); } + +// qsort but with a user void* +static void qsort_with_context(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg) { +#if _WIN32 + qsort_s(base, nmemb, size, compar, arg); +#elif __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) + // GNU doesn't have qsort_s ): + qsort_r(base, nmemb, size, compar, arg); +#else +#error "No qsort_r/qsort_s. You can try filling in this function if you know what you're doing." +#endif +} |