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:
27
init.lua
27
init.lua
@@ -384,8 +384,13 @@ end
|
|||||||
--- 1. Context-menu (if open) always wins.
|
--- 1. Context-menu (if open) always wins.
|
||||||
--- 2. Iterate reverse z_tier (top → normal → hud), within tier
|
--- 2. Iterate reverse z_tier (top → normal → hud), within tier
|
||||||
--- reverse-open-order. For each open window:
|
--- 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).
|
--- - input_block="none": skip hit-test entirely (HUD passes through).
|
||||||
--- - else: hit-test bounds; on hit route to widget.
|
--- - 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
|
--- 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).
|
--- 4. Else drop (no game-routing — module handles its own input).
|
||||||
@@ -407,6 +412,17 @@ local function _dispatch_event(event)
|
|||||||
if win and win.open and win.opts.z_tier == tier_name then
|
if win and win.open and win.opts.z_tier == tier_name then
|
||||||
if win.opts.input_block == "all" then
|
if win.opts.input_block == "all" then
|
||||||
saw_modal = true
|
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
|
end
|
||||||
if win.opts.input_block ~= "none" then
|
if win.opts.input_block ~= "none" then
|
||||||
local b = resolve_bounds(win.opts.layout, sw, sh)
|
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
|
and event.y >= b.y and event.y < b.y + b.h then
|
||||||
-- Close-button intercept (top-right X): left-click
|
-- Close-button intercept (top-right X): left-click
|
||||||
-- closes the window before forwarding to the widget.
|
-- 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
|
local cb = win._close_button_rect
|
||||||
if not cb then
|
if not cb then
|
||||||
cb = close_button_rect(b.x, b.y, b.w,
|
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).
|
--- no-op when it's focused (modder must close(id) explicit).
|
||||||
--- input_block = "none" | "self" | "all" — defaults per tier:
|
--- input_block = "none" | "self" | "all" — defaults per tier:
|
||||||
--- hud → "none", normal → "self", top → "all".
|
--- 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)
|
function M.register(widget_id, widget_def, opts)
|
||||||
if type(widget_id) ~= "string" then
|
if type(widget_id) ~= "string" then
|
||||||
error("panel.register: widget_id must be a string, got " .. type(widget_id))
|
error("panel.register: widget_id must be a string, got " .. type(widget_id))
|
||||||
|
|||||||
Reference in New Issue
Block a user