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
+116
View File
@@ -14,6 +14,7 @@
#include <gui/svg.hpp>
#include <gui/dialogs.hpp>
#include <gui/clipboard.hpp>
#include <gui/mtk/widgets.hpp>
#include <gui/stb_math.h>
extern "C" {
@@ -59,6 +60,14 @@ static constexpr Color STATUS_TEXT = Color::from_rgb(0xE0, 0xE0, 0xE0);
static constexpr Color PATHBAR_BG = Color::from_rgb(0xF0, 0xF0, 0xF0);
static constexpr Color WHITE = Color::from_rgb(0xFF, 0xFF, 0xFF);
enum ContextItem {
CTX_CUT = 0,
CTX_COPY,
CTX_PASTE,
CTX_DELETE,
CTX_COUNT,
};
// ============================================================================
// Global state
// ============================================================================
@@ -101,6 +110,9 @@ int g_sel_end = 0;
bool g_has_selection = false;
bool g_mouse_selecting = false;
// Context menu
mtk::ContextMenuState g_ctx_menu = {};
// Pathbar
bool g_pathbar_open = false;
bool g_pathbar_save = false;
@@ -501,6 +513,57 @@ static bool paste_from_clipboard() {
return true;
}
static bool context_item_enabled(int item) {
int selected = selection_length();
switch (item) {
case CTX_CUT:
case CTX_COPY:
case CTX_DELETE:
return selected > 0;
case CTX_PASTE:
return gui::clipboard_has_text();
default:
return false;
}
}
static void build_context_items(mtk::ContextMenuItem items[CTX_COUNT]) {
items[CTX_CUT] = {"Cut", CTX_CUT, context_item_enabled(CTX_CUT)};
items[CTX_COPY] = {"Copy", CTX_COPY, context_item_enabled(CTX_COPY)};
items[CTX_PASTE] = {"Paste", CTX_PASTE, context_item_enabled(CTX_PASTE)};
items[CTX_DELETE] = {"Delete", CTX_DELETE, context_item_enabled(CTX_DELETE)};
}
static bool selection_contains_pos(int pos) {
if (!g_has_selection) return false;
int ss, se;
sel_range(&ss, &se);
return pos >= ss && pos < se;
}
static void close_context_menu() {
mtk::context_menu_close(g_ctx_menu);
}
static bool perform_context_action(int item) {
if (!context_item_enabled(item)) return false;
switch (item) {
case CTX_CUT:
return cut_selection_to_clipboard();
case CTX_COPY:
return copy_selection_to_clipboard();
case CTX_PASTE:
return paste_from_clipboard();
case CTX_DELETE:
delete_selection();
set_status("Deleted selection");
return true;
default:
return false;
}
}
static bool looks_binary(const char* data, int len) {
if (!data || len <= 0) return false;
@@ -625,6 +688,22 @@ static int hit_test(int mx, int my, int editor_y) {
return g_line_offsets[clicked_line] + clicked_col;
}
static void draw_context_menu(uint32_t* pixels, int W, int H) {
if (!g_ctx_menu.open) return;
Canvas c(pixels, W, H);
mtk::Theme theme = mtk::make_theme(ACCENT);
theme.border = BORDER_COLOR;
theme.text = TEXT_COLOR;
mtk::ContextMenuItem items[CTX_COUNT];
build_context_items(items);
mtk::draw_context_menu(c, g_ctx_menu, items, CTX_COUNT, theme,
mtk::CONTEXT_MENU_DEFAULT_W,
mtk::CONTEXT_MENU_ITEM_H,
g_font, FONT_SIZE);
}
// ============================================================================
// Rendering
// ============================================================================
@@ -880,6 +959,8 @@ static void render(uint32_t* pixels) {
int rw = g_font->measure_text(status_right, FONT_SIZE);
g_font->draw_to_buffer(pixels, W, H, W - rw - 4, sty, status_right, STATUS_TEXT, FONT_SIZE);
}
draw_context_menu(pixels, W, H);
}
// ============================================================================
@@ -948,6 +1029,11 @@ static void open_pathbar_save() {
static bool handle_key(Montauk::KeyEvent& key, int win_id) {
if (!key.pressed) return false;
if (g_ctx_menu.open) {
close_context_menu();
if (key.scancode == 0x01) return true;
}
// ---- Pathbar mode ----
if (g_pathbar_open) {
if (key.ascii == '\n' || key.ascii == '\r') {
@@ -1140,6 +1226,7 @@ static bool handle_mouse(Montauk::WinEvent& ev, int win_id) {
uint8_t btns = ev.mouse.buttons;
uint8_t prev = ev.mouse.prev_buttons;
bool clicked = (btns & 1) && !(prev & 1);
bool right_clicked = (btns & 2) && !(prev & 2);
bool held = (btns & 1) != 0;
bool released = !(btns & 1) && (prev & 1);
int scroll = ev.mouse.scroll;
@@ -1148,6 +1235,22 @@ static bool handle_mouse(Montauk::WinEvent& ev, int win_id) {
int editor_y = TOOLBAR_H + (g_pathbar_open ? PATHBAR_H : 0);
int text_area_h = g_win_h - editor_y - STATUS_H;
if (g_ctx_menu.open) {
if (scroll != 0) {
close_context_menu();
return true;
}
mtk::ContextMenuItem items[CTX_COUNT];
build_context_items(items);
int action = mtk::context_menu_handle_mouse(g_ctx_menu, items, CTX_COUNT,
mx, my, btns, prev,
g_win_w, g_win_h);
if (action >= 0)
perform_context_action(action);
return action != mtk::CONTEXT_MENU_NONE;
}
// ---- Toolbar clicks ----
if (clicked && my < TOOLBAR_H) {
// Open button
@@ -1180,6 +1283,19 @@ static bool handle_mouse(Montauk::WinEvent& ev, int win_id) {
}
// ---- Editor click ----
if (right_clicked && my >= editor_y && my < editor_y + text_area_h
&& mx > LINE_NUM_W && mx < g_win_w - SCROLLBAR_W) {
int pos = hit_test(mx, my, editor_y);
if (!selection_contains_pos(pos)) {
g_cursor_pos = pos;
update_cursor_pos();
clear_selection();
}
g_mouse_selecting = false;
mtk::context_menu_open(g_ctx_menu, mx, my);
return true;
}
if (clicked && my >= editor_y && my < editor_y + text_area_h
&& mx > LINE_NUM_W && mx < g_win_w - SCROLLBAR_W) {
int pos = hit_test(mx, my, editor_y);