summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2021-01-25 17:07:09 -0500
committerLeo Tenenbaum <pommicket@gmail.com>2021-01-25 17:07:09 -0500
commita56f549a266e14cdc00a98e8dc3e154f5ac6c23e (patch)
treeb513d9bbf656fe1ac054e3201fe10ac94de461ef
parentf6d49d377ac136fc29457b3b4501f0488b6412e3 (diff)
error box; generally better error handling
-rw-r--r--Untitled4
-rw-r--r--buffer.c2
-rw-r--r--colors.h8
-rw-r--r--command.c3
-rw-r--r--config.c6
-rw-r--r--main.c113
-rw-r--r--math.c30
-rw-r--r--menu.c31
-rw-r--r--ted-base.c4
-rw-r--r--ted.cfg5
-rw-r--r--ted.h3
-rw-r--r--ui.c16
12 files changed, 172 insertions, 53 deletions
diff --git a/Untitled b/Untitled
index e988e21..d222a6a 100644
--- a/Untitled
+++ b/Untitled
@@ -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
diff --git a/buffer.c b/buffer.c
index 422f03c..03d9b9e 100644
--- a/buffer.c
+++ b/buffer.c
@@ -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;
diff --git a/colors.h b/colors.h
index 96b26a4..047228c 100644
--- a/colors.h
+++ b/colors.h
@@ -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) {
diff --git a/command.c b/command.c
index d90153d..a8dc7e2 100644
--- a/command.c
+++ b/command.c
@@ -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;
diff --git a/config.c b/config.c
index 39a5d80..46a389f 100644
--- a/config.c
+++ b/config.c
@@ -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);
}
diff --git a/main.c b/main.c
index 8895c1f..aea8588 100644
--- a/main.c
+++ b/main.c
@@ -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);
+ }
}
diff --git a/math.c b/math.c
index af89421..9d72aa2 100644
--- a/math.c
+++ b/math.c
@@ -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;
}
diff --git a/menu.c b/menu.c
index 7e1db53..c98fbc8 100644
--- a/menu.c
+++ b/menu.c
@@ -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;
diff --git a/ted-base.c b/ted-base.c
index 80d14e3..83c8208 100644
--- a/ted-base.c
+++ b/ted-base.c
@@ -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.");
}
diff --git a/ted.cfg b/ted.cfg
index c9608a5..8660f04 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -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
diff --git a/ted.h b/ted.h
index 8556d36..15c13c6 100644
--- a/ted.h
+++ b/ted.h
@@ -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"
diff --git a/ui.c b/ui.c
index 8f90bbb..e9fea11 100644
--- a/ui.c
+++ b/ui.c
@@ -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);
}