feat: improve MTK input boxes, right-click context menus
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user