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/// __Experimental__
256///
257/// Convert to Outcome from bool.
258///
259/// The problem is that there are two possible combinations
260/// * true/false -> Changed/Unchanged
261/// * true/false -> Changed/Continue
262///
263/// Which one, depends on the use-case. Do you want the event
264/// to be handled finally or do you want to give a parent widget
265/// the chance to react to an event unless your widget really
266/// does something worth-wile.
267///
268/// This occurs for navigation keys.
269/// - Left navigates backwards in your widget.
270///   - When the start is reached the widget is done.
271///     - There may be a super widget that can do it's super navigation
272///       if it knows that fact.
273///       => return Outcome::Changed as long as you change your location
274///          and Outcome::Continue otherwise.
275///
276/// For the Changed/Unchanged pair I don't have a good example, so
277/// maybe that was the wrong default anyway. But I can't change the
278/// `From<bool>` behavior as that would *really* break things.
279///
280/// Hence, this trait.  
281///
282pub trait FromBool {
283    fn as_changed_unchanged(self) -> Outcome;
284    fn as_changed_continue(self) -> Outcome;
285}
286
287impl FromBool for bool {
288    fn as_changed_unchanged(self) -> Outcome {
289        match self {
290            true => Outcome::Changed,
291            false => Outcome::Unchanged,
292        }
293    }
294
295    fn as_changed_continue(self) -> Outcome {
296        match self {
297            true => Outcome::Changed,
298            false => Outcome::Continue,
299        }
300    }
301}
302
303/// Tries to unify the currently 3 flow! constructs.
304///
305/// * `flow!(expr) -> event_flow!(return expr)`
306///   The non Result case stays gets a `return`. It's rather uncommon
307///   to __not__ have a Result during event-handling, so this should be fine.
308/// * `try_flow!(expr) -> event_flow!(expr)`
309///   This becomes the main branch.
310/// * `break_flow!('x: expr) -> event_flow!(break 'x expr)`
311///   This now matches actual rust syntax, which is good for rustfmt.
312///   The `break 'x` is stripped and reapplied after result-conversion.
313///
314/// __...___
315///
316/// I'll try this out... If it's fine I'll leave the other macros
317/// with a discouraging remark and promote this variant.
318///
319/// note: of course the default is diametrical when you write
320/// library code for a new widget. as usual :-|. stick with it
321/// though as libraries should be used more often than written.
322#[macro_export]
323macro_rules! event_flow {
324    (log $n:ident: return $x:expr) => {{
325        use log::debug;
326        use $crate::ConsumedEvent;
327        let r = $x;
328        if r.is_consumed() {
329            debug!("{} {:#?}", stringify!($n), r);
330            return r.into();
331        } else {
332            debug!("{} continue", stringify!($n));
333        }
334    }};
335    (log $n:ident: break $l:lifetime $x:expr) => {{
336        use log::debug;
337        use $crate::ConsumedEvent;
338        let r = $x;
339        if r.is_consumed() {
340            debug!("{} {:#?}", stringify!($n), r);
341            break $l r.into();
342        } else {
343            debug!("{} continue", stringify!($n));
344        }
345    }};
346    (log $n:ident: $x:expr) => {{
347        use log::debug;
348        use $crate::ConsumedEvent;
349        let r = $x;
350        if r.is_consumed() {
351            debug!("{} {:#?}", stringify!($n), r);
352            return Ok(r.into());
353        } else {
354            debug!("{} continue", stringify!($n));
355        }
356    }};
357    (break $l:lifetime $x:expr) => {{
358        use $crate::ConsumedEvent;
359        let r = $x;
360        if r.is_consumed() {
361            break $l r.into();
362        }
363    }};
364    (return $x:expr) => {{
365        use $crate::ConsumedEvent;
366        let r = $x;
367        if r.is_consumed() {
368            return r.into();
369        }
370    }};
371    ($x:expr) => {{
372        use $crate::ConsumedEvent;
373        let r = $x;
374        if r.is_consumed() {
375            return Ok(r.into());
376        }
377    }};
378
379}
380
381/// Use `event_flow!` instead.
382///
383/// Returns from the current function if the block returns
384/// a value for which `[ConsumedEvent::is_consumed] == true`.
385///
386/// This breaks the control-flow of the current function effectively.
387///
388/// As the return type of the current function can differ from
389/// whatever function has been called, an `ìnto()` conversion
390/// is thrown in too.  
391///
392/// *The difference to [try_flow] is that this on doesn't Ok-wrap the result.*
393///
394/// Extras: If you add a marker as in `flow!(log ident: {...});`
395/// the result of the operation is written to the log.
396#[macro_export]
397macro_rules! flow {
398    (log $n:ident: $x:expr) => {{
399        use log::debug;
400        use $crate::ConsumedEvent;
401        let r = $x;
402        if r.is_consumed() {
403            debug!("{} {:#?}", stringify!($n), r);
404            return r.into();
405        } else {
406            debug!("{} continue", stringify!($n));
407        }
408    }};
409    ($x:expr) => {{
410        use $crate::ConsumedEvent;
411        let r = $x;
412        if r.is_consumed() {
413            return r.into();
414        }
415    }};
416}
417
418/// Use `event_flow!` instead.
419///
420/// Returns from the current function if the block returns
421/// a value for which `[ConsumedEvent::is_consumed] == true`.
422///
423/// This breaks the control-flow of the current function effectively.
424///
425/// As the return type of the current function can differ from
426/// whatever function has been called, an `ìnto()` conversion
427/// is thrown in too.
428///
429/// *The difference to [flow] is that this one Ok-wraps the result.*
430///
431/// Extras: If you add a marker as in `try_flow!(log ident: {...});`
432/// the result of the operation is written to the log.
433#[macro_export]
434macro_rules! try_flow {
435    (log $n:ident: $x:expr) => {{
436        use log::debug;
437        use $crate::ConsumedEvent;
438        let r = $x;
439        if r.is_consumed() {
440            debug!("{} {:#?}", stringify!($n), r);
441            return Ok(r.into());
442        } else {
443            debug!("{} continue", stringify!($n));
444        }
445    }};
446    ($x:expr) => {{
447        use $crate::ConsumedEvent;
448        let r = $x;
449        if r.is_consumed() {
450            return Ok(r.into());
451        }
452    }};
453}
454
455/// Use `event_flow!` instead.
456///
457/// This macro doesn't return from the current function, but
458/// does a labeled break if the block returns a value for
459/// which `[ConsumedEvent::is_consumed] == true`.
460///
461/// It also does and `into()` conversion and *breaks* with the
462/// result to the enclosing block given as a `'l:{}` labeled block.
463///
464/// * The difference to [try_flow] is that this on doesn't Ok-wrap the result.*
465/// * The difference to [flow] is that this breaks instead of return_ing.
466///
467/// Extras: If you add a marker as in `break_flow!(log 'f: ident: {...});`
468/// the result of the operation is written to the log.
469#[macro_export]
470macro_rules! break_flow {
471    (log $n:ident: $l:lifetime: $x:expr) => {{
472        use log::debug;
473        use $crate::ConsumedEvent;
474        let r = $x;
475        if r.is_consumed() {
476            debug!("{} {:#?}", stringify!($n), r);
477            break $l r.into();
478        } else {
479            debug!("{} continue", stringify!($n));
480        }
481    }};
482    ($l:lifetime: $x:expr) => {{
483        use $crate::ConsumedEvent;
484        let r = $x;
485        if r.is_consumed() {
486            break $l r.into();
487        }
488    }};
489}
490
491mod _private {
492    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
493    pub struct NonExhaustive;
494}