diff options
Diffstat (limited to 'src/app.c')
| -rw-r--r-- | src/app.c | 223 |
1 files changed, 202 insertions, 21 deletions
@@ -770,6 +770,36 @@ static void app_reload_font_if_needed(app_t *app) { app->dirty = 1; } +void app_sync_editor_ui(app_t *app) { + if (!app || !app->ed) return; + + const char *path = app->ed->theme.font_path && app->ed->theme.font_path[0] + ? app->ed->theme.font_path + : app->font_path; + int font_path_mismatch = + path && path[0] && + (!app->font_path[0] || strcmp(app->font_path, path) != 0); + int font_size_mismatch = app->font.size_px != app->ed->theme.font_size; + + if (app->font_revision_seen != app->ed->font_revision || + font_path_mismatch || + font_size_mismatch) { + app_reload_font_if_needed(app); + app->font_revision_seen = app->ed->font_revision; + } + + if (app->ui_revision_seen != app->ed->ui_revision) { + app->ui_revision_seen = app->ed->ui_revision; + app->dirty = 1; + } + + if (app->message_revision_seen != app->ed->message_revision) { + app->message_revision_seen = app->ed->message_revision; + snprintf(app->message, sizeof(app->message), "%s", app->ed->message); + app->dirty = 1; + } +} + static int app_handle_prompt_request(app_t *app) { if (!app || !app->ed) return 0; @@ -797,7 +827,7 @@ static int app_execute_command(app_t *app, const char *command) { int result = ecex_execute_command(app->ed, command); if (result == 0) { - app_reload_font_if_needed(app); + app_sync_editor_ui(app); app_handle_prompt_request(app); } @@ -915,9 +945,7 @@ static void app_submit_prompt(app_t *app) { break; } - if (result == 0) { - app_reload_font_if_needed(app); - } + if (result == 0) app_sync_editor_ui(app); char msg[ECEX_MINIBUFFER_SIZE]; if (result == 0) { @@ -989,12 +1017,15 @@ static void app_enter_prefix(app_t *app, const char *first_key) { app->prefix_len = strlen(app->prefix); app->message[0] = '\0'; + ecex_notify_prefix_hooks(app->ed, app->prefix, ECEX_PREFIX_HOOK_BEGIN); + app_sync_editor_ui(app); app->dirty = 1; } static void app_cancel_prefix(app_t *app) { if (!app) return; + ecex_notify_prefix_hooks(app->ed, app->prefix, ECEX_PREFIX_HOOK_CANCEL); app->mode = APP_MODE_EDIT; app->prefix[0] = '\0'; app->prefix_len = 0; @@ -1032,6 +1063,8 @@ static void app_finish_prefix(app_t *app) { const char *command = ecex_lookup_key_for_buffer(app->ed, ecex_current_buffer(app->ed), app->prefix); if (command) { + ecex_notify_prefix_hooks(app->ed, app->prefix, ECEX_PREFIX_HOOK_FINISH); + app_sync_editor_ui(app); if (app_execute_command(app, command) == 0) { if (app->mode == APP_MODE_EDIT) { char msg[ECEX_MINIBUFFER_SIZE]; @@ -1059,6 +1092,8 @@ static void app_finish_prefix(app_t *app) { } if (key_sequence_has_prefix(app, app->prefix)) { + ecex_notify_prefix_hooks(app->ed, app->prefix, ECEX_PREFIX_HOOK_UPDATE); + app_sync_editor_ui(app); app->dirty = 1; return; } @@ -1066,6 +1101,7 @@ static void app_finish_prefix(app_t *app) { char msg[ECEX_MINIBUFFER_SIZE]; snprintf(msg, sizeof(msg), "Undefined key: %s", app->prefix); + ecex_notify_prefix_hooks(app->ed, app->prefix, ECEX_PREFIX_HOOK_UNDEFINED); app->mode = APP_MODE_EDIT; app->prefix[0] = '\0'; app->prefix_len = 0; @@ -1091,7 +1127,24 @@ static float app_status_height(app_t *app) { return line_h + pad_y * 2.0f; } -static float app_minibuffer_height(app_t *app) { return app_status_height(app); } +static size_t app_minibuffer_row_count(app_t *app) { + if (!app || app->mode != APP_MODE_PREFIX || !app->message[0]) return 1; + + size_t rows = 1; + for (const char *p = app->message; *p; ++p) { + if (*p != '\n') continue; + rows++; + if (rows >= ECEX_MINIBUFFER_MAX_ROWS) return ECEX_MINIBUFFER_MAX_ROWS; + } + return rows; +} + +static float app_minibuffer_height(app_t *app) { + float line_h = app->font.line_height > 1.0f ? app->font.line_height : app->font.size_px * 1.2f; + float pad_y = app->font.size_px * 0.35f; + if (pad_y < 4.0f) pad_y = 4.0f; + return line_h * (float)app_minibuffer_row_count(app) + pad_y * 2.0f; +} static int app_minibuffer_visible(app_t *app) { return app->mode == APP_MODE_MX || app->mode == APP_MODE_PREFIX || @@ -1182,6 +1235,29 @@ static int app_window_rect_at(app_t *app, return 0; } +static int app_page_scroll_rendered_buffer(app_t *app, int direction) { + if (!app || !app->ed || direction == 0) return 0; + + ecex_window_t *win = ecex_current_window(app->ed); + buffer_t *buf = win && win->buffer ? win->buffer : ecex_current_buffer(app->ed); + if (!buf || !buf->render_fn || !(buf->render_flags & ECEX_RENDER_REPLACE_CONTENT)) { + return 0; + } + + size_t step = 1; + + if (direction > 0) { + size_t line_count = buffer_line_count(buf); + size_t max_scroll = line_count > 0 ? line_count - 1 : 0; + buf->scroll_line = buf->scroll_line + step > max_scroll ? max_scroll : buf->scroll_line + step; + } else { + buf->scroll_line = buf->scroll_line > step ? buf->scroll_line - step : 0; + } + + app->dirty = 1; + return 1; +} + static int app_dispatch_buffer_mouse(app_t *app, int event, double px, double py, int button) { if (!app || !app->ed) return 0; @@ -1221,9 +1297,8 @@ static int app_dispatch_buffer_mouse(app_t *app, int event, double px, double py if (local_y < -32768) local_y = -32768; if (app_trace_callbacks_enabled()) { - fprintf(stderr, "ecex-log: mouse_callback buffer=%p fn=%p event=%d x=%d y=%d button=%d userdata=%p\n", - (void *)buf, (void *)buf->mouse_fn, event, local_x, local_y, button, buf->mouse_userdata); - fflush(stderr); + ecex_logf("mouse_callback buffer=%p fn=%p event=%d x=%d y=%d button=%d userdata=%p", + (void *)buf, (void *)buf->mouse_fn, event, local_x, local_y, button, buf->mouse_userdata); } int result = buf->mouse_fn(app->ed, buf, event, local_x, local_y, button, buf->mouse_userdata); @@ -1265,9 +1340,8 @@ static void mouse_button_callback(GLFWwindow *window, int button, int action, in app->mouse_capture_button = ebutton; app->mouse_capture_active = 1; if (app_trace_callbacks_enabled()) { - fprintf(stderr, "ecex-log: mouse_capture_start buffer=%p button=%d\n", - (void *)app->mouse_capture_buffer, ebutton); - fflush(stderr); + ecex_logf("mouse_capture_start buffer=%p button=%d", + (void *)app->mouse_capture_buffer, ebutton); } } return; @@ -1277,9 +1351,8 @@ static void mouse_button_callback(GLFWwindow *window, int button, int action, in app_dispatch_buffer_mouse(app, ECEX_MOUSE_RELEASE, x, y, ebutton); if (!app->mouse_capture_active || app->mouse_capture_button == ebutton) { if (app_trace_callbacks_enabled() && app->mouse_capture_active) { - fprintf(stderr, "ecex-log: mouse_capture_end buffer=%p button=%d\n", - (void *)app->mouse_capture_buffer, ebutton); - fflush(stderr); + ecex_logf("mouse_capture_end buffer=%p button=%d", + (void *)app->mouse_capture_buffer, ebutton); } app->mouse_capture_active = 0; app->mouse_capture_buffer = NULL; @@ -1303,7 +1376,25 @@ static void scroll_callback(GLFWwindow *window, double xoffset, double yoffset) (void)xoffset; app_t *app = glfwGetWindowUserPointer(window); if (!app || !app->ed) return; - buffer_t *buf = ecex_current_buffer(app->ed); + + glfwGetFramebufferSize(window, &app->width, &app->height); + + double x = 0.0; + double y = 0.0; + glfwGetCursorPos(window, &x, &y); + + buffer_t *buf = NULL; + size_t wi = 0; + if (app_window_rect_at(app, x, y, &wi, NULL, NULL, NULL, NULL) && + wi < app->ed->window_count) { + buf = app->ed->windows[wi].buffer; + } + + if (!buf) { + ecex_window_t *win = ecex_current_window(app->ed); + buf = win && win->buffer ? win->buffer : ecex_current_buffer(app->ed); + } + if (!buf) return; if (yoffset < 0.0) buf->scroll_line += 3; else if (yoffset > 0.0) buf->scroll_line = buf->scroll_line > 3 ? buf->scroll_line - 3 : 0; @@ -1407,8 +1498,47 @@ static int key_event_to_string(int key, strncat(prefix, "M-", sizeof(prefix) - strlen(prefix) - 1); } + if (mods & GLFW_MOD_SHIFT) { + if (base[0] >= 'a' && base[0] <= 'z' && base[1] == '\0') { + if (mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER)) { + strncat(prefix, "S-", sizeof(prefix) - strlen(prefix) - 1); + } else { + base[0] = (char)(base[0] - 'a' + 'A'); + } + } else if (base[1] == '\0') { + switch (base[0]) { + case '`': base[0] = '~'; break; + case '1': base[0] = '!'; break; + case '2': base[0] = '@'; break; + case '3': base[0] = '#'; break; + case '4': base[0] = '$'; break; + case '5': base[0] = '%'; break; + case '6': base[0] = '^'; break; + case '7': base[0] = '&'; break; + case '8': base[0] = '*'; break; + case '9': base[0] = '('; break; + case '0': base[0] = ')'; break; + case '-': base[0] = '_'; break; + case '=': base[0] = '+'; break; + case '[': base[0] = '{'; break; + case ']': base[0] = '}'; break; + case '\\': base[0] = '|'; break; + case ';': base[0] = ':'; break; + case '\'': base[0] = '"'; break; + case ',': base[0] = '<'; break; + case '.': base[0] = '>'; break; + case '/': base[0] = '?'; break; + default: + strncat(prefix, "S-", sizeof(prefix) - strlen(prefix) - 1); + break; + } + } else { + strncat(prefix, "S-", sizeof(prefix) - strlen(prefix) - 1); + } + } + if (mods & GLFW_MOD_SUPER) { - strncat(prefix, "S-", sizeof(prefix) - strlen(prefix) - 1); + strncat(prefix, "Super-", sizeof(prefix) - strlen(prefix) - 1); } snprintf(out, out_size, "%s%s", prefix, base); @@ -1440,6 +1570,19 @@ static int key_produces_text(int key, int scancode) { return name && name[0] && name[1] == '\0'; } +static int is_modifier_key(int key) { + return key == GLFW_KEY_LEFT_SHIFT || + key == GLFW_KEY_RIGHT_SHIFT || + key == GLFW_KEY_LEFT_CONTROL || + key == GLFW_KEY_RIGHT_CONTROL || + key == GLFW_KEY_LEFT_ALT || + key == GLFW_KEY_RIGHT_ALT || + key == GLFW_KEY_LEFT_SUPER || + key == GLFW_KEY_RIGHT_SUPER || + key == GLFW_KEY_CAPS_LOCK || + key == GLFW_KEY_NUM_LOCK; +} + static void try_execute_keybind(app_t *app, const char *key_name) { const char *command = ecex_lookup_key_for_buffer(app->ed, ecex_current_buffer(app->ed), key_name); if (!command) return; @@ -1541,13 +1684,46 @@ static void key_callback(GLFWwindow *window, app->current_mods = mods; - if (app->mode == APP_MODE_EDIT && key == GLFW_KEY_F1) { + if (is_modifier_key(key)) { + return; + } + + if ((mods & GLFW_MOD_CONTROL) && + !(mods & (GLFW_MOD_ALT | GLFW_MOD_SHIFT | GLFW_MOD_SUPER)) && + is_layout_char_key(key, scancode, 'g')) { + if (app->mode == APP_MODE_MX) { + app_cancel_mx(app); + } else if (app->mode == APP_MODE_PROMPT) { + app_cancel_prompt(app); + } else if (app->mode == APP_MODE_ISEARCH) { + app_cancel_isearch(app); + } else if (app->mode == APP_MODE_PREFIX) { + app_cancel_prefix(app); + } else { + buffer_t *buf = ecex_current_buffer(app->ed); + if (buf) buffer_clear_mark(buf); + app_message(app, "Quit"); + } + if (key_produces_text(key, scancode)) { + app->suppress_next_char = 1; + } + return; + } + + if (app->mode == APP_MODE_EDIT && + (mods & GLFW_MOD_ALT) && + !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT | GLFW_MOD_SUPER)) && + is_layout_char_key(key, scancode, 'x')) { + if (key_produces_text(key, scancode)) { + app->suppress_next_char = 1; + } app_enter_mx(app); return; } if (app->mode == APP_MODE_EDIT && (mods & GLFW_MOD_ALT) && + !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT | GLFW_MOD_SUPER)) && is_layout_char_key(key, scancode, 'g')) { app_enter_prefix(app, "M-g"); return; @@ -1555,6 +1731,7 @@ static void key_callback(GLFWwindow *window, if (app->mode == APP_MODE_EDIT && (mods & GLFW_MOD_CONTROL) && + !(mods & (GLFW_MOD_ALT | GLFW_MOD_SHIFT | GLFW_MOD_SUPER)) && is_layout_char_key(key, scancode, 'x')) { app_enter_prefix(app, "C-x"); return; @@ -1713,11 +1890,15 @@ static void key_callback(GLFWwindow *window, } } - if (key == GLFW_KEY_ESCAPE) { - glfwSetWindowShouldClose(window, GLFW_TRUE); - glfwPostEmptyEvent(); - return; + if (key == GLFW_KEY_PAGE_DOWN) { + if (app_page_scroll_rendered_buffer(app, 1)) return; + } + + if (key == GLFW_KEY_PAGE_UP) { + if (app_page_scroll_rendered_buffer(app, -1)) return; } + + if (key == GLFW_KEY_ESCAPE) return; } void app_init(app_t *app, ecex_t *ed) { |
