From 15159d7fd5ffef819271ce9ea32b1545c4a35885 Mon Sep 17 00:00:00 2001 From: Calic Date: Mon, 1 Jun 2026 19:38:42 +0200 Subject: [PATCH] refactor(map-editor): generalize cheatsheet into MODALS framework Introduces a single-stack modal table where each entry declares title, dimensions, render fn and on_click fn. The existing Cheatsheet becomes the framework's first entry; behaviour is preserved exactly. Dispatcher absorbs clicks while any modal is open and routes them to the modal's on_click for close-decision. Co-Authored-By: Claude Opus 4.7 (1M context) --- init.lua | 78 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/init.lua b/init.lua index b379f39..bcae7d3 100644 --- a/init.lua +++ b/init.lua @@ -478,6 +478,29 @@ local ui = (function() { key="LMB on Layer row", action="Set as active layer" }, } + -- ===================================================================== + -- Modal framework (0.3.0) + -- Each modal: { title, w, h, render(mx, my), on_click(mx, my, btn) -> close? } + -- Single-stack: only state.modal_open at a time. Esc + click-outside + -- handled centrally in the dispatcher. + -- ===================================================================== + local MODALS = {} -- populated below + + -- Dispatcher returns true if the click was consumed (always true when a + -- modal is open — modals fully absorb input). + local function dispatch_modal_click(mx, my, button) + local id = state.get_modal_open() + if not id then return false end + local m = MODALS[id] + if not m then + -- Defensive: unknown id, close it. + state.close_modal() + return true + end + local close = m.on_click(mx, my, button) + if close then state.close_modal() end + return true + end -- ===================================================================== -- Helpers @@ -601,6 +624,33 @@ local ui = (function() } end + -- MODALS entries (defined here, after helpers + layout fns are declared, + -- so closures can reference rgba / cheatsheet_layout as upvalues). + MODALS.cheatsheet = { + title = "Hotkeys (I to close)", + w = CHEAT_W, + h = CHEAT_H, + render = function(mx, my) + local c = cheatsheet_layout() + -- Dim the whole screen behind the modal + local screen_w, screen_h = engine.window.size() + engine.render.draw_rect(0, 0, screen_w, screen_h, rgba(0x000000, 0xA0)) + engine.render.draw_rect(c.x, c.y, c.w, c.h, rgba(COL_MODAL_BG, 0xFF)) + engine.render.draw_rect_lines(c.x, c.y, c.w, c.h, rgba(COL_MODAL_BORDER, 0xFF)) + engine.render.draw_text("Hotkeys (I to close)", c.x + 12, c.y + 10, 14, + rgba(COL_TEXT, 0xFF)) + local row_y = c.y + 38 + for _, entry in ipairs(CHEATSHEET) do + engine.render.draw_text(entry.key, c.x + 16, row_y, 12, rgba(COL_TEXT, 0xFF)) + engine.render.draw_text(entry.action, c.x + 140, row_y, 12, rgba(COL_TEXT_MUTED, 0xFF)) + row_y = row_y + 20 + end + end, + on_click = function(mx, my, button) + return true -- any click closes + end, + } + -- ===================================================================== -- Drawing -- ===================================================================== @@ -742,24 +792,6 @@ local ui = (function() rgba(COL_TEXT, 0xFF)) end - local function draw_cheatsheet(mx, my) - if state.get_modal_open() ~= "cheatsheet" then return end - local c = cheatsheet_layout() - -- Dim the whole screen behind the modal - local screen_w, screen_h = engine.window.size() - engine.render.draw_rect(0, 0, screen_w, screen_h, rgba(0x000000, 0xA0)) - engine.render.draw_rect(c.x, c.y, c.w, c.h, rgba(COL_MODAL_BG, 0xFF)) - engine.render.draw_rect_lines(c.x, c.y, c.w, c.h, rgba(COL_MODAL_BORDER, 0xFF)) - engine.render.draw_text("Hotkeys (I to close)", c.x + 12, c.y + 10, 14, - rgba(COL_TEXT, 0xFF)) - local row_y = c.y + 38 - for _, entry in ipairs(CHEATSHEET) do - engine.render.draw_text(entry.key, c.x + 16, row_y, 12, rgba(COL_TEXT, 0xFF)) - engine.render.draw_text(entry.action, c.x + 140, row_y, 12, rgba(COL_TEXT_MUTED, 0xFF)) - row_y = row_y + 20 - end - end - -- ===================================================================== -- Public API -- ===================================================================== @@ -770,7 +802,10 @@ local ui = (function() draw_side_panel(mx, my) draw_palette(mx, my) draw_status(mx, my) - draw_cheatsheet(mx, my) -- last so it overlays everything when open + local mid = state.get_modal_open() + if mid and MODALS[mid] then + MODALS[mid].render(mx, my) + end end -- 0.2.0c.3 debug overlay: per-cell slot/rot annotation. Independent @@ -869,6 +904,11 @@ local ui = (function() -- Hit-test mouse click. Returns true if the click was consumed by UI. -- button: "left" | "right" function M.handle_click(mx, my, button) + -- Modal absorbs all clicks while open. + if dispatch_modal_click(mx, my, button) then + return true + end + -- Top toolbar buttons for _, b in ipairs(toolbar_buttons_layout()) do if in_rect(mx, my, b.x, b.y, b.w, b.h) then