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) <noreply@anthropic.com>
This commit is contained in:
78
init.lua
78
init.lua
@@ -478,6 +478,29 @@ local ui = (function()
|
|||||||
{ key="LMB on Layer row", action="Set as active layer" },
|
{ 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
|
-- Helpers
|
||||||
@@ -601,6 +624,33 @@ local ui = (function()
|
|||||||
}
|
}
|
||||||
end
|
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
|
-- Drawing
|
||||||
-- =====================================================================
|
-- =====================================================================
|
||||||
@@ -742,24 +792,6 @@ local ui = (function()
|
|||||||
rgba(COL_TEXT, 0xFF))
|
rgba(COL_TEXT, 0xFF))
|
||||||
end
|
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
|
-- Public API
|
||||||
-- =====================================================================
|
-- =====================================================================
|
||||||
@@ -770,7 +802,10 @@ local ui = (function()
|
|||||||
draw_side_panel(mx, my)
|
draw_side_panel(mx, my)
|
||||||
draw_palette(mx, my)
|
draw_palette(mx, my)
|
||||||
draw_status(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
|
end
|
||||||
|
|
||||||
-- 0.2.0c.3 debug overlay: per-cell slot/rot annotation. Independent
|
-- 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.
|
-- Hit-test mouse click. Returns true if the click was consumed by UI.
|
||||||
-- button: "left" | "right"
|
-- button: "left" | "right"
|
||||||
function M.handle_click(mx, my, button)
|
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
|
-- Top toolbar buttons
|
||||||
for _, b in ipairs(toolbar_buttons_layout()) do
|
for _, b in ipairs(toolbar_buttons_layout()) do
|
||||||
if in_rect(mx, my, b.x, b.y, b.w, b.h) then
|
if in_rect(mx, my, b.x, b.y, b.w, b.h) then
|
||||||
|
|||||||
Reference in New Issue
Block a user