diff options
author | Leo Tenenbaum <pommicket@gmail.com> | 2021-01-25 17:07:09 -0500 |
---|---|---|
committer | Leo Tenenbaum <pommicket@gmail.com> | 2021-01-25 17:07:09 -0500 |
commit | a56f549a266e14cdc00a98e8dc3e154f5ac6c23e (patch) | |
tree | b513d9bbf656fe1ac054e3201fe10ac94de461ef | |
parent | f6d49d377ac136fc29457b3b4501f0488b6412e3 (diff) |
error box; generally better error handling
-rw-r--r-- | Untitled | 4 | ||||
-rw-r--r-- | buffer.c | 2 | ||||
-rw-r--r-- | colors.h | 8 | ||||
-rw-r--r-- | command.c | 3 | ||||
-rw-r--r-- | config.c | 6 | ||||
-rw-r--r-- | main.c | 113 | ||||
-rw-r--r-- | math.c | 30 | ||||
-rw-r--r-- | menu.c | 31 | ||||
-rw-r--r-- | ted-base.c | 4 | ||||
-rw-r--r-- | ted.cfg | 5 | ||||
-rw-r--r-- | ted.h | 3 | ||||
-rw-r--r-- | ui.c | 16 |
12 files changed, 172 insertions, 53 deletions
@@ -1,4 +1,2 @@ 1 2 3 4 -1 4 9 16 -1 8 27 64 -1 16 81 256
\ No newline at end of file +56 78 910 1112
\ No newline at end of file @@ -1895,7 +1895,7 @@ void buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float y2) { sel_start = buffer->selection_pos; } else assert(0); - for (u32 line_idx = maxu32(sel_start.line, start_line); line_idx <= sel_end.line; ++line_idx) { + for (u32 line_idx = max_u32(sel_start.line, start_line); line_idx <= sel_end.line; ++line_idx) { Line *line = &buffer->lines[line_idx]; u32 index1 = line_idx == sel_start.line ? sel_start.index : 0; u32 index2 = line_idx == sel_end.line ? sel_end.index : line->len; @@ -12,6 +12,9 @@ ENUM_U16 { COLOR_MENU_BACKDROP, COLOR_MENU_BG, COLOR_MENU_HL, + COLOR_ERROR_TEXT, + COLOR_ERROR_BG, + COLOR_ERROR_BORDER, COLOR_COUNT } ENUM_U16_END(ColorSetting); @@ -33,7 +36,10 @@ static ColorName const color_names[COLOR_COUNT] = { {COLOR_SELECTION_BG, "selection-bg"}, {COLOR_MENU_BACKDROP, "menu-backdrop"}, {COLOR_MENU_BG, "menu-bg"}, - {COLOR_MENU_HL, "menu-hl"} + {COLOR_MENU_HL, "menu-hl"}, + {COLOR_ERROR_TEXT, "error-text"}, + {COLOR_ERROR_BG, "error-bg"}, + {COLOR_ERROR_BORDER, "error-border"} }; static ColorSetting color_setting_from_str(char const *str) { @@ -136,6 +136,9 @@ void command_execute(Ted *ted, Command c, i64 argument) { if (buffer) buffer_cut(buffer); break; case CMD_PASTE: + // @TEST @TODO: delete me + ted_seterr(ted, "Very long test error the quick brown fox jumps over the lazy dog. ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz. Thank you very much. It was the best of times. It was the worst of times. It was the age of reason. It was the age of foolishness."); + if (buffer) buffer_paste(buffer); break; @@ -318,6 +318,12 @@ void config_read(Ted *ted, char const *filename) { } else { config_err(cfg, "Invalid scrolloff: %s.", value); } + } else if (streq(key, "error-display-time")) { + if (is_integer && integer >= 0 && integer < U16_MAX) { + settings->error_display_time = (u16)integer; + } else { + config_err(cfg, "Invalid error display time: %s.", value); + } } else { config_err(cfg, "Unrecognized core setting: %s.", key); } @@ -1,6 +1,7 @@ // @TODO: +// - save as +// - auto-indent // - Windows installation -// - error bar #include "base.h" no_warn_start #if _WIN32 @@ -83,6 +84,22 @@ int main(int argc, char **argv) { #endif setlocale(LC_ALL, ""); // allow unicode + // read command-line arguments + char const *starting_filename = "Untitled"; + switch (argc) { + case 0: case 1: break; + case 2: + starting_filename = argv[1]; + break; + default: + fprintf(stderr, "Usage: %s [filename]\n", argv[0]); + return EXIT_FAILURE; + } + + SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); // if this program is sent a SIGTERM/SIGINT, don't turn it into a quit event + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) + die("%s", SDL_GetError()); + Ted *ted = calloc(1, sizeof *ted); if (!ted) { die("Not enough memory available to run ted."); @@ -160,10 +177,6 @@ int main(int argc, char **argv) { die("Error reading config: %s", ted_geterr(ted)); } - SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); // if this program is sent a SIGTERM/SIGINT, don't turn it into a quit event - if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) - die("%s", SDL_GetError()); - SDL_Window *window = SDL_CreateWindow("ted", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_SHOWN|SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); if (!window) @@ -203,25 +216,14 @@ int main(int argc, char **argv) { buffer_create(buffer, ted); ted->active_buffer = buffer; - char const *starting_filename = "Untitled"; - - switch (argc) { - case 0: case 1: break; - case 2: - starting_filename = argv[1]; - break; - default: - die("Usage: %s [filename]", argv[0]); - break; - } if (fs_file_exists(starting_filename)) { if (!buffer_load_file(buffer, starting_filename)) - die("Error loading file: %s", buffer_geterr(buffer)); + ted_seterr(ted, "Couldn't load file: %s", buffer_geterr(buffer)); } else { buffer_new_file(buffer, starting_filename); if (buffer_haserr(buffer)) - die("Error creating file: %s", buffer_geterr(buffer)); + ted_seterr(ted, "Couldn't create file: %s", buffer_geterr(buffer)); } } @@ -281,6 +283,15 @@ int main(int argc, char **argv) { } break; case SDL_MOUSEBUTTONDOWN: { Uint32 button = event.button.button; + + if (button == SDL_BUTTON_LEFT) { + // shift+left click = right click + if (shift_down) button = SDL_BUTTON_RIGHT; + // ctrl+left click = middle click + if (ctrl_down) button = SDL_BUTTON_MIDDLE; + } + + float x = (float)event.button.x, y = (float)event.button.y; if (button < arr_count(ted->nmouse_clicks) && ted->nmouse_clicks[button] < arr_count(ted->mouse_clicks[button])) { @@ -359,11 +370,6 @@ int main(int argc, char **argv) { buffer_insert_utf8_at_cursor(buffer, text); } break; } - - if (ted_haserr(ted)) { - // @TODO: better error handling - die("%s", ted_geterr(ted)); - } } if (!(SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_LMASK)) { @@ -418,13 +424,14 @@ int main(int argc, char **argv) { glClearColor(bg_color[0], bg_color[1], bg_color[2], bg_color[3]); } glClear(GL_COLOR_BUFFER_BIT); + + Font *font = ted->font; { float x1 = 50, y1 = 50, x2 = window_width-50, y2 = window_height-50; buffer_render(&ted->main_buffer, x1, y1, x2, y2); if (text_has_err()) { - die("Text error: %s\n", text_get_err()); - break; + ted_seterr(ted, "Couldn't render text: %s", text_get_err()); } } @@ -433,9 +440,61 @@ int main(int argc, char **argv) { menu_render(ted, menu); } + // check if there's a new error if (ted_haserr(ted)) { - // @TODO: better error handling - die("%s", ted_geterr(ted)); + ted->error_time = time_get_seconds(); + str_cpy(ted->error_shown, sizeof ted->error_shown, ted->error); + ted_clearerr(ted); + } + + // error box + if (*ted->error_shown) { + double t = time_get_seconds(); + double time_passed = t - ted->error_time; + if (time_passed > settings->error_display_time) { + // stop showing error + ted->error_shown[0] = '\0'; + } else { + // @TODO(eventually): output a log + // @TODO: middle click to dismiss + float padding = settings->padding; + float char_width = text_font_char_width(font); + float char_height = text_font_char_height(font); + Rect r = rect_centered(V2(window_width * 0.5f, window_height * 0.9f), + V2(menu_get_width(ted), 3 * char_height + 2 * padding)); + + glBegin(GL_QUADS); + gl_color_rgba(colors[COLOR_ERROR_BG]); + rect_render(r); + gl_color_rgba(colors[COLOR_ERROR_BORDER]); + rect_render_border(r, settings->border_thickness); + glEnd(); + gl_color_rgba(colors[COLOR_ERROR_TEXT]); + + + float text_x1 = rect_x1(r) + padding, text_x2 = rect_x2(r) - padding; + float text_y1 = rect_y1(r) + padding; + + TextRenderState text_state = {.x = text_x1, .y = text_y1, + .min_x = -FLT_MAX, .max_x = FLT_MAX, .min_y = -FLT_MAX, .max_y = FLT_MAX, + .render = true}; + mbstate_t mbstate = {0}; + char *p = ted->error_shown, *end = p + strlen(p); + + text_chars_begin(font); + while (p != end) { + char32_t c = 0; + size_t n = mbrtoc32(&c, p, (size_t)(end - p), &mbstate); + if (n > (size_t)-3) { ++p; continue; } // invalid UTF-8; this shouldn't happen + if (n != (size_t)-3) p += n; + if (text_state.x + char_width >= text_x2) { + text_state.x = text_x1; + text_state.y += char_height; + } + text_render_char(font, &text_state, c); + } + text_chars_end(font); + } } @@ -46,7 +46,7 @@ static int clampi(int x, int a, int b) { return x; } -static i32 clampi32(i32 x, i32 a, i32 b) { +static i32 clamp_i32(i32 x, i32 a, i32 b) { if (x < a) return a; if (x > b) return b; return x; @@ -74,11 +74,35 @@ static double mind(double a, double b) { return a < b ? a : b; } -static u32 minu32(u32 a, u32 b) { +static u32 min_u32(u32 a, u32 b) { return a < b ? a : b; } -static u32 maxu32(u32 a, u32 b) { +static u32 max_u32(u32 a, u32 b) { + return a > b ? a : b; +} + +static i32 min_i32(i32 a, i32 b) { + return a < b ? a : b; +} + +static i32 max_i32(i32 a, i32 b) { + return a > b ? a : b; +} + +static u64 min_u64(u64 a, u64 b) { + return a < b ? a : b; +} + +static u64 max_u64(u64 a, u64 b) { + return a > b ? a : b; +} + +static i64 min_i64(i64 a, i64 b) { + return a < b ? a : b; +} + +static i64 max_i64(i64 a, i64 b) { return a > b ? a : b; } @@ -19,13 +19,17 @@ static void menu_close(Ted *ted, bool restore_prev_active_buffer) { buffer_clear(&ted->line_buffer); } +static float menu_get_width(Ted *ted) { + Settings *settings = &ted->settings; + return minf(settings->max_menu_width, ted->window_width - 2.0f * settings->padding); +} + // returns the rectangle of the screen coordinates of the menu static Rect menu_rect(Ted *ted) { Settings *settings = &ted->settings; float window_width = ted->window_width, window_height = ted->window_height; float padding = settings->padding; - float menu_width = settings->max_menu_width; - menu_width = minf(menu_width, window_width - 2 * padding); + float menu_width = menu_get_width(ted); return rect( V2(window_width * 0.5f - 0.5f * menu_width, padding), V2(menu_width, window_height - 2 * padding) @@ -63,27 +67,22 @@ static void menu_render(Ted *ted, Menu menu) { if (menu == MENU_OPEN) { float padding = settings->padding; - float menu_x1 = window_width * 0.5f - 300; - float menu_x2 = window_width * 0.5f + 300; - menu_x1 = maxf(menu_x1, padding); - menu_x2 = minf(menu_x2, window_width - padding); - float menu_y1 = padding; - float menu_y2 = window_height - padding; - Rect menu_rect = rect4(menu_x1, menu_y1, menu_x2, menu_y2); - float inner_padding = 10; + Rect rect = menu_rect(ted); + float menu_x1, menu_y1, menu_x2, menu_y2; + rect_coords(rect, &menu_x1, &menu_y1, &menu_x2, &menu_y2); // menu rectangle & border glBegin(GL_QUADS); gl_color_rgba(colors[COLOR_MENU_BG]); - rect_render(menu_rect); + rect_render(rect); gl_color_rgba(colors[COLOR_BORDER]); - rect_render_border(menu_rect, settings->border_thickness); + rect_render_border(rect, settings->border_thickness); glEnd(); - menu_x1 += inner_padding; - menu_y1 += inner_padding; - menu_x2 -= inner_padding; - menu_y2 -= inner_padding; + menu_x1 += padding; + menu_y1 += padding; + menu_x2 -= padding; + menu_y2 -= padding; FileSelector *fs = &ted->file_selector; @@ -16,6 +16,10 @@ char const *ted_geterr(Ted *ted) { return ted->error; } +void ted_clearerr(Ted *ted) { + ted->error[0] = '\0'; +} + static void ted_out_of_mem(Ted *ted) { ted_seterr(ted, "Out of memory."); } @@ -15,6 +15,7 @@ text-size = 18 border-thickness = 1 max-menu-width = 600 padding = 10 +error-display-time = 10 [keyboard] # motion and selection @@ -87,3 +88,7 @@ bg = #001 menu-backdrop = #0004 menu-bg = #222 menu-hl = #afa2 +# error box colors +error-border = #f00 +error-bg = #800 +error-text = #fdd @@ -8,6 +8,7 @@ typedef struct { u32 colors[COLOR_COUNT]; u16 text_size; u16 max_menu_width; + u16 error_display_time; u8 tab_width; u8 cursor_width; u8 undo_save_time; @@ -115,9 +116,11 @@ typedef struct Ted { FileSelector file_selector; TextBuffer line_buffer; // general-purpose line buffer for inputs -- used for menus TextBuffer main_buffer; + double error_time; // time error box was opened (in seconds -- see time_get_seconds) KeyAction key_actions[KEY_COMBO_COUNT]; char cwd[TED_PATH_MAX]; // current working directory char error[256]; + char error_shown[256]; // error display in box on screen } Ted; // should the working directory be searched for files? set to true if the executable isn't "installed" @@ -318,7 +318,20 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) { // free previous entries file_selector_clear_entries(fs); // get new entries - char **files = fs_list_directory(cwd); + char **files; + // if the directory we're in gets deleted, go back a directory. + for (u32 i = 0; i < 100; ++i) { + files = fs_list_directory(cwd); + if (files) break; + else if (i == 0) { + if (fs_path_type(cwd) == FS_NON_EXISTENT) + ted_seterr(ted, "%s is not a directory.", cwd); + else + ted_seterr(ted, "Can't list directory %s.", cwd); + } + file_selector_cd(fs, ".."); + } + if (files) { u32 nfiles; for (nfiles = 0; files[nfiles]; ++nfiles); @@ -367,7 +380,6 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) { } free(files); - } else { ted_seterr(ted, "Couldn't list directory '%s'.", cwd); } |