From 68066ad410368b0e5d66d7fa983bea882eba1eb6 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Tue, 2 Mar 2021 18:24:37 -0500 Subject: command selector working! --- buffer.c | 57 ++++++++++++++++++++++++++++++++++++++++- main.c | 87 +++++++++++++++++---------------------------------------------- menu.c | 88 +++++++++++++++++++++++++++++++++++++++++----------------------- ted.c | 2 +- ted.h | 1 + ui.c | 1 + 6 files changed, 139 insertions(+), 97 deletions(-) diff --git a/buffer.c b/buffer.c index b016d9e..bdba7ff 100644 --- a/buffer.c +++ b/buffer.c @@ -677,7 +677,7 @@ static u32 buffer_index_to_column(TextBuffer *buffer, u32 line, u32 index) { char32_t *str = buffer->lines[line].str; u32 col = 0; uint tab_width = buffer_settings(buffer)->tab_width; - for (u32 i = 0; i < index; ++i) { + for (u32 i = 0; i < index && i < buffer->lines[line].len; ++i) { switch (str[i]) { case '\t': { do @@ -2093,6 +2093,53 @@ u32 buffer_last_rendered_line(TextBuffer *buffer) { return clamp_u32(line, 0, buffer->nlines); } +// returns true if the buffer "used" this event +bool buffer_handle_click(Ted *ted, TextBuffer *buffer, v2 click, u8 times) { + BufferPos buffer_pos; + if (buffer_pixels_to_pos(buffer, click, &buffer_pos)) { + // user clicked on buffer + if (!ted->menu || buffer->is_line_buffer) { + ted_switch_to_buffer(ted, buffer); + } + if (buffer == ted->active_buffer) { + switch (ted->key_modifier) { + case KEY_MODIFIER_SHIFT: + // select to position + buffer_select_to_pos(buffer, buffer_pos); + break; + case KEY_MODIFIER_CTRL: + if (!buffer->is_line_buffer) { + buffer_cursor_move_to_pos(buffer, buffer_pos); + String32 word = buffer_word_at_cursor(buffer); + if (word.len) { + char *tag = str32_to_utf8_cstr(word); + if (tag) { + tag_goto(buffer->ted, tag); + free(tag); + } + } + } + break; + case 0: + buffer_cursor_move_to_pos(buffer, buffer_pos); + switch ((times - 1) % 3) { + case 0: break; // single-click + case 1: // double-click: select word + buffer_select_word(buffer); + break; + case 2: // triple-click: select line + buffer_select_line(buffer); + break; + } + ted->drag_buffer = buffer; + break; + } + return true; + } + } + return false; +} + // Render the text buffer in the given rectangle void buffer_render(TextBuffer *buffer, Rect r) { if (r.size.x < 1 || r.size.y < 1) { @@ -2163,6 +2210,14 @@ void buffer_render(TextBuffer *buffer, Rect r) { buffer->x1 = x1; buffer->y1 = y1; buffer->x2 = x2; buffer->y2 = y2; if (x1 == x2 || y1 == y2) return; + if (buffer->is_line_buffer) { + // handle clicks + // this is only done for line buffers, so that ctrl+click works properly (and happens in one frame). + for (u32 i = 0; i < ted->nmouse_clicks[SDL_BUTTON_LEFT]; ++i) { + buffer_handle_click(ted, buffer, ted->mouse_clicks[SDL_BUTTON_LEFT][i], ted->mouse_click_times[SDL_BUTTON_LEFT][i]); + } + } + // change cursor to ibeam when it's hovering over the buffer if ((!ted->menu || buffer == &ted->line_buffer) && rect_contains_point(rect4(x1, y1, x2, y2), ted->mouse_pos)) { ted->cursor = ted->cursor_ibeam; diff --git a/main.c b/main.c index 8f1c298..70b36d0 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,4 @@ // @TODO: -// - command selector // - :open-config // - test on BSD @@ -245,51 +244,6 @@ static void ted_update_window_dimensions(Ted *ted) { gl_window_height = ted->window_height = (float)h; } -// returns true if the buffer "used" this event -static bool handle_buffer_click(Ted *ted, TextBuffer *buffer, v2 click, u8 times) { - BufferPos buffer_pos; - if (buffer_pixels_to_pos(buffer, click, &buffer_pos)) { - // user clicked on buffer - if (!ted->menu) { - ted_switch_to_buffer(ted, buffer); - } - if (buffer == ted->active_buffer) { - switch (ted->key_modifier) { - case KEY_MODIFIER_SHIFT: - // select to position - buffer_select_to_pos(buffer, buffer_pos); - break; - case KEY_MODIFIER_CTRL: { - buffer_cursor_move_to_pos(buffer, buffer_pos); - String32 word = buffer_word_at_cursor(buffer); - if (word.len) { - char *tag = str32_to_utf8_cstr(word); - if (tag) { - tag_goto(buffer->ted, tag); - free(tag); - } - } - } break; - case 0: - buffer_cursor_move_to_pos(buffer, buffer_pos); - switch ((times - 1) % 3) { - case 0: break; // single-click - case 1: // double-click: select word - buffer_select_word(buffer); - break; - case 2: // triple-click: select line - buffer_select_line(buffer); - break; - } - ted->drag_buffer = buffer; - break; - } - return true; - } - } - return false; -} - #if _WIN32 INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) { @@ -566,6 +520,9 @@ int main(int argc, char **argv) { Uint32 time_at_last_frame = SDL_GetTicks(); strbuf_cpy(ted->error, config_err); + + SDL_DisplayMode display_mode = {0}; + SDL_GetDisplayMode(0, 0, &display_mode); while (!ted->quit) { #if DEBUG @@ -628,27 +585,25 @@ int main(int argc, char **argv) { } } - if (add) { + if (add) { // handle mouse click // we need to do this here, and not in buffer_render, because ctrl+click (go to definition) // could switch to a different buffer. - for (u32 i = 0; i < TED_MAX_NODES; ++i) { - if (ted->nodes_used[i]) { - Node *node = &ted->nodes[i]; - if (node->tabs) { - buffer = &ted->buffers[node->tabs[node->active_tab]]; - if (handle_buffer_click(ted, buffer, pos, times)) { - add = false; - break; + // line buffer click handling, IS done in buffer_render (yes this is less than ideal) + if (!ted->menu) { + for (u32 i = 0; i < TED_MAX_NODES; ++i) { + if (ted->nodes_used[i]) { + Node *node = &ted->nodes[i]; + if (node->tabs) { + buffer = &ted->buffers[node->tabs[node->active_tab]]; + if (buffer_handle_click(ted, buffer, pos, times)) { + add = false; + break; + } } } } } - if (ted->find) { - add = add && !handle_buffer_click(ted, &ted->find_buffer, pos, times); - if (ted->replace) - add = add && !handle_buffer_click(ted, &ted->replace_buffer, pos, times); - } if (add) { ted->mouse_clicks[button][ted->nmouse_clicks[button]] = pos; ted->mouse_click_times[button][ted->nmouse_clicks[button]] = times; @@ -905,10 +860,14 @@ int main(int argc, char **argv) { SDL_SetWindowTitle(window, ted->window_title); SDL_SetCursor(ted->cursor); - i32 ms_wait = (i32)((frame_end_noswap - frame_start) * 1000); - if (ms_wait > 0) { - ms_wait -= 1; // give swap an extra ms to make sure it's actually vsynced - SDL_Delay((u32)ms_wait); + // annoyingly, SDL_GL_SwapWindow seems to be a busy loop on my laptop for some reason... + int refresh_rate = display_mode.refresh_rate; + if (refresh_rate) { + i32 ms_wait = 1000 / refresh_rate - (i32)((frame_end_noswap - frame_start) * 1000); + if (ms_wait > 0) { + ms_wait -= 1; // give swap an extra ms to make sure it's actually vsynced + SDL_Delay((u32)ms_wait); + } } SDL_GL_SwapWindow(window); PROFILE_TIME(frame_end); diff --git a/menu.c b/menu.c index 54f05df..868a4a4 100644 --- a/menu.c +++ b/menu.c @@ -26,11 +26,12 @@ static void menu_close(Ted *ted) { case MENU_GOTO_LINE: buffer_clear(&ted->line_buffer); break; - case MENU_COMMAND_SELECTOR: + case MENU_COMMAND_SELECTOR: { + Selector *selector = &ted->command_selector; buffer_clear(&ted->line_buffer); buffer_clear(&ted->argument_buffer); - free(ted->command_selector.entries); - break; + free(selector->entries); selector->entries = NULL; selector->n_entries = 0; + } break; } ted->menu = MENU_NONE; ted->selector_open = NULL; @@ -74,6 +75,9 @@ static void menu_open(Ted *ted, Menu menu) { case MENU_COMMAND_SELECTOR: { 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; } break; } } @@ -107,6 +111,10 @@ static Rect menu_rect(Ted *ted) { static void menu_update(Ted *ted) { Menu menu = ted->menu; + Settings const *settings = &ted->settings; + u32 const *colors = settings->colors; + TextBuffer *line_buffer = &ted->line_buffer; + assert(menu); switch (menu) { case MENU_NONE: break; @@ -224,7 +232,6 @@ static void menu_update(Ted *ted) { } } break; case MENU_GOTO_LINE: { - TextBuffer *line_buffer = &ted->line_buffer; char *contents = str32_to_utf8_cstr(buffer_get_line(line_buffer, 0)); char *end; long line_number = strtol(contents, &end, 0); @@ -248,32 +255,38 @@ static void menu_update(Ted *ted) { case MENU_COMMAND_SELECTOR: { Selector *selector = &ted->command_selector; SelectorEntry *entries = selector->entries = calloc(arr_count(command_names), sizeof *selector->entries); + char *search_term = str32_to_utf8_cstr(buffer_get_line(line_buffer, 0)); if (entries) { + SelectorEntry *entry = entries; for (size_t i = 0; i < arr_count(command_names); ++i) { - if (command_names[i].cmd != CMD_UNKNOWN) { - char const *name = command_names[i].name; - entries[i].name = name; - entries[i].color = COLOR_TEXT; + char const *name = command_names[i].name; + if (command_names[i].cmd != CMD_UNKNOWN && stristr(name, search_term)) { + entry->name = name; + entry->color = colors[COLOR_TEXT]; + ++entry; } } - selector->n_entries = arr_count(command_names); + selector->n_entries = (u32)(entry - entries); } char *chosen_command = selector_update(ted, &ted->command_selector); if (chosen_command) { Command c = command_from_str(chosen_command); - char *argument = str32_to_utf8_cstr(buffer_get_line(&ted->argument_buffer, 0)), *endp = NULL; - long long arg = strtoll(argument, &endp, 0); + if (c != CMD_UNKNOWN) { + char *argument = str32_to_utf8_cstr(buffer_get_line(&ted->argument_buffer, 0)), *endp = NULL; + long long arg = strtoll(argument, &endp, 0); - if (*endp == '\0') { - menu_close(ted); - command_execute(ted, c, arg); + if (*endp == '\0') { + menu_close(ted); + command_execute(ted, c, arg); + } + + free(argument); } - free(argument); free(chosen_command); } - free(selector->entries); selector->entries = NULL; selector->n_entries = 0; + free(search_term); } break; } } @@ -305,8 +318,8 @@ static void menu_render(Ted *ted) { float padding = settings->padding; Rect bounds = menu_rect(ted); - float menu_x1, menu_y1, menu_x2, menu_y2; - rect_coords(bounds, &menu_x1, &menu_y1, &menu_x2, &menu_y2); + float x1, y1, x2, y2; + rect_coords(bounds, &x1, &y1, &x2, &y2); if (menu == MENU_OPEN || menu == MENU_SAVE_AS || menu == MENU_GOTO_DEFINITION || menu == MENU_COMMAND_SELECTOR) { // menu rectangle & border @@ -314,10 +327,10 @@ static void menu_render(Ted *ted) { gl_geometry_rect_border(bounds, settings->border_thickness, colors[COLOR_BORDER]); gl_geometry_draw(); - menu_x1 += padding; - menu_y1 += padding; - menu_x2 -= padding; - menu_y2 -= padding; + x1 += padding; + y1 += padding; + x2 -= padding; + y2 -= padding; } @@ -339,20 +352,20 @@ static void menu_render(Ted *ted) { case MENU_SAVE_AS: { if (menu == MENU_OPEN) { - text_utf8(font_bold, "Open...", menu_x1, menu_y1, colors[COLOR_TEXT]); + text_utf8(font_bold, "Open...", x1, y1, colors[COLOR_TEXT]); } else if (menu == MENU_SAVE_AS) { - text_utf8(font_bold, "Save as...", menu_x1, menu_y1, colors[COLOR_TEXT]); + text_utf8(font_bold, "Save as...", x1, y1, colors[COLOR_TEXT]); } text_render(font_bold); - menu_y1 += char_height_bold * 0.75f + padding; + y1 += char_height_bold * 0.75f + padding; FileSelector *fs = &ted->file_selector; - fs->bounds = rect4(menu_x1, menu_y1, menu_x2, menu_y2); + fs->bounds = rect4(x1, y1, x2, y2); file_selector_render(ted, fs); } break; case MENU_GOTO_DEFINITION: { - tag_selector_render(ted, rect4(menu_x1, menu_y1, menu_x2, menu_y2)); + tag_selector_render(ted, rect4(x1, y1, x2, y2)); } break; case MENU_GOTO_LINE: { float menu_height = char_height + 2 * padding; @@ -361,7 +374,6 @@ static void menu_render(Ted *ted) { gl_geometry_rect_border(r, settings->border_thickness, colors[COLOR_BORDER]); char const *text = "Go to line..."; v2 text_size = text_get_size_v2(font_bold, text); - float x1, y1, x2, y2; rect_coords(r, &x1, &y1, &x2, &y2); x1 += padding; y1 += padding; @@ -376,8 +388,22 @@ static void menu_render(Ted *ted) { // line buffer buffer_render(&ted->line_buffer, rect4(x1, y1, x2, y2)); } break; - case MENU_COMMAND_SELECTOR: - selector_render(ted, &ted->command_selector); - break; + case MENU_COMMAND_SELECTOR: { + float line_buffer_height = char_height; + + // argument field + char const *text = "Argument"; + text_utf8(font_bold, text, x1, y1, colors[COLOR_TEXT]); + float x = x1 + text_get_size_v2(font_bold, text).x + padding; + buffer_render(&ted->argument_buffer, rect4(x, y1, x2, y1 + line_buffer_height)); + + y1 += line_buffer_height + padding; + + Selector *selector = &ted->command_selector; + selector->bounds = rect4(x1, y1, x2, y2); + selector_render(ted, selector); + + text_render(font_bold); + } break; } } diff --git a/ted.c b/ted.c index 8b6c657..325755f 100644 --- a/ted.c +++ b/ted.c @@ -101,7 +101,7 @@ static void ted_load_fonts(Ted *ted) { // sets the active buffer to this buffer, and updates active_node, etc. accordingly // you can pass NULL to buffer to make it so no buffer is active. -static void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer) { +void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer) { ted->active_buffer = buffer; if (ted->find) find_update(ted, true); diff --git a/ted.h b/ted.h index f67a27c..ddbedc8 100644 --- a/ted.h +++ b/ted.h @@ -326,3 +326,4 @@ typedef struct Ted { } Ted; void command_execute(Ted *ted, Command c, i64 argument); +void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer); diff --git a/ui.c b/ui.c index 45ea056..be3b35c 100644 --- a/ui.c +++ b/ui.c @@ -102,6 +102,7 @@ static char *selector_update(Ted *ted, Selector *s) { return ret; } +// NOTE: also renders the line buffer static void selector_render(Ted *ted, Selector *s) { Settings const *settings = &ted->settings; u32 const *colors = settings->colors; -- cgit v1.2.3