From the user's play, reproduced on Maroon Lake's customisation screen with the pointer scripted over each control (the lab's R3D_CLICKS): nothing on it was ever pressed or hovered. - A screen's root <div> round a positioned panel (a component's root holding the kit Screen's modal) lays out to 0x0, and the hit test (fr_pick) and :hover (fr_under) only walked into a child whose box held the point - so nothing under it was reachable. A box with no size holds no point and clips nothing: both look through it now (fr_empty), without hovering it. - A control's parts (a select's < and >, a range's thumb) are made by ct_part, which never set `hovered`: `select .ui-prev:hover` could not match anywhere. It is set as an element's is. Golden ui_pointer_through: the panel's button pressed and hovered through the empty root, and a select's > hovered (before this: pressed 0, hovered 0). The 36 ui examples pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
2.5 KiB
Text
60 lines
2.5 KiB
Text
# pointer_pick.ludic - what the pointer is on: the topmost element under it in painting order (a
|
|
# positioned one by its z-index over the flow), and from there out to the nearest element that wants
|
|
# what is asked - a control to press, or pointer events. pointer-events: none lets it through.
|
|
const PK_CONTROL: int = 0
|
|
const PK_POINTER: int = 1
|
|
const PK_WHEEL: int = 2
|
|
function fr_in(n: UiNode, px: float, py: float) -> bool {
|
|
if n.ex != null and n.ex.no_pointer { return false }
|
|
return px >= n.x and py >= n.y and px < n.x + n.cw and py < n.y + n.ch
|
|
}
|
|
# the nearest element that wants `kind`, out from the topmost one under the point: the children are
|
|
# walked the other way from how they are painted (positioned ones by z-index, highest first, then
|
|
# the flow, last first; a popover is on the top layer, not here), with no list made for it
|
|
function fr_pick(n: UiNode, px: float, py: float, kind: int) -> UiNode {
|
|
if n.children == null { return fr_wants(n, kind) }
|
|
var lo = 1000000
|
|
var hi = -1000000
|
|
for i in 0 .. len(n.children) {
|
|
let c = n.children[i]
|
|
if c.pos != 0 and not c.popover {
|
|
lo = Math.min(lo, c.z)
|
|
hi = Math.max(hi, c.z)
|
|
}
|
|
}
|
|
var z = hi
|
|
while z >= lo {
|
|
var i = len(n.children) - 1
|
|
while i >= 0 {
|
|
let c = n.children[i]
|
|
if c.pos != 0 and not c.popover and c.z == z and fr_in(c, px, py) { return fr_pick_in(n, c, px, py, kind) }
|
|
i -= 1
|
|
}
|
|
z -= 1
|
|
}
|
|
var k = len(n.children) - 1
|
|
while k >= 0 {
|
|
let c = n.children[k]
|
|
if c.pos == 0 and not c.popover and fr_in(c, px, py) { return fr_pick_in(n, c, px, py, kind) }
|
|
if c.pos == 0 and not c.popover and fr_empty(c) {
|
|
let r = fr_pick(c, px, py, kind)
|
|
if r != null and r != c { return r }
|
|
}
|
|
k -= 1
|
|
}
|
|
return fr_wants(n, kind)
|
|
}
|
|
# a box with no size of its own - a component's root <div> round a positioned screen - holds no point
|
|
# and clips nothing: what it holds is looked through it (a screen in one was pressed and hovered never)
|
|
function fr_empty(n: UiNode) -> bool { return n.children != null and (n.cw <= 0.0 or n.ch <= 0.0) }
|
|
function fr_pick_in(n: UiNode, c: UiNode, px: float, py: float, kind: int) -> UiNode {
|
|
let r = fr_pick(c, px, py, kind)
|
|
if r != null { return r }
|
|
return fr_wants(n, kind)
|
|
}
|
|
function fr_wants(n: UiNode, kind: int) -> UiNode {
|
|
if kind == PK_CONTROL and (n.focusable or n.act != null) { return n }
|
|
if kind == PK_POINTER and pe_wants(n, false) { return n }
|
|
if kind == PK_WHEEL and pe_wants(n, true) { return n }
|
|
return null
|
|
}
|