ratatui_kit/hooks/
use_events.rs

1use std::{pin::pin, task::Poll};
2
3use crossterm::event::Event;
4use futures::Stream;
5use ratatui::layout::Rect;
6
7use crate::{Hook, Hooks, TerminalEvents};
8
9mod private {
10    pub trait Sealed {}
11    impl Sealed for crate::hooks::Hooks<'_, '_> {}
12}
13
14pub trait UseEvents: private::Sealed {
15    /// 注册全局事件监听器,适合快捷键、全局输入等场景。
16    fn use_events<F>(&mut self, f: F)
17    where
18        F: FnMut(Event) + Send + 'static;
19
20    /// 注册仅作用于当前组件的事件监听器,适合局部交互。
21    fn use_local_events<F>(&mut self, f: F)
22    where
23        F: FnMut(Event) + Send + 'static;
24}
25
26impl UseEvents for Hooks<'_, '_> {
27    fn use_events<F>(&mut self, f: F)
28    where
29        F: FnMut(Event) + Send + 'static,
30    {
31        let h = self.use_hook(move || UseEventsImpl {
32            events: None,
33            component_area: Default::default(),
34            in_component: false,
35            f: None,
36        });
37        h.f = Some(Box::new(f));
38    }
39
40    fn use_local_events<F>(&mut self, f: F)
41    where
42        F: FnMut(Event) + Send + 'static,
43    {
44        let h = self.use_hook(move || UseEventsImpl {
45            events: None,
46            component_area: Default::default(),
47            in_component: true,
48            f: None,
49        });
50        h.f = Some(Box::new(f));
51    }
52}
53
54struct UseEventsImpl {
55    f: Option<Box<dyn FnMut(Event) + Send>>,
56    events: Option<TerminalEvents<Event>>,
57    in_component: bool,
58    component_area: Rect,
59}
60
61impl Hook for UseEventsImpl {
62    fn poll_change(
63        mut self: std::pin::Pin<&mut Self>,
64        cx: &mut std::task::Context,
65    ) -> std::task::Poll<()> {
66        while let Some(Poll::Ready(Some(event))) = self
67            .events
68            .as_mut()
69            .map(|events| pin!(events).poll_next(cx))
70        {
71            let area = self.component_area;
72            let in_component = self.in_component;
73            if let Some(f) = &mut self.f {
74                if in_component {
75                    match event {
76                        Event::Mouse(mouse_event) => {
77                            if mouse_event.row >= area.y && mouse_event.column >= area.x {
78                                let row = mouse_event.row - area.y;
79                                let column = mouse_event.column - area.x;
80
81                                if row < area.height && column < area.width {
82                                    f(event);
83                                }
84                            }
85                        }
86                        _ => {
87                            f(event);
88                        }
89                    }
90                } else {
91                    f(event);
92                }
93            }
94        }
95        Poll::Pending
96    }
97
98    fn post_component_update(&mut self, updater: &mut crate::ComponentUpdater) {
99        if self.events.is_none() {
100            self.events = updater.terminal().events().ok();
101        }
102    }
103
104    fn pre_component_draw(&mut self, drawer: &mut crate::ComponentDrawer) {
105        self.component_area = drawer.area;
106    }
107}