Merge commit '9b3d3e3' into lang/foundations

This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 13:27:18 +03:00
commit f38581754a
5 changed files with 67 additions and 2 deletions

View file

@ -0,0 +1,55 @@
# ui_pointer_through.ludic - a screen whose root <div> holds only a positioned panel (a component's
# root round a modal) has no size of its own; the pointer looks through it, so the panel's button is
# pressed and hovered - once nothing under it was. A select's < and > answer :hover as elements do.
import "ludic.ui"
program UiPointerThrough {
numbers float
state ThroughState {
got: int = 0
v: int = 1
}
view V (through_st: mut ThroughState) {
v = through_st.v
on hit() { through_st.got += 1 }
on set(x: int) { through_st.v = x }
}
const PAGE: string = "<ui><screen name='s'><div><div style='position: fixed; left: 40px; top: 40px; width: 300px; height: 200px'><button id='b' style='width: 120px; height: 40px' on-click='hit()'>Go</button><select value='{v}' on-change='set(event.value)'><option value='{0}'>A</option><option value='{1}'>B</option></select></div></div></screen></ui>"
function frame(ui_st: mut UiState, x: float, y: float, down: bool) -> UiNode {
let i = new UiInput
i.x = x
i.y = y
i.down = down
ui_input(ui_st, i)
ui_show(ui_st, "s", view_v(), 0.0, 0.0, 400.0, 400.0)
let root: UiNode = ui_nodes(ui_st, "s", view_v())
ui_place(ui_st, root, 0.0, 0.0, 400.0, 400.0)
return root
}
function find(n: UiNode, tag: string, cls: string) -> UiNode {
var has = cls == ""
for i in 0 .. len(n.classes) { if n.classes[i] == cls { has = true } }
if n.tag == tag and has { return n }
for i in 0 .. len(n.children) {
let f = find(n.children[i], tag, cls)
if f != null { return f }
}
return null
}
entry (ui_st: mut UiState, through_st: ThroughState) {
ui_backend(ui_st, new UiBackend)
ui_load_text(ui_st, PAGE, "s.xml")
var root = frame(ui_st, -1.0, -1.0, false)
let b = find(root, "button", "")
let x = b.x + b.cw / 2.0
let y = b.y + b.ch / 2.0
frame(ui_st, x, y, false)
root = frame(ui_st, x, y, false)
let hover = ui_hex(find(root, "button", "").bg)
frame(ui_st, x, y, true)
frame(ui_st, x, y, false)
let nx = find(root, "span", "ui-next")
frame(ui_st, nx.x + nx.cw / 2.0, nx.y + nx.ch / 2.0, false)
root = frame(ui_st, nx.x + nx.cw / 2.0, nx.y + nx.ch / 2.0, false)
print(`pressed {through_st.got}, hovered {hover}; > hovered {find(root, "span", "ui-next").hovered}`)
}
}

View file

@ -7,6 +7,7 @@
function ct_part(ui_st: mut UiState, n: UiNode, cls: string, text: string) -> UiNode {
let p = nd_take(ui_st)
p.key = mm_join(ui_st, n.key, "/", cls)
p.hovered = fr_was_hovered(ui_st, p.key) # a part answers :hover as an element does (a select's < and >)
p.tag = "span"
p.env = n.env
p.sheet = n.sheet

View file

@ -42,8 +42,9 @@ function fr_active(ui_st: UiState, n: UiNode) -> bool {
return ui_st.fr_key_press != "" and lc_mine(ui_st.fr_key_press, n.key)
}
function fr_under(n: UiNode, px: float, py: float, keys: []string) -> void {
if px < n.x or py < n.y or px >= n.x + n.cw or py >= n.y + n.ch { return }
if n.key != "" { ui_kept(keys, n.key) }
let out = px < n.x or py < n.y or px >= n.x + n.cw or py >= n.y + n.ch
if out and not fr_empty(n) { return }
if not out and n.key != "" { ui_kept(keys, n.key) }
for i in 0 .. len(n.children) { fr_under(n.children[i], px, py, keys) }
}
# the control (or pressable element) the pointer is on: out from the topmost element under it

View file

@ -36,10 +36,17 @@ function fr_pick(n: UiNode, px: float, py: float, kind: int) -> UiNode {
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 }

View file

@ -1246,6 +1246,7 @@ function cmd_dev_test() -> int {
controller_case("library/ui_box", "", "scale 1.5 | panel 35,75 150x90 | missing 1", "ui_box.ludic (L11: ui_scale and ui_box, the interface's scale and an element's laid-out box, read from outside)")
controller_case("library/ui_emit_click", "", "got 2 | pressed: got 1", "ui_emit_click.ludic (L11: a component's `emit click` reaches its user's on-click, by the mouse and by a press)")
controller_case("library/ui_select_arrows", "", "prev 0 next next 2", "ui_select_arrows.ludic (L11: a select's < steps back and its > forward)")
controller_case("library/ui_pointer_through", "", "pressed 1, hovered #4a5058; > hovered 1", "ui_pointer_through.ludic (L11: the pointer looks through a root with no size to a positioned panel's button; a select's > answers :hover)")
controller_case("library/ui_submit", "", "[hi] chat '' | [hi][yo][Ada] chat '' name 'Ada'", "ui_submit.ludic (L11: on-submit on a text field by Enter or A, clear-on-submit, the focus kept)")
controller_case("library/ui_zoom", "", "plain 100 pad 10 text 16 h 20 | half 50 pad 5 text 8 h 10 | half of twice 100 | fit 50 em 16", "ui_zoom.ludic (L11: zoom scales a subtree's lengths, text and layout, multiplies, and takes min() and a length over a length)")
controller_case("library/ui_hold", "", "31 frames 500 ms t 500 | 121 frames 500 ms t 500 | pad 16 frames 250 ms t 250 | dududu", "ui_hold.ludic (L11: on-hold every frame a button is held, by the pointer or A, with dt and t on the clock at any rate)")