diff options
-rw-r--r-- | Untitled | 1 | ||||
-rw-r--r-- | buffer.c | 98 | ||||
-rw-r--r-- | colors.h (renamed from colors.c) | 0 | ||||
-rw-r--r-- | command.c | 18 | ||||
-rw-r--r-- | command.h | 5 | ||||
-rw-r--r-- | config.c | 8 | ||||
-rw-r--r-- | main.c | 124 | ||||
-rw-r--r-- | settings.h | 0 | ||||
-rw-r--r-- | ted-base.c | 72 | ||||
-rw-r--r-- | ted.cfg | 4 | ||||
-rw-r--r-- | ted.h | 74 | ||||
-rw-r--r-- | text.c | 19 | ||||
-rw-r--r-- | util.c | 1 |
13 files changed, 259 insertions, 165 deletions
@@ -1,4 +1,3 @@ 1 2 3 4 t e s t he ll o!! !! - @@ -1,50 +1,9 @@ // Text buffers - These store the contents of a file. -// a position in the buffer -typedef struct { - u32 line; - u32 index; // index of character in line (not the same as column, since a tab is settings->tab_width columns) -} BufferPos; - -typedef struct { - u32 len; - u32 capacity; - char32_t *str; -} Line; - -// this refers to replacing prev_len characters (found in prev_text) at pos with new_len characters -typedef struct BufferEdit { - BufferPos pos; - u32 new_len; - u32 prev_len; - char32_t *prev_text; - double time; // time at start of edit (i.e. the time just before the edit), in seconds since epoch -} BufferEdit; - -typedef struct { - char const *filename; - Settings *settings; - double scroll_x, scroll_y; // number of characters scrolled in the x/y direction - Font *font; - BufferPos cursor_pos; - BufferPos selection_pos; // if selection is true, the text between selection_pos and cursor_pos is selected. - bool selection; - bool store_undo_events; // set to false to disable undo events - float x1, y1, x2, y2; - u32 nlines; - u32 lines_capacity; - Line *lines; - char error[128]; - BufferEdit *undo_history; // dynamic array of undo history - BufferEdit *redo_history; // dynamic array of redo history -} TextBuffer; - - -void buffer_create(TextBuffer *buffer, Font *font, Settings *settings) { +void buffer_create(TextBuffer *buffer, Ted *ted) { util_zero_memory(buffer, sizeof *buffer); - buffer->font = font; buffer->store_undo_events = true; - buffer->settings = settings; + buffer->ted = ted; } // this is a macro so we get -Wformat warnings @@ -153,6 +112,16 @@ BufferPos buffer_end_of_file(TextBuffer *buffer) { return (BufferPos){.line = buffer->nlines - 1, .index = buffer->lines[buffer->nlines-1].len}; } +// Get the font used for this buffer. +static inline Font *buffer_font(TextBuffer *buffer) { + return buffer->ted->font; +} + +// Get the settings used for this buffer. +static inline Settings *buffer_settings(TextBuffer *buffer) { + return &buffer->ted->settings; +} + // Returns a simple checksum of the buffer. // This is only used for testing, and shouldn't be relied on. static u64 buffer_checksum(TextBuffer *buffer) { @@ -211,7 +180,7 @@ static BufferPos buffer_pos_advance(TextBuffer *buffer, BufferPos pos, size_t nc while (line != end) { u32 chars_from_this_line = line->len - index; if (chars_left <= chars_from_this_line) { - index += chars_left; + index += (u32)chars_left; pos.index = index; pos.line = (u32)(line - buffer->lines); return pos; @@ -380,7 +349,7 @@ static bool buffer_edit_does_anything(TextBuffer *buffer, BufferEdit *edit) { // has enough time passed since the last edit that we should create a new one? static bool buffer_edit_split(TextBuffer *buffer) { double curr_time = time_get_seconds(); - double undo_time_cutoff = buffer->settings->undo_save_time; // only keep around edits for this long (in seconds). + double undo_time_cutoff = buffer_settings(buffer)->undo_save_time; // only keep around edits for this long (in seconds). BufferEdit *last_edit = arr_lastp(buffer->undo_history); if (!last_edit) return true; return curr_time - last_edit->time > undo_time_cutoff; @@ -466,8 +435,7 @@ void buffer_free(TextBuffer *buffer) { arr_free(buffer->undo_history); arr_free(buffer->redo_history); - Settings *settings = buffer->settings; - Font *font = buffer->font; + Ted *ted = buffer->ted; // zero buffer, except for error char error[sizeof buffer->error]; @@ -475,7 +443,7 @@ void buffer_free(TextBuffer *buffer) { memset(buffer, 0, sizeof *buffer); memcpy(buffer->error, error, sizeof error); - buffer_create(buffer, font, settings); + buffer_create(buffer, ted); } @@ -611,10 +579,10 @@ static void buffer_print(TextBuffer const *buffer) { 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) { switch (str[i]) { case U'\t': { - uint tab_width = buffer->settings->tab_width; do ++col; while (col % tab_width); @@ -635,10 +603,10 @@ static u32 buffer_column_to_index(TextBuffer *buffer, u32 line, u32 column) { char32_t *str = buffer->lines[line].str; u32 len = buffer->lines[line].len; u32 col = 0; + uint tab_width = buffer_settings(buffer)->tab_width; for (u32 i = 0; i < len; ++i) { switch (str[i]) { case U'\t': { - uint tab_width = buffer->settings->tab_width; do { if (col == column) return i; @@ -679,12 +647,12 @@ void buffer_text_dimensions(TextBuffer *buffer, u32 *lines, u32 *columns) { // returns the number of rows of text that can fit in the buffer, rounded down. int buffer_display_lines(TextBuffer *buffer) { - return (int)((buffer->y2 - buffer->y1) / text_font_char_height(buffer->font)); + return (int)((buffer->y2 - buffer->y1) / text_font_char_height(buffer_font(buffer))); } // returns the number of columns of text that can fit in the buffer, rounded down. int buffer_display_cols(TextBuffer *buffer) { - return (int)((buffer->x2 - buffer->x1) / text_font_char_width(buffer->font)); + return (int)((buffer->x2 - buffer->x1) / text_font_char_width(buffer_font(buffer))); } // make sure we don't scroll too far @@ -729,9 +697,9 @@ v2 buffer_pos_to_pixels(TextBuffer *buffer, BufferPos pos) { u32 line = pos.line, index = pos.index; // we need to convert the index to a column u32 col = buffer_index_to_column(buffer, line, index); - float x = (float)((double)col - buffer->scroll_x) * text_font_char_width(buffer->font) + buffer->x1; - float y = (float)((double)line - buffer->scroll_y) * text_font_char_height(buffer->font) + buffer->y1 - + text_font_char_height(buffer->font) * 0.2f; // slightly nudge + Font *font = buffer_font(buffer); + float x = (float)((double)col - buffer->scroll_x) * text_font_char_width(font) + buffer->x1; + float y = (float)((double)line - buffer->scroll_y + 0.2f /* nudge */) * text_font_char_height(font) + buffer->y1; return V2(x, y); } @@ -739,12 +707,13 @@ v2 buffer_pos_to_pixels(TextBuffer *buffer, BufferPos pos) { // returns false if the position is not inside the buffer. bool buffer_pixels_to_pos(TextBuffer *buffer, v2 pixel_coords, BufferPos *pos) { float x = pixel_coords.x, y = pixel_coords.y; + Font *font = buffer_font(buffer); pos->line = pos->index = 0; x -= buffer->x1; y -= buffer->y1; - x /= text_font_char_width(buffer->font); - y /= text_font_char_height(buffer->font); + x /= text_font_char_width(font); + y /= text_font_char_height(font); double display_col = (double)x; if (display_col < 0 || display_col >= buffer_display_cols(buffer)) return false; @@ -1139,7 +1108,7 @@ BufferPos buffer_insert_text_at_pos(TextBuffer *buffer, BufferPos pos, String32 buffer_edit(buffer, pos, 0, (u32)str.len); } else { // merge this edit into the previous one. - last_edit->new_len += str.len; + last_edit->new_len += (u32)str.len; } } @@ -1386,7 +1355,7 @@ void buffer_delete_chars_at_pos(TextBuffer *buffer, BufferPos pos, i64 nchars_) if (new_text_del_end > last_edit->new_len) new_text_del_end = last_edit->new_len; if (new_text_del_end > new_text_del_start) { // shrink length to get rid of that text - last_edit->new_len -= new_text_del_end - new_text_del_start; + last_edit->new_len -= (u32)(new_text_del_end - new_text_del_start); } } @@ -1617,13 +1586,16 @@ void buffer_check_valid(TextBuffer *buffer) { // Render the text buffer in the given rectangle void buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float y2) { - Font *font = buffer->font; + // Correct the scroll, because the window size might have changed + buffer_correct_scroll(buffer); + + Font *font = buffer_font(buffer); u32 nlines = buffer->nlines; Line *lines = buffer->lines; float char_width = text_font_char_width(font), char_height = text_font_char_height(font); float header_height = char_height; - Settings *settings = buffer->settings; + Settings *settings = buffer_settings(buffer); u32 *colors = settings->colors; // get screen coordinates of cursor @@ -1693,7 +1665,6 @@ void buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float y2) { u32 column = 0; u32 start_line = (u32)buffer->scroll_y; // line to start rendering from - //debug_print("Rendering from " U32_FMT, start_line); if (buffer->selection) { // draw selection glBegin(GL_QUADS); @@ -1761,7 +1732,7 @@ void buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float y2) { case U'\n': assert(0); break; case U'\r': break; // for CRLF line endings case U'\t': { - uint tab_width = buffer->settings->tab_width; + uint tab_width = settings->tab_width; do { text_render_char(font, &text_state, U' '); ++column; @@ -1778,7 +1749,6 @@ void buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float y2) { text_state.x = render_start_x; if (text_state.y > text_state.max_y) { // made it to the bottom of the buffer view. - //debug_println(" to " U32_FMT ".", line_idx); break; } text_state.y += text_font_char_height(font); @@ -19,6 +19,8 @@ char const *command_to_str(Command c) { void command_execute(Ted *ted, Command c, i64 argument) { TextBuffer *buffer = ted->active_buffer; + Settings *settings = &ted->settings; + switch (c) { case CMD_UNKNOWN: case CMD_COUNT: @@ -120,7 +122,23 @@ void command_execute(Ted *ted, Command c, i64 argument) { case CMD_REDO: buffer_redo(buffer, argument); break; + + case CMD_TEXT_SIZE_INCREASE: { + i64 new_text_size = settings->text_size + argument; + if (new_text_size >= TEXT_SIZE_MIN && new_text_size <= TEXT_SIZE_MAX) { + settings->text_size = (u16)new_text_size; + ted_load_font(ted); + } + } break; + case CMD_TEXT_SIZE_DECREASE: { + i64 new_text_size = settings->text_size - argument; + if (new_text_size >= TEXT_SIZE_MIN && new_text_size <= TEXT_SIZE_MAX) { + settings->text_size = (u16)new_text_size; + ted_load_font(ted); + } + } break; } + if (buffer_haserr(buffer)) { strncpy(ted->error, buffer_geterr(buffer), sizeof ted->error - 1); buffer_clearerr(buffer); @@ -38,6 +38,9 @@ ENUM_U16 { CMD_UNDO, CMD_REDO, + CMD_TEXT_SIZE_INCREASE, + CMD_TEXT_SIZE_DECREASE, + CMD_COUNT } ENUM_U16_END(Command); @@ -78,5 +81,7 @@ static CommandName const command_names[CMD_COUNT] = { {"save", CMD_SAVE}, {"undo", CMD_UNDO}, {"redo", CMD_REDO}, + {"increase-text-size", CMD_TEXT_SIZE_INCREASE}, + {"decrease-text-size", CMD_TEXT_SIZE_DECREASE} }; @@ -106,7 +106,7 @@ static u32 config_parse_key_combo(ConfigReader *cfg, char const *str) { {"Tilde", "~", SDL_SCANCODE_GRAVE, 1} }; - // @OPTIMIZE: sort key_names; do a binary search + // @OPTIMIZE: sort key_names (and split keyname1/2); do a binary search for (size_t i = 0; i < arr_count(key_names); ++i) { KeyName const *k = &key_names[i]; if (streq(str, k->keyname1) || (k->keyname2 && streq(str, k->keyname2))) { @@ -286,6 +286,12 @@ void config_read(Ted *ted, char const *filename) { } else { config_err(cfg, "Invalid cursor blink time: %s.", value); } + } else if (streq(key, "text-size")) { + if (is_integer && integer >= TEXT_SIZE_MIN && integer <= TEXT_SIZE_MAX) { + settings->text_size = (u16)integer; + } else { + config_err(cfg, "Invalid text size: %s.", value); + } } else { config_err(cfg, "Unrecognized core setting: %s.", key); } @@ -18,22 +18,17 @@ no_warn_end #define TED_PATH_MAX 256 -#include "command.h" +#include "text.h" #include "util.c" -#include "filesystem.c" -#include "colors.c" -typedef struct { - float cursor_blink_time_on, cursor_blink_time_off; - u32 colors[COLOR_COUNT]; - u8 tab_width; - u8 cursor_width; - u8 undo_save_time; -} Settings; -#include "time.c" -#include "unicode.h" #define MATH_GL #include "math.c" -#include "text.h" + +#include "unicode.h" +#include "command.h" +#include "colors.h" +#include "ted.h" +#include "filesystem.c" +#include "time.c" #include "string32.c" #include "arr.c" #include "buffer.c" @@ -57,38 +52,6 @@ static void die(char const *fmt, ...) { exit(EXIT_FAILURE); } -// should the working directory be searched for files? set to true if the executable isn't "installed" -static bool ted_search_cwd = false; -#if _WIN32 -// @TODO -#else -static char const *const ted_global_data_dir = "/usr/share/ted"; -#endif - -// Check the various places a file could be, and return the full path. -static Status ted_get_file(char const *name, char *out, size_t outsz) { -#if _WIN32 - #error "@TODO(windows)" -#else - if (ted_search_cwd && fs_file_exists(name)) { - // check in current working directory - str_cpy(out, outsz, name); - return true; - } - - char *home = getenv("HOME"); - if (home) { - str_printf(out, outsz, "%s/.local/share/ted/%s", home, name); - if (!fs_file_exists(out)) { - str_printf(out, outsz, "%s/%s", ted_global_data_dir, name); - if (!fs_file_exists(out)) - return false; - } - } - return true; -#endif -} - #if _WIN32 INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) { @@ -176,22 +139,13 @@ int main(int argc, char **argv) { SDL_GL_SetSwapInterval(1); // vsync - Font *font = NULL; - { - char font_filename[TED_PATH_MAX]; - if (ted_get_file("assets/font.ttf", font_filename, sizeof font_filename)) { - font = text_font_load(font_filename, 16); - if (!font) { - die("Couldn't load font: %s", text_get_err()); - } - } else { - die("Couldn't find font file. There is probably a problem with your ted installation."); - } - } + ted_load_font(ted); + if (ted_haserr(ted)) + die("Error loadng font: %s", ted_geterr(ted)); TextBuffer text_buffer; TextBuffer *buffer = &text_buffer; - buffer_create(buffer, font, settings); + buffer_create(buffer, ted); ted->active_buffer = buffer; char const *starting_filename = "Untitled"; @@ -220,6 +174,9 @@ int main(int argc, char **argv) { Uint32 time_at_last_frame = SDL_GetTicks(); bool quit = false; + bool ctrl_down = false; + bool shift_down = false; + bool alt_down = false; while (!quit) { #if DEBUG //printf("\033[H\033[2J"); @@ -227,14 +184,11 @@ int main(int argc, char **argv) { SDL_Event event; Uint8 const *keyboard_state = SDL_GetKeyboardState(NULL); - bool ctrl_down = keyboard_state[SDL_SCANCODE_LCTRL] || keyboard_state[SDL_SCANCODE_RCTRL]; - bool shift_down = keyboard_state[SDL_SCANCODE_LSHIFT] || keyboard_state[SDL_SCANCODE_RSHIFT]; - bool alt_down = keyboard_state[SDL_SCANCODE_LALT] || keyboard_state[SDL_SCANCODE_RALT]; - u32 key_modifier = (u32)ctrl_down << KEY_MODIFIER_CTRL_BIT - | (u32)shift_down << KEY_MODIFIER_SHIFT_BIT - | (u32)alt_down << KEY_MODIFIER_ALT_BIT; while (SDL_PollEvent(&event)) { + u32 key_modifier = (u32)ctrl_down << KEY_MODIFIER_CTRL_BIT + | (u32)shift_down << KEY_MODIFIER_SHIFT_BIT + | (u32)alt_down << KEY_MODIFIER_ALT_BIT; // @TODO: make a function to handle text buffer events switch (event.type) { case SDL_QUIT: @@ -279,6 +233,18 @@ int main(int argc, char **argv) { break; case SDL_KEYDOWN: { SDL_Scancode scancode = event.key.keysym.scancode; + switch (scancode) { + case SDL_SCANCODE_LCTRL: + case SDL_SCANCODE_RCTRL: + ctrl_down = true; break; + case SDL_SCANCODE_LSHIFT: + case SDL_SCANCODE_RSHIFT: + shift_down = true; break; + case SDL_SCANCODE_LALT: + case SDL_SCANCODE_RALT: + alt_down = true; break; + default: break; + } SDL_Keymod modifier = event.key.keysym.mod; u32 key_combo = (u32)scancode << 3 | (u32)((modifier & (KMOD_LCTRL|KMOD_RCTRL)) != 0) << KEY_MODIFIER_CTRL_BIT | @@ -298,13 +264,37 @@ int main(int argc, char **argv) { } } } break; + case SDL_KEYUP: { + SDL_Scancode scancode = event.key.keysym.scancode; + switch (scancode) { + case SDL_SCANCODE_LCTRL: + case SDL_SCANCODE_RCTRL: + ctrl_down = false; break; + case SDL_SCANCODE_LSHIFT: + case SDL_SCANCODE_RSHIFT: + shift_down = false; break; + case SDL_SCANCODE_LALT: + case SDL_SCANCODE_RALT: + alt_down = false; break; + default: break; + } + } break; case SDL_TEXTINPUT: { char *text = event.text.text; - buffer_insert_utf8_at_cursor(buffer, text); + if (key_modifier == 0) // unfortunately, some key combinations like ctrl+minus still register as a "-" text input event + buffer_insert_utf8_at_cursor(buffer, text); } break; } + + if (ted_haserr(ted)) { + die("%s", ted_geterr(ted)); + } } + u32 key_modifier = (u32)ctrl_down << KEY_MODIFIER_CTRL_BIT + | (u32)shift_down << KEY_MODIFIER_SHIFT_BIT + | (u32)alt_down << KEY_MODIFIER_ALT_BIT; + double frame_dt; { Uint32 time_this_frame = SDL_GetTicks(); @@ -351,7 +341,7 @@ int main(int argc, char **argv) { float x1 = 50, y1 = 50, x2 = window_widthf-50, y2 = window_heightf-50; buffer_render(buffer, x1, y1, x2, y2); if (text_has_err()) { - debug_println("Text error: %s\n", text_get_err()); + die("Text error: %s\n", text_get_err()); break; } } @@ -369,7 +359,7 @@ int main(int argc, char **argv) { SDL_DestroyWindow(window); SDL_Quit(); buffer_free(buffer); - text_font_free(font); + text_font_free(ted->font); free(ted); #if _WIN32 for (int i = 0; i < argc; ++i) diff --git a/settings.h b/settings.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/settings.h @@ -1,26 +1,3 @@ -typedef struct { - u32 line_number; // config line number where this was set - Command command; // this will be 0 (COMMAND_UNKNOWN) if there's no action for the key - i64 argument; -} KeyAction; - -#define SCANCODE_COUNT 0x120 // SDL scancodes should be less than this value. -// a "key combo" is some subset of {control, shift, alt} + some key. -#define KEY_COMBO_COUNT (SCANCODE_COUNT << 3) -#define KEY_MODIFIER_CTRL_BIT 0 -#define KEY_MODIFIER_SHIFT_BIT 1 -#define KEY_MODIFIER_ALT_BIT 2 -#define KEY_MODIFIER_CTRL (1<<KEY_MODIFIER_CTRL_BIT) -#define KEY_MODIFIER_SHIFT (1<<KEY_MODIFIER_SHIFT_BIT) -#define KEY_MODIFIER_ALT (1<<KEY_MODIFIER_ALT_BIT) -// ctrl+alt+c is encoded as SDL_SCANCODE_C << 3 | KEY_MODIFIER_CTRL | KEY_MODIFIER_ALT - -typedef struct { - TextBuffer *active_buffer; - Settings settings; - KeyAction key_actions[KEY_COMBO_COUNT]; - char error[256]; -} Ted; // this is a macro so we get -Wformat warnings #define ted_seterr(buffer, ...) \ @@ -56,3 +33,52 @@ static void *ted_realloc(Ted *ted, void *p, size_t new_size) { return ret; } +// should the working directory be searched for files? set to true if the executable isn't "installed" +static bool ted_search_cwd = false; +#if _WIN32 +// @TODO +#else +static char const *const ted_global_data_dir = "/usr/share/ted"; +#endif + +// Check the various places a file could be, and return the full path. +static Status ted_get_file(char const *name, char *out, size_t outsz) { +#if _WIN32 + #error "@TODO(windows)" +#else + if (ted_search_cwd && fs_file_exists(name)) { + // check in current working directory + str_cpy(out, outsz, name); + return true; + } + + char *home = getenv("HOME"); + if (home) { + str_printf(out, outsz, "%s/.local/share/ted/%s", home, name); + if (!fs_file_exists(out)) { + str_printf(out, outsz, "%s/%s", ted_global_data_dir, name); + if (!fs_file_exists(out)) + return false; + } + } + return true; +#endif +} + +static void ted_load_font(Ted *ted) { + char font_filename[TED_PATH_MAX]; + if (ted_get_file("assets/font.ttf", font_filename, sizeof font_filename)) { + Font *font = text_font_load(font_filename, ted->settings.text_size); + if (font) { + if (ted->font) { + text_font_free(ted->font); + } + ted->font = font; + } else { + ted_seterr(ted, "Couldn't load font: %s", text_get_err()); + text_clear_err(); + } + } else { + ted_seterr(ted, "Couldn't find font file. There is probably a problem with your ted installation."); + } +} @@ -9,6 +9,7 @@ cursor-blink-time-off = 0.3 # if you do a bunch of typing, then undo, it will generally # undo the past this many seconds of editing. undo-save-time = 6 +text-size = 16 [keyboard] # motion and selection @@ -54,6 +55,9 @@ Ctrl+s = :save Ctrl+z = :undo Ctrl+Shift+z = :redo +Ctrl++ = 3 :increase-text-size +Ctrl+- = 3 :decrease-text-size + [colors] border = #a77 cursor-line-bg = #222 @@ -0,0 +1,74 @@ +#define TEXT_SIZE_MIN 6 +#define TEXT_SIZE_MAX 70 + +typedef struct { + float cursor_blink_time_on, cursor_blink_time_off; + u32 colors[COLOR_COUNT]; + u16 text_size; + u8 tab_width; + u8 cursor_width; + u8 undo_save_time; +} Settings; + +#define SCANCODE_COUNT 0x120 // SDL scancodes should be less than this value. +// a "key combo" is some subset of {control, shift, alt} + some key. +#define KEY_COMBO_COUNT (SCANCODE_COUNT << 3) +#define KEY_MODIFIER_CTRL_BIT 0 +#define KEY_MODIFIER_SHIFT_BIT 1 +#define KEY_MODIFIER_ALT_BIT 2 +#define KEY_MODIFIER_CTRL (1<<KEY_MODIFIER_CTRL_BIT) +#define KEY_MODIFIER_SHIFT (1<<KEY_MODIFIER_SHIFT_BIT) +#define KEY_MODIFIER_ALT (1<<KEY_MODIFIER_ALT_BIT) +// ctrl+alt+c is encoded as SDL_SCANCODE_C << 3 | KEY_MODIFIER_CTRL | KEY_MODIFIER_ALT + +typedef struct { + u32 line_number; // config line number where this was set + Command command; // this will be 0 (COMMAND_UNKNOWN) if there's no action for the key + i64 argument; +} KeyAction; + +// a position in the buffer +typedef struct { + u32 line; + u32 index; // index of character in line (not the same as column, since a tab is settings->tab_width columns) +} BufferPos; + +typedef struct { + u32 len; + u32 capacity; + char32_t *str; +} Line; + +// this refers to replacing prev_len characters (found in prev_text) at pos with new_len characters +typedef struct BufferEdit { + BufferPos pos; + u32 new_len; + u32 prev_len; + char32_t *prev_text; + double time; // time at start of edit (i.e. the time just before the edit), in seconds since epoch +} BufferEdit; + +typedef struct { + char const *filename; + struct Ted *ted; // we keep a back-pointer to the ted instance so we don't have to pass it in to every buffer function + double scroll_x, scroll_y; // number of characters scrolled in the x/y direction + BufferPos cursor_pos; + BufferPos selection_pos; // if selection is true, the text between selection_pos and cursor_pos is selected. + bool selection; + bool store_undo_events; // set to false to disable undo events + float x1, y1, x2, y2; + u32 nlines; + u32 lines_capacity; + Line *lines; + char error[128]; + BufferEdit *undo_history; // dynamic array of undo history + BufferEdit *redo_history; // dynamic array of redo history +} TextBuffer; + +typedef struct Ted { + Font *font; + TextBuffer *active_buffer; + Settings settings; + KeyAction key_actions[KEY_COMBO_COUNT]; + char error[256]; +} Ted; @@ -47,10 +47,10 @@ static void text_set_err(char const *fmt, ...) { } } -static void text_load_char_page(Font *font, int page) { +static Status text_load_char_page(Font *font, int page) { if (font->char_pages[page]) { // already loaded - return; + return true; } font->char_pages[page] = calloc(CHAR_PAGE_SIZE, sizeof *font->char_pages[page]); for (int bitmap_width = 128, bitmap_height = 128; bitmap_width <= 4096; bitmap_width *= 2, bitmap_height *= 2) { @@ -93,7 +93,9 @@ static void text_load_char_page(Font *font, int page) { if (text_has_err()) { free(font->char_pages[page]); font->char_pages[page] = NULL; + return false; } + return true; } Font *text_font_load(char const *ttf_filename, float font_size) { @@ -114,8 +116,8 @@ Font *text_font_load(char const *ttf_filename, float font_size) { if (bytes_read == file_size) { font->char_height = font_size; font->ttf_data = file_data; - text_load_char_page(font, 0); // load page with Latin text, etc. - { // calculate width of the character 'a' + if (text_load_char_page(font, 0)) { // load page with Latin text, etc. + // calculate width of the character 'a' stbtt_aligned_quad q = {0}; float x = 0, y = 0; stbtt_GetBakedQuad(font->char_pages[0], font->tex_widths[0], font->tex_heights[0], @@ -158,11 +160,12 @@ static void text_render_with_page(Font *font, int page) { // we were rendering chars from another page. glEnd(); // stop doing that } - text_load_char_page(font, page); // load the page if necessary - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, font->textures[page]); + if (text_load_char_page(font, page)) { // load the page if necessary + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, font->textures[page]); + font->curr_page = page; + } glBegin(GL_QUADS); - font->curr_page = page; } } @@ -16,7 +16,6 @@ static bool util_is_power_of_2(u64 x) { } static void util_zero_memory(void *mem, size_t size) { - extern void *memset(void *s, int c, size_t n); memset(mem, 0, size); } |