fix: chromeless suppresses phantom close-button + top-tier modal swallows immediately
- chromeless=true now correctly suppresses the close-button hit-test. Previously, chromeless widgets had an invisible 26x26 close-zone at the top-right that could close the widget on accidental click. Affected map-editor.toolbar: the mode-pill at top-right overlapped the phantom zone and clicking it closed the toolbar. - input_block="all" now swallows outside-clicks immediately, not just when no other widget claims the event. Previously, modal dialogs leaked clicks to lower-tier widgets behind them. - register docstring lists chromeless opt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
29
init.lua
29
init.lua
@@ -384,10 +384,15 @@ end
|
||||
--- 1. Context-menu (if open) always wins.
|
||||
--- 2. Iterate reverse z_tier (top → normal → hud), within tier
|
||||
--- reverse-open-order. For each open window:
|
||||
--- - If input_block="all" AND click is OUTSIDE its bounds: swallow
|
||||
--- the event IMMEDIATELY (modal owns the screen — do not let
|
||||
--- lower-tier widgets claim outside-clicks behind it).
|
||||
--- - input_block="none": skip hit-test entirely (HUD passes through).
|
||||
--- - else: hit-test bounds; on hit route to widget.
|
||||
--- - chromeless=true suppresses the close-button hit-test (the
|
||||
--- widget owns its full bounds, including the top-right area).
|
||||
--- 3. If no window claimed the event AND any open window has
|
||||
--- input_block="all": swallow (modal block).
|
||||
--- input_block="all": swallow (modal block).
|
||||
--- 4. Else drop (no game-routing — module handles its own input).
|
||||
local function _dispatch_event(event)
|
||||
-- Context-menu always wins if open (consumes the next click).
|
||||
@@ -407,6 +412,17 @@ local function _dispatch_event(event)
|
||||
if win and win.open and win.opts.z_tier == tier_name then
|
||||
if win.opts.input_block == "all" then
|
||||
saw_modal = true
|
||||
-- Modal swallow: if the click is outside this modal's
|
||||
-- bounds, swallow immediately so lower-tier widgets
|
||||
-- behind it cannot claim it. Inside-bounds clicks fall
|
||||
-- through to normal routing below.
|
||||
if event.kind == "click" and event.x and event.y then
|
||||
local mb = resolve_bounds(win.opts.layout, sw, sh)
|
||||
if not (event.x >= mb.x and event.x < mb.x + mb.w
|
||||
and event.y >= mb.y and event.y < mb.y + mb.h) then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
if win.opts.input_block ~= "none" then
|
||||
local b = resolve_bounds(win.opts.layout, sw, sh)
|
||||
@@ -416,7 +432,10 @@ local function _dispatch_event(event)
|
||||
and event.y >= b.y and event.y < b.y + b.h then
|
||||
-- Close-button intercept (top-right X): left-click
|
||||
-- closes the window before forwarding to the widget.
|
||||
if event.button == "left" then
|
||||
-- Chromeless widgets have no chrome (no X), so we
|
||||
-- must NOT hit-test the phantom close-zone — the
|
||||
-- widget owns its full bounds.
|
||||
if event.button == "left" and not win.opts.chromeless then
|
||||
local cb = win._close_button_rect
|
||||
if not cb then
|
||||
cb = close_button_rect(b.x, b.y, b.w,
|
||||
@@ -518,6 +537,12 @@ end
|
||||
--- no-op when it's focused (modder must close(id) explicit).
|
||||
--- input_block = "none" | "self" | "all" — defaults per tier:
|
||||
--- hud → "none", normal → "self", top → "all".
|
||||
--- chromeless = false (default) | true — when true, suppresses the
|
||||
--- panel-lib chrome (title-bar, border, X close-button).
|
||||
--- Widget's render(ctx) draws into the full bounds
|
||||
--- without panel-lib decoration. Useful for HUD-style
|
||||
--- widgets that have their own visual design (status
|
||||
--- bars, toolbars with their own theme).
|
||||
function M.register(widget_id, widget_def, opts)
|
||||
if type(widget_id) ~= "string" then
|
||||
error("panel.register: widget_id must be a string, got " .. type(widget_id))
|
||||
|
||||
Reference in New Issue
Block a user