1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#![doc = include_str!("../readme.md")]

use std::cmp::max;

pub mod crossterm;
pub mod util;

/// All the regular and expected event-handling a widget can do.
///
/// All the normal key-handling, maybe dependent on an internal
/// focus-state, all the mouse-handling.
#[derive(Debug, Default)]
pub struct Regular;

/// Handle mouse-events only. Useful whenever you want to write new key-bindings,
/// but keep the mouse-events.
#[derive(Debug, Default)]
pub struct MouseOnly;

/// Popup/Overlays are a bit difficult to handle, as there is no z-order/area tree,
/// which would direct mouse interactions. We can simulate a z-order in the
/// event-handler by trying the things with a higher z-order first.
///
/// If a widget should be seen as pure overlay, it would define only a Popup
/// event-handler. In the event-handling functions you would call all Popup
/// event-handlers before the regular ones.
///
/// Example:
/// * Context menu. If the context-menu is active, it can consume all mouse-events
///   that fall into its range, and the widgets behind it only get the rest.
/// * Menubar. Would define _two_ event-handlers, a regular one for all events
///   on the main menu bar, and a popup event-handler for the menus. The event-handling
///   function calls the popup handler first and the regular one at some time later.
#[derive(Debug)]
pub struct Popup;

/// Event-handling for a dialog like widget.
///
/// Similar to [Popup] but with the extra that it consumes _all_ events when active.
/// No regular widget gets any event, and we have modal behaviour.
#[derive(Debug)]
pub struct Dialog;

/// Event-handler for double-click on a widget.
///
/// Events for this handler must be processed *before* calling
/// any other event-handling routines for the same table.
/// Otherwise, the regular event-handling might interfere with
/// recognition of double-clicks by consuming the first click.
///
/// This event-handler doesn't consume the first click, just
/// the second one.
#[derive(Debug, Default)]
pub struct DoubleClick;

///
/// A very broad trait for an event handler.
///
/// Ratatui widgets have two separate structs, one that implements
/// Widget/StatefulWidget and the associated State. As the StatefulWidget
/// has a lifetime and is not meant to be kept, HandleEvent should be
/// implemented for the state struct. It can then modify some state and
/// the tui can be rendered anew with that changed state.
///
/// HandleEvent is not limited to State structs of course, any Type
/// that wants to interact with events can implement it.
///
/// * Event - The actual event type.
/// * Qualifier - The qualifier allows creating more than one event-handler
///         for a widget.
///
///   This can be used as a variant of type-state, where the type given
///   selects the widget's behaviour, or to give some external context
///   to the widget, or to write your own key-bindings for a widget.
///
/// * R - Result of event-handling. This can give information to the
///   application what changed due to handling the event. This can
///   be very specific for each widget, but there is one general [Outcome]
///   that describes a minimal set of results.
///
///   There should be one value that indicates 'I don't know this event'.
///   This is expressed with the ConsumedEvent trait.
///
pub trait HandleEvent<Event, Qualifier, R: ConsumedEvent> {
    /// Handle an event.
    ///
    /// * self - The widget state.
    /// * event - Event struct.
    /// * qualifier - Event handling qualifier.
    ///   This library defines some standard values [Regular], [MouseOnly],
    ///   [Popup] and [Dialog].
    ///
    ///     Further ideas:
    ///     * ReadOnly
    ///     * Special behaviour like DoubleClick, HotKey.
    /// * Returns some result, see [Outcome]
    fn handle(&mut self, event: &Event, qualifier: Qualifier) -> R;
}

/// Catch all event-handler for the null state `()`.
impl<E, Q> HandleEvent<E, Q, Outcome> for () {
    fn handle(&mut self, _event: &E, _qualifier: Q) -> Outcome {
        Outcome::Continue
    }
}

/// When calling multiple event-handlers, the minimum information required
/// from the result is consumed the event/didn't consume the event.
///
/// See also [flow], [flow_ok] and [or_else] macros.
pub trait ConsumedEvent {
    /// Is this the 'consumed' result.
    fn is_consumed(&self) -> bool;

    /// Or-Else chaining with `is_consumed()` as the split.
    fn or_else<F>(self, f: F) -> Self
    where
        F: FnOnce() -> Self,
        Self: Sized,
    {
        if self.is_consumed() {
            self
        } else {
            f()
        }
    }

    /// Then-chaining. Returns max(self, f()).
    fn then<F>(self, f: F) -> Self
    where
        Self: Sized + Ord,
        F: FnOnce() -> Self,
    {
        max(self, f())
    }
}

impl<V, E> ConsumedEvent for Result<V, E>
where
    V: ConsumedEvent,
{
    fn is_consumed(&self) -> bool {
        match self {
            Ok(v) => v.is_consumed(),
            Err(_) => true, // this can somewhat be argued for.
        }
    }
}

/// The baseline outcome for an event-handler.
///
/// A widget can define its own type, if it has more things to report.
/// It would be nice if those types are convertible to/from Outcome.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Outcome {
    /// The given event has not been used at all.
    Continue,
    /// The event has been recognized, but nothing noticeable has changed.
    /// Further processing for this event may stop.
    /// Rendering the ui is not necessary.
    Unchanged,
    /// The event has been recognized and there is some change due to it.
    /// Further processing for this event may stop.
    /// Rendering the ui is advised.
    Changed,
}

impl ConsumedEvent for Outcome {
    fn is_consumed(&self) -> bool {
        !matches!(self, Outcome::Continue)
    }
}

/// Widgets often define functions that return bool to indicate a changed state.
/// This converts `true` / `false` to `Outcome::Changed` / `Outcome::Unchanged`.
impl From<bool> for Outcome {
    fn from(value: bool) -> Self {
        if value {
            Outcome::Changed
        } else {
            Outcome::Unchanged
        }
    }
}

/// Breaks the control-flow if the block returns a value
/// for which [ConsumedEvent::is_consumed] is true.
///
/// It does the classic `into()`-conversion and returns the result.
///
/// *The difference to [flow_ok] is that this on doesn't Ok-wrap the result.*
///
/// Extras: If you add a marker as in `flow_ok!(log ident: {...});`
/// the result of the operation is written to the log.
///
/// Extras: Focus handling is tricky, see [rat-focus](https://docs.rs/rat-focus/).
/// The result of focus handling is a second result of an event-handler,
/// that must be combined to form the single return value that a function
/// can have.
/// Therefore, one more extension for this macro:
/// `flow_ok!(_do_something_with_an_outcome(), consider focus_outcome)`.
/// This returns max(outcome, focus_outcome).
#[macro_export]
macro_rules! flow {
    (log $n:ident: $x:expr) => {{
        use log::debug;
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            debug!("{} {:#?}", stringify!($n), r);
            return r.into();
        } else {
            debug!("{} continue", stringify!($n));
            _ = r;
        }
    }};
    ($x:expr, consider $f:expr) => {{
        use std::cmp::max;
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            return max(r.into(), $f);
        } else {
            _ = r;
        }
    }};
    ($x:expr) => {{
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            return r.into();
        } else {
            _ = r;
        }
    }};
}

/// Breaks the control-flow if the block returns a value
/// for which [ConsumedEvent::is_consumed] is true.
///
/// It does the classic `into()`-conversion and returns the result.
///
/// *The difference to [flow] is that this one Ok-wraps the result.*
///
/// Extras: If you add a marker as in `flow_ok!(log ident: {...});`
/// the result of the operation is written to the log.
///
/// Extras: Focus handling is tricky, see [rat-focus](https://docs.rs/rat-focus/).
/// The result of focus handling is a second result of an event-handler,
/// that must be combined to form the single return value that a function
/// can have.
/// Therefore, one more extension for this macro:
/// `flow_ok!(_do_something_with_an_outcome(), consider focus_outcome)`.
/// This returns max(outcome, focus_outcome).
#[macro_export]
macro_rules! flow_ok {
    (log $n:ident: $x:expr) => {{
        use log::debug;
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            debug!("{} {:#?}", stringify!($n), r);
            return Ok(r.into());
        } else {
            debug!("{} continue", stringify!($n));
            _ = r;
        }
    }};
    ($x:expr, consider $f:expr) => {{
        use std::cmp::max;
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            return Ok(max(r.into(), $f));
        } else {
            _ = r;
        }
    }};
    ($x:expr) => {{
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            return Ok(r.into());
        } else {
            _ = r;
        }
    }};
}

/// Another control-flow macro based on [ConsumedEvent].
///
/// If you don't want to return early from event-handling, you can use this.
///
/// Define a mut that holds the result, and `or_else!` through all
/// event-handlers.
///
/// ```not_rust
/// let mut r;
///
/// r = first_activity();
/// or_else!(r, second_activity());
/// or_else!(r, third_activity());
/// ```
///
/// This executes `second_activity()` if `!r.is_consumed()` and stores the
/// result in r. The same with `third_activity` ...
///
#[macro_export]
macro_rules! or_else {
    ($x:ident, $e:expr) => {
        if !$crate::ConsumedEvent::is_consumed(&$x) {
            $x = $e;
        }
    };
}