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