rat-event 2.1.0

ratatui event handler trait for widgets
Documentation
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
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
#![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 dependent on an internal focus-state
/// All the mouse-handling.
#[derive(Debug, Default, Clone, Copy)]
pub struct Regular;

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

/// With the latest enhancement of `Focus` this is obsolete.
///
/// Focus collects all the areas of widgets and containers and can set a z-index
/// for each area. With this information it runs a hit-test for each mouse event
/// and sets and sets the `mouse-focus` flag for each widget and container.
/// This works well with popups and overlapping widgets. This makes the
/// differentiation between regular widgets and widgets that might show a popup
/// that overlaps other widgets obsolete.
///
/// If you don't use Focus you will still have to consider the information below.
///
/// __Obsoleted__
///
/// 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 might show a popup, you have to call its event-handling
/// before any other widgets that might be (partially) hidden behind the
/// widget. This applies for e.g. Menubar, Choice and ComboBox.
/// To make this difference visible in the application code, these widgets
/// use `Popup` as marker for their event-handling instead of `Regular`.
///
/// If you implement a widget with a popup, you must make sure to consume __all__
/// mouse-events within the widget area to prevent any interaction with
/// a hidden widget.
#[derive(Debug, Default, Clone, Copy)]
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, Default, Clone, Copy)]
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 widget.
/// 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, Clone, Copy)]
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, Return>
where
    Return: ConsumedEvent,
{
    /// Handle an event.
    ///
    /// * self - The widget state.
    /// * event - Event type.
    /// * qualifier - Event handling qualifier.
    ///   This library defines some standard values [Regular], [MouseOnly].
    ///   Further ideas:
    ///     * ReadOnly
    ///     * Special behaviour like DoubleClick, HotKey.
    /// * Returns some result, see [Outcome]
    fn handle(&mut self, event: &Event, qualifier: Qualifier) -> Return;
}

/// 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 'has consumed/didn't consume' the event.
///
/// The event-handler **may** also react to the event and not call it
/// 'consuming the event'. But this is tricky, non-obvious and frowned upon.
/// The caller **may** also just ignore the fact.
///
/// See also [flow] and [try_flow] and the extra [break_flow].
pub trait ConsumedEvent {
    /// Is this the 'consumed' result.
    fn is_consumed(&self) -> bool;

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

    /// Or-Else chaining with `is_consumed()` as the split.
    #[inline(always)]
    fn or_else_try<F, E>(self, f: F) -> Result<Self, E>
    where
        Self: Sized,
        F: FnOnce() -> Result<Self, E>,
    {
        if self.is_consumed() {
            Ok(self)
        } else {
            Ok(f()?)
        }
    }

    /// And_then-chaining based on is_consumed().
    /// Returns max(self, f()).
    #[inline(always)]
    fn and_then<F>(self, f: F) -> Self
    where
        Self: Sized + Ord,
        F: FnOnce() -> Self,
    {
        if self.is_consumed() {
            max(self, f())
        } else {
            self
        }
    }

    /// And_then-chaining based on is_consumed().
    /// Returns max(self, f()).
    #[inline(always)]
    fn and_then_try<F, E>(self, f: F) -> Result<Self, E>
    where
        Self: Sized + Ord,
        F: FnOnce() -> Result<Self, E>,
    {
        if self.is_consumed() {
            Ok(max(self, f()?))
        } else {
            Ok(self)
        }
    }

    /// Then-chaining. Returns max(self, f()).
    #[inline(always)]
    #[deprecated(since = "1.2.2", note = "use and_then()")]
    fn and<F>(self, f: F) -> Self
    where
        Self: Sized + Ord,
        F: FnOnce() -> Self,
    {
        if self.is_consumed() {
            max(self, f())
        } else {
            self
        }
    }

    /// Then-chaining. Returns max(self, f()).
    #[inline(always)]
    #[deprecated(since = "1.2.2", note = "use and_then_try()")]
    fn and_try<F, E>(self, f: F) -> Result<Self, E>
    where
        Self: Sized + Ord,
        F: FnOnce() -> Result<Self, E>,
    {
        if self.is_consumed() {
            Ok(max(self, f()?))
        } else {
            Ok(self)
        }
    }
}

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,
        }
    }
}

/// 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`
/// and implement `ConsumedEvent` as well.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Outcome {
    /// The given event has not been used at all.
    #[default]
    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 {
        *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
        }
    }
}

/// __Experimental__
///
/// Convert to Outcome from bool.
///
/// The problem is that there are two possible combinations
/// * true/false -> Changed/Unchanged
/// * true/false -> Changed/Continue
///
/// Which one, depends on the use-case. Do you want the event
/// to be handled finally or do you want to give a parent widget
/// the chance to react to an event unless your widget really
/// does something worth-wile.
///
/// This occurs for navigation keys.
/// - Left navigates backwards in your widget.
///   - When the start is reached the widget is done.
///     - There may be a super widget that can do it's super navigation
///       if it knows that fact.
///       => return Outcome::Changed as long as you change your location
///          and Outcome::Continue otherwise.
///
/// For the Changed/Unchanged pair I don't have a good example, so
/// maybe that was the wrong default anyway. But I can't change the
/// `From<bool>` behavior as that would *really* break things.
///
/// Hence, this trait.  
///
pub trait FromBool {
    fn as_changed_unchanged(self) -> Outcome;
    fn as_changed_continue(self) -> Outcome;
}

impl FromBool for bool {
    fn as_changed_unchanged(self) -> Outcome {
        match self {
            true => Outcome::Changed,
            false => Outcome::Unchanged,
        }
    }

    fn as_changed_continue(self) -> Outcome {
        match self {
            true => Outcome::Changed,
            false => Outcome::Continue,
        }
    }
}

/// Tries to unify the currently 3 flow! constructs.
///
/// * `flow!(expr) -> event_flow!(return expr)`
///   The non Result case stays gets a `return`. It's rather uncommon
///   to __not__ have a Result during event-handling, so this should be fine.
/// * `try_flow!(expr) -> event_flow!(expr)`
///   This becomes the main branch.
/// * `break_flow!('x: expr) -> event_flow!(break 'x expr)`
///   This now matches actual rust syntax, which is good for rustfmt.
///   The `break 'x` is stripped and reapplied after result-conversion.
///
/// __...___
///
/// I'll try this out... If it's fine I'll leave the other macros
/// with a discouraging remark and promote this variant.
///
/// note: of course the default is diametrical when you write
/// library code for a new widget. as usual :-|. stick with it
/// though as libraries should be used more often than written.
#[macro_export]
macro_rules! event_flow {
    (log $n:ident: return $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));
        }
    }};
    (log $n:ident: break $l:lifetime $x:expr) => {{
        use log::debug;
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            debug!("{} {:#?}", stringify!($n), r);
            break $l r.into();
        } else {
            debug!("{} continue", stringify!($n));
        }
    }};
    (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));
        }
    }};
    (break $l:lifetime $x:expr) => {{
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            break $l r.into();
        }
    }};
    (return $x:expr) => {{
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            return r.into();
        }
    }};
    ($x:expr) => {{
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            return Ok(r.into());
        }
    }};

}

/// Use `event_flow!` instead.
///
/// Returns from the current function if the block returns
/// a value for which `[ConsumedEvent::is_consumed] == true`.
///
/// This breaks the control-flow of the current function effectively.
///
/// As the return type of the current function can differ from
/// whatever function has been called, an `ìnto()` conversion
/// is thrown in too.  
///
/// *The difference to [try_flow] is that this on doesn't Ok-wrap the result.*
///
/// Extras: If you add a marker as in `flow!(log ident: {...});`
/// the result of the operation is written to the log.
#[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));
        }
    }};
    ($x:expr) => {{
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            return r.into();
        }
    }};
}

/// Use `event_flow!` instead.
///
/// Returns from the current function if the block returns
/// a value for which `[ConsumedEvent::is_consumed] == true`.
///
/// This breaks the control-flow of the current function effectively.
///
/// As the return type of the current function can differ from
/// whatever function has been called, an `ìnto()` conversion
/// is thrown in too.
///
/// *The difference to [flow] is that this one Ok-wraps the result.*
///
/// Extras: If you add a marker as in `try_flow!(log ident: {...});`
/// the result of the operation is written to the log.
#[macro_export]
macro_rules! try_flow {
    (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));
        }
    }};
    ($x:expr) => {{
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            return Ok(r.into());
        }
    }};
}

/// Use `event_flow!` instead.
///
/// This macro doesn't return from the current function, but
/// does a labeled break if the block returns a value for
/// which `[ConsumedEvent::is_consumed] == true`.
///
/// It also does and `into()` conversion and *breaks* with the
/// result to the enclosing block given as a `'l:{}` labeled block.
///
/// * The difference to [try_flow] is that this on doesn't Ok-wrap the result.*
/// * The difference to [flow] is that this breaks instead of return_ing.
///
/// Extras: If you add a marker as in `break_flow!(log 'f: ident: {...});`
/// the result of the operation is written to the log.
#[macro_export]
macro_rules! break_flow {
    (log $n:ident: $l:lifetime: $x:expr) => {{
        use log::debug;
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            debug!("{} {:#?}", stringify!($n), r);
            break $l r.into();
        } else {
            debug!("{} continue", stringify!($n));
        }
    }};
    ($l:lifetime: $x:expr) => {{
        use $crate::ConsumedEvent;
        let r = $x;
        if r.is_consumed() {
            break $l r.into();
        }
    }};
}

mod _private {
    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
    pub struct NonExhaustive;
}