summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-08-14 12:59:35 -0300
committerpommicket <pommicket@gmail.com>2023-08-14 12:59:35 -0300
commitefe5ae8cec7221779b9a6395eeb9b10b8974dd44 (patch)
tree4fd8162d810cf0a6e11a8d2505b6eabcb6a8e1c6
parentccf3778572d461ef6319bd8c6b34fb68baea23ad (diff)
start new selector system - search not working yet
-rw-r--r--config.c15
-rw-r--r--ide-definitions.c68
-rw-r--r--main.c5
-rw-r--r--menu.c68
-rw-r--r--ted-internal.h47
-rw-r--r--ted.h97
-rw-r--r--ui.c270
-rw-r--r--util.c1
8 files changed, 302 insertions, 269 deletions
diff --git a/config.c b/config.c
index ff5f25c..665f124 100644
--- a/config.c
+++ b/config.c
@@ -1245,7 +1245,7 @@ static char *last_separator(char *path) {
return NULL;
}
-char *settings_get_root_dir(Settings *settings, const char *path) {
+char *settings_get_root_dir(const Settings *settings, const char *path) {
char best_path[TED_PATH_MAX];
*best_path = '\0';
u32 best_path_score = 0;
@@ -1300,3 +1300,16 @@ char *settings_get_root_dir(Settings *settings, const char *path) {
return str_dup(pathbuf);
}
}
+
+u32 settings_color(const Settings *settings, ColorSetting color) {
+ if (color >= COLOR_COUNT) {
+ assert(0);
+ return 0xff00ffff;
+ }
+ return settings->colors[color];
+}
+
+void settings_color_floats(const Settings *settings, ColorSetting color, float f[4]) {
+ rgba_u32_to_floats(settings_color(settings, color), f);
+
+}
diff --git a/ide-definitions.c b/ide-definitions.c
index fd06ebc..b853207 100644
--- a/ide-definitions.c
+++ b/ide-definitions.c
@@ -9,7 +9,7 @@ struct Definitions {
/// last query string which we sent a request for
char *last_request_query;
/// for "go to definition of..." menu
- Selector selector;
+ Selector *selector;
/// an array of all definitions (gotten from workspace/symbols) for "go to definition" menu
SymbolInfo *all_definitions;
};
@@ -114,8 +114,7 @@ static void definitions_clear_entries(Definitions *defs) {
free(def->detail);
}
arr_clear(defs->all_definitions);
- arr_clear(defs->selector.entries);
- defs->selector.n_entries = 0;
+ selector_clear(defs->selector);
}
static int definition_entry_qsort_cmp(const void *av, const void *bv) {
@@ -134,41 +133,6 @@ static int definition_entry_qsort_cmp(const void *av, const void *bv) {
return strcmp(a->detail, b->detail);
}
-// put the entries matching the search term into the selector.
-static void definitions_selector_filter_entries(Ted *ted) {
- Definitions *defs = ted->definitions;
- Selector *sel = &defs->selector;
-
- // create selector entries based on search term
- char *search_term = str32_to_utf8_cstr(buffer_get_line(ted->line_buffer, 0));
-
- arr_clear(sel->entries);
-
- for (u32 i = 0; i < arr_len(defs->all_definitions); ++i) {
- SymbolInfo *info = &defs->all_definitions[i];
- if (!search_term || strstr_case_insensitive(info->name, search_term)) {
- SelectorEntry *entry = arr_addp(sel->entries);
- entry->name = info->name;
- entry->color = info->color;
- entry->detail = info->detail;
- // this isn't exactly ideal but we're sorting these entries so
- // it's probably the nicest way of keeping track of the definition
- // this corresponds to
- entry->userdata = i;
- }
- // don't try to display too many entries
- if (arr_len(sel->entries) >= 1000)
- break;
- }
- free(search_term);
-
- arr_qsort(sel->entries, definition_entry_qsort_cmp);
-
- sel->n_entries = arr_len(sel->entries);
- sel->cursor = clamp_u32(sel->cursor, 0, sel->n_entries);
-}
-
-
void definitions_process_lsp_response(Ted *ted, LSP *lsp, const LSPResponse *response) {
Definitions *defs = ted->definitions;
if (response->request.id != defs->last_request.id) {
@@ -242,8 +206,6 @@ void definitions_process_lsp_response(Ted *ted, LSP *lsp, const LSPResponse *res
def->position.pos.line + 1);
}
- definitions_selector_filter_entries(ted);
-
} break;
default:
debug_println("?? bad request type in %s : %u:%u", __func__, response->request.id, response->request.type);
@@ -286,7 +248,7 @@ static void definitions_selector_open(Ted *ted) {
}
ted_switch_to_buffer(ted, ted->line_buffer);
buffer_select_all(ted->active_buffer);
- defs->selector.cursor = 0;
+ selector_set_cursor(defs->selector, 0);
}
@@ -301,11 +263,8 @@ static bool definitions_selector_close(Ted *ted) {
static void definitions_selector_update(Ted *ted) {
Definitions *defs = ted->definitions;
- Selector *sel = &defs->selector;
- sel->enable_cursor = true;
+ Selector *sel = defs->selector;
- definitions_selector_filter_entries(ted);
-
// send new request if search term has changed.
// this is needed because e.g. clangd gives an incomplete list
definitions_send_request_if_needed(ted);
@@ -315,11 +274,13 @@ static void definitions_selector_update(Ted *ted) {
// for LSP go-to-definition, we ignore `chosen` and use the cursor instead.
// this is because a single symbol can have multiple definitions,
// e.g. with overloading.
- if (sel->cursor >= sel->n_entries) {
- assert(0);
+ SelectorEntry cursor_entry = {0};
+ if (!selector_get_cursor_entry(sel, &cursor_entry)) {
+ assert(0); // shouldn't happen since `chosen` is true.
return;
}
- u64 def_idx = sel->entries[sel->cursor].userdata;
+
+ u64 def_idx = cursor_entry.userdata;
if (def_idx >= arr_len(defs->all_definitions)) {
assert(0);
return;
@@ -343,8 +304,8 @@ static void definitions_selector_update(Ted *ted) {
static void definitions_selector_render(Ted *ted) {
Rect bounds = selection_menu_render_bg(ted);
Definitions *defs = ted->definitions;
- Selector *sel = &defs->selector;
- sel->bounds = bounds;
+ Selector *sel = defs->selector;
+ selector_set_bounds(sel, bounds);
selector_render(ted, sel);
}
@@ -358,10 +319,13 @@ void definitions_init(Ted *ted) {
strbuf_cpy(info.name, MENU_GOTO_DEFINITION);
menu_register(ted, &info);
- ted->definitions = calloc(1, sizeof *ted->definitions);
+ Definitions *defs = ted->definitions = ted_calloc(ted, 1, sizeof *ted->definitions);
+ defs->selector = selector_new();
}
void definitions_quit(Ted *ted) {
- free(ted->definitions);
+ Definitions *defs = ted->definitions;
+ selector_free(defs->selector);
+ free(defs);
ted->definitions = NULL;
}
diff --git a/main.c b/main.c
index 230d987..65501c2 100644
--- a/main.c
+++ b/main.c
@@ -1,6 +1,6 @@
/*
TODO:
-- public Selector/FileSelector API
+- selector search
- public Settings API
FUTURE FEATURES:
@@ -495,6 +495,7 @@ int main(int argc, char **argv) {
}
#endif
+ ted->file_selector = file_selector_new();
gl_geometry_init();
text_init();
menu_init(ted);
@@ -1203,6 +1204,8 @@ int main(int argc, char **argv) {
definitions_quit(ted);
menu_quit(ted);
arr_free(ted->edit_notifys);
+
+ file_selector_free(ted->file_selector); ted->file_selector = NULL;
for (int i = 0; i < TED_LSP_MAX; ++i) {
diff --git a/menu.c b/menu.c
index e126fd1..a1efad5 100644
--- a/menu.c
+++ b/menu.c
@@ -167,11 +167,11 @@ void menu_shell_down(Ted *ted) {
static void open_menu_open(Ted *ted) {
ted_switch_to_buffer(ted, ted->line_buffer);
- ted->file_selector.create_menu = false;
+ file_selector_set_create(ted->file_selector, false);
}
static void open_menu_update(Ted *ted) {
- char *selected_file = file_selector_update(ted, &ted->file_selector);
+ char *selected_file = file_selector_update(ted, ted->file_selector);
if (selected_file) {
// open that file!
menu_close(ted);
@@ -181,21 +181,21 @@ static void open_menu_update(Ted *ted) {
}
static void open_menu_render(Ted *ted) {
- FileSelector *fs = &ted->file_selector;
- strbuf_cpy(fs->title, "Open...");
- fs->bounds = selection_menu_render_bg(ted);
+ FileSelector *fs = ted->file_selector;
+ file_selector_set_title(fs, "Open...");
+ file_selector_set_bounds(fs, selection_menu_render_bg(ted));
file_selector_render(ted, fs);
}
static bool open_menu_close(Ted *ted) {
- file_selector_free(&ted->file_selector);
+ file_selector_clear(ted->file_selector);
buffer_clear(ted->line_buffer);
return true;
}
static void save_as_menu_open(Ted *ted) {
ted_switch_to_buffer(ted, ted->line_buffer);
- ted->file_selector.create_menu = true;
+ file_selector_set_create(ted->file_selector, true);
}
static void save_as_menu_update(Ted *ted) {
@@ -223,7 +223,7 @@ static void save_as_menu_update(Ted *ted) {
break;
}
} else {
- char *selected_file = file_selector_update(ted, &ted->file_selector);
+ char *selected_file = file_selector_update(ted, ted->file_selector);
if (selected_file) {
TextBuffer *buffer = ted->prev_active_buffer;
if (buffer) {
@@ -253,14 +253,14 @@ static void save_as_menu_render(Ted *ted) {
return;
}
- FileSelector *fs = &ted->file_selector;
- strbuf_cpy(fs->title, "Save as...");
- fs->bounds = selection_menu_render_bg(ted);
+ FileSelector *fs = ted->file_selector;
+ file_selector_set_title(fs, "Save as...");
+ file_selector_set_bounds(fs, selection_menu_render_bg(ted));
file_selector_render(ted, fs);
}
static bool save_as_menu_close(Ted *ted) {
- file_selector_free(&ted->file_selector);
+ file_selector_clear(ted->file_selector);
buffer_clear(ted->line_buffer);
return true;
}
@@ -351,33 +351,15 @@ static bool ask_reload_menu_close(Ted *ted) {
static void command_selector_open(Ted *ted) {
ted_switch_to_buffer(ted, ted->line_buffer);
buffer_insert_char_at_cursor(ted->argument_buffer, '1');
- Selector *selector = &ted->command_selector;
- selector->enable_cursor = true;
- selector->cursor = 0;
+ Selector *selector = ted->command_selector;
+ selector_set_cursor(selector, 0);
}
static void command_selector_update(Ted *ted) {
- const Settings *settings = ted_active_settings(ted);
- const u32 *colors = settings->colors;
TextBuffer *line_buffer = ted->line_buffer;
- Selector *selector = &ted->command_selector;
- SelectorEntry *entries = selector->entries = calloc(CMD_COUNT, sizeof *selector->entries);
+ Selector *selector = ted->command_selector;
char *search_term = str32_to_utf8_cstr(buffer_get_line(line_buffer, 0));
- if (entries) {
- SelectorEntry *entry = entries;
- for (Command c = 0; c < CMD_COUNT; ++c) {
- const char *name = command_to_str(c);
- if (c != CMD_UNKNOWN && *name && strstr_case_insensitive(name, search_term)) {
- entry->name = name;
- entry->color = colors[COLOR_TEXT];
- ++entry;
- }
- }
- selector->n_entries = (u32)(entry - entries);
- selector_sort_entries_by_name(selector);
- }
-
- char *chosen_command = selector_update(ted, &ted->command_selector);
+ char *chosen_command = selector_update(ted, selector);
if (chosen_command) {
Command c = command_from_str(chosen_command);
if (c != CMD_UNKNOWN) {
@@ -423,18 +405,16 @@ static void command_selector_render(Ted *ted) {
y1 += line_buffer_height + padding;
- Selector *selector = &ted->command_selector;
- selector->bounds = rect4(x1, y1, x2, y2);
+ Selector *selector = ted->command_selector;
+ selector_set_bounds(selector, rect4(x1, y1, x2, y2));
selector_render(ted, selector);
text_render(font_bold);
}
static bool command_selector_close(Ted *ted) {
- Selector *selector = &ted->command_selector;
buffer_clear(ted->line_buffer);
buffer_clear(ted->argument_buffer);
- free(selector->entries); selector->entries = NULL; selector->n_entries = 0;
return true;
}
@@ -564,6 +544,17 @@ void menu_init(Ted *ted) {
ted_add_edit_notify(ted, menu_edit_notify, ted);
+ ted->command_selector = selector_new();
+ for (Command c = 0; c < CMD_COUNT; ++c) {
+ const char *name = command_to_str(c);
+ if (c != CMD_UNKNOWN && *name) {
+ SelectorEntry entry = {
+ .name = name
+ };
+ selector_add_entry(ted->command_selector, &entry);
+ }
+ }
+
MenuInfo save_as_menu = {
.open = save_as_menu_open,
.update = save_as_menu_update,
@@ -630,4 +621,5 @@ void menu_init(Ted *ted) {
void menu_quit(Ted *ted) {
arr_clear(ted->all_menus);
+ selector_free(ted->command_selector);
}
diff --git a/ted-internal.h b/ted-internal.h
index d35b14c..406ce55 100644
--- a/ted-internal.h
+++ b/ted-internal.h
@@ -179,49 +179,6 @@ typedef struct EditNotifyInfo {
EditNotifyID id;
} EditNotifyInfo;
-/// an entry in a selector menu (e.g. the "open" menu)
-typedef struct {
- /// label
- const char *name;
- /// if not NULL, this will show on the right side of the entry.
- const char *detail;
- /// color to draw text in
- u32 color;
- /// use this for whatever you want
- u64 userdata;
-} SelectorEntry;
-
-struct Selector {
- SelectorEntry *entries;
- u32 n_entries;
- Rect bounds;
- /// index where the selector thing is
- u32 cursor;
- float scroll;
- /// whether or not we should let the user select entries using a cursor.
- bool enable_cursor;
-};
-
-/// file entries for file selectors
-typedef struct {
- /// just the file name
- char *name;
- /// full path
- char *path;
- FsType type;
-} FileEntry;
-
-struct FileSelector {
- char title[32];
- Selector sel;
- Rect bounds;
- u32 n_entries;
- FileEntry *entries;
- char cwd[TED_PATH_MAX];
- /// indicates that this is for creating files, not opening files
- bool create_menu;
-};
-
/// max tabs per node
#define TED_MAX_TABS 100
/// max strings in all config files
@@ -334,8 +291,8 @@ struct Ted {
/// index of currently open menu, or 0 if no menu is open
u32 menu_open_idx;
void *menu_context;
- FileSelector file_selector;
- Selector command_selector;
+ FileSelector *file_selector;
+ Selector *command_selector;
/// general-purpose line buffer for inputs -- used for menus
TextBuffer *line_buffer;
/// use for "find" term in find/find+replace
diff --git a/ted.h b/ted.h
index d754e3e..8434501 100644
--- a/ted.h
+++ b/ted.h
@@ -132,6 +132,28 @@ typedef struct Selector Selector;
/// a selector menu for files (e.g. the "open" menu)
typedef struct FileSelector FileSelector;
+/// an entry in a \ref Selector
+///
+/// only `name` needs to be filled in; everything else can be zeroed.
+typedef struct SelectorEntry {
+ /// color to draw text in
+ ///
+ /// if this is zero, \ref COLOR_TEXT will be used.
+ ColorSetting color;
+ /// label
+ ///
+ /// a copy of this string will be made, so you can free the pointer immediately after calling \ref selector_add_entry
+ const char *name;
+ /// if not NULL, this will show on the right side of the entry.
+ ///
+ /// a copy of this string will be made, so you can free the pointer immediately after calling \ref selector_add_entry
+ const char *detail;
+ /// use this for whatever you want
+ u64 userdata;
+ /// reserved for future use -- must be zeroed.
+ char reserved[32];
+} SelectorEntry;
+
/// a split or collection of tabs
///
/// this handles ted's split-screen and tab features.
@@ -786,7 +808,12 @@ void command_execute_string_argument(Ted *ted, Command c, const char *string);
/// returns the best guess for the root directory of the project containing absolute path `path`.
///
/// the return value should be freed.
-char *settings_get_root_dir(Settings *settings, const char *path);
+char *settings_get_root_dir(const Settings *settings, const char *path);
+/// get color in `0xRRGGBBAA` format
+u32 settings_color(const Settings *settings, ColorSetting color);
+/// get color as four floats
+void settings_color_floats(const Settings *settings, ColorSetting color, float f[4]);
+
// === find.c ===
/// which buffer will be searched?
@@ -1168,10 +1195,48 @@ EditNotifyID ted_add_edit_notify(Ted *ted, EditNotify notify, void *context);
void ted_remove_edit_notify(Ted *ted, EditNotifyID id);
// === ui.c ===
+/// get a good size of button for this text
+vec2 button_get_size(Ted *ted, const char *text);
+/// render button
+void button_render(Ted *ted, Rect button, const char *text, u32 color);
+/// returns `true` if the button was clicked on.
+bool button_update(Ted *ted, Rect button);
+/// returns selected option, or 0 if none was selected
+PopupOption popup_update(Ted *ted, u32 options);
+/// render popup menu.
+///
+/// `options` should be a bitwise-or of the `POPUP_*` constants.
+void popup_render(Ted *ted, u32 options, const char *title, const char *body);
+/// update and render checkbox
+vec2 checkbox_frame(Ted *ted, bool *value, const char *label, vec2 pos);
+/// create a new selector
+Selector *selector_new(void);
+/// set location where selector will be rendered.
+void selector_set_bounds(Selector *s, Rect bounds);
+/// add a new entry to this selector
+void selector_add_entry(Selector *s, const SelectorEntry *entry);
+/// set cursor position
+void selector_set_cursor(Selector *s, u32 cursor);
+/// get cursor position
+u32 selector_get_cursor(Selector *s);
+/// get entry at index in selector.
+Status selector_get_entry(Selector *s, u32 index, SelectorEntry *entry);
+/// get entry at selector cursor
+///
+/// note that this can fail, e.g. if `s` has no entries.
+Status selector_get_cursor_entry(Selector *s, SelectorEntry *entry);
+/// clear all entries from this selector, WITHOUT resetting scroll or cursor position
+void selector_clear_entries(Selector *s);
+/// reset selector to \ref selector_new state
+void selector_clear(Selector *s);
+/// free resources used by selector
+void selector_free(Selector *s);
/// move selector cursor up by `n` entries
void selector_up(Ted *ted, Selector *s, i64 n);
/// move selector cursor down by `n` entries
void selector_down(Ted *ted, Selector *s, i64 n);
+/// sort entries by comparison function
+void selector_sort_entries(Selector *s, int (*compar)(void *context, const SelectorEntry *e1, const SelectorEntry *e2), void *context);
/// sort entries alphabetically
void selector_sort_entries_by_name(Selector *s);
/// returns a null-terminated UTF-8 string of the entry selected, or `NULL` if none was.
@@ -1181,8 +1246,18 @@ void selector_sort_entries_by_name(Selector *s);
char *selector_update(Ted *ted, Selector *s);
/// render selector
///
-/// NOTE: also renders the line buffer
+/// make sure you call \ref selector_set_bounds before this.
void selector_render(Ted *ted, Selector *s);
+/// create a new file selector
+FileSelector *file_selector_new(void);
+/// set whether this file selector should allow inputting non-existent files
+void file_selector_set_create(FileSelector *s, bool create);
+/// free resources used by file selector
+void file_selector_free(FileSelector *s);
+/// set bounds for file selector
+void file_selector_set_bounds(FileSelector *s, Rect bounds);
+/// set file selector title (displayed above the selector in bold)
+void file_selector_set_title(FileSelector *s, const char *title);
/// free resources used by file selector
void file_selector_free(FileSelector *fs);
/// returns the name of the selected file, or `NULL` if none was selected.
@@ -1190,21 +1265,11 @@ void file_selector_free(FileSelector *fs);
/// the returned pointer should be freed.
char *file_selector_update(Ted *ted, FileSelector *fs);
/// render file selector
-void file_selector_render(Ted *ted, FileSelector *fs);
-/// get a good size of button for this text
-vec2 button_get_size(Ted *ted, const char *text);
-/// render button
-void button_render(Ted *ted, Rect button, const char *text, u32 color);
-/// returns `true` if the button was clicked on.
-bool button_update(Ted *ted, Rect button);
-/// returns selected option, or 0 if none was selected
-PopupOption popup_update(Ted *ted, u32 options);
-/// render popup menu.
///
-/// `options` should be a bitwise-or of the `POPUP_*` constants.
-void popup_render(Ted *ted, u32 options, const char *title, const char *body);
-/// update and render checkbox
-vec2 checkbox_frame(Ted *ted, bool *value, const char *label, vec2 pos);
+/// make sure you call \ref file_selector_set_bounds before this.
+void file_selector_render(Ted *ted, FileSelector *fs);
+/// clear cwd, etc.
+void file_selector_clear(FileSelector *fs);
#ifdef __cplusplus
diff --git a/ui.c b/ui.c
index 9481df6..4f06b78 100644
--- a/ui.c
+++ b/ui.c
@@ -7,8 +7,100 @@
#include <unistd.h>
#endif
+struct Selector {
+ SelectorEntry *entries;
+ Rect bounds;
+ u32 cursor;
+ float scroll;
+ bool enable_cursor;
+};
+
+struct FileSelector {
+ char title[32];
+ Selector sel;
+ Rect bounds;
+ char cwd[TED_PATH_MAX];
+ /// indicates that this is for creating files, not opening files
+ bool create_menu;
+};
+
static Status file_selector_cd_(Ted *ted, FileSelector *fs, const char *path, int symlink_depth);
+static void selector_init(Selector *s) {
+ s->enable_cursor = true;
+}
+
+Selector *selector_new(void) {
+ Selector *s = calloc(1, sizeof *s);
+ selector_init(s);
+ return s;
+}
+
+void selector_free(Selector *s) {
+ selector_clear(s);
+ free(s);
+}
+
+void selector_clear_entries(Selector *s) {
+ arr_foreach_ptr(s->entries, SelectorEntry, e) {
+ free((void *)e->name);
+ free((void *)e->detail);
+ }
+ arr_clear(s->entries);
+}
+
+void selector_clear(Selector *s) {
+ selector_clear_entries(s);
+ s->scroll = 0;
+ s->cursor = 0;
+}
+
+void selector_set_cursor(Selector *s, u32 pos) {
+ s->cursor = pos;
+}
+
+u32 selector_get_cursor(Selector *s) {
+ return s->cursor;
+}
+
+Status selector_get_entry(Selector *s, u32 index, SelectorEntry *entry) {
+ if (index >= arr_len(s->entries))
+ return false;
+ *entry = s->entries[index];
+ return true;
+}
+
+void selector_set_bounds(Selector *s, Rect bounds) {
+ s->bounds = bounds;
+}
+
+Status selector_get_cursor_entry(Selector *s, SelectorEntry *entry) {
+ return selector_get_entry(s, s->cursor, entry);
+}
+
+FileSelector *file_selector_new(void) {
+ FileSelector *s = calloc(1, sizeof *s);
+ selector_init(&s->sel);
+ return s;
+}
+
+void file_selector_set_create(FileSelector *s, bool create) {
+ s->create_menu = create;
+}
+
+void file_selector_free(FileSelector *s) {
+ file_selector_clear(s);
+ free(s);
+}
+
+void file_selector_set_bounds(FileSelector *s, Rect bounds) {
+ s->bounds = bounds;
+}
+
+void file_selector_set_title(FileSelector *s, const char *title) {
+ strbuf_cpy(s->title, title);
+}
+
static float selector_entries_start_y(Ted *ted, const Selector *s) {
float padding = ted_active_settings(ted)->padding;
@@ -24,7 +116,7 @@ static u32 selector_n_display_entries(Ted *ted, const Selector *s) {
}
static void selector_clamp_scroll(Ted *ted, Selector *s) {
- float max_scroll = (float)s->n_entries - (float)selector_n_display_entries(ted, s);
+ float max_scroll = (float)arr_len(s->entries) - (float)selector_n_display_entries(ted, s);
if (max_scroll < 0) max_scroll = 0;
s->scroll = clampf(s->scroll, 0, max_scroll);
}
@@ -51,11 +143,11 @@ static bool selector_entry_pos(Ted *ted, const Selector *s, u32 i, Rect *r) {
}
void selector_up(Ted *ted, Selector *s, i64 n) {
- if (!s->enable_cursor || s->n_entries == 0) {
+ if (!s->enable_cursor || arr_len(s->entries) == 0) {
// can't do anything
return;
}
- s->cursor = (u32)mod_i64(s->cursor - n, s->n_entries);
+ s->cursor = (u32)mod_i64(s->cursor - n, arr_len(s->entries));
selector_scroll_to_cursor(ted, s);
}
@@ -69,7 +161,7 @@ static int selectory_entry_cmp_name(const void *av, const void *bv) {
}
void selector_sort_entries_by_name(Selector *s) {
- qsort(s->entries, s->n_entries, sizeof *s->entries, selectory_entry_cmp_name);
+ qsort(s->entries, arr_len(s->entries), sizeof *s->entries, selectory_entry_cmp_name);
}
char *selector_update(Ted *ted, Selector *s) {
@@ -77,7 +169,7 @@ char *selector_update(Ted *ted, Selector *s) {
TextBuffer *line_buffer = ted->line_buffer;
ted->selector_open = s;
- for (u32 i = 0; i < s->n_entries; ++i) {
+ for (u32 i = 0; i < arr_len(s->entries); ++i) {
// check if this entry was clicked on
Rect entry_rect;
if (selector_entry_pos(ted, s, i, &entry_rect)) {
@@ -95,7 +187,7 @@ char *selector_update(Ted *ted, Selector *s) {
if (!ret) {
if (s->enable_cursor) {
// select this option
- if (s->cursor < s->n_entries)
+ if (s->cursor < arr_len(s->entries))
ret = str_dup(s->entries[s->cursor].name);
} else {
@@ -114,7 +206,6 @@ char *selector_update(Ted *ted, Selector *s) {
void selector_render(Ted *ted, Selector *s) {
const Settings *settings = ted_active_settings(ted);
- const u32 *colors = settings->colors;
Font *font = ted->font;
float padding = settings->padding;
@@ -123,24 +214,25 @@ void selector_render(Ted *ted, Selector *s) {
float x1, y1, x2, y2;
rect_coords(bounds, &x1, &y1, &x2, &y2);
- for (u32 i = 0; i < s->n_entries; ++i) {
+ for (u32 i = 0; i < arr_len(s->entries); ++i) {
// highlight entry user is hovering over/selecting
Rect entry_rect;
if (selector_entry_pos(ted, s, i, &entry_rect)) {
rect_clip_to_rect(&entry_rect, bounds);
if (rect_contains_point(entry_rect, ted->mouse_pos) || (s->enable_cursor && s->cursor == i)) {
// highlight it
- gl_geometry_rect(entry_rect, colors[COLOR_MENU_HL]);
+ gl_geometry_rect(entry_rect, settings_color(settings, COLOR_MENU_HL));
}
}
}
gl_geometry_draw();
- // search buffer
- float line_buffer_height = ted_line_buffer_height(ted);
- buffer_render(ted->line_buffer, rect4(x1, y1, x2, y1 + line_buffer_height));
- y1 += line_buffer_height;
-
+ {
+ float line_buffer_height = ted_line_buffer_height(ted);
+ buffer_render(ted->line_buffer, rect4(x1, y1, x2, y1 + line_buffer_height));
+ y1 += line_buffer_height;
+ }
+
TextRenderState text_state = text_render_state_default;
text_state.min_x = x1;
text_state.max_x = x2;
@@ -149,8 +241,8 @@ void selector_render(Ted *ted, Selector *s) {
text_state.render = true;
// render entries themselves
- SelectorEntry *entries = s->entries;
- for (u32 i = 0; i < s->n_entries; ++i) {
+ SelectorEntry *const entries = s->entries;
+ for (u32 i = 0; i < arr_len(entries); ++i) {
Rect r;
if (selector_entry_pos(ted, s, i, &r)) {
SelectorEntry *entry = &entries[i];
@@ -158,7 +250,7 @@ void selector_render(Ted *ted, Selector *s) {
text_state.x = x; text_state.y = y;
// draw name
- rgba_u32_to_floats(entry->color, text_state.color);
+ settings_color_floats(settings, entry->color ? entry->color : COLOR_TEXT, text_state.color);
text_state_break_kerning(&text_state);
text_utf8_with_state(font, &text_state, entry->name);
@@ -168,7 +260,7 @@ void selector_render(Ted *ted, Selector *s) {
TextRenderState detail_state = text_state;
detail_state.x = maxd(text_state.x + 2 * padding, x2 - detail_size);
- rgba_u32_to_floats(colors[COLOR_COMMENT], detail_state.color);
+ settings_color_floats(settings, COLOR_COMMENT, detail_state.color);
text_utf8_with_state(font, &detail_state, entry->detail);
}
}
@@ -176,45 +268,21 @@ void selector_render(Ted *ted, Selector *s) {
text_render(font);
}
-
-// clear the entries in the file selector
-static void file_selector_clear_entries(FileSelector *fs) {
- for (u32 i = 0; i < fs->n_entries; ++i) {
- free(fs->entries[i].name);
- free(fs->entries[i].path);
- }
- free(fs->entries);
- arr_free(fs->sel.entries);
- fs->entries = NULL;
- fs->n_entries = fs->sel.n_entries = 0;
-}
-
-void file_selector_free(FileSelector *fs) {
- file_selector_clear_entries(fs);
+void file_selector_clear(FileSelector *fs) {
+ selector_clear(&fs->sel);
memset(fs, 0, sizeof *fs);
}
-static int qsort_file_entry_cmp(void *search_termv, const void *av, const void *bv) {
- const char *search_term = search_termv;
- const FileEntry *a = av, *b = bv;
+static int file_selector_entry_cmp(void *context, const SelectorEntry *a, const SelectorEntry *b) {
+ (void)context;
+ FsType a_type = (FsType)a->userdata, b_type = (FsType)b->userdata;
// put directories first
- if (a->type == FS_DIRECTORY && b->type != FS_DIRECTORY) {
+ if (a_type == FS_DIRECTORY && b_type != FS_DIRECTORY) {
return -1;
}
- if (a->type != FS_DIRECTORY && b->type == FS_DIRECTORY) {
+ if (a_type != FS_DIRECTORY && b_type == FS_DIRECTORY) {
return +1;
}
- if (search_term) {
- bool a_prefix = str_has_prefix(a->name, search_term);
- bool b_prefix = str_has_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);
}
@@ -335,6 +403,18 @@ static bool file_selector_cd(Ted *ted, FileSelector *fs, const char *path) {
return file_selector_cd_(ted, fs, path, 0);
}
+static ColorSetting color_setting_for_file_type(FsType type) {
+ switch (type) {
+ case FS_FILE: return COLOR_TEXT;
+ case FS_DIRECTORY: return COLOR_TEXT_FOLDER;
+ default: return COLOR_TEXT_OTHER;
+ }
+}
+
+void selector_sort_entries(Selector *s, int (*compar)(void *context, const SelectorEntry *e1, const SelectorEntry *e2), void *context) {
+ qsort_with_context(s->entries, arr_len(s->entries), sizeof *s->entries, (int (*) (void *, const void *, const void *))compar, context);
+}
+
char *file_selector_update(Ted *ted, FileSelector *fs) {
TextBuffer *line_buffer = ted->line_buffer;
String32 search_term32 = buffer_get_line(line_buffer, 0);
@@ -412,7 +492,8 @@ char *file_selector_update(Ted *ted, FileSelector *fs) {
}
// free previous entries
- file_selector_clear_entries(fs);
+ selector_clear_entries(&fs->sel);
+
// get new entries
FsDirectoryEntry **files;
// if the directory we're in gets deleted, go back a directory.
@@ -428,55 +509,23 @@ char *file_selector_update(Ted *ted, FileSelector *fs) {
file_selector_cd(ted, fs, "..");
}
- char *search_term = str32_to_utf8_cstr(buffer_get_line(line_buffer, 0));
if (files) {
- u32 nfiles;
- for (nfiles = 0; files[nfiles]; ++nfiles);
-
- // filter entries
- bool increment = true;
- for (u32 i = 0; i < nfiles; i += increment, increment = true) {
- // remove if the file name does not contain the search term,
- bool remove = search_term && *search_term && !strstr_case_insensitive(files[i]->name, search_term);
- // or if this is just the current directory
- remove |= streq(files[i]->name, ".");
- if (remove) {
- // remove this one
- free(files[i]);
- --nfiles;
- if (nfiles) {
- files[i] = files[nfiles];
- }
- increment = false;
- }
- }
-
- if (nfiles) {
- FileEntry *entries = ted_calloc(ted, nfiles, sizeof *entries);
- if (entries) {
- fs->n_entries = nfiles;
- if (fs->sel.cursor >= fs->n_entries) fs->sel.cursor = nfiles - 1;
- fs->entries = entries;
- for (u32 i = 0; i < nfiles; ++i) {
- char *name = files[i]->name;
- entries[i].name = str_dup(name);
- entries[i].type = files[i]->type;
- // add cwd to start of file name
- size_t path_size = strlen(name) + strlen(cwd) + 3;
- char *path = ted_calloc(ted, 1, path_size);
- if (path) {
- path_full(cwd, name, path, path_size);
- entries[i].path = path;
- } else {
- entries[i].path = NULL; // what can we do?
- }
- free(files[i]);
- files[i] = NULL;
- }
+ for (u32 i = 0; files[i]; ++i) {
+ char *name = files[i]->name;
+ if (streq(name, ".")) {
+ continue;
}
- qsort_with_context(entries, nfiles, sizeof *entries, qsort_file_entry_cmp, search_term);
+ SelectorEntry entry = {
+ .color = color_setting_for_file_type(files[i]->type),
+ .name = name,
+ .userdata = files[i]->type,
+ };
+ selector_add_entry(&fs->sel, &entry);
}
+ selector_sort_entries(&fs->sel, file_selector_entry_cmp, NULL);
+ for (u32 i = 0; files[i]; ++i)
+ free(files[i]);
free(files);
// set cwd to this (if no buffers are open, the "open" menu should use the last file selector's cwd)
strbuf_cpy(ted->cwd, cwd);
@@ -484,7 +533,6 @@ char *file_selector_update(Ted *ted, FileSelector *fs) {
ted_error(ted, "Couldn't list directory '%s'.", cwd);
}
- free(search_term);
return NULL;
}
@@ -524,26 +572,6 @@ void file_selector_render(Ted *ted, FileSelector *fs) {
// render selector
Selector *sel = &fs->sel;
sel->bounds = bounds;
- arr_clear(sel->entries);
- for (u32 i = 0; i < fs->n_entries; ++i) {
- ColorSetting color = 0;
- switch (fs->entries[i].type) {
- case FS_FILE:
- color = COLOR_TEXT;
- break;
- case FS_DIRECTORY:
- color = COLOR_TEXT_FOLDER;
- break;
- default:
- color = COLOR_TEXT_OTHER;
- break;
- }
- SelectorEntry entry = {.name = fs->entries[i].name, .color = colors[color]};
- arr_add(sel->entries, entry);
- }
- if (sel->entries)
- sel->n_entries = fs->n_entries;
-
selector_render(ted, sel);
text_render(font_bold);
}
@@ -690,3 +718,13 @@ vec2 checkbox_frame(Ted *ted, bool *value, const char *label, vec2 pos) {
text_render(font);
return vec2_add(size, (vec2){checkbox_size + padding * 0.5f, 0});
}
+
+void selector_add_entry(Selector *s, const SelectorEntry *entry) {
+ SelectorEntry s_entry = {
+ .color = entry->color,
+ .name = str_dup(entry->name),
+ .detail = str_dup(entry->detail),
+ .userdata = entry->userdata,
+ };
+ arr_add(s->entries, s_entry);
+}
diff --git a/util.c b/util.c
index 2be0f19..b146c82 100644
--- a/util.c
+++ b/util.c
@@ -167,6 +167,7 @@ size_t strn_len(const char *src, size_t n) {
// duplicates at most n characters from src
char *strn_dup(const char *src, size_t n) {
+ if (!src) return NULL;
size_t len = strn_len(src, n);
if (n > len)
n = len;