Skip to main content

fission_core/input/
mod.rs

1use crate::env::{Clipboard, InteractionStateMap, ScrollStateMap, TextEditStateMap};
2use crate::event::InputEvent;
3use crate::{ActionEnvelope, ActionInput};
4use fission_ir::{CoreIR, NodeId, Op};
5use fission_layout::{LayoutSnapshot, TextMeasurer};
6use std::sync::Arc;
7
8pub mod gesture;
9pub mod hover;
10pub mod slider;
11pub mod text;
12
13pub struct ControllerContext<'a> {
14    pub ir: &'a CoreIR,
15    pub layout: &'a LayoutSnapshot,
16    pub text_edit: &'a mut TextEditStateMap,
17    pub interaction: &'a mut InteractionStateMap,
18    pub scroll: &'a mut ScrollStateMap,
19    pub gesture: &'a mut crate::env::GestureState,
20    pub clipboard: Option<&'a Arc<dyn Clipboard>>,
21    pub measurer: Option<&'a Arc<dyn TextMeasurer>>,
22    // We queue actions here instead of dispatching immediately to keep Controller pure logic
23    pub dispatched_actions: Vec<(NodeId, ActionEnvelope, ActionInput)>,
24}
25
26pub trait InputController {
27    fn handle_event(&mut self, ctx: &mut ControllerContext, event: &InputEvent) -> bool;
28}
29
30pub(crate) fn action_scope_for_node(ir: &CoreIR, node_id: NodeId) -> Option<u128> {
31    let mut current_id = Some(node_id);
32    while let Some(id) = current_id {
33        let Some(node) = ir.nodes.get(&id) else {
34            break;
35        };
36        if let Op::Semantics(semantics) = &node.op {
37            if let Some(scope_id) = semantics.action_scope_id {
38                return Some(scope_id);
39            }
40        }
41        current_id = node.parent;
42    }
43    None
44}
45
46pub(crate) fn scoped_action_input(ir: &CoreIR, target: NodeId, input: ActionInput) -> ActionInput {
47    if let Some(scope_id) = action_scope_for_node(ir, target) {
48        ActionInput::scoped_raw(scope_id, target, input)
49    } else {
50        input
51    }
52}