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