rat_event/lib.rs
1#![doc = include_str!("../readme.md")]
2
3use std::cmp::max;
4
5pub mod crossterm;
6pub mod util;
7
8/// All the regular and expected event-handling a widget can do.
9///
10/// All the normal key-handling dependent on an internal focus-state
11/// All the mouse-handling.
12#[derive(Debug, Default, Clone, Copy)]
13pub struct Regular;
14
15/// Handle mouse-events only. Useful whenever you want to write new key-bindings,
16/// but keep the mouse-events.
17#[derive(Debug, Default, Clone, Copy)]
18pub struct MouseOnly;
19
20/// Popup/Overlays are a bit difficult to handle, as there is no z-order/area tree,
21/// which would direct mouse interactions. We can simulate a z-order in the
22/// event-handler by trying the things with a higher z-order first.
23///
24/// If a widget should be seen as pure overlay, it would define only a Popup
25/// event-handler. In the event-handling functions you would call all Popup
26/// event-handlers before the regular ones.
27///
28/// Example:
29/// * Context menu. If the context-menu is active, it can consume all mouse-events
30/// that fall into its range, and the widgets behind it only get the rest.
31/// * Menubar. Would define _two_ event-handlers, a regular one for all events
32/// on the main menu bar, and a popup event-handler for the menus. The event-handling
33/// function calls the popup handler first and the regular one at some time later.
34#[derive(Debug, Default, Clone, Copy)]
35pub struct Popup;
36
37/// Event-handling for a dialog like widget.
38///
39/// Similar to [Popup] but with the extra that it consumes _all_ events when active.
40/// No regular widget gets any event, and we have modal behaviour.
41#[derive(Debug, Default, Clone, Copy)]
42pub struct Dialog;
43
44/// Event-handler for double-click on a widget.
45///
46/// Events for this handler must be processed *before* calling
47/// any other event-handling routines for the same widget.
48/// Otherwise, the regular event-handling might interfere with
49/// recognition of double-clicks by consuming the first click.
50///
51/// This event-handler doesn't consume the first click, just
52/// the second one.
53#[derive(Debug, Default, Clone, Copy)]
54pub struct DoubleClick;
55
56///
57/// A very broad trait for an event handler.
58///
59/// Ratatui widgets have two separate structs, one that implements
60/// Widget/StatefulWidget and the associated State. As the StatefulWidget
61/// has a lifetime and is not meant to be kept, HandleEvent should be
62/// implemented for the state struct. It can then modify some state and
63/// the tui can be rendered anew with that changed state.
64///
65/// HandleEvent is not limited to State structs of course, any Type
66/// that wants to interact with events can implement it.
67///
68/// * Event - The actual event type.
69/// * Qualifier - The qualifier allows creating more than one event-handler
70/// for a widget.
71///
72/// This can be used as a variant of type-state, where the type given
73/// selects the widget's behaviour, or to give some external context
74/// to the widget, or to write your own key-bindings for a widget.
75///
76/// * R - Result of event-handling. This can give information to the
77/// application what changed due to handling the event. This can
78/// be very specific for each widget, but there is one general [Outcome]
79/// that describes a minimal set of results.
80///
81/// There should be one value that indicates 'I don't know this event'.
82/// This is expressed with the ConsumedEvent trait.
83///
84pub trait HandleEvent<Event, Qualifier, Return>
85where
86 Return: ConsumedEvent,
87{
88 /// Handle an event.
89 ///
90 /// * self - The widget state.
91 /// * event - Event type.
92 /// * qualifier - Event handling qualifier.
93 /// This library defines some standard values [Regular], [MouseOnly].
94 /// Further ideas:
95 /// * ReadOnly
96 /// * Special behaviour like DoubleClick, HotKey.
97 /// * Returns some result, see [Outcome]
98 fn handle(&mut self, event: &Event, qualifier: Qualifier) -> Return;
99}
100
101/// Catch all event-handler for the null state `()`.
102impl<E, Q> HandleEvent<E, Q, Outcome> for () {
103 fn handle(&mut self, _event: &E, _qualifier: Q) -> Outcome {
104 Outcome::Continue
105 }
106}
107
108/// When calling multiple event-handlers, the minimum information required
109/// from the result is 'has consumed/didn't consume' the event.
110///
111/// The event-handler **may** also react to the event and not call it
112/// 'consuming the event'. But this is tricky, non-obvious and frowned upon.
113/// The caller **may** also just ignore the fact.
114///
115/// See also [flow] and [try_flow] and the extra [break_flow].
116pub trait ConsumedEvent {
117 /// Is this the 'consumed' result.
118 fn is_consumed(&self) -> bool;
119
120 /// Or-Else chaining with `is_consumed()` as the split.
121 #[inline(always)]
122 fn or_else<F>(self, f: F) -> Self
123 where
124 F: FnOnce() -> Self,
125 Self: Sized,
126 {
127 if self.is_consumed() { self } else { f() }
128 }
129
130 /// Or-Else chaining with `is_consumed()` as the split.
131 #[inline(always)]
132 fn or_else_try<F, E>(self, f: F) -> Result<Self, E>
133 where
134 Self: Sized,
135 F: FnOnce() -> Result<Self, E>,
136 {
137 if self.is_consumed() {
138 Ok(self)
139 } else {
140 Ok(f()?)
141 }
142 }
143
144 /// And_then-chaining based on is_consumed().
145 /// Returns max(self, f()).
146 #[inline(always)]
147 fn and_then<F>(self, f: F) -> Self
148 where
149 Self: Sized + Ord,
150 F: FnOnce() -> Self,
151 {
152 if self.is_consumed() {
153 max(self, f())
154 } else {
155 self
156 }
157 }
158
159 /// And_then-chaining based on is_consumed().
160 /// Returns max(self, f()).
161 #[inline(always)]
162 fn and_then_try<F, E>(self, f: F) -> Result<Self, E>
163 where
164 Self: Sized + Ord,
165 F: FnOnce() -> Result<Self, E>,
166 {
167 if self.is_consumed() {
168 Ok(max(self, f()?))
169 } else {
170 Ok(self)
171 }
172 }
173
174 /// Then-chaining. Returns max(self, f()).
175 #[inline(always)]
176 #[deprecated(since = "1.2.2", note = "use and_then()")]
177 fn and<F>(self, f: F) -> Self
178 where
179 Self: Sized + Ord,
180 F: FnOnce() -> Self,
181 {
182 if self.is_consumed() {
183 max(self, f())
184 } else {
185 self
186 }
187 }
188
189 /// Then-chaining. Returns max(self, f()).
190 #[inline(always)]
191 #[deprecated(since = "1.2.2", note = "use and_then_try()")]
192 fn and_try<F, E>(self, f: F) -> Result<Self, E>
193 where
194 Self: Sized + Ord,
195 F: FnOnce() -> Result<Self, E>,
196 {
197 if self.is_consumed() {
198 Ok(max(self, f()?))
199 } else {
200 Ok(self)
201 }
202 }
203}
204
205impl<V, E> ConsumedEvent for Result<V, E>
206where
207 V: ConsumedEvent,
208{
209 fn is_consumed(&self) -> bool {
210 match self {
211 Ok(v) => v.is_consumed(),
212 Err(_) => true,
213 }
214 }
215}
216
217/// The baseline outcome for an event-handler.
218///
219/// A widget can define its own type, if it has more things to report.
220/// It would be nice if those types are convertible to/from `Outcome`
221/// and implement `ConsumedEvent` as well.
222#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
223pub enum Outcome {
224 /// The given event has not been used at all.
225 #[default]
226 Continue,
227 /// The event has been recognized, but nothing noticeable has changed.
228 /// Further processing for this event may stop.
229 /// Rendering the ui is not necessary.
230 Unchanged,
231 /// The event has been recognized and there is some change due to it.
232 /// Further processing for this event may stop.
233 /// Rendering the ui is advised.
234 Changed,
235}
236
237impl ConsumedEvent for Outcome {
238 fn is_consumed(&self) -> bool {
239 *self != Outcome::Continue
240 }
241}
242
243/// Widgets often define functions that return bool to indicate a changed state.
244/// This converts `true` / `false` to `Outcome::Changed` / `Outcome::Unchanged`.
245impl From<bool> for Outcome {
246 fn from(value: bool) -> Self {
247 if value {
248 Outcome::Changed
249 } else {
250 Outcome::Unchanged
251 }
252 }
253}
254
255/// Returns from the current function if the block returns
256/// a value for which `[ConsumedEvent::is_consumed] == true`.
257///
258/// This breaks the control-flow of the current function effectively.
259///
260/// As the return type of the current function can differ from
261/// whatever function has been called, an `ìnto()` conversion
262/// is thrown in too.
263///
264/// *The difference to [try_flow] is that this on doesn't Ok-wrap the result.*
265///
266/// Extras: If you add a marker as in `flow!(log ident: {...});`
267/// the result of the operation is written to the log.
268#[macro_export]
269macro_rules! flow {
270 (log $n:ident: $x:expr) => {{
271 use log::debug;
272 use $crate::ConsumedEvent;
273 let r = $x;
274 if r.is_consumed() {
275 debug!("{} {:#?}", stringify!($n), r);
276 return r.into();
277 } else {
278 debug!("{} continue", stringify!($n));
279 }
280 }};
281 ($x:expr) => {{
282 use $crate::ConsumedEvent;
283 let r = $x;
284 if r.is_consumed() {
285 return r.into();
286 }
287 }};
288}
289
290/// Returns from the current function if the block returns
291/// a value for which `[ConsumedEvent::is_consumed] == true`.
292///
293/// This breaks the control-flow of the current function effectively.
294///
295/// As the return type of the current function can differ from
296/// whatever function has been called, an `ìnto()` conversion
297/// is thrown in too.
298///
299/// *The difference to [flow] is that this one Ok-wraps the result.*
300///
301/// Extras: If you add a marker as in `try_flow!(log ident: {...});`
302/// the result of the operation is written to the log.
303#[macro_export]
304macro_rules! try_flow {
305 (log $n:ident: $x:expr) => {{
306 use log::debug;
307 use $crate::ConsumedEvent;
308 let r = $x;
309 if r.is_consumed() {
310 debug!("{} {:#?}", stringify!($n), r);
311 return Ok(r.into());
312 } else {
313 debug!("{} continue", stringify!($n));
314 }
315 }};
316 ($x:expr) => {{
317 use $crate::ConsumedEvent;
318 let r = $x;
319 if r.is_consumed() {
320 return Ok(r.into());
321 }
322 }};
323}
324
325/// This macro doesn't return from the current function, but
326/// does a labeled break if the block returns a value for
327/// which `[ConsumedEvent::is_consumed] == true`.
328///
329/// It also does and `into()` conversion and *breaks* with the
330/// result to the enclosing block given as a `'l:{}` labeled block.
331///
332/// * The difference to [try_flow] is that this on doesn't Ok-wrap the result.*
333/// * The difference to [flow] is that this breaks instead of return_ing.
334///
335/// Extras: If you add a marker as in `break_flow!(log 'f: ident: {...});`
336/// the result of the operation is written to the log.
337#[macro_export]
338macro_rules! break_flow {
339 (log $n:ident: $l:lifetime: $x:expr) => {{
340 use log::debug;
341 use $crate::ConsumedEvent;
342 let r = $x;
343 if r.is_consumed() {
344 debug!("{} {:#?}", stringify!($n), r);
345 break $l r.into();
346 } else {
347 debug!("{} continue", stringify!($n));
348 }
349 }};
350 ($l:lifetime: $x:expr) => {{
351 use $crate::ConsumedEvent;
352 let r = $x;
353 if r.is_consumed() {
354 break $l r.into();
355 }
356 }};
357}
358
359mod _private {
360 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
361 pub struct NonExhaustive;
362}