feat(map-editor): dropdown render for action/modal/toggle/separator

Renders the open dropdown for the active menu category with a
three-column item layout (indicator | label | hotkey). Toggle items
show their bound is_on() state with a [v]/[ ] indicator, modal
items append "...", separators draw as thin rules, disabled items
render in a muted colour. Submenu chevron is wired but submenus
themselves land in a later slice.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Calic
2026-06-01 19:49:53 +02:00
parent 2eebd3d2d5
commit 2c3015685c

View File

@@ -612,6 +612,92 @@ local ui = (function()
return out return out
end end
local function dropdown_width_for(items)
local w = DROPDOWN_MIN_W
for _, it in ipairs(items) do
if it.type ~= "separator" then
local label_w = engine.render.measure_text(it.label or "", 12)
local needed = DROPDOWN_INDICATOR_W + label_w + DROPDOWN_HOTKEY_W + 2 * DROPDOWN_PAD_X
if needed > w then w = needed end
end
end
return w
end
local function dropdown_layout(cat_layout)
local items = cat_layout.items
local w = dropdown_width_for(items)
local rows = {}
local y = cat_layout.y + cat_layout.h + DROPDOWN_PAD_Y
for i, it in ipairs(items) do
local row_h = (it.type == "separator") and 6 or DROPDOWN_ITEM_H
rows[i] = { item = it, x = cat_layout.x, y = y, w = w, h = row_h }
y = y + row_h
end
return {
x = cat_layout.x,
y = cat_layout.y + cat_layout.h + DROPDOWN_PAD_Y,
w = w,
h = y - (cat_layout.y + cat_layout.h + DROPDOWN_PAD_Y) + DROPDOWN_PAD_Y,
rows = rows,
}
end
local function item_is_disabled(item)
local d = item.disabled
if type(d) == "function" then return d() end
return d == true
end
local function draw_dropdown_item(row, hovered)
local it = row.item
if it.type == "separator" then
engine.render.draw_line(row.x + 6, row.y + 3,
row.x + row.w - 6, row.y + 3,
rgba(COL_TEXT_MUTED, 0x60))
return
end
local disabled = item_is_disabled(it)
if hovered and not disabled then
engine.render.draw_rect(row.x, row.y, row.w, row.h,
rgba(COL_ROW_HOVER, 0xFF))
end
local text_col = disabled and COL_TEXT_DISABLED or COL_TEXT
-- Left indicator gutter (toggle: [v] / [ ])
if it.type == "toggle" then
local on = it.is_on and it.is_on() or false
engine.render.draw_text(on and "[v]" or "[ ]",
row.x + 4, row.y + 5, 11,
rgba(on and COL_CHECK or COL_TEXT_MUTED, 0xFF))
end
-- Label
local label = it.label or ""
if it.type == "modal" then label = label .. "..." end
engine.render.draw_text(label,
row.x + DROPDOWN_INDICATOR_W + 2, row.y + 5, 12,
rgba(text_col, 0xFF))
-- Right column: hotkey or submenu chevron
if it.type == "submenu" then
engine.render.draw_text(">",
row.x + row.w - DROPDOWN_PAD_X - 6, row.y + 5, 12,
rgba(COL_TEXT_MUTED, 0xFF))
elseif it.hotkey then
engine.render.draw_text(it.hotkey,
row.x + row.w - DROPDOWN_HOTKEY_W, row.y + 5, 11,
rgba(COL_TEXT_MUTED, 0xFF))
end
end
local function draw_dropdown(cat_layout, mx, my)
local dd = dropdown_layout(cat_layout)
engine.render.draw_rect(dd.x, dd.y, dd.w, dd.h, rgba(COL_MODAL_BG, 0xFA))
engine.render.draw_rect_lines(dd.x, dd.y, dd.w, dd.h, rgba(COL_MODAL_BORDER, 0xFF))
for _, row in ipairs(dd.rows) do
local hovered = in_rect(mx, my, row.x, row.y, row.w, row.h)
draw_dropdown_item(row, hovered)
end
end
local function draw_menu_bar(mx, my) local function draw_menu_bar(mx, my)
local screen_w = engine.window.size() local screen_w = engine.window.size()
engine.render.draw_rect(0, 0, screen_w, MENU_BAR_H, rgba(COL_TOOLBAR_BG, 0xFF)) engine.render.draw_rect(0, 0, screen_w, MENU_BAR_H, rgba(COL_TOOLBAR_BG, 0xFF))
@@ -884,6 +970,15 @@ local ui = (function()
function M.draw() function M.draw()
local mx, my = engine.input.get_mouse_pos() local mx, my = engine.input.get_mouse_pos()
draw_menu_bar(mx, my) draw_menu_bar(mx, my)
local open_name = state.get_menu_open()
if open_name then
for _, cat in ipairs(menu_bar_categories_layout()) do
if cat.name == open_name then
draw_dropdown(cat, mx, my)
break
end
end
end
draw_top_toolbar(mx, my) draw_top_toolbar(mx, my)
draw_mode_pill() draw_mode_pill()
draw_side_panel(mx, my) draw_side_panel(mx, my)