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    fn use_events<F>(&mut self, f: F)
16    where
17        F: FnMut(Event) + Send + 'static;
18
19    fn use_local_events<F>(&mut self, f: F)
20    where
21        F: FnMut(Event) + Send + 'static;
22}
23
24impl UseEvents for Hooks<'_, '_> {
25    fn use_events<F>(&mut self, f: F)
26    where
27        F: FnMut(Event) + Send + 'static,
28    {
29        let h = self.use_hook(move || UseEventsImpl {
30            events: None,
31            component_area: Default::default(),
32            in_component: false,
33            f: None,
34        });
35        h.f = Some(Box::new(f));
36    }
37
38    fn use_local_events<F>(&mut self, f: F)
39    where
40        F: FnMut(Event) + Send + 'static,
41    {
42        let h = self.use_hook(move || UseEventsImpl {
43            events: None,
44            component_area: Default::default(),
45            in_component: true,
46            f: None,
47        });
48        h.f = Some(Box::new(f));
49    }
50}
51
52struct UseEventsImpl {
53    f: Option<Box<dyn FnMut(Event) + Send>>,
54    events: Option<TerminalEvents<Event>>,
55    in_component: bool,
56    component_area: Rect,
57}
58
59impl Hook for UseEventsImpl {
60    fn poll_change(
61        mut self: std::pin::Pin<&mut Self>,
62        cx: &mut std::task::Context,
63    ) -> std::task::Poll<()> {
64        while let Some(Poll::Ready(Some(event))) = self
65            .events
66            .as_mut()
67            .map(|events| pin!(events).poll_next(cx))
68        {
69            let area = self.component_area;
70            let in_component = self.in_component;
71            if let Some(f) = &mut self.f {
72                if in_component {
73                    match event {
74                        Event::Mouse(mouse_event) => {
75                            if mouse_event.row >= area.y && mouse_event.column >= area.x {
76                                let row = mouse_event.row - area.y;
77                                let column = mouse_event.column - area.x;
78
79                                if row < area.height && column < area.width {
80                                    f(event);
81                                }
82                            }
83                        }
84                        _ => {
85                            f(event);
86                        }
87                    }
88                } else {
89                    f(event);
90                }
91            }
92        }
93        Poll::Pending
94    }
95
96    fn post_component_update(&mut self, updater: &mut crate::ComponentUpdater) {
97        if self.events.is_none() {
98            self.events = updater.terminal().events().ok();
99        }
100    }
101
102    fn post_component_draw(&mut self, drawer: &mut crate::ComponentDrawer) {
103        self.component_area = drawer.area;
104    }
105}