diff options
-rw-r--r-- | buffer.c | 14 | ||||
-rw-r--r-- | command.c | 6 | ||||
-rw-r--r-- | command.h | 5 | ||||
-rw-r--r-- | find.c | 28 | ||||
-rw-r--r-- | main.c | 79 | ||||
-rw-r--r-- | menu.c | 5 | ||||
-rw-r--r-- | node.c | 5 | ||||
-rw-r--r-- | ted.cfg | 1 | ||||
-rw-r--r-- | ted.h | 3 | ||||
-rw-r--r-- | text.c | 58 | ||||
-rw-r--r-- | text.h | 27 | ||||
-rw-r--r-- | ui.c | 23 |
12 files changed, 121 insertions, 133 deletions
@@ -1970,7 +1970,6 @@ void buffer_render(TextBuffer *buffer, Rect r) { text_state.max_y = y2; float y = render_start_y; - text_chars_begin(font); u32 cursor_line = buffer->cursor_pos.line; for (u32 line = start_line; line < nlines; ++line) { char str[32] = {0}; @@ -1980,11 +1979,10 @@ void buffer_render(TextBuffer *buffer, Rect r) { rgba_u32_to_floats(colors[line == cursor_line ? COLOR_CURSOR_LINE_NUMBER : COLOR_LINE_NUMBERS], text_state.color); text_state.x = x; text_state.y = y; - text_render_chars_utf8(font, &text_state, str); + text_utf8_with_state(font, &text_state, str); y += char_height; if (y > y2) break; } - text_chars_end(font); x1 += line_number_width; x1 += 2; // a little bit of padding @@ -2088,8 +2086,6 @@ void buffer_render(TextBuffer *buffer, Rect r) { buffer->longest_line_on_screen = 0; - text_chars_begin(font); - TextRenderState text_state = text_render_state_default; text_state.x = render_start_x; text_state.y = render_start_y; @@ -2121,12 +2117,12 @@ void buffer_render(TextBuffer *buffer, Rect r) { case '\t': { uint tab_width = settings->tab_width; do { - text_render_char(font, &text_state, ' '); + text_char_with_state(font, &text_state, ' '); ++column; } while (column % tab_width); } break; default: - text_render_char(font, &text_state, c); + text_char_with_state(font, &text_state, c); ++column; break; } @@ -2144,9 +2140,7 @@ void buffer_render(TextBuffer *buffer, Rect r) { arr_free(char_types); - - - text_chars_end(font); + text_render(font); if (buffer == ted->active_buffer) { // render cursor @@ -251,6 +251,12 @@ void command_execute(Ted *ted, Command c, i64 argument) { if (ted->active_node) node_tab_switch(ted, ted->active_node, argument); break; + case CMD_FIND: + ted->find = true; + buffer_clear(&ted->find_buffer); + ted->active_buffer = &ted->find_buffer; + break; + case CMD_ESCAPE: if (*ted->error_shown) { // dismiss error box @@ -45,10 +45,12 @@ ENUM_U16 { CMD_NEW, CMD_UNDO, CMD_REDO, + CMD_QUIT, + CMD_COPY, CMD_CUT, CMD_PASTE, - CMD_QUIT, + CMD_FIND, CMD_TAB_CLOSE, CMD_TAB_SWITCH, // argument = index of tab (starting at 0) @@ -110,6 +112,7 @@ static CommandName const command_names[CMD_COUNT] = { {"copy", CMD_COPY}, {"cut", CMD_CUT}, {"paste", CMD_PASTE}, + {"find", CMD_FIND}, {"tab-close", CMD_TAB_CLOSE}, {"tab-switch", CMD_TAB_SWITCH}, {"tab-next", CMD_TAB_NEXT}, @@ -0,0 +1,28 @@ +static float find_menu_height(Ted *ted) { + Font *font = ted->font, *font_bold = ted->font_bold; + float char_height = text_font_char_height(font), + char_height_bold = text_font_char_height(font_bold); + Settings const *settings = &ted->settings; + float padding = settings->padding; + + return char_height_bold + char_height + 4 * padding; +} + +static void find_menu_frame(Ted *ted) { + Font *font = ted->font, *font_bold = ted->font_bold; + float const char_height = text_font_char_height(font), + char_height_bold = text_font_char_height(font_bold); + + Settings const *settings = &ted->settings; + float const padding = settings->padding; + float const menu_height = find_menu_height(ted); + float const window_width = ted->window_width, window_height = ted->window_height; + u32 const *colors = settings->colors; + + Rect menu_bounds = rect(V2(padding, window_height - menu_height), V2(window_width - 2*padding, menu_height - padding)); + + gl_geometry_rect_border(menu_bounds, 1, colors[COLOR_BORDER]); + + gl_geometry_draw(); + (void)char_height; (void)char_height_bold; +} @@ -51,6 +51,7 @@ no_warn_end #include "menu.c" #include "command.c" #include "config.c" +#include "find.c" #if PROFILE #define PROFILE_TIME(var) double var = time_get_seconds(); @@ -93,6 +94,13 @@ static void APIENTRY gl_message_callback(GLenum source, GLenum type, unsigned in } #endif +static void ted_update_window_dimensions(Ted *ted) { + int w = 0, h = 0; + SDL_GetWindowSize(ted->window, &w, &h); + gl_window_width = ted->window_width = (float)w; + gl_window_height = ted->window_height = (float)h; +} + #if _WIN32 INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow) { @@ -117,41 +125,6 @@ int main(int argc, char **argv) { #endif setlocale(LC_ALL, ""); // allow unicode - int error = 0; - PCRE2_SIZE error_pos = 0; - pcre2_code *regex = pcre2_compile(U"a(.)b", 5, 0, &error, &error_pos, NULL); - if (regex) { - print("Compiled to %p\n",(void *)regex); - pcre2_match_data *match_data = pcre2_match_data_create(100, NULL); - if (match_data) { - int ret = pcre2_match(regex, U"acb", 3, 0, 0, match_data, NULL); - if (ret > 0) { - size_t group_count = (size_t)ret; - PCRE2_SIZE *groups = pcre2_get_ovector_pointer(match_data); - for (size_t i = 0; i < group_count; ++i) { - size_t start = (size_t)groups[2*i]; - size_t end = (size_t)groups[2*i+1]; - print("%zu-%zu\n", start, end); - } - print("Match\n"); - } else { - print("No match\n"); - } - pcre2_match_data_free(match_data); - } else { - print("Couldn't create match data.\n"); - } - pcre2_code_free(regex); - } else { - char32_t buf[256] = {0}; - size_t len = (size_t)pcre2_get_error_message(error, buf, sizeof buf - 1); - char *error_cstr = str32_to_utf8_cstr(str32(buf, len)); - if (error_cstr) { - print("Error %d at %zu: %s\n", error, error_pos, error_cstr); - free(error_cstr); - } - } - // read command-line arguments char const *starting_filename = NULL; @@ -272,6 +245,8 @@ int main(int argc, char **argv) { SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_SHOWN|SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); if (!window) die("%s", SDL_GetError()); + + ted->window = window; { // set icon char icon_filename[TED_PATH_MAX]; @@ -397,15 +372,7 @@ int main(int argc, char **argv) { //printf("%p\n",(void *)ted->drag_buffer); - float window_width = 0, window_height = 0; - { - int window_width_int = 0, window_height_int = 0; - SDL_GetWindowSize(window, &window_width_int, &window_height_int); - ted->window_width = (float)window_width_int; - ted->window_height = (float)window_height_int; - } - window_width = ted->window_width, window_height = ted->window_height; - gl_window_width = window_width, gl_window_height = window_height; + ted_update_window_dimensions(ted); while (SDL_PollEvent(&event)) { TextBuffer *buffer = ted->active_buffer; @@ -582,15 +549,8 @@ int main(int argc, char **argv) { buffer_scroll(active_buffer, +scroll_amount_x, 0); } - // update window dimensions - { - int window_width_int = 0, window_height_int = 0; - SDL_GetWindowSize(window, &window_width_int, &window_height_int); - ted->window_width = (float)window_width_int; - ted->window_height = (float)window_height_int; - } - window_width = ted->window_width, window_height = ted->window_height; - + ted_update_window_dimensions(ted); + float window_width = ted->window_width, window_height = ted->window_height; // set up GL glEnable(GL_BLEND); @@ -603,16 +563,20 @@ int main(int argc, char **argv) { } glClear(GL_COLOR_BUFFER_BIT); - Font *font = ted->font; if (ted->active_node) { float x1 = 25, y1 = 25, x2 = window_width-25, y2 = window_height-25; + if (ted->find) { + find_menu_frame(ted); + if (ted->find) y2 -= find_menu_height(ted); + } Node *node = ted->root; node_frame(ted, node, rect4(x1, y1, x2, y2)); } else { - text_render_anchored(font, "Press Ctrl+O to open a file or Ctrl+N to create a new one.", + text_utf8_anchored(font, "Press Ctrl+O to open a file or Ctrl+N to create a new one.", window_width * 0.5f, window_height * 0.5f, colors[COLOR_TEXT_SECONDARY], ANCHOR_MIDDLE); + text_render(font); } Menu menu = ted->menu; @@ -671,9 +635,12 @@ int main(int argc, char **argv) { TextRenderState text_state = text_render_state_default; text_state.min_x = text_x1; text_state.max_x = text_x2; + text_state.x = text_x1; + text_state.y = text_y1; text_state.wrap = true; rgba_u32_to_floats(colors[COLOR_ERROR_TEXT], text_state.color); - text_render_with_state(font, &text_state, ted->error_shown, text_x1, text_y1); + text_utf8_with_state(font, &text_state, ted->error_shown); + text_render(font); } } @@ -234,10 +234,11 @@ static void menu_render(Ted *ted, Menu menu) { if (menu == MENU_OPEN) { - text_render(font_bold, "Open...", menu_x1, menu_y1, colors[COLOR_TEXT]); + text_utf8(font_bold, "Open...", menu_x1, menu_y1, colors[COLOR_TEXT]); } else if (menu == MENU_SAVE_AS) { - text_render(font_bold, "Save as...", menu_x1, menu_y1, colors[COLOR_TEXT]); + text_utf8(font_bold, "Save as...", menu_x1, menu_y1, colors[COLOR_TEXT]); } + text_render(font_bold); menu_y1 += char_height_bold * 0.75f + padding; @@ -126,7 +126,6 @@ static void node_frame(Ted *ted, Node *node, Rect r) { } TextRenderState text_state = text_render_state_default; - text_chars_begin(font); for (u16 i = 0; i < ntabs; ++i) { TextBuffer *buffer = &ted->buffers[node->tabs[i]]; char tab_title[256]; @@ -150,11 +149,11 @@ static void node_frame(Ted *ted, Node *node, Rect r) { rgba_u32_to_floats(colors[COLOR_TEXT], text_state.color); text_state.x = tab_rect.pos.x; text_state.y = tab_rect.pos.y; - text_render_chars_utf8(font, &text_state, tab_title); + text_utf8_with_state(font, &text_state, tab_title); } gl_geometry_draw(); - text_chars_end(font); + text_render(font); } u16 buffer_index = node->tabs[node->active_tab]; @@ -81,6 +81,7 @@ Ctrl+Shift+s = :save-as Ctrl+q = :quit Ctrl+z = :undo Ctrl+Shift+z = :redo +Ctrl+f = :find Ctrl+c = :copy Ctrl+x = :cut Ctrl+v = :paste @@ -191,6 +191,7 @@ typedef struct Node { // max tabs per node #define TED_MAX_TABS 100 typedef struct Ted { + SDL_Window *window; Font *font_bold; Font *font; TextBuffer *active_buffer; @@ -210,10 +211,12 @@ typedef struct Ted { Menu menu; FileSelector file_selector; TextBuffer line_buffer; // general-purpose line buffer for inputs -- used for menus + TextBuffer find_buffer; // use for "find" term in find/find+replace 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!! + bool find; // is the find menu open? Command warn_unsaved; // if non-zero, the user is trying to execute this command, but there are unsaved changes char warn_unsaved_names[TED_PATH_MAX]; // comma-separated list of files with unsaved changes (only applicable if warn_unsaved != 0) char warn_overwrite[TED_PATH_MAX]; // file name user is trying to overwrite @@ -32,7 +32,6 @@ struct Font { // TTF data (i.e. the contents of the TTF file) u8 *ttf_data; TextTriangle *triangles[CHAR_PAGE_COUNT]; // triangles to render for each page - int curr_page; }; TextRenderState const text_render_state_default = { @@ -182,7 +181,6 @@ Font *text_font_load(char const *ttf_filename, float font_size) { 'a', &x, &y, &q, 1); font->char_width = x; } - font->curr_page = -1; } else { text_set_err("Couldn't read font file."); } @@ -212,15 +210,7 @@ float text_font_char_width(Font *font) { return font->char_width; } -static void text_render_with_page(Font *font, int page) { - font->curr_page = page; -} - -void text_chars_begin(Font *font) { - text_render_with_page(font, 0); // start by loading Latin text -} - -void text_chars_end(Font *font) { +void text_render(Font *font) { for (uint i = 0; i < CHAR_PAGE_COUNT; ++i) { if (font->triangles[i]) { // render these triangles @@ -254,7 +244,7 @@ void text_chars_end(Font *font) { } } -void text_render_char(Font *font, TextRenderState *state, char32_t c) { +void text_char_with_state(Font *font, TextRenderState *state, char32_t c) { top: if (c >= 0x40000 && c < 0xE0000){ // these Unicode code points are currently unassigned. replace them with a Unicode box. @@ -265,8 +255,11 @@ top: if (c >= UNICODE_CODE_POINTS) c = UNICODE_BOX_CHARACTER; // code points this big should never appear in valid Unicode uint page = c / CHAR_PAGE_SIZE; uint index = c % CHAR_PAGE_SIZE; - if (state->render) - text_render_with_page(font, (int)page); + if (state->render) { + if (!font->char_pages[page]) + if (!text_load_char_page(font, (int)page)) + return; + } stbtt_bakedchar *char_data = font->char_pages[page]; float const char_height = font->char_height; if (char_data) { // if page was successfully loaded @@ -317,13 +310,13 @@ top: TextVertex v4 = {{x1, y0}, {s1, t0}, {r, g, b, a}}; TextTriangle triangle1 = {v1, v2, v3}; TextTriangle triangle2 = {v3, v4, v1}; - arr_add(font->triangles[font->curr_page], triangle1); - arr_add(font->triangles[font->curr_page], triangle2); + arr_add(font->triangles[page], triangle1); + arr_add(font->triangles[page], triangle2); } } } -void text_render_chars_utf8(Font *font, TextRenderState *state, char const *str) { +void text_utf8_with_state(Font *font, TextRenderState *state, char const *str) { char const *end = str + strlen(str); while (str != end) { char32_t c = 0; @@ -332,38 +325,31 @@ void text_render_chars_utf8(Font *font, TextRenderState *state, char const *str) break; } else if (ret == (size_t)-1) { // invalid UTF-8 - text_render_char(font, state, '?'); + text_char_with_state(font, state, '?'); ++str; } else { str += ret; - text_render_char(font, state, c); + text_char_with_state(font, state, c); } } } -void text_render_with_state(Font *font, TextRenderState *render_state, char const *text, float x, float y) { - if (render_state->render) text_chars_begin(font); - render_state->x = x; - render_state->y = y; - text_render_chars_utf8(font, render_state, text); - if (render_state->render) text_chars_end(font); - -} - -static void text_render_internal(Font *font, char const *text, float *x, float *y, u32 color, bool render) { +static void text_render_utf8_internal(Font *font, char const *text, float *x, float *y, u32 color, bool render) { TextRenderState render_state = text_render_state_default; render_state.render = render; + render_state.x = *x; + render_state.y = *y; rgba_u32_to_floats(color, render_state.color); - text_render_with_state(font, &render_state, text, *x, *y); + text_utf8_with_state(font, &render_state, text); *x = render_state.x; *y = render_state.y; } -void text_render(Font *font, char const *text, float x, float y, u32 color) { - text_render_internal(font, text, &x, &y, color, true); +void text_utf8(Font *font, char const *text, float x, float y, u32 color) { + text_render_utf8_internal(font, text, &x, &y, color, true); } -void text_render_anchored(Font *font, char const *text, float x, float y, u32 color, Anchor anchor) { +void text_utf8_anchored(Font *font, char const *text, float x, float y, u32 color, Anchor anchor) { float w = 0, h = 0; // width, height of text text_get_size(font, text, &w, &h); float hw = w * 0.5f, hh = h * 0.5f; // half-width, half-height @@ -378,12 +364,12 @@ void text_render_anchored(Font *font, char const *text, float x, float y, u32 co case ANCHOR_BOTTOM_MIDDLE: x -= hw; y -= h; break; case ANCHOR_BOTTOM_RIGHT: x -= w; y -= h; break; } - text_render(font, text, x, y, color); + text_utf8(font, text, x, y, color); } void text_get_size(Font *font, char const *text, float *width, float *height) { float x = 0, y = 0; - text_render_internal(font, text, &x, &y, 0, false); + text_render_utf8_internal(font, text, &x, &y, 0, false); if (width) *width = x; if (height) *height = y + font->char_height; } @@ -392,7 +378,7 @@ void text_get_size32(Font *font, char32_t const *text, u64 len, float *width, fl TextRenderState render_state = text_render_state_default; render_state.render = false; for (u64 i = 0; i < len; ++i) { - text_render_char(font, &render_state, text[i]); + text_char_with_state(font, &render_state, text[i]); } if (width) *width = render_state.x; if (height) *height = render_state.y + font->char_height * (2/3.0f); @@ -4,8 +4,13 @@ #include <uchar.h> // A text-rendering interface. -// You can either use the simple API (text_render) -// or the character-by-character API (text_chars_begin, text_chars_end, text_render_char) +// Example usage: +// Font *font = text_font_load("font.ttf", 18); +// if (font) { +// text_utf8(font, "Hello", 5, 5, 0xFF0000FF); +// text_utf8(font, "Goodbye", 5, 100, 0x00FF00FF); +// text_render(font); +// } typedef struct Font Font; @@ -46,24 +51,16 @@ extern float text_font_char_height(Font *font); // Width of the character 'a' of this font in pixels. // This is meant to be only used for monospace fonts. extern float text_font_char_width(Font *font); -// Render some UTF-8 text to the screen (simple interface). -extern void text_render(Font *font, char const *text, float x, float y, u32 color); // Get the dimensions of some text. extern void text_get_size(Font *font, char const *text, float *width, float *height); extern void text_get_size32(Font *font, char32_t const *text, u64 len, float *width, float *height); -// Write text, but using a state, starting at (x, y) -- state->x and state->y are ignored. This allows you to control min/max_x/y. -extern void text_render_with_state(Font *font, TextRenderState *state, char const *text, float x, float y); -extern void text_render_anchored(Font *font, char const *text, float x, float y, u32 color, Anchor anchor); -// Begin writing characters. -extern void text_chars_begin(Font *font); -// Finish writing characters. -extern void text_chars_end(Font *font); -// Render a single character. -extern void text_render_char(Font *font, TextRenderState *state, char32_t c); -// Render a null-terminated UTF-8 string (must be within text_chars_begin/end). -extern void text_render_chars_utf8(Font *font, TextRenderState *state, char const *str); +extern void text_utf8(Font *font, char const *text, float x, float y, u32 color); +extern void text_utf8_anchored(Font *font, char const *text, float x, float y, u32 color, Anchor anchor); +extern void text_char_with_state(Font *font, TextRenderState *state, char32_t c); +extern void text_utf8_with_state(Font *font, TextRenderState *state, char const *str); // Free memory used by font. extern void text_font_free(Font *font); +extern void text_render(Font *font); // The "default" text rendering state - everything you need to just render text normally. // This lets you do stuff like: @@ -8,7 +8,7 @@ static float file_selector_entries_start_y(Ted const *ted, FileSelector const *f float char_height = text_font_char_height(ted->font); return bounds.pos.y + char_height * 0.75f + padding // make room for cwd - + char_height * 1.75f + padding; // make room for line buffer + + char_height * 1.25f + padding; // make room for line buffer } // number of file entries that can be displayed on the screen @@ -423,7 +423,7 @@ static void file_selector_render(Ted *ted, FileSelector *fs) { rect_coords(bounds, &x1, &y1, &x2, &y2); // current working directory - text_render(font, fs->cwd, x1, y1, colors[COLOR_TEXT]); + text_utf8(font, fs->cwd, x1, y1, colors[COLOR_TEXT]); y1 += char_height + padding; // search buffer @@ -438,7 +438,7 @@ static void file_selector_render(Ted *ted, FileSelector *fs) { Rect r; if (file_selector_entry_pos(ted, fs, i, &r)) { rect_clip_to_rect(&r, text_bounds); - if (rect_contains_point(r, ted->mouse_pos) || + if (rect_contains_point(r, ted->mouse_pos) || ((!fs->create_menu || buffer_empty(&ted->line_buffer)) // only highlight selected for create menus if there is no search term (because that will be the name of the file) && fs->selected == i)) { gl_geometry_rect(r, colors[COLOR_MENU_HL]); @@ -453,7 +453,7 @@ static void file_selector_render(Ted *ted, FileSelector *fs) { text_state.min_y = y1; text_state.max_y = y2; text_state.render = true; - text_chars_begin(font); + // render file names themselves for (u32 i = 0; i < n_entries; ++i) { Rect r; @@ -473,13 +473,12 @@ static void file_selector_render(Ted *ted, FileSelector *fs) { } text_state.x = x; text_state.y = y; rgba_u32_to_floats(colors[color], text_state.color); - text_render_chars_utf8(font, &text_state, entries[i].name); + text_utf8_with_state(font, &text_state, entries[i].name); } } - text_chars_end(font); + text_render(font); } -// make sure you call gl_geometry_draw when you are ready to draw all the buttons that have been rendered! static void button_render(Ted *ted, Rect button, char const *text, u32 color) { u32 const *colors = ted->settings.colors; @@ -493,7 +492,8 @@ static void button_render(Ted *ted, Rect button, char const *text, u32 color) { gl_geometry_draw(); v2 pos = rect_center(button); - text_render_anchored(ted->font, text, pos.x, pos.y, color, ANCHOR_MIDDLE); + text_utf8_anchored(ted->font, text, pos.x, pos.y, color, ANCHOR_MIDDLE); + text_render(ted->font); } // returns true if the button was clicked on. @@ -581,7 +581,7 @@ static void popup_render(Ted *ted, u32 options, char const *title, char const *b v2 title_size = {0}; text_get_size(font_bold, title, &title_size.x, &title_size.y); v2 title_pos = v2_sub(V2(window_width * 0.5f, y), V2(title_size.x * 0.5f, 0)); - text_render(font_bold, title, title_pos.x, title_pos.y, colors[COLOR_TEXT]); + text_utf8(font_bold, title, title_pos.x, title_pos.y, colors[COLOR_TEXT]); // body text float text_x1 = rect_x1(r) + padding; @@ -591,7 +591,10 @@ static void popup_render(Ted *ted, u32 options, char const *title, char const *b state.min_x = text_x1; state.max_x = text_x2; state.wrap = true; + state.x = text_x1; + state.y = y + char_height_bold + padding; rgba_u32_to_floats(colors[COLOR_TEXT], state.color); - text_render_with_state(font, &state, body, text_x1, y + char_height_bold + padding); + text_utf8_with_state(font, &state, body); + text_render(font); } |