Skip to main content

ratatui_kit/hooks/
use_input.rs

1// 输入层与事件 handler 钩子:取代旧的 `use_events` / `use_local_events`。
2//
3// - [`UseInputLayer::use_input_layer`]:声明一个输入层(模态独占等),返回**同帧**句柄。
4// - [`UseEventHandler::use_event_handler`]:注册一个可消费的事件 handler。
5//
6// 两者均在组件函数体内经 `SystemContext` 当帧登记到 `InputRuntime`(取得守卫即用即弃)。
7// 因此必须在 **context-aware** 的 `Hooks` 上调用:函数组件(`#[component]`)由宏自动
8// `with_context_stack` 升级,开箱即用;**手写 `Component`** 需在 `update` 体内先
9// `let mut hooks = hooks.with_context_stack(updater.component_context_stack());`。
10
11use std::{cell::Cell, rc::Rc};
12
13use crossterm::event::Event;
14use ratatui::layout::Rect;
15
16use super::{Hook, Hooks};
17use crate::{
18    SystemContext, UseContext,
19    input::{CurrentLayer, EventOptions, EventPriority, EventResult, EventScope, InputLayer},
20};
21
22mod private {
23    pub trait Sealed {}
24    impl Sealed for crate::hooks::Hooks<'_, '_> {}
25}
26
27pub trait UseInputLayer: private::Sealed {
28    // 声明一个输入层。`open=true` 时本帧参与分发;`blocks_lower=true` 时作为活跃栈顶截断更低层。
29    //
30    // 返回的 [`InputLayer`] 句柄仅**同帧**有效(每帧重新铸造):可传给子树(`Modal` 的 `layer` prop)
31    // 或本组件 handler 的 [`EventScope::Layer`]。**禁止**存入 `use_state` 跨帧使用。
32    fn use_input_layer(&mut self, open: bool, blocks_lower: bool) -> InputLayer;
33}
34
35pub trait UseEventHandler: private::Sealed {
36    // 注册一个事件 handler。`scope` 决定归属层、`priority` 决定同层投递顺序;闭包返回
37    // [`EventResult::Consumed`] 截断后续 handler。
38    fn use_event_handler<F>(&mut self, scope: EventScope, priority: EventPriority, f: F)
39    where
40        F: FnMut(Event) -> EventResult + 'static;
41
42    // 带选项(如鼠标 `hit_test` 命中过滤)的注册。
43    fn use_event_handler_with_options<F>(
44        &mut self,
45        scope: EventScope,
46        priority: EventPriority,
47        options: EventOptions,
48        f: F,
49    ) where
50        F: FnMut(Event) -> EventResult + 'static;
51}
52
53// `use_input_layer` 的占位 hook:无跨帧状态,仅占用一个稳定的 hook 顺序槽(满足 React 式顺序规则)。
54struct UseInputLayerImpl;
55impl Hook for UseInputLayerImpl {}
56
57impl UseInputLayer for Hooks<'_, '_> {
58    fn use_input_layer(&mut self, open: bool, blocks_lower: bool) -> InputLayer {
59        self.use_hook(|| UseInputLayerImpl); // 占顺序槽
60        // 当帧经 context 直接登记,取得守卫即用即弃。
61        let mut sys = self.use_context_mut::<SystemContext>();
62        sys.input.push_layer(open, blocks_lower)
63    }
64}
65
66// `use_event_handler` 的 hook:跨帧持有 `Rc<Cell<Rect>>`,在 `pre_component_draw` 回填**上一帧** area
67// 供鼠标 `hit_test`。闭包本身不跨帧保存(每帧经 `register_handler` 移交 `InputRuntime`,下帧重建)。
68struct UseEventHandlerImpl {
69    area: Rc<Cell<Rect>>,
70}
71
72impl Hook for UseEventHandlerImpl {
73    fn pre_component_draw(&mut self, drawer: &mut crate::ComponentDrawer) {
74        self.area.set(drawer.area);
75    }
76}
77
78impl UseEventHandler for Hooks<'_, '_> {
79    fn use_event_handler<F>(&mut self, scope: EventScope, priority: EventPriority, f: F)
80    where
81        F: FnMut(Event) -> EventResult + 'static,
82    {
83        self.use_event_handler_with_options(scope, priority, EventOptions::default(), f);
84    }
85
86    fn use_event_handler_with_options<F>(
87        &mut self,
88        scope: EventScope,
89        priority: EventPriority,
90        options: EventOptions,
91        f: F,
92    ) where
93        F: FnMut(Event) -> EventResult + 'static,
94    {
95        // area 共享句柄:跨帧持有(use_hook),交给本帧的 HandlerEntry,pre_component_draw 回填。
96        let area = {
97            let hook = self.use_hook(|| UseEventHandlerImpl {
98                area: Rc::new(Cell::new(Rect::default())),
99            });
100            hook.area.clone()
101        };
102
103        // 归属解析:Global → 无层;Layer(h) → 显式层;Current → context 最近 CurrentLayer,无则 root 层。
104        let layer = match scope {
105            EventScope::Global => None,
106            EventScope::Layer(h) => Some(h.id),
107            EventScope::Current => {
108                let id = self
109                    .try_use_context::<CurrentLayer>()
110                    .map(|c| c.0)
111                    .unwrap_or_else(|| self.use_context::<SystemContext>().input.root_layer());
112                Some(id)
113            }
114        };
115
116        // 当帧登记 handler,守卫即用即弃。
117        let mut sys = self.use_context_mut::<SystemContext>();
118        sys.input
119            .register_handler(layer, priority, options, area, Box::new(f));
120    }
121}