diff options
author | Leo Tenenbaum <pommicket@gmail.com> | 2021-01-29 22:33:31 -0500 |
---|---|---|
committer | Leo Tenenbaum <pommicket@gmail.com> | 2021-01-29 22:33:31 -0500 |
commit | 55fd631d86769e719f81206901bc1c3fb598fb5e (patch) | |
tree | eca599d625d8d55d03c2d793e5f85d51f712a2c2 | |
parent | 3cc173dd38e85c751bb1a9f9288ebe974e567ba4 (diff) |
quitting, closing tabs
-rw-r--r-- | Untitled | 2 | ||||
-rw-r--r-- | command.c | 21 | ||||
-rw-r--r-- | command.h | 2 | ||||
-rw-r--r-- | main.c | 13 | ||||
-rw-r--r-- | math.c | 19 | ||||
-rw-r--r-- | node.c | 51 | ||||
-rw-r--r-- | ted.c (renamed from ted-base.c) | 93 | ||||
-rw-r--r-- | ted.cfg | 1 | ||||
-rw-r--r-- | ted.h | 1 |
9 files changed, 164 insertions, 39 deletions
@@ -1,3 +1,3 @@ -aa 1 2 3 4 +aaaa 1 2 3 4 56 78 910 1112 testing @@ -147,17 +147,21 @@ void command_execute(Ted *ted, Command c, i64 argument) { if (buffer) { if (buffer->filename && streq(buffer->filename, TED_UNTITLED)) { // don't worry, this won't catch files called "Untitled"; buffer->filename is the full path. - goto save_as; + command_execute(ted, CMD_SAVE_AS, 1); + return; } buffer_save(buffer); } break; case CMD_SAVE_AS: - save_as: if (buffer && !buffer->is_line_buffer) { menu_open(ted, MENU_SAVE_AS); } break; + case CMD_QUIT: + // @TODO: check for unsaved changes in all buffers + ted->quit = true; + break; case CMD_UNDO: if (buffer) buffer_undo(buffer, argument); break; @@ -189,11 +193,20 @@ void command_execute(Ted *ted, Command c, i64 argument) { } } break; + case CMD_TAB_CLOSE: { + Node *node = ted->active_node; + if (node) { + node_tab_close(ted, node, node->active_tab); + } else { + command_execute(ted, CMD_QUIT, 1); + return; + } + } break; case CMD_TAB_NEXT: - node_tab_next(ted, ted->active_node, argument); + if (ted->active_node) node_tab_next(ted, ted->active_node, argument); break; case CMD_TAB_PREV: - node_tab_prev(ted, ted->active_node, argument); + if (ted->active_node) node_tab_prev(ted, ted->active_node, argument); break; case CMD_ESCAPE: @@ -47,6 +47,7 @@ ENUM_U16 { CMD_COPY, CMD_CUT, CMD_PASTE, + CMD_QUIT, CMD_TAB_CLOSE, CMD_TAB_NEXT, @@ -100,6 +101,7 @@ static CommandName const command_names[CMD_COUNT] = { {"new", CMD_NEW}, {"save", CMD_SAVE}, {"save-as", CMD_SAVE_AS}, + {"quit", CMD_QUIT}, {"undo", CMD_UNDO}, {"redo", CMD_REDO}, {"copy", CMD_COPY}, @@ -1,6 +1,8 @@ // @TODO: // - when closing tabs/window, warn on unsaved changes -// - try opening a file you don't have read permission for +// - try opening a file you don't have read permission for -- check for memory leaks! + +// - show something informative when there's no nodes open (i.e. ted->active_node == NULL). // - split // - Windows installation #include "base.h" @@ -42,7 +44,7 @@ no_warn_end #include "string32.c" #include "arr.c" #include "buffer.c" -#include "ted-base.c" +#include "ted.c" #include "ui.c" #include "node.c" #include "command.c" @@ -271,8 +273,7 @@ int main(int argc, char **argv) { Uint32 time_at_last_frame = SDL_GetTicks(); - bool quit = false; - while (!quit) { + while (!ted->quit) { #if DEBUG //printf("\033[H\033[2J"); #endif @@ -312,7 +313,7 @@ int main(int argc, char **argv) { | (u32)alt_down << KEY_MODIFIER_ALT_BIT; switch (event.type) { case SDL_QUIT: - quit = true; + command_execute(ted, CMD_QUIT, 1); break; case SDL_MOUSEWHEEL: { // scroll with mouse wheel @@ -476,7 +477,7 @@ int main(int argc, char **argv) { Font *font = ted->font; { - float x1 = 50, y1 = 50, x2 = window_width-50, y2 = window_height-50; + float x1 = 25, y1 = 25, x2 = window_width-25, y2 = window_height-25; Node *node = ted->root; node_frame(ted, node, rect4(x1, y1, x2, y2)); } @@ -46,12 +46,31 @@ static int clampi(int x, int a, int b) { return x; } +static i16 clamp_i16(i16 x, i16 a, i16 b) { + if (x < a) return a; + if (x > b) return b; + return x; +} + +static u16 clamp_u16(u16 x, u16 a, u16 b) { + if (x < a) return a; + if (x > b) return b; + return x; +} + static i32 clamp_i32(i32 x, i32 a, i32 b) { if (x < a) return a; if (x > b) return b; return x; } +static u32 clamp_u32(u32 x, u32 a, u32 b) { + if (x < a) return a; + if (x > b) return b; + return x; +} + + // remap x from the interval [from_a, from_b] to the interval [to_a, to_b], NOT clamping if x is outside the "from" interval. static float remapf(float x, float from_a, float from_b, float to_a, float to_b) { float pos = (x - from_a) / (from_b - from_a); @@ -20,6 +20,55 @@ static void node_tab_prev(Ted *ted, Node *node, i64 n) { node_tab_next(ted, node, -n); } +static void node_free(Node *node) { + arr_free(node->tabs); + memset(node, 0, sizeof *node); +} + +static void node_close(Ted *ted, Node *node) { + // @TODO(split) + + // delete all associated buffers + arr_foreach_ptr(node->tabs, u16, tab) { + u16 buffer_index = *tab; + ted_delete_buffer(ted, buffer_index); + } + + node_free(node); + + u16 node_index = (u16)(node - ted->nodes); + assert(node_index < TED_MAX_NODES); + ted->nodes_used[node_index] = false; + if (ted->active_node == node) { + ted->active_node = NULL; + } +} + +// close tab, WITHOUT checking for unsaved changes! +static void node_tab_close(Ted *ted, Node *node, u16 index) { + u16 ntabs = (u16)arr_len(node->tabs); + assert(index < ntabs); + if (ntabs == 1) { + // only 1 tab left, just close the node + node_close(ted, node); + } else { + u16 buffer_index = node->tabs[index]; + // remove tab from array + memmove(&node->tabs[index], &node->tabs[index + 1], (ntabs - (index + 1)) * sizeof *node->tabs); + arr_remove_last(node->tabs); + ted_delete_buffer(ted, buffer_index); + + ntabs = (u16)arr_len(node->tabs); // update ntabs + assert(ntabs); + // make sure active tab is valid + node->active_tab = clamp_u16(node->active_tab, 0, ntabs - 1); + if (ted->active_node == node) { + // fix active buffer if necessary + ted->active_buffer = &ted->buffers[node->tabs[node->active_tab]]; + } + } +} + static void node_frame(Ted *ted, Node *node, Rect r) { if (node->tabs) { bool is_active = node == ted->active_node; @@ -75,7 +124,9 @@ static void node_frame(Ted *ted, Node *node, Rect r) { } TextBuffer *buffer = &ted->buffers[buffer_index]; + assert(ted->buffers_used[buffer_index]); Rect buffer_rect = rect_translate(r, V2(0, tab_bar_height)); + buffer_rect.size.y -= tab_bar_height; buffer_render(buffer, buffer_rect); } else { @@ -102,7 +102,17 @@ static i32 ted_new_buffer(Ted *ted) { return -1; } -// returns the index of an available node, or -1 if none are available +// Opposite of ted_new_buffer +// Make sure you set active_buffer to something else if you delete it! +static void ted_delete_buffer(Ted *ted, u16 index) { + TextBuffer *buffer = &ted->buffers[index]; + if (buffer == ted->active_buffer) + ted->active_buffer = NULL; // make sure we don't set the active buffer to something invalid + buffer_free(buffer); + ted->buffers_used[index] = false; +} + +// Returns the index of an available node, or -1 if none are available static i32 ted_new_node(Ted *ted) { bool *nodes_used = ted->nodes_used; for (i32 i = 0; i < TED_MAX_NODES; ++i) { @@ -111,52 +121,79 @@ static i32 ted_new_node(Ted *ted) { return i; } } - ted_seterr(ted, "Too many buffers open!"); + ted_seterr(ted, "Too many nodes."); return -1; } -static void node_free(Node *node) { - arr_free(node->tabs); -} +static void node_tab_close(Ted *ted, Node *node, u16 index); -// returns true on success -static bool ted_open_file(Ted *ted, char const *filename) { +// Open a new buffer. Fills out *tab to the index of the tab used, and *buffer_idx to the index of the buffer. +// Returns true on success. +static Status ted_open_buffer(Ted *ted, u16 *buffer_idx, u16 *tab) { i32 new_buffer_index = ted_new_buffer(ted); if (new_buffer_index >= 0) { Node *node = ted->active_node; + if (!node) { + // no active node; let's create one! + i32 node_idx = ted_new_node(ted); + if (node_idx >= 0) { + node = &ted->nodes[node_idx]; + ted->active_node = node; + } else { + ted_delete_buffer(ted, (u16)new_buffer_index); + return false; + } + } if (arr_len(node->tabs) < TED_MAX_TABS) { arr_add(node->tabs, (u16)new_buffer_index); TextBuffer *new_buffer = &ted->buffers[new_buffer_index]; - if (node->tabs && buffer_load_file(new_buffer, filename)) { - ted->active_buffer = new_buffer; - node->active_tab = (u16)(arr_len(node->tabs) - 1); - return true; - } + ted->active_buffer = new_buffer; + node->active_tab = (u16)(arr_len(node->tabs) - 1); + *buffer_idx = (u16)new_buffer_index; + *tab = node->active_tab; + return true; } else { ted_seterr(ted, "Too many tabs."); + ted_delete_buffer(ted, (u16)new_buffer_index); + return false; } + } else { + return false; } - return false; } -static void ted_new_file(Ted *ted) { - i32 new_buffer_index = ted_new_buffer(ted); - if (new_buffer_index >= 0) { - Node *node = ted->active_node; - if (arr_len(node->tabs) < TED_MAX_TABS) { - arr_add(node->tabs, (u16)new_buffer_index); - TextBuffer *new_buffer = &ted->buffers[new_buffer_index]; - if (node->tabs) { - buffer_new_file(new_buffer, TED_UNTITLED); - if (!buffer_haserr(new_buffer)) { - ted->active_buffer = new_buffer; - node->active_tab = (u16)(arr_len(node->tabs) - 1); - } - } +// Returns true on success +static bool ted_open_file(Ted *ted, char const *filename) { + u16 buffer_idx, tab_idx; + if (ted_open_buffer(ted, &buffer_idx, &tab_idx)) { + TextBuffer *buffer = &ted->buffers[buffer_idx]; + if (buffer_load_file(buffer, filename)) { + return true; } else { - ted_seterr(ted, "Too many tabs."); + node_tab_close(ted, ted->active_node, tab_idx); + ted_delete_buffer(ted, (u16)buffer_idx); + return false; + } + } else { + return false; + } +} + +static bool ted_new_file(Ted *ted) { + u16 buffer_idx, tab_idx; + if (ted_open_buffer(ted, &buffer_idx, &tab_idx)) { + TextBuffer *buffer = &ted->buffers[buffer_idx]; + buffer_new_file(buffer, TED_UNTITLED); + if (!buffer_haserr(buffer)) { + return true; + } else { + node_tab_close(ted, ted->active_node, tab_idx); + ted_delete_buffer(ted, (u16)buffer_idx); + return false; } + } else { + return false; } } @@ -67,6 +67,7 @@ Ctrl+o = :open Ctrl+n = :new Ctrl+s = :save Ctrl+Shift+s = :save-as +Ctrl+q = :quit Ctrl+z = :undo Ctrl+Shift+z = :redo Ctrl+c = :copy @@ -138,6 +138,7 @@ typedef struct Ted { double error_time; // time error box was opened (in seconds -- see time_get_seconds) KeyAction key_actions[KEY_COMBO_COUNT]; bool search_cwd; // should the working directory be searched for files? set to true if the executable isn't "installed" + bool quit; // if set to true, the window will close next frame. NOTE: this doesn't check for unsaved changes!! char warn_overwrite[TED_PATH_MAX]; // file name user is trying to overwrite char local_data_dir[TED_PATH_MAX]; char global_data_dir[TED_PATH_MAX]; |