From 132dcb648981050990e34a44925e6b54d0dc008c Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Wed, 6 Jan 2021 17:36:07 -0500 Subject: more open menu, border thickness setting, fixed small text clipping issue --- buffer.c | 36 ++++++++++++++++++----------- colors.h | 2 ++ config.c | 6 +++++ filesystem-posix.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ filesystem-win.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ filesystem.c | 19 --------------- main.c | 47 ++++++++++++++++++++++--------------- math.c | 3 ++- menu.c | 55 +++++++++++++++++++++++++++++++++++++++++++ ted.cfg | 4 +++- ted.h | 2 ++ text.c | 8 +++---- util.c | 9 ++++++++ 13 files changed, 264 insertions(+), 58 deletions(-) create mode 100644 filesystem-posix.c create mode 100644 filesystem-win.c delete mode 100644 filesystem.c create mode 100644 menu.c diff --git a/buffer.c b/buffer.c index 3445595..f86eeaa 100644 --- a/buffer.c +++ b/buffer.c @@ -1603,10 +1603,13 @@ void buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float y2) { float char_width = text_font_char_width(font), char_height = text_font_char_height(font); float header_height = char_height; + Ted *ted = buffer->ted; Settings *settings = buffer_settings(buffer); u32 *colors = settings->colors; + float border_thickness = settings->border_thickness; + // get screen coordinates of cursor v2 cursor_display_pos = buffer_pos_to_pixels(buffer, buffer->cursor_pos); // the rectangle that the cursor is rendered as @@ -1614,15 +1617,15 @@ void buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float y2) { u32 border_color = colors[COLOR_BORDER]; // color of border around buffer - // bounding box around buffer & header + // bounding box around buffer + glBegin(GL_QUADS); gl_color_rgba(border_color); - glBegin(GL_LINE_STRIP); - glVertex2f(x1,y1); - glVertex2f(x1,y2); - glVertex2f(x2,y2); - glVertex2f(x2,y1); - glVertex2f(x1-1,y1); + rect_render_border(rect4(x1, y1, x2, y2), border_thickness); glEnd(); + x1 += border_thickness * 0.5f; + y1 += border_thickness * 0.5f; + x2 -= border_thickness * 0.5f; + y2 -= border_thickness * 0.5f; TextRenderState text_state = { .x = 0, .y = 0, @@ -1645,17 +1648,22 @@ void buffer_render(TextBuffer *buffer, float x1, float y1, float x2, float y2) { x = x2 - checksum_w; text_render_with_state(font, &text_state, checksum, x, y); #endif + + y1 += header_height + 0.5f * border_thickness; + + // line separating header from buffer proper + glBegin(GL_QUADS); + gl_color_rgba(border_color); + glVertex2f(x1, y1 - 0.5f * border_thickness); + glVertex2f(x2, y1 - 0.5f * border_thickness); + glVertex2f(x2, y1 + 0.5f * border_thickness); + glVertex2f(x1, y1 + 0.5f * border_thickness); + glEnd(); + y1 += 0.5f * border_thickness; } - y1 += header_height; buffer->x1 = x1; buffer->y1 = y1; buffer->x2 = x2; buffer->y2 = y2; - // line separating header from buffer proper - glBegin(GL_LINES); - gl_color_rgba(border_color); - glVertex2f(x1, y1); - glVertex2f(x2, y1); - glEnd(); // highlight line cursor is on diff --git a/colors.h b/colors.h index c3b978d..d51d7e7 100644 --- a/colors.h +++ b/colors.h @@ -7,6 +7,7 @@ ENUM_U16 { COLOR_BORDER, COLOR_TEXT, COLOR_SELECTION_BG, + COLOR_MENU_BACKDROP, COLOR_MENU_BG, COLOR_COUNT @@ -25,6 +26,7 @@ static ColorName const color_names[COLOR_COUNT] = { {COLOR_BORDER, "border"}, {COLOR_TEXT, "text"}, {COLOR_SELECTION_BG, "selection-bg"}, + {COLOR_MENU_BACKDROP, "menu-backdrop"}, {COLOR_MENU_BG, "menu-bg"} }; diff --git a/config.c b/config.c index 4847aad..57a69f4 100644 --- a/config.c +++ b/config.c @@ -292,6 +292,12 @@ void config_read(Ted *ted, char const *filename) { } else { config_err(cfg, "Invalid text size: %s.", value); } + } else if (streq(key, "border-thickness")) { + if (is_integer && integer > 0 && integer < 30) { + settings->border_thickness = (u8)integer; + } else { + config_err(cfg, "Invalid border thickness: %s.", value); + } } else { config_err(cfg, "Unrecognized core setting: %s.", key); } diff --git a/filesystem-posix.c b/filesystem-posix.c new file mode 100644 index 0000000..4913e30 --- /dev/null +++ b/filesystem-posix.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include + +// Does this file exist? Returns false for directories. +static bool fs_file_exists(char const *path) { + struct stat statbuf = {0}; + if (stat(path, &statbuf) != 0) + return false; + return S_ISREG(statbuf.st_mode); +} + +// Returns a NULL-terminated array of the files in this directory, or NULL if the directory does not exist. +// When you're done with the file names, call free on each one, then on the array. +// NOTE: The files aren't returned in any particular order! +static char **fs_list_directory(char const *dirname) { + char **ret = NULL; + DIR *dir = opendir(dirname); + if (dir) { + struct dirent *ent; + char **filenames = NULL; + size_t nentries = 0; + size_t filename_idx = 0; + + while (readdir(dir)) ++nentries; + rewinddir(dir); + filenames = (char **)calloc(nentries+1, sizeof *filenames); + + while ((ent = readdir(dir))) { + if (ent->d_type == DT_REG) { + char const *filename = ent->d_name; + size_t len = strlen(filename); + char *filename_copy = (char *)malloc(len+1); + if (!filename_copy) break; + strcpy(filename_copy, filename); + if (filename_idx < nentries) // this could actually fail if someone creates files between calculating nentries and here. + filenames[filename_idx++] = filename_copy; + } + } + ret = filenames; + closedir(dir); + } + return ret; +} + +static int fs_mkdir(char const *path) { + if (mkdir(path, 0755) == 0) { + // directory created successfully + return 1; + } else if (errno == EEXIST) { + struct stat statbuf = {0}; + if (stat(path, &statbuf) == 0) { + if (S_ISDIR(statbuf.st_mode)) { + // already exists, and it's a directory + return 0; + } else { + // already exists, but not a directory + return -1; + } + } else { + return -1; + } + } else { + return -1; + } +} diff --git a/filesystem-win.c b/filesystem-win.c new file mode 100644 index 0000000..4a66c09 --- /dev/null +++ b/filesystem-win.c @@ -0,0 +1,63 @@ +// see filesystem-posix.c for function documentation +#include +#include + +static bool fs_file_exists(char const *path) { + struct _stat statbuf = {0}; + if (_stat(path, &statbuf) != 0) + return false; + return statbuf.st_mode == _S_IFREG; +} + +static char **fs_list_directory(char const *dirname) { + char file_pattern[256] = {0}; + char **ret = NULL; + WIN32_FIND_DATA find_data; + HANDLE fhandle; + sprintf_s(file_pattern, sizeof file_pattern, "%s\\*", dirname); + fhandle = FindFirstFileA(file_pattern, &find_data); + if (fhandle != INVALID_HANDLE_VALUE) { + // first, figure out number of files + int nfiles = 1, idx = 0; + char **files; + while (FindNextFile(fhandle, &find_data)) { + if (!(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + ++nfiles; + } + } + FindClose(fhandle); + // now, fill out files array + files = malloc((nfiles + 1) * sizeof *files); + if (files) { + fhandle = FindFirstFileA(file_pattern, &find_data); + if (fhandle != INVALID_HANDLE_VALUE) { + do { + if (!(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + if (idx < nfiles) { + char *dup = _strdup(find_data.cFileName); + if (dup) { + files[idx++] = dup; + } else break; // stop now + } + } + } while (FindNextFile(fhandle, &find_data)); + files[idx] = NULL; + FindClose(fhandle); + ret = files; + } + } + } + return ret; +} + +static int fs_mkdir(char const *path) { + if (CreateDirectoryA(path, NULL)) { + // directory created successfully + return 1; + } else { + if (GetLastError() == ERROR_ALREADY_EXISTS) // directory already exists + return 0; + else + return -1; // some other error + } +} diff --git a/filesystem.c b/filesystem.c deleted file mode 100644 index f68fe72..0000000 --- a/filesystem.c +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include -#if __unix__ -#include -#endif - -static bool fs_file_exists(char const *path) { -#if _WIN32 - struct _stat statbuf = {0}; - if (_stat(path, &statbuf) != 0) - return false; - return statbuf.st_mode == _S_IFREG; -#else - struct stat statbuf = {0}; - if (stat(path, &statbuf) != 0) - return false; - return S_ISREG(statbuf.st_mode); -#endif -} diff --git a/main.c b/main.c index 06269ab..85fe7b3 100644 --- a/main.c +++ b/main.c @@ -21,12 +21,18 @@ no_warn_end #include "util.c" #define MATH_GL #include "math.c" +#if _WIN32 +#include "filesystem-win.c" +#elif __unix__ +#include "filesystem-posix.c" +#else +#error "Unrecognized operating system." +#endif #include "unicode.h" #include "command.h" #include "colors.h" #include "ted.h" -#include "filesystem.c" #include "time.c" #include "string32.c" #include "arr.c" @@ -34,6 +40,7 @@ no_warn_end #include "ted-base.c" #include "command.c" #include "config.c" +#include "menu.c" static void die(char const *fmt, ...) { char buf[256] = {0}; @@ -80,8 +87,12 @@ int main(int argc, char **argv) { // @TODO(windows): GetModuleFileNameW #else ssize_t len = readlink("/proc/self/exe", executable_path, sizeof executable_path - 1); - executable_path[len] = '\0'; - ted_search_cwd = !str_is_prefix(executable_path, "/usr"); + if (len == -1) { + // some posix systems don't have /proc/self/exe. oh well. + } else { + executable_path[len] = '\0'; + ted_search_cwd = !str_is_prefix(executable_path, "/usr"); + } #endif } @@ -173,7 +184,7 @@ int main(int argc, char **argv) { } - u32 *colors = settings->colors; + u32 *colors = settings->colors; (void)colors; Uint32 time_at_last_frame = SDL_GetTicks(); @@ -186,6 +197,14 @@ int main(int argc, char **argv) { //printf("\033[H\033[2J"); #endif + { + 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; + } + float window_width = ted->window_width, window_height = ted->window_height; + SDL_Event event; Uint8 const *keyboard_state = SDL_GetKeyboardState(NULL); @@ -338,14 +357,11 @@ int main(int argc, char **argv) { } - int window_width = 0, window_height = 0; - SDL_GetWindowSize(window, &window_width, &window_height); - float window_widthf = (float)window_width, window_heightf = (float)window_height; // set up GL glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glViewport(0, 0, window_width, window_height); + glViewport(0, 0, (GLsizei)window_width, (GLsizei)window_height); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // pixel coordinates; down is positive y @@ -358,7 +374,7 @@ int main(int argc, char **argv) { glClear(GL_COLOR_BUFFER_BIT); { - float x1 = 50, y1 = 50, x2 = window_widthf-50, y2 = window_heightf-50; + float x1 = 50, y1 = 50, x2 = window_width-50, y2 = window_height-50; buffer_render(&text_buffer, x1, y1, x2, y2); if (text_has_err()) { die("Text error: %s\n", text_get_err()); @@ -366,16 +382,9 @@ int main(int argc, char **argv) { } } - if (ted->menu) { - glBegin(GL_QUADS); - gl_color_rgba(colors[COLOR_MENU_BG]); - rect_render(rect(V2(0, 0), V2(window_widthf, window_heightf))); - glEnd(); - switch (ted->menu) { - case MENU_NONE: assert(0); break; - case MENU_OPEN: - break; - } + Menu menu = ted->menu; + if (menu) { + menu_render(ted, menu); } #if DEBUG diff --git a/math.c b/math.c index d4adf73..c76e3cc 100644 --- a/math.c +++ b/math.c @@ -655,7 +655,8 @@ static void rect_render(Rect r) { glVertex2f(x1, y2); } -static void rect_render_border(Rect r, float border_radius) { +static void rect_render_border(Rect r, float border_thickness) { + float border_radius = border_thickness * 0.5f; float x1 = r.pos.x, y1 = r.pos.y, x2 = x1 + r.size.x, y2 = y1 + r.size.y; //float a = 0.3f; // for debugging diff --git a/menu.c b/menu.c new file mode 100644 index 0000000..6862d0e --- /dev/null +++ b/menu.c @@ -0,0 +1,55 @@ + +static void menu_render(Ted *ted, Menu menu) { + Settings *settings = &ted->settings; + u32 *colors = settings->colors; + Font *font = ted->font; + float window_width = ted->window_width, window_height = ted->window_height; + float char_height = text_font_char_height(font); + + // render backdrop + glBegin(GL_QUADS); + gl_color_rgba(colors[COLOR_MENU_BACKDROP]); + rect_render(rect(V2(0, 0), V2(window_width, window_height))); + glEnd(); + + if (menu == MENU_OPEN) { + char const *directory = "."; + float padding = 20; + 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); + + // menu rectangle & border + glBegin(GL_QUADS); + gl_color_rgba(colors[COLOR_MENU_BG]); + rect_render(menu_rect); + gl_color_rgba(colors[COLOR_BORDER]); + rect_render_border(menu_rect, settings->border_thickness); + glEnd(); + + char **files = fs_list_directory(directory); + if (files) { + u32 nfiles = 0; + for (char **p = files; *p; ++p) ++nfiles; + qsort(files, nfiles, sizeof *files, str_qsort_case_insensitive_cmp); + + { // render file names + float x = menu_x1 + 10, y = menu_y1 + char_height * 0.75f + 10; + TextRenderState text_render_state = {.min_x = menu_x1, .max_x = menu_x2, .min_y = menu_y1, .max_y = menu_y2}; + gl_color_rgba(colors[COLOR_TEXT]); + for (u32 i = 0; i < nfiles; ++i) { + text_render_with_state(font, &text_render_state, files[i], x, y); + y += char_height; + } + } + + for (u32 i = 0; i < nfiles; ++i) free(files[i]); + free(files); + } + + } +} diff --git a/ted.cfg b/ted.cfg index f18dc12..be0ed17 100644 --- a/ted.cfg +++ b/ted.cfg @@ -10,6 +10,7 @@ cursor-blink-time-off = 0.3 # undo the past this many seconds of editing. undo-save-time = 6 text-size = 16 +border-thickness = 1 [keyboard] # motion and selection @@ -68,4 +69,5 @@ text = #fff bg = #001 # The entire screen gets filled with this color when a menu (e.g. the "open" menu) is shown. # By making it transparent, we can dim everything else while the menu is open. -menu-bg = #0004 +menu-backdrop = #0004 +menu-bg = #222 diff --git a/ted.h b/ted.h index 8c475a9..5cc5f4e 100644 --- a/ted.h +++ b/ted.h @@ -8,6 +8,7 @@ typedef struct { u8 tab_width; u8 cursor_width; u8 undo_save_time; + u8 border_thickness; } Settings; #define SCANCODE_COUNT 0x120 // SDL scancodes should be less than this value. @@ -77,6 +78,7 @@ typedef struct Ted { // the old active buffer needs to be restored. that's what this stores. TextBuffer *prev_active_buffer; Settings settings; + float window_width, window_height; Menu menu; KeyAction key_actions[KEY_COMBO_COUNT]; char error[256]; diff --git a/text.c b/text.c index 3044ecb..09b5778 100644 --- a/text.c +++ b/text.c @@ -210,8 +210,8 @@ void text_render_char(Font *font, TextRenderState *state, char32_t c) { } if (x1 >= max_x) { // right side of character is clipped - s1 = (max_x-x0) / (x1-x0) * (s1-s0) + s0; - x1 = max_x; + s1 = (max_x-1-x0) / (x1-x0) * (s1-s0) + s0; + x1 = max_x-1; } if (y0 < min_y) { // top side of character is clipped @@ -220,8 +220,8 @@ void text_render_char(Font *font, TextRenderState *state, char32_t c) { } if (y1 >= max_y) { // bottom side of character is clipped - t1 = (max_y-y0) / (y1-y0) * (t1-t0) + t0; - y1 = max_y; + t1 = (max_y-1-y0) / (y1-y0) * (t1-t0) + t0; + y1 = max_y-1; } glTexCoord2f(s0,t0); glVertex2f(x0,y0); glTexCoord2f(s0,t1); glVertex2f(x0,y1); diff --git a/util.c b/util.c index aa4a737..5bde9b6 100644 --- a/util.c +++ b/util.c @@ -146,3 +146,12 @@ static bool str_satisfies(char const *s, int (*predicate)(int)) { return true; } +// function to be passed into qsort for case insensitive sorting +static int str_qsort_case_insensitive_cmp(const void *av, const void *bv) { + char const *const *a = av, *const *b = bv; +#if _WIN32 + return _stricmp(*a, *b); +#else + return strcasecmp(*a, *b); +#endif +} -- cgit v1.2.3