feat(map-editor): submenu support — View > Layers, Tools > Mode
One level of submenu nesting. View > Layers exposes all eight terrain-stack layers as toggles, sharing state with the right-side Layers panel. Tools > Mode mirrors the Mode-pill's auto/direct toggle with a Tab hotkey shown on Direct. Submenu opens by clicking the parent item; clicking elsewhere closes everything. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
115
init.lua
115
init.lua
@@ -67,6 +67,7 @@ local state = (function()
|
||||
dirty = false,
|
||||
picker_open = false, -- atlas+tile picker expanded? (legacy 0.1.0)
|
||||
menu_open = nil, -- 0.3.0: nil | <category_name> (e.g. "File")
|
||||
submenu_open = nil, -- 0.3.0: label of the open submenu item within menu_open
|
||||
modal_open = nil, -- 0.3.0: nil | <modal_id> (single-stack)
|
||||
show_layers_panel = true, -- 0.3.0: Window > Layers Panel toggle
|
||||
show_palette = true, -- 0.3.0: Window > Palette toggle
|
||||
@@ -149,7 +150,14 @@ local state = (function()
|
||||
|
||||
function M.set_menu_open(name) state.menu_open = name end
|
||||
function M.get_menu_open() return state.menu_open end
|
||||
function M.close_menu() state.menu_open = nil end
|
||||
function M.close_menu()
|
||||
state.menu_open = nil
|
||||
state.submenu_open = nil
|
||||
end
|
||||
|
||||
function M.set_submenu_open(label) state.submenu_open = label end
|
||||
function M.get_submenu_open() return state.submenu_open end
|
||||
function M.close_submenu() state.submenu_open = nil end
|
||||
|
||||
function M.is_layers_panel_shown() return state.show_layers_panel end
|
||||
function M.toggle_layers_panel() state.show_layers_panel = not state.show_layers_panel end
|
||||
@@ -200,6 +208,7 @@ local state = (function()
|
||||
state.snap_vertex = nil
|
||||
state.drag_paint = nil
|
||||
state.menu_open = nil
|
||||
state.submenu_open = nil
|
||||
state.modal_open = nil
|
||||
state.show_layers_panel = true
|
||||
state.show_palette = true
|
||||
@@ -511,6 +520,42 @@ local ui = (function()
|
||||
{ type = "submenu", label = "Mode", items = nil }, -- populated in T10
|
||||
}
|
||||
|
||||
do
|
||||
local layer_items = {}
|
||||
for _, name in ipairs(LAYER_PANEL_ROWS) do
|
||||
local layer_name = name
|
||||
layer_items[#layer_items + 1] = {
|
||||
type = "toggle",
|
||||
label = name,
|
||||
is_on = function() return state.is_layer_visible(layer_name) end,
|
||||
fire = function() state.toggle_layer_visible(layer_name) end,
|
||||
}
|
||||
end
|
||||
for _, it in ipairs(VIEW_ITEMS) do
|
||||
if it.type == "submenu" and it.label == "Layers" then
|
||||
it.items = layer_items
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local mode_items = {
|
||||
{ type = "toggle", label = "Auto-Tile",
|
||||
is_on = function() return mode_is("auto-tile") end,
|
||||
fire = function() actions.set_mode("auto-tile") end },
|
||||
{ type = "toggle", label = "Direct", hotkey = "Tab",
|
||||
is_on = function() return mode_is("direct") end,
|
||||
fire = function() actions.set_mode("direct") end },
|
||||
}
|
||||
for _, it in ipairs(TOOLS_ITEMS) do
|
||||
if it.type == "submenu" and it.label == "Mode" then
|
||||
it.items = mode_items
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local WINDOW_ITEMS = {
|
||||
{ type = "toggle", label = "Layers Panel",
|
||||
is_on = function() return state.is_layers_panel_shown() end,
|
||||
@@ -768,6 +813,37 @@ local ui = (function()
|
||||
local hovered = in_rect(mx, my, row.x, row.y, row.w, row.h)
|
||||
draw_dropdown_item(row, hovered)
|
||||
end
|
||||
|
||||
-- Submenu render (one level deep)
|
||||
local sub_label = state.get_submenu_open()
|
||||
if sub_label then
|
||||
for _, row in ipairs(dd.rows) do
|
||||
local it = row.item
|
||||
if it.type == "submenu" and it.label == sub_label and it.items then
|
||||
local sub_w = dropdown_width_for(it.items)
|
||||
local sub_x = dd.x + dd.w
|
||||
local sub_y = row.y
|
||||
local sub_h = 0
|
||||
for _, si in ipairs(it.items) do
|
||||
sub_h = sub_h + ((si.type == "separator") and 6 or DROPDOWN_ITEM_H)
|
||||
end
|
||||
sub_h = sub_h + DROPDOWN_PAD_Y * 2
|
||||
engine.render.draw_rect(sub_x, sub_y, sub_w, sub_h,
|
||||
rgba(COL_MODAL_BG, 0xFA))
|
||||
engine.render.draw_rect_lines(sub_x, sub_y, sub_w, sub_h,
|
||||
rgba(COL_MODAL_BORDER, 0xFF))
|
||||
local sy = sub_y + DROPDOWN_PAD_Y
|
||||
for _, si in ipairs(it.items) do
|
||||
local row_h = (si.type == "separator") and 6 or DROPDOWN_ITEM_H
|
||||
local sub_row = { item = si, x = sub_x, y = sy, w = sub_w, h = row_h }
|
||||
local hovered = in_rect(mx, my, sub_row.x, sub_row.y, sub_row.w, sub_row.h)
|
||||
draw_dropdown_item(sub_row, hovered)
|
||||
sy = sy + row_h
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function fire_item(it)
|
||||
@@ -1215,7 +1291,9 @@ local ui = (function()
|
||||
if button == "left" then
|
||||
for _, row in ipairs(dd.rows) do
|
||||
if in_rect(mx, my, row.x, row.y, row.w, row.h) then
|
||||
if row.item.type ~= "separator" then
|
||||
if row.item.type == "submenu" then
|
||||
state.set_submenu_open(row.item.label)
|
||||
elseif row.item.type ~= "separator" then
|
||||
if fire_item(row.item) then
|
||||
state.close_menu()
|
||||
end
|
||||
@@ -1229,6 +1307,39 @@ local ui = (function()
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Submenu items (one level deep)
|
||||
local sub_label = state.get_submenu_open()
|
||||
if sub_label then
|
||||
for _, cat in ipairs(menu_bar_categories_layout()) do
|
||||
if cat.name == open_name then
|
||||
local dd2 = dropdown_layout(cat)
|
||||
for _, row in ipairs(dd2.rows) do
|
||||
local it = row.item
|
||||
if it.type == "submenu" and it.label == sub_label and it.items then
|
||||
local sub_x = dd2.x + dd2.w
|
||||
local sub_y = row.y
|
||||
local sy = sub_y + DROPDOWN_PAD_Y
|
||||
for _, si in ipairs(it.items) do
|
||||
local row_h = (si.type == "separator") and 6 or DROPDOWN_ITEM_H
|
||||
if in_rect(mx, my, sub_x, sy, dropdown_width_for(it.items), row_h) then
|
||||
if si.type ~= "separator" then
|
||||
if fire_item(si) then
|
||||
state.close_menu()
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
sy = sy + row_h
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Click outside the dropdown closes it (no return — fall through so
|
||||
-- the click can also hit other UI like canvas).
|
||||
state.close_menu()
|
||||
|
||||
Reference in New Issue
Block a user