ludic/packages/ludic.ui/pointer_events.ludic
Orkuncakilkaya 19fcf60599 wip(0.S3): packages and examples migrated again from their pre-0.S sources in one run
ludic migrate state packages <every example program> packages/ludic.lab/example/plate.ludic
  1804 vars into 126 states, 64 into lets; 23498 edits in 460 files

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 15:31:34 +03:00

83 lines
3.5 KiB
Text

# pointer_events.ludic - on-pointerdown, on-pointermove, on-pointerup, on-wheel and on-drag on any
# element, with event.x / event.y inside it, event.dx / event.dy, event.button and event.wheel; a
# native takes the same through ui_native_input. A press captures the pointer until it is let go.
export property UiPointer {
kind: string = "" # pointerdown, pointermove, pointerup, wheel or drag
x: float = 0.0 # inside the element
y: float = 0.0
dx: float = 0.0 # how far the pointer moved this frame
dy: float = 0.0
button: int = 0 # 0 the left button, 1 the right, 2 the middle
wheel: float = 0.0
}
const PE_NAMES: string = " pointerdown pointermove pointerup wheel drag "
function pe_wants(n: UiNode, wheel: bool) -> bool {
if n.kind == UI_NATIVE and n.native != null and n.native.input != null { return true }
if n.on_names == null { return false }
for i in 0 .. len(n.on_names) {
if wheel and n.on_names[i] == "wheel" { return true }
if not wheel and lk_in(PE_NAMES, n.on_names[i]) { return true }
}
return false
}
# the pointer's events this frame: to what captured it, else to what it is over
function pe_frame(ui_st: mut UiState, root: UiNode, scope: UiNode) -> void {
let i = ui_st.in_now
let p = ui_st.in_last
let moved = p.x >= 0.0 and (i.x != p.x or i.y != p.y)
var over: UiNode = null
if fr_in(scope, i.x, i.y) { over = fr_pick(scope, i.x, i.y, PK_POINTER) }
if ui_st.pe_key != "" {
let c = fr_node(root, ui_st.pe_key)
if c != null and moved { pe_send(ui_st, c, "pointermove", ui_st.pe_btn) }
if c != null and moved { pe_send(ui_st, c, "drag", ui_st.pe_btn) }
if not (i.down or i.down_right or i.down_middle) {
if c != null { pe_send(ui_st, c, "pointerup", ui_st.pe_btn) }
ui_st.pe_key = ""
}
} else if over != null {
let b = pe_pressed(i, p)
if b >= 0 { pe_down(ui_st, over, b) } else if moved { pe_send(ui_st, over, "pointermove", 0) }
}
if i.wheel != 0.0 and over != null { pe_send(ui_st, over, "wheel", 0) }
}
function pe_pressed(i: UiInput, p: UiInput) -> int {
if i.down and not p.down { return 0 }
if i.down_right and not p.down_right { return 1 }
if i.down_middle and not p.down_middle { return 2 }
return -1
}
function pe_down(ui_st: mut UiState, n: UiNode, b: int) -> void {
ui_st.pe_key = n.key
ui_st.pe_btn = b
if not pe_send(ui_st, n, "pointerdown", b) { ui_st.pe_key = "" }
}
# one event: a native's input answers at once (false: not its to take); an element's on-<kind> runs
# once the frame is drawn, with the pointer's fields on `event`
function pe_send(ui_st: mut UiState, n: UiNode, kind: string, b: int) -> bool {
let e = new UiPointer
e.kind = kind
e.x = ui_st.in_now.x - n.x
e.y = ui_st.in_now.y - n.y
if ui_st.in_last.x >= 0.0 {
e.dx = ui_st.in_now.x - ui_st.in_last.x
e.dy = ui_st.in_now.y - ui_st.in_last.y
}
e.button = b
e.wheel = ui_st.in_now.wheel
if n.kind == UI_NATIVE and n.native != null and n.native.input != null { return n.native.input(n, e) }
let v = Value.object()
Value.put(v, "x", Value.float(e.x))
Value.put(v, "y", Value.float(e.y))
Value.put(v, "dx", Value.float(e.dx))
Value.put(v, "dy", Value.float(e.dy))
Value.put(v, "button", Value.int(b))
Value.put(v, "wheel", Value.float(e.wheel))
ui_fire(ui_st, n, kind, v)
return true
}
# a scroll box leaves the wheel to what is under the pointer inside it and takes it itself
function pe_takes_wheel(box: UiNode, px: float, py: float) -> bool {
let t = fr_pick(box, px, py, PK_WHEEL)
return t != null and t != box
}