feat: improve MTK input boxes, right-click context menus

This commit is contained in:
2026-05-06 08:03:14 +02:00
parent f19e97966d
commit 8997164203
15 changed files with 1337 additions and 256 deletions
+1
View File
@@ -542,6 +542,7 @@ void wp_init_empty_document(WordProcessorState* wp) {
wp->line_spacing_dropdown_open = false;
wp->divider_flyout_open = false;
wp->special_char_flyout_open = false;
mtk::context_menu_reset(wp->context_menu);
wp->page_view_mode = false;
wp->pending_page_view_toggle = false;
+99
View File
@@ -65,6 +65,59 @@ static bool wp_paste_from_clipboard(WordProcessorState* wp) {
return true;
}
bool wp_context_item_enabled(WordProcessorState* wp, int item) {
switch (item) {
case WP_CTX_CUT:
case WP_CTX_COPY:
case WP_CTX_DELETE:
return wp && wp->has_selection;
case WP_CTX_PASTE:
return gui::clipboard_has_text();
default:
return false;
}
}
void wp_build_context_items(WordProcessorState* wp,
mtk::ContextMenuItem items[WP_CTX_COUNT]) {
items[WP_CTX_CUT] = {"Cut", WP_CTX_CUT,
wp_context_item_enabled(wp, WP_CTX_CUT)};
items[WP_CTX_COPY] = {"Copy", WP_CTX_COPY,
wp_context_item_enabled(wp, WP_CTX_COPY)};
items[WP_CTX_PASTE] = {"Paste", WP_CTX_PASTE,
wp_context_item_enabled(wp, WP_CTX_PASTE)};
items[WP_CTX_DELETE] = {"Delete", WP_CTX_DELETE,
wp_context_item_enabled(wp, WP_CTX_DELETE)};
}
static bool wp_selection_contains_abs(WordProcessorState* wp, int abs) {
if (!wp || !wp->has_selection) return false;
int sel_s = 0;
int sel_e = 0;
wp_sel_range(wp, &sel_s, &sel_e);
return abs >= sel_s && abs < sel_e;
}
static bool wp_perform_context_action(WordProcessorState* wp, int item) {
if (!wp_context_item_enabled(wp, item)) return false;
switch (item) {
case WP_CTX_CUT:
return wp_cut_selection_to_clipboard(wp);
case WP_CTX_COPY:
return wp_copy_selection_to_clipboard(wp);
case WP_CTX_PASTE:
return wp_paste_from_clipboard(wp);
case WP_CTX_DELETE:
wp_delete_selection(wp);
wp_history_checkpoint(wp);
wp_set_status(wp, "Deleted selection");
return true;
default:
return false;
}
}
static void wp_open_pathbar_for_open(WordProcessorState* wp) {
char path[256] = {};
char msg[160] = {};
@@ -255,6 +308,26 @@ void wp_handle_mouse(const Montauk::WinEvent& ev) {
recompute_w = WP_A4_PAGE_W;
}
if (wp->context_menu.open) {
if (ev.mouse.scroll != 0) {
mtk::context_menu_close(wp->context_menu);
return;
}
mtk::ContextMenuItem items[WP_CTX_COUNT];
wp_build_context_items(wp, items);
int action = mtk::context_menu_handle_mouse(wp->context_menu,
items, WP_CTX_COUNT,
local_x, local_y,
ev.mouse.buttons,
ev.mouse.prev_buttons,
g_win_w, g_win_h);
if (action >= 0)
wp_perform_context_action(wp, action);
if (action != mtk::CONTEXT_MENU_NONE)
return;
}
wp->scrollbar.handle_mouse(local_x, local_y, ev.mouse.buttons, ev.mouse.prev_buttons, ev.mouse.scroll);
if (wp->font_dropdown_open && wp_left_pressed(ev.mouse.buttons, ev.mouse.prev_buttons)) {
@@ -464,6 +537,27 @@ void wp_handle_mouse(const Montauk::WinEvent& ev) {
wp_recompute_wrap(wp, recompute_w, WP_SCREEN_DPI);
bool right_pressed = wp_right_pressed(ev.mouse.buttons, ev.mouse.prev_buttons);
bool in_text_area = local_y >= edit_y && local_y < edit_y + text_area_h &&
local_x >= 0 && local_x < g_win_w - WP_SCROLLBAR_W;
if (in_text_area && wp->page_view_mode) {
in_text_area = local_x >= page_off_x && local_x < page_off_x + WP_A4_PAGE_W &&
local_y >= page_off_y && local_y < page_off_y + WP_A4_PAGE_H;
}
if (right_pressed && in_text_area) {
wp_close_dropdowns(wp);
int abs = wp_hit_test_text(wp, local_x, local_y, edit_y, page_off_x, page_off_y);
if (!wp_selection_contains_abs(wp, abs)) {
wp_pos_to_run(wp, abs, &wp->cursor_run, &wp->cursor_offset);
wp_clear_selection(wp);
}
wp->mouse_selecting = false;
mtk::context_menu_open(wp->context_menu, local_x, local_y);
return;
}
if (wp_left_pressed(ev.mouse.buttons, ev.mouse.prev_buttons) &&
local_y >= edit_y && local_y < edit_y + text_area_h) {
wp_close_dropdowns(wp);
@@ -502,6 +596,11 @@ void wp_handle_key(const Montauk::KeyEvent& key) {
WordProcessorState* wp = &g_wp;
if (!key.pressed) return;
if (wp->context_menu.open) {
mtk::context_menu_close(wp->context_menu);
if (key.scancode == 0x01) return;
}
if (wp->show_pathbar) {
if (key.ascii == '\n' || key.ascii == '\r') {
wp_commit_pathbar(wp);
+17
View File
@@ -35,6 +35,21 @@ static void wp_draw_ui_icon_button(Canvas& c, int x, int y, int w, int h,
wp_draw_ui_button(c, x, y, w, h, fallback, bg, fg, radius);
}
static void wp_draw_context_menu(Canvas& c, WordProcessorState* wp) {
if (!wp || !wp->context_menu.open) return;
mtk::Theme theme = mtk::make_theme(colors::ACCENT);
theme.border = colors::BORDER;
theme.text = colors::TEXT_COLOR;
mtk::ContextMenuItem items[WP_CTX_COUNT];
wp_build_context_items(wp, items);
mtk::draw_context_menu(c, wp->context_menu, items, WP_CTX_COUNT, theme,
mtk::CONTEXT_MENU_DEFAULT_W,
mtk::CONTEXT_MENU_ITEM_H,
g_ui_font, fonts::UI_SIZE);
}
static int wp_special_char_flyout_x() {
int dx = WP_BTN_SECTION_X + 24 - WP_SPECIAL_CHAR_FLYOUT_W;
if (dx + WP_SPECIAL_CHAR_FLYOUT_W > g_win_w) dx = g_win_w - WP_SPECIAL_CHAR_FLYOUT_W;
@@ -666,4 +681,6 @@ void wp_render() {
WP_SPECIAL_CHAR_OPTIONS[i].label, colors::TEXT_COLOR);
}
}
wp_draw_context_menu(c, wp);
}
@@ -13,6 +13,7 @@
#include <gui/standalone.hpp>
#include <gui/truetype.hpp>
#include <gui/svg.hpp>
#include <gui/mtk/widgets.hpp>
extern "C" {
#include <string.h>
@@ -239,6 +240,18 @@ inline bool wp_left_released(uint8_t buttons, uint8_t prev_buttons) {
return !(buttons & 0x01) && (prev_buttons & 0x01);
}
inline bool wp_right_pressed(uint8_t buttons, uint8_t prev_buttons) {
return (buttons & 0x02) && !(prev_buttons & 0x02);
}
enum WpContextItem : int {
WP_CTX_CUT = 0,
WP_CTX_COPY,
WP_CTX_PASTE,
WP_CTX_DELETE,
WP_CTX_COUNT,
};
struct WpScrollbar {
Rect bounds;
int content_height;
@@ -370,6 +383,7 @@ struct WordProcessorState {
bool line_spacing_dropdown_open;
bool divider_flyout_open;
bool special_char_flyout_open;
mtk::ContextMenuState context_menu;
bool page_view_mode;
bool pending_page_view_toggle;
@@ -462,6 +476,9 @@ bool wp_redo(WordProcessorState* wp);
bool wp_print_document(WordProcessorState* wp, char* out_status, int out_status_len);
bool wp_export_pdf_document(WordProcessorState* wp, const char* path, char* out_status, int out_status_len);
bool wp_context_item_enabled(WordProcessorState* wp, int item);
void wp_build_context_items(WordProcessorState* wp,
mtk::ContextMenuItem items[WP_CTX_COUNT]);
void wp_render();
void wp_handle_mouse(const Montauk::WinEvent& ev);