Files
prosperon/clay_input.cm
2025-12-04 21:54:57 -06:00

111 lines
2.9 KiB
Plaintext

// clay_input.cm - Input handling for clay UI
// Separates input concerns from layout/rendering
var geometry = use('geometry')
var point = use('point')
var clay = use('clay')
var clay_input = {}
function rect_contains(node, pos) {
var bb = geometry.rect_move(node.boundingbox, node.config.offset || {x: 0, y: 0})
return geometry.rect_point_inside(bb, pos)
}
function pointer_enabled(node) {
var p = node.config.pointer_events
if (!p || p == 'auto') return true
if (p == 'none') return false
return true
}
function should_skip_children(node) {
var p = node.config.pointer_events
if (p == 'box-only') return true
return false
}
function should_skip_self(node) {
var p = node.config.pointer_events
if (p == 'box-none') return true
return false
}
function find_path(node, path, pos) {
if (!pointer_enabled(node)) return null
if (!rect_contains(node, pos)) return null
var next_path = path.concat(node)
if (node[clay.CHILDREN] && !should_skip_children(node)) {
// Children drawn later should be tested first; reverse if your render order differs
for (var i = node[clay.CHILDREN].length - 1; i >= 0; i--) {
var child = node[clay.CHILDREN][i]
var child_path = find_path(child, next_path, pos)
if (child_path) return child_path
}
}
if (should_skip_self(node)) return null
return next_path
}
clay_input.deepest = function deepest(tree_root, pos) {
var path = find_path(tree_root, [], pos) || []
var deepest = path.length ? path[path.length - 1] : null
return deepest
}
clay_input.bubble = function bubble(deepest, prop) {
var current = deepest
while (current) {
if (current.config && current.config[prop])
return current
current = current[clay.PARENT]
}
return null
}
clay_input.click = function click(tree_root, mousepos, button = 'left') {
var deepest = clay_input.deepest(tree_root, mousepos)
var action_target = clay_input.bubble(deepest, 'action')
if (action_target && action_target.config.action) action_target.config.action()
}
clay_input.get_actionable = function get_actionable(tree_root) {
var actionable = []
function walk(node) {
if (node.config.action) actionable.push(node)
if (node[clay.CHILDREN])
for (var child of node[clay.CHILDREN]) walk(child)
}
walk(tree_root)
return actionable
}
clay_input.filter = function filter(tree_root, predicate) {
var results = []
function rec(node) {
if (predicate(node)) results.push(node)
if (node[clay.CHILDREN])
for (var child of node[clay.CHILDREN]) rec(child)
}
rec(tree_root)
return results
}
clay_input.find_by_id = function find_by_id(tree_root, id) {
function rec(node) {
if (node.id == id) return node
if (node[clay.CHILDREN])
for (var child of node[clay.CHILDREN]) {
var f = rec(child)
if (f) return f
}
return null
}
return rec(tree_root)
}
return clay_input