ludic/packages/ludic.ui/pointer_events.ludic

87 lines
3.6 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 = ui_st.pe_evs[ui_st.pe_at]
let v = ui_st.pe_vals[ui_st.pe_at]
ui_st.pe_at = (ui_st.pe_at + 1) % PE_RING
e.dx = 0.0
e.dy = 0.0
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) }
Value.be_float(Value.get(v, "x"), e.x)
Value.be_float(Value.get(v, "y"), e.y)
Value.be_float(Value.get(v, "dx"), e.dx)
Value.be_float(Value.get(v, "dy"), e.dy)
Value.be_int(Value.get(v, "button"), b)
Value.be_float(Value.get(v, "wheel"), 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
}