Skip to main content

ratatui_kit/input/
mod.rs

1// 输入事件运行时:单一 raw 事件源 → 中央分发器 `InputRuntime`。
2//
3// 取代旧的「广播订阅」模型(每个 `use_events` 各自订阅、所有 handler 平等收到同一事件)。
4// 核心能力:
5// - **输入层栈**(`InputLayer` + `blocks_lower`):模态层独占输入,背景层被截断。
6// - **事件消费**([`EventResult`]):`Consumed` 截断后续 handler。
7// - **优先级 / 作用域**([`EventPriority`] / [`EventScope`]):分层有序投递。
8// - **每帧重建**:`begin_frame` 在每帧 update 开头清空层与 handler,组件在 update 期间重新登记,
9//   因此关闭的弹窗 / 卸载的组件下一帧自动退出,无跨帧持久状态、无泄漏。
10//
11// 运行时单线程渲染,故 handler 闭包不要求 `Send + Sync`。
12
13use std::{cell::Cell, collections::HashMap, rc::Rc};
14
15use crossterm::event::Event;
16use ratatui::layout::Rect;
17
18// handler 处理事件后的结果。`Default = Ignored`(让事件继续向后传)。
19#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
20pub enum EventResult {
21    // 未消费,继续投递给后续 handler。
22    #[default]
23    Ignored,
24    // 已消费,停止向后续 handler 传播。
25    Consumed,
26}
27
28// 事件投递优先级。同一层内 `High` 先于 `Normal` 先于 `Low`。
29#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
30pub enum EventPriority {
31    Low = 0,
32    #[default]
33    Normal = 1,
34    High = 2,
35}
36
37// 输入层身份。每帧由 [`InputRuntime`] 单调铸造,**跨帧不复用、不稳定**。
38//
39// [`InputLayer`] 句柄仅在**同一帧**内由父组件传给子组件用于 [`EventScope::Layer`] 显式归属;
40// **禁止**存入 `use_state` 跨帧使用(下一帧该 id 已不在层栈,对应 handler 会静默失聪)。
41#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
42pub struct LayerId(u64);
43
44// 用户持有的输入层句柄(`Copy`)。由 `use_input_layer` 返回。
45#[derive(Clone, Copy, PartialEq, Eq, Debug)]
46pub struct InputLayer {
47    pub(crate) id: LayerId,
48}
49
50// handler 的归属作用域。
51#[derive(Clone, Copy, PartialEq, Eq)]
52pub enum EventScope {
53    // 继承 context 栈中最近的 [`CurrentLayer`],无则归属 root 层。
54    // 用于背景组件与 `Modal` 子树内的 handler。
55    Current,
56    // 显式绑定到给定层。用于 handler 注册在 `Modal` 父组件、而 `Modal` 经该句柄开层的场景。
57    Layer(InputLayer),
58    // 真全局:不受任何 `blocks_lower` 截断(如 Resize、全局帮助键)。
59    Global,
60}
61
62// handler 登记选项。
63#[derive(Clone, Copy, Default)]
64pub struct EventOptions {
65    // `true` 时鼠标事件仅在 handler 所属组件区域内命中才调用(键盘等事件不受影响)。
66    // 复刻旧 `use_local_events` 的命中过滤。
67    pub hit_test: bool,
68}
69
70// context 注入项:子树据此把 [`EventScope::Current`] 解析到所属层。
71// 由 `Modal` / `use_input_layer` 经 `update_children(.., Some(Context::owned(CurrentLayer(id))))` 注入。
72#[derive(Clone, Copy)]
73pub(crate) struct CurrentLayer(pub(crate) LayerId);
74
75// 本帧一个输入层的登记。注册序(在 `layers` 中的下标)= update 自顶向下 = z 序:下标越大越靠上。
76struct LayerEntry {
77    id: LayerId,
78    // `true` 时作为活跃栈顶会截断其下所有非 `Global` handler(模态独占)。
79    blocks_lower: bool,
80}
81
82// 本帧一个 handler 的登记。
83struct HandlerEntry {
84    // `None` = Global;`Some` = 归属层(`Current` 已解析为具体 `LayerId`)。
85    layer: Option<LayerId>,
86    priority: EventPriority,
87    // 注册序,作为同层同优先级的稳定 tie-break(自顶向下,父先于子)。
88    order: usize,
89    options: EventOptions,
90    // handler 所属组件区域,由 owning hook 在 `pre_component_draw` 经共享句柄回填(上一帧尺寸)。
91    area: Rc<Cell<Rect>>,
92    f: Box<dyn FnMut(Event) -> EventResult>,
93}
94
95// 中央事件运行时,挂在 `SystemContext` 上。每帧重建层与 handler 表。
96#[derive(Default)]
97pub(crate) struct InputRuntime {
98    layers: Vec<LayerEntry>,
99    handlers: Vec<HandlerEntry>,
100    next_layer_id: u64,
101    root_layer: Option<LayerId>,
102}
103
104impl InputRuntime {
105    // 每帧 update 开始时调用:清空上一帧的层与 handler,铸造并压入 root 层(`blocks_lower=false`)。
106    pub(crate) fn begin_frame(&mut self) {
107        self.layers.clear();
108        self.handlers.clear();
109        let root = self.mint_layer_id();
110        self.root_layer = Some(root);
111        self.layers.push(LayerEntry {
112            id: root,
113            blocks_lower: false,
114        });
115    }
116
117    // 当前帧 root 层 id。`begin_frame` 后必然存在。
118    pub(crate) fn root_layer(&self) -> LayerId {
119        self.root_layer.expect("begin_frame 未调用")
120    }
121
122    fn mint_layer_id(&mut self) -> LayerId {
123        let id = LayerId(self.next_layer_id);
124        self.next_layer_id = self.next_layer_id.wrapping_add(1);
125        id
126    }
127
128    // 组件 update 期登记一个输入层,返回句柄。
129    //
130    // `open=false` 时仍铸造并返回 id(供同帧 `use_event_handler(Layer(h))` 绑定),
131    // 但**不入** `layers` 栈 → 绑定到它的 handler 因不在活跃集而静默跳过。
132    pub(crate) fn push_layer(&mut self, open: bool, blocks_lower: bool) -> InputLayer {
133        let id = self.mint_layer_id();
134        if open {
135            self.layers.push(LayerEntry { id, blocks_lower });
136        }
137        InputLayer { id }
138    }
139
140    // 组件 update 期登记一个 handler。`layer=None` 表示全局 handler。
141    pub(crate) fn register_handler(
142        &mut self,
143        layer: Option<LayerId>,
144        priority: EventPriority,
145        options: EventOptions,
146        area: Rc<Cell<Rect>>,
147        f: Box<dyn FnMut(Event) -> EventResult>,
148    ) {
149        let order = self.handlers.len();
150        self.handlers.push(HandlerEntry {
151            layer,
152            priority,
153            order,
154            options,
155            area,
156            f,
157        });
158    }
159
160    // 在一次 render(update + draw)完整返回后、非借用期调用:把一个 raw 事件分发给本帧 handler。
161    //
162    // 两个 phase:
163    // 1. **Global**:所有 `layer=None` handler,按 `(priority desc, order asc)`,遇 `Consumed` 终止全程。
164    // 2. **层内**:活跃层(从栈顶向下遇首个 `blocks_lower` 截断)的 handler,
165    //    按 `(层 z-order desc, priority desc, order asc)`——**z-order 第一键,不跨层比 priority**,遇 `Consumed` 早停。
166    pub(crate) fn dispatch(&mut self, event: Event) {
167        // 活跃层集:从栈顶(末尾)向下,遇首个 blocks_lower=true 截断(含该层)。
168        let cut = self
169            .layers
170            .iter()
171            .rposition(|e| e.blocks_lower)
172            .unwrap_or(0);
173        // 活跃层 id -> z 序(在 layers 中的下标,越大越靠上)。
174        let active: HashMap<LayerId, usize> = self.layers[cut..]
175            .iter()
176            .enumerate()
177            .map(|(off, e)| (e.id, cut + off))
178            .collect();
179
180        // mem::take 取出 handlers 遍历,消除「持 &mut self.handlers 调闭包」的自借用脆弱性。
181        let mut handlers = std::mem::take(&mut self.handlers);
182
183        // Phase 1:Global handler,按 (priority desc, order asc)。Consumed 即终止全程。
184        let mut global_idx: Vec<usize> = (0..handlers.len())
185            .filter(|&i| handlers[i].layer.is_none())
186            .collect();
187        global_idx.sort_by(|&a, &b| {
188            handlers[b]
189                .priority
190                .cmp(&handlers[a].priority)
191                .then(handlers[a].order.cmp(&handlers[b].order))
192        });
193        if Self::run_handlers(&mut handlers, &global_idx, &event) {
194            return;
195        }
196
197        // Phase 2:活跃层内,按 (z-order desc, priority desc, order asc)。
198        let mut layer_idx: Vec<usize> = (0..handlers.len())
199            .filter(|&i| handlers[i].layer.is_some_and(|l| active.contains_key(&l)))
200            .collect();
201        layer_idx.sort_by(|&a, &b| {
202            let za = active[&handlers[a].layer.unwrap()];
203            let zb = active[&handlers[b].layer.unwrap()];
204            zb.cmp(&za) // z-order 降序:更靠栈顶的层先
205                .then(handlers[b].priority.cmp(&handlers[a].priority)) // priority 降序
206                .then(handlers[a].order.cmp(&handlers[b].order)) // 注册序升序
207        });
208        Self::run_handlers(&mut handlers, &layer_idx, &event);
209        // handlers 在此 drop(即弃);下一帧 begin_frame 后由组件重建。
210    }
211
212    // 按给定顺序依次调用 handler,遇 `Consumed` 早停并返回 `true`
213    // (供 Phase 1 决定是否截断 Phase 2)。
214    fn run_handlers(handlers: &mut [HandlerEntry], order: &[usize], event: &Event) -> bool {
215        for &i in order {
216            if Self::call_handler(&mut handlers[i], event) == EventResult::Consumed {
217                return true;
218            }
219        }
220        false
221    }
222
223    // 调用单个 handler,先做鼠标命中过滤(仅当 `hit_test` 且事件为鼠标事件)。
224    // 区域外视作未调用,返回 `Ignored` 让分发继续下一个候选。
225    fn call_handler(h: &mut HandlerEntry, event: &Event) -> EventResult {
226        if h.options.hit_test
227            && let Event::Mouse(m) = event
228        {
229            let a = h.area.get();
230            let hit = m.column >= a.x
231                && m.column < a.x.saturating_add(a.width)
232                && m.row >= a.y
233                && m.row < a.y.saturating_add(a.height);
234            if !hit {
235                return EventResult::Ignored;
236            }
237        }
238        (h.f)(event.clone())
239    }
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245    use crossterm::event::{
246        KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind,
247    };
248    use std::cell::RefCell;
249
250    type Log = Rc<RefCell<Vec<&'static str>>>;
251
252    fn key() -> Event {
253        Event::Key(KeyEvent::new(KeyCode::Char('x'), KeyModifiers::NONE))
254    }
255
256    fn mouse_at(col: u16, row: u16) -> Event {
257        Event::Mouse(MouseEvent {
258            kind: MouseEventKind::Down(MouseButton::Left),
259            column: col,
260            row,
261            modifiers: KeyModifiers::NONE,
262        })
263    }
264
265    fn full_area() -> Rc<Cell<Rect>> {
266        Rc::new(Cell::new(Rect::new(0, 0, 100, 100)))
267    }
268
269    fn handler(
270        log: &Log,
271        tag: &'static str,
272        result: EventResult,
273    ) -> Box<dyn FnMut(Event) -> EventResult> {
274        let log = log.clone();
275        Box::new(move |_| {
276            log.borrow_mut().push(tag);
277            result
278        })
279    }
280
281    fn opts(hit_test: bool) -> EventOptions {
282        EventOptions { hit_test }
283    }
284
285    // ① blocks_lower 截断背景层
286    #[test]
287    fn blocks_lower_truncates_background() {
288        let log: Log = Default::default();
289        let mut rt = InputRuntime::default();
290        rt.begin_frame();
291        let root = rt.root_layer();
292        rt.register_handler(
293            Some(root),
294            EventPriority::Normal,
295            opts(false),
296            full_area(),
297            handler(&log, "bg", EventResult::Ignored),
298        );
299        let modal = rt.push_layer(true, true);
300        rt.register_handler(
301            Some(modal.id),
302            EventPriority::Normal,
303            opts(false),
304            full_area(),
305            handler(&log, "modal", EventResult::Ignored),
306        );
307        rt.dispatch(key());
308        assert_eq!(*log.borrow(), ["modal"]);
309    }
310
311    // ② 嵌套 blocks_lower 只激活最顶层
312    #[test]
313    fn nested_blocks_lower_activates_only_top() {
314        let log: Log = Default::default();
315        let mut rt = InputRuntime::default();
316        rt.begin_frame();
317        let root = rt.root_layer();
318        rt.register_handler(
319            Some(root),
320            EventPriority::Normal,
321            opts(false),
322            full_area(),
323            handler(&log, "root", EventResult::Ignored),
324        );
325        let l1 = rt.push_layer(true, true);
326        rt.register_handler(
327            Some(l1.id),
328            EventPriority::Normal,
329            opts(false),
330            full_area(),
331            handler(&log, "l1", EventResult::Ignored),
332        );
333        let l2 = rt.push_layer(true, true);
334        rt.register_handler(
335            Some(l2.id),
336            EventPriority::Normal,
337            opts(false),
338            full_area(),
339            handler(&log, "l2", EventResult::Ignored),
340        );
341        rt.dispatch(key());
342        assert_eq!(*log.borrow(), ["l2"]);
343    }
344
345    // ②b 顶部非阻塞层仍活跃,但首个 blocks_lower 以下失活
346    #[test]
347    fn non_blocking_layers_above_blocker_remain_active() {
348        let log: Log = Default::default();
349        let mut rt = InputRuntime::default();
350        rt.begin_frame();
351        let root = rt.root_layer();
352        rt.register_handler(
353            Some(root),
354            EventPriority::Normal,
355            opts(false),
356            full_area(),
357            handler(&log, "root", EventResult::Ignored),
358        );
359        let modal = rt.push_layer(true, true);
360        rt.register_handler(
361            Some(modal.id),
362            EventPriority::Normal,
363            opts(false),
364            full_area(),
365            handler(&log, "modal", EventResult::Ignored),
366        );
367        let toast = rt.push_layer(true, false);
368        rt.register_handler(
369            Some(toast.id),
370            EventPriority::Normal,
371            opts(false),
372            full_area(),
373            handler(&log, "toast", EventResult::Ignored),
374        );
375
376        rt.dispatch(key());
377        assert_eq!(*log.borrow(), ["toast", "modal"]);
378    }
379
380    // ③ Consumed 截断后续 handler
381    #[test]
382    fn consumed_stops_subsequent() {
383        let log: Log = Default::default();
384        let mut rt = InputRuntime::default();
385        rt.begin_frame();
386        let root = rt.root_layer();
387        rt.register_handler(
388            Some(root),
389            EventPriority::Normal,
390            opts(false),
391            full_area(),
392            handler(&log, "first", EventResult::Consumed),
393        );
394        rt.register_handler(
395            Some(root),
396            EventPriority::Normal,
397            opts(false),
398            full_area(),
399            handler(&log, "second", EventResult::Ignored),
400        );
401        rt.dispatch(key());
402        assert_eq!(*log.borrow(), ["first"]);
403    }
404
405    // ④ Ignored 继续传播(同层按注册序)
406    #[test]
407    fn ignored_continues_propagation() {
408        let log: Log = Default::default();
409        let mut rt = InputRuntime::default();
410        rt.begin_frame();
411        let root = rt.root_layer();
412        rt.register_handler(
413            Some(root),
414            EventPriority::Normal,
415            opts(false),
416            full_area(),
417            handler(&log, "first", EventResult::Ignored),
418        );
419        rt.register_handler(
420            Some(root),
421            EventPriority::Normal,
422            opts(false),
423            full_area(),
424            handler(&log, "second", EventResult::Ignored),
425        );
426        rt.dispatch(key());
427        assert_eq!(*log.borrow(), ["first", "second"]);
428    }
429
430    // ⑤ 层 z-order 优先于 priority(下层 High 不抢上层 Normal)
431    #[test]
432    fn layer_z_order_beats_priority() {
433        let log: Log = Default::default();
434        let mut rt = InputRuntime::default();
435        rt.begin_frame();
436        let root = rt.root_layer();
437        rt.register_handler(
438            Some(root),
439            EventPriority::High,
440            opts(false),
441            full_area(),
442            handler(&log, "bg_high", EventResult::Ignored),
443        );
444        let top = rt.push_layer(true, false); // 非阻塞上层
445        rt.register_handler(
446            Some(top.id),
447            EventPriority::Normal,
448            opts(false),
449            full_area(),
450            handler(&log, "top_normal", EventResult::Ignored),
451        );
452        rt.dispatch(key());
453        assert_eq!(*log.borrow(), ["top_normal", "bg_high"]);
454    }
455
456    // ⑥ Global 独立 phase:先跑且可 Consumed 截断
457    #[test]
458    fn global_phase_first_and_can_consume() {
459        let log: Log = Default::default();
460        let mut rt = InputRuntime::default();
461        rt.begin_frame();
462        let root = rt.root_layer();
463        rt.register_handler(
464            None,
465            EventPriority::Normal,
466            opts(false),
467            full_area(),
468            handler(&log, "global", EventResult::Consumed),
469        );
470        rt.register_handler(
471            Some(root),
472            EventPriority::High,
473            opts(false),
474            full_area(),
475            handler(&log, "layer", EventResult::Ignored),
476        );
477        rt.dispatch(key());
478        assert_eq!(*log.borrow(), ["global"]);
479    }
480
481    // ⑥b Global Ignored 不截断(observer 语义)
482    #[test]
483    fn global_ignored_does_not_truncate() {
484        let log: Log = Default::default();
485        let mut rt = InputRuntime::default();
486        rt.begin_frame();
487        let root = rt.root_layer();
488        rt.register_handler(
489            None,
490            EventPriority::Normal,
491            opts(false),
492            full_area(),
493            handler(&log, "global", EventResult::Ignored),
494        );
495        rt.register_handler(
496            Some(root),
497            EventPriority::Normal,
498            opts(false),
499            full_area(),
500            handler(&log, "layer", EventResult::Ignored),
501        );
502        rt.dispatch(key());
503        assert_eq!(*log.borrow(), ["global", "layer"]);
504    }
505
506    // ⑦ handler 绑定 inactive/missing 层时不调用
507    #[test]
508    fn inactive_layer_handler_skipped() {
509        let log: Log = Default::default();
510        let mut rt = InputRuntime::default();
511        rt.begin_frame();
512        let inactive = rt.push_layer(false, true); // open=false → 不入栈
513        rt.register_handler(
514            Some(inactive.id),
515            EventPriority::Normal,
516            opts(false),
517            full_area(),
518            handler(&log, "inactive", EventResult::Ignored),
519        );
520        rt.dispatch(key());
521        assert!(log.borrow().is_empty());
522    }
523
524    // ⑧ hit_test 区域外跳过、区域内命中
525    #[test]
526    fn hit_test_skips_outside_area() {
527        let log: Log = Default::default();
528        let mut rt = InputRuntime::default();
529        rt.begin_frame();
530        let root = rt.root_layer();
531        let area = Rc::new(Cell::new(Rect::new(0, 0, 10, 10)));
532        rt.register_handler(
533            Some(root),
534            EventPriority::Normal,
535            opts(true),
536            area,
537            handler(&log, "hit", EventResult::Consumed),
538        );
539        rt.dispatch(mouse_at(50, 50)); // 区域外
540        assert!(log.borrow().is_empty());
541
542        rt.begin_frame(); // dispatch 已清 handlers,重建
543        let root2 = rt.root_layer();
544        let area2 = Rc::new(Cell::new(Rect::new(0, 0, 10, 10)));
545        rt.register_handler(
546            Some(root2),
547            EventPriority::Normal,
548            opts(true),
549            area2,
550            handler(&log, "hit", EventResult::Consumed),
551        );
552        rt.dispatch(mouse_at(5, 5)); // 命中
553        assert_eq!(*log.borrow(), ["hit"]);
554    }
555}