66 lines
1.7 KiB
Plaintext
66 lines
1.7 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_input = {}
|
|
|
|
// Hit-test boxes against a mouse position
|
|
// boxes: array of box objects from clay.draw()
|
|
// mousepos: {x, y} position to test
|
|
// prev_state: previous input state for tracking changes
|
|
clay_input.hit = function hit(boxes, mouse, prev_state = {}) {
|
|
var mousepos = {...mouse}
|
|
mousepos.x *= 640
|
|
mousepos.y *= 360
|
|
var hovered = null
|
|
var clicked = null
|
|
|
|
// Find the topmost hovered box (iterate in reverse for proper z-order)
|
|
for (var i = boxes.length - 1; i >= 0; i--) {
|
|
var box = boxes[i]
|
|
var boundingbox = geometry.rect_move(box.boundingbox, box.config.offset)
|
|
if (geometry.rect_point_inside(boundingbox, mousepos)) {
|
|
hovered = box
|
|
break
|
|
}
|
|
}
|
|
|
|
// Update hover state
|
|
if (hovered && hovered.config.hovered) {
|
|
hovered.state = hovered.state || {}
|
|
hovered.state.hovered = true
|
|
}
|
|
|
|
// Clear previous hover state if different
|
|
if (prev_state.hovered && prev_state.hovered != hovered) {
|
|
prev_state.hovered.state = prev_state.hovered.state || {}
|
|
prev_state.hovered.state.hovered = false
|
|
}
|
|
|
|
return {
|
|
hovered: hovered,
|
|
clicked: clicked
|
|
}
|
|
}
|
|
|
|
// Handle click events
|
|
clay_input.click = function click(boxes, mousepos, button = 'left') {
|
|
var hit_result = clay_input.hit(boxes, mousepos)
|
|
var clicked = hit_result.hovered
|
|
|
|
if (clicked && clicked.config.action) {
|
|
clicked.config.action()
|
|
return clicked
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
// Get boxes with actions for navigation
|
|
clay_input.get_actionable = function get_actionable(boxes) {
|
|
return boxes.filter(box => box.config.action)
|
|
}
|
|
|
|
return clay_input |