summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2021-02-21 12:40:26 -0500
committerLeo Tenenbaum <pommicket@gmail.com>2021-02-21 12:40:26 -0500
commit617907fb4731d67f6e7aca17b9dd7881f2093aad (patch)
treede7378592c0ccd7d37a08cab05dfb4092efcd745
parent34fa190c3d490dd8cfec37d27bc03182f2a725a2 (diff)
started to generalize selector code
-rw-r--r--command.c32
-rw-r--r--main.c9
-rw-r--r--ted.h23
-rw-r--r--ui.c248
4 files changed, 159 insertions, 153 deletions
diff --git a/command.c b/command.c
index 7c76fa2..b00e35b 100644
--- a/command.c
+++ b/command.c
@@ -19,7 +19,6 @@ 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;
@@ -38,11 +37,11 @@ void command_execute(Ted *ted, Command c, i64 argument) {
if (buffer) buffer_cursor_move_right(buffer, argument);
break;
case CMD_UP:
- if (file_selector->open) file_selector_up(ted, file_selector, argument);
+ if (ted->selector_open) selector_up(ted, ted->selector_open, argument);
else if (buffer) buffer_cursor_move_up(buffer, argument);
break;
case CMD_DOWN:
- if (file_selector->open) file_selector_down(ted, file_selector, argument);
+ if (ted->selector_open) selector_down(ted, ted->selector_open, argument);
else if (buffer) buffer_cursor_move_down(buffer, argument);
break;
case CMD_SELECT_LEFT:
@@ -121,24 +120,14 @@ void command_execute(Ted *ted, Command c, i64 argument) {
case CMD_NEWLINE_BACK:
if (!buffer) {
} else if (buffer->is_line_buffer) {
- switch (ted->menu) {
- case MENU_NONE:
- if (ted->find) {
- if (buffer == &ted->find_buffer || buffer == &ted->replace_buffer) {
- if (c == CMD_NEWLINE)
- find_next(ted);
- else
- find_prev(ted);
- }
+ ted->line_buffer_submitted = true;
+ if (ted->find) {
+ if (buffer == &ted->find_buffer || buffer == &ted->replace_buffer) {
+ if (c == CMD_NEWLINE)
+ find_next(ted);
+ else
+ find_prev(ted);
}
- break;
- case MENU_ASK_RELOAD:
- case MENU_WARN_UNSAVED:
- break;
- case MENU_OPEN:
- case MENU_SAVE_AS: {
- ted->file_selector.submitted = true;
- } break;
}
} else {
buffer_newline(buffer);
@@ -226,7 +215,8 @@ void command_execute(Ted *ted, Command c, i64 argument) {
break;
case CMD_CUT:
if (buffer) buffer_cut(buffer);
- break; case CMD_PASTE:
+ break;
+ case CMD_PASTE:
if (buffer) buffer_paste(buffer);
break;
diff --git a/main.c b/main.c
index 2a47545..8af990c 100644
--- a/main.c
+++ b/main.c
@@ -1,5 +1,6 @@
// @TODO:
// - Ctrl+D = :find-definition --- tag menu (see all tags, select one)
+// - fix automatic horizontal scrolling
// - goto line
@@ -8,11 +9,11 @@
// - split
-// - completion
-
// - Windows installation
+// - on crash, output backtrace to log
// - restore previously opened files (setting: restore-session)
-// - on crash, output backtrace to log, save buffers to temp directory
+
+// - completion
#include "base.h"
no_warn_start
@@ -392,6 +393,8 @@ int main(int argc, char **argv) {
memset(ted->nmouse_clicks, 0, sizeof ted->nmouse_clicks);
ted->scroll_total_x = ted->scroll_total_y = 0;
+ ted->line_buffer_submitted = false;
+
ted_update_window_dimensions(ted);
u32 key_modifier = (u32)ctrl_down << KEY_MODIFIER_CTRL_BIT
| (u32)shift_down << KEY_MODIFIER_SHIFT_BIT
diff --git a/ted.h b/ted.h
index e9b8904..6444b5f 100644
--- a/ted.h
+++ b/ted.h
@@ -168,6 +168,20 @@ ENUM_U16 {
MENU_ASK_RELOAD, // prompt about whether to reload file which has ben changed by another program
} ENUM_U16_END(Menu);
+typedef struct {
+ char const *name;
+ u32 color;
+} SelectorEntry;
+
+typedef struct {
+ SelectorEntry *entries;
+ u32 n_entries;
+ Rect bounds;
+ u32 cursor; // index where the selector thing is
+ float scroll;
+ bool enable_cursor;
+} Selector;
+
// file entries for file selectors
typedef struct {
char *name; // just the file name
@@ -176,14 +190,11 @@ typedef struct {
} FileEntry;
typedef struct {
+ Selector sel;
Rect bounds;
u32 n_entries;
- u32 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.
bool create_menu; // this is for creating files, not opening files
} FileSelector;
@@ -237,6 +248,7 @@ typedef struct Ted {
Menu menu;
FileSelector file_selector;
TextBuffer line_buffer; // general-purpose line buffer for inputs -- used for menus
+ bool line_buffer_submitted; // set to true if the line buffer was just submitted this frame.
TextBuffer find_buffer; // use for "find" term in find/find+replace
TextBuffer replace_buffer; // "replace" for find+replace
TextBuffer build_buffer; // buffer for build output (view only)
@@ -259,6 +271,9 @@ typedef struct Ted {
BuildError *build_errors; // dynamic array of build errors
u32 build_error; // build error we are currently "on"
+ // points to a selector if any is open, otherwise NULL.
+ Selector *selector_open;
+
Process build_process;
// When we read the stdout from the build process, the tail end of the read could be an
// incomplete UTF-8 code point. This is where we store that "tail end" until more
diff --git a/ui.c b/ui.c
index 7640215..39a4dc2 100644
--- a/ui.c
+++ b/ui.c
@@ -2,50 +2,119 @@
#include <fcntl.h>
#endif
-static float file_selector_entries_start_y(Ted const *ted, FileSelector const *fs) {
- Rect bounds = fs->bounds;
- float padding = ted->settings.padding;
- float char_height = text_font_char_height(ted->font);
- return bounds.pos.y
- + char_height * 0.75f + padding // make room for cwd
- + char_height * 1.25f + padding; // make room for line buffer
+static float selector_entries_start_y(Ted const *ted, Selector const *s) {
+ (void)ted;
+ return s->bounds.pos.y;
}
-// number of file entries that can be displayed on the screen
-static u32 file_selector_n_display_entries(Ted const *ted, FileSelector const *fs) {
+// number of entries that can be displayed on the screen
+static u32 selector_n_display_entries(Ted const *ted, Selector const *s) {
float char_height = text_font_char_height(ted->font);
- float entries_h = rect_y2(fs->bounds) - file_selector_entries_start_y(ted, fs);
+ float entries_h = rect_y2(s->bounds) - selector_entries_start_y(ted, s);
return (u32)(entries_h / char_height);
}
-static void file_selector_clamp_scroll(Ted const *ted, FileSelector *fs) {
- float max_scroll = (float)fs->n_entries - (float)file_selector_n_display_entries(ted, fs);
+static void selector_clamp_scroll(Ted const *ted, Selector *s) {
+ float max_scroll = (float)s->n_entries - (float)selector_n_display_entries(ted, s);
if (max_scroll < 0) max_scroll = 0;
- fs->scroll = clampf(fs->scroll, 0, max_scroll);
+ s->scroll = clampf(s->scroll, 0, max_scroll);
}
-static void file_selector_scroll_to_selected(Ted const *ted, FileSelector *fs) {
- u32 n_display_entries = file_selector_n_display_entries(ted, fs);
+static void selector_scroll_to_cursor(Ted const *ted, Selector *s) {
+ u32 n_display_entries = selector_n_display_entries(ted, s);
float scrolloff = ted->settings.scrolloff;
- float min_scroll = (float)fs->selected - ((float)n_display_entries - scrolloff);
- float max_scroll = (float)fs->selected - scrolloff;
- fs->scroll = clampf(fs->scroll, min_scroll, max_scroll);
- file_selector_clamp_scroll(ted, fs);
+ float min_scroll = (float)s->cursor - ((float)n_display_entries - scrolloff);
+ float max_scroll = (float)s->cursor - scrolloff;
+ s->scroll = clampf(s->scroll, min_scroll, max_scroll);
+ selector_clamp_scroll(ted, s);
}
-// where is the ith entry in the file selector on the screen?
+// where is the ith entry in the selector on the screen?
// returns false if it's completely offscreen
-static bool file_selector_entry_pos(Ted const *ted, FileSelector const *fs,
- u32 i, Rect *r) {
- Rect bounds = fs->bounds;
+static bool selector_entry_pos(Ted const *ted, Selector const *s, u32 i, Rect *r) {
+ Rect bounds = s->bounds;
float char_height = text_font_char_height(ted->font);
- *r = rect(V2(bounds.pos.x, file_selector_entries_start_y(ted, fs)
- - char_height * fs->scroll
+ *r = rect(V2(bounds.pos.x, selector_entries_start_y(ted, s)
+ - char_height * s->scroll
+ char_height * (float)i),
V2(bounds.size.x, char_height));
return rect_clip_to_rect(r, bounds);
}
+static void selector_up(Ted const *ted, Selector *s, i64 n) {
+ if (!s->enable_cursor || s->n_entries == 0) {
+ // can't do anything
+ return;
+ }
+ s->cursor = (u32)mod_i64(s->cursor - n, s->n_entries);
+ selector_scroll_to_cursor(ted, s);
+}
+
+static void selector_down(Ted const *ted, Selector *s, i64 n) {
+ selector_up(ted, s, -n);
+}
+
+// returns index of entry selected, or -1 for none
+static int selector_update(Ted *ted, Selector *s) {
+ int ret = -1;
+ for (u32 i = 0; i < s->n_entries; ++i) {
+ // check if this entry was clicked on
+ Rect entry_rect;
+ if (selector_entry_pos(ted, s, i, &entry_rect)) {
+ for (uint c = 0; c < ted->nmouse_clicks[SDL_BUTTON_LEFT]; ++c) {
+ if (rect_contains_point(entry_rect, ted->mouse_clicks[SDL_BUTTON_LEFT][c])) {
+ // this option was selected
+ ret = (int)i;
+ }
+ }
+ }
+ }
+ // apply scroll
+ float scroll_speed = 2.5f;
+ s->scroll += scroll_speed * (float)ted->scroll_total_y;
+
+ return ret;
+}
+
+static void selector_render(Ted *ted, Selector *s) {
+ Settings const *settings = &ted->settings;
+ u32 const *colors = settings->colors;
+ Font *font = ted->font;
+
+ Rect bounds = s->bounds;
+
+ for (u32 i = 0; i < s->n_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_draw();
+
+ TextRenderState text_state = text_render_state_default;
+ rect_coords(bounds, &text_state.min_x, &text_state.min_y, &text_state.max_x, &text_state.max_y);
+ text_state.render = true;
+
+ // render entries themselves
+ SelectorEntry *entries = s->entries;
+ for (u32 i = 0; i < s->n_entries; ++i) {
+ Rect r;
+ if (selector_entry_pos(ted, s, i, &r)) {
+ float x = r.pos.x, y = r.pos.y;
+ text_state.x = x; text_state.y = y;
+ rgba_u32_to_floats(entries[i].color, text_state.color);
+ text_utf8_with_state(font, &text_state, entries[i].name);
+ }
+ }
+ 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) {
@@ -53,8 +122,9 @@ static void file_selector_clear_entries(FileSelector *fs) {
free(fs->entries[i].path);
}
free(fs->entries);
+ arr_free(fs->sel.entries);
fs->entries = NULL;
- fs->n_entries = 0;
+ fs->n_entries = fs->sel.n_entries = 0;
}
// returns true if there are any directory entries
@@ -72,19 +142,6 @@ static void file_selector_free(FileSelector *fs) {
memset(fs, 0, sizeof *fs);
}
-static void file_selector_up(Ted const *ted, FileSelector *fs, i64 n) {
- if (fs->n_entries == 0) {
- // can't do anything
- return;
- }
- fs->selected = (u32)mod_i64(fs->selected - n, fs->n_entries);
- file_selector_scroll_to_selected(ted, fs);
-}
-
-static void file_selector_down(Ted const *ted, FileSelector *fs, i64 n) {
- file_selector_up(ted, fs, -n);
-}
-
static int qsort_file_entry_cmp(void *search_termv, void const *av, void const *bv) {
char const *search_term = search_termv;
FileEntry const *a = av, *b = bv;
@@ -218,16 +275,14 @@ static Status file_selector_cd_(Ted const *ted, FileSelector *fs, char const *pa
// other members of ALL_PATH_SEPARATORS
// returns false if this path doesn't exist or isn't a directory
static bool file_selector_cd(Ted const *ted, FileSelector *fs, char const *path) {
- fs->selected = 0;
- fs->scroll = 0;
+ fs->sel.cursor = 0;
+ fs->sel.scroll = 0;
return file_selector_cd_(ted, 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;
@@ -273,36 +328,8 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) {
char *search_term = search_term32.len ? str32_to_utf8_cstr(search_term32) : NULL;
- for (u32 i = 0; i < fs->n_entries; ++i) {
- Rect r = {0};
- FileEntry *entry = &fs->entries[i];
- char *name = entry->name, *path = entry->path;
- FsType type = entry->type;
- // check if this entry was clicked on
- if (file_selector_entry_pos(ted, fs, i, &r)) {
- for (u32 c = 0; c < ted->nmouse_clicks[SDL_BUTTON_LEFT]; ++c) {
- if (rect_contains_point(r, ted->mouse_clicks[SDL_BUTTON_LEFT][c])) {
- // this option was selected
- switch (type) {
- case FS_FILE:
- free(search_term);
- if (path) return str_dup(path);
- break;
- case FS_DIRECTORY:
- file_selector_cd(ted, fs, name);
- buffer_clear(line_buffer); // clear search term
- break;
- default: break;
- }
- }
- }
- }
-
- }
-
- bool submitted = fs->submitted;
- fs->submitted = false;
+ bool submitted = ted->line_buffer_submitted;
// user pressed enter in search bar
if (submitted) {
@@ -313,8 +340,8 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) {
free(search_term);
return str_dup(path);
} else {
- if (fs->selected < fs->n_entries) {
- FileEntry *entry = &fs->entries[fs->selected];
+ if (fs->sel.cursor < fs->n_entries) {
+ FileEntry *entry = &fs->entries[fs->sel.cursor];
switch (entry->type) {
case FS_FILE:
free(search_term);
@@ -373,7 +400,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;
+ 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];
@@ -401,11 +428,6 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) {
ted_seterr(ted, "Couldn't list directory '%s'.", cwd);
}
- // apply scroll
- float scroll_speed = 2.5f;
- fs->scroll += scroll_speed * (float)ted->scroll_total_y;
- file_selector_clamp_scroll(ted, fs);
-
free(search_term);
return NULL;
}
@@ -414,8 +436,6 @@ static void file_selector_render(Ted *ted, FileSelector *fs) {
Settings const *settings = &ted->settings;
u32 const *colors = settings->colors;
Rect bounds = fs->bounds;
- u32 n_entries = fs->n_entries;
- FileEntry const *entries = fs->entries;
Font *font = ted->font;
float padding = settings->padding;
float char_height = text_font_char_height(font);
@@ -431,52 +451,30 @@ static void file_selector_render(Ted *ted, FileSelector *fs) {
buffer_render(&ted->line_buffer, rect4(x1, y1, x2, y1 + line_buffer_height));
y1 += line_buffer_height;
-
- Rect text_bounds = rect4(x1, y1, x2, y2);
- for (u32 i = 0; i < n_entries; ++i) {
- // highlight entry user is hovering over/selecting
- Rect r;
- if (file_selector_entry_pos(ted, fs, i, &r)) {
- rect_clip_to_rect(&r, text_bounds);
- if (rect_contains_point(r, ted->mouse_pos) ||
- ((!fs->create_menu || buffer_empty(&ted->line_buffer)) // only highlight selected for create menus if there is no search term (because that will be the name of the file)
- && fs->selected == i)) {
- gl_geometry_rect(r, colors[COLOR_MENU_HL]);
- }
+ // render selector
+ Selector *sel = &fs->sel;
+ sel->bounds = rect4(x1, y1, x2, y2); // selector takes up remaining space
+ 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);
}
- gl_geometry_draw();
-
- TextRenderState text_state = text_render_state_default;
- text_state.min_x = x1;
- text_state.max_x = x2;
- text_state.min_y = y1;
- text_state.max_y = y2;
- text_state.render = true;
+ if (sel->entries)
+ sel->n_entries = fs->n_entries;
- // render file names themselves
- for (u32 i = 0; i < n_entries; ++i) {
- Rect r;
- if (file_selector_entry_pos(ted, fs, i, &r)) {
- float x = r.pos.x, y = r.pos.y;
- ColorSetting color = 0;
- switch (entries[i].type) {
- case FS_FILE:
- color = COLOR_TEXT;
- break;
- case FS_DIRECTORY:
- color = COLOR_TEXT_FOLDER;
- break;
- default:
- color = COLOR_TEXT_OTHER;
- break;
- }
- text_state.x = x; text_state.y = y;
- rgba_u32_to_floats(colors[color], text_state.color);
- text_utf8_with_state(font, &text_state, entries[i].name);
- }
- }
- text_render(font);
+ selector_render(ted, sel);
}
static void button_render(Ted *ted, Rect button, char const *text, u32 color) {