# pointer.ludic - the pointer: what it is over is :hover (and all around it, as in CSS), a press on a # control focuses it and makes it :active, letting go over it is a click. A dragged thumb or scrollbar # keeps the pointer until let go; a press that takes a scrollbar or closes a popover presses nothing else. function fr_was_hovered(ui_st: UiState, key: string) -> bool { return sel_has(ui_st.fr_hover, key) } function fr_pointer(ui_st: mut UiState, root: UiNode) -> void { let i = ui_st.in_now ui_st.fr_down_now = i.down let keys = ui_st.fr_hover_sp # the spare list, emptied; last frame's goes spare List.clear(keys) let top = pp_top(root) var scope = root if top != null { scope = top } fr_under(scope, i.x, i.y, keys) ui_st.fr_hover_sp = ui_st.fr_hover ui_st.fr_hover = keys if top != null and pp_pointer(ui_st, top) { return } if ui_st.sc_drag != "" or ct_key_mouse(ui_st, root) { return } let hit = fr_hit(scope, i.x, i.y) pe_frame(ui_st, root, scope) if i.down and not ui_st.in_was_down { fr_press(ui_st, hit, i.x, i.y) } if i.down and ui_st.fr_press_key != "" { ct_drag(ui_st, root, ui_st.fr_press_key, i.x, i.y) } if not i.down and ui_st.in_was_down { let was = fr_node(root, ui_st.fr_press_key) if was != null { ui_fire(ui_st, was, "up", ui_st.fc_none) } if hit != null and hit.key == ui_st.fr_press_key and hit.enabled { ct_activate_at(ui_st, hit, i.x, i.y) } ui_st.fr_press_key = "" } } function fr_press(ui_st: mut UiState, hit: UiNode, x: float, y: float) -> void { ct_key_pressed_away(ui_st, hit) ui_st.fr_press_key = "" if hit == null { return } ui_st.fr_press_key = hit.key if hit.focusable { ui_focus(ui_st, hit.key) } if hit.enabled { ui_fire(ui_st, hit, "down", ui_st.fc_none) } ct_press(ui_st, hit, x, y) } # :active - the press began on it, or inside it, and is still held (by the pointer, Enter or A) function fr_active(ui_st: UiState, n: UiNode) -> bool { if ui_st.fr_down_now and ui_st.fr_press_key != "" and lc_mine(ui_st.fr_press_key, n.key) { return true } if ui_st.pe_key != "" and lc_mine(ui_st.pe_key, n.key) { return true } 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 { 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 function fr_hit(n: UiNode, px: float, py: float) -> UiNode { if not fr_in(n, px, py) { return null } return fr_pick(n, px, py, PK_CONTROL) } export function ui_hit(root: UiNode, px: float, py: float) -> UiNode { return fr_hit(root, px, py) } function fr_node(n: UiNode, key: string) -> UiNode { if key == "" { return null } if n.key == key { return n } for i in 0 .. len(n.children) { let f = fr_node(n.children[i], key) if f != null { return f } } return null }