summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base.h4
-rw-r--r--command.c7
-rw-r--r--math.c6
-rw-r--r--ted.cfg2
-rw-r--r--ted.h2
-rw-r--r--ui.c67
-rw-r--r--util.c12
7 files changed, 76 insertions, 24 deletions
diff --git a/base.h b/base.h
index 9ecbda4..afd320a 100644
--- a/base.h
+++ b/base.h
@@ -5,6 +5,10 @@
#define NDEBUG 1
#endif
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
#if _WIN32
#include <windows.h>
#include <shlobj.h>
diff --git a/command.c b/command.c
index 442989b..40636b6 100644
--- a/command.c
+++ b/command.c
@@ -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);
diff --git a/math.c b/math.c
index c920a80..af89421 100644
--- a/math.c
+++ b/math.c
@@ -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;
diff --git a/ted.cfg b/ted.cfg
index 0033965..a28424e 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -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
diff --git a/ted.h b/ted.h
index 9746c6f..f1374b3 100644
--- a/ted.h
+++ b/ted.h
@@ -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;
diff --git a/ui.c b/ui.c
index 7ffada0..8184ecd 100644
--- a/ui.c
+++ b/ui.c
@@ -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);
diff --git a/util.c b/util.c
index bf0f71f..a6dd3d9 100644
--- a/util.c
+++ b/util.c
@@ -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
+}