unsegen/input/
mod.rs

1//! Raw terminal input events, common abstractions for application (component) behavior and means
2//! to easily distribute events.
3//!
4//! # Example:
5//! ```
6//! use unsegen::input::*;
7//! use std::io::Read;
8//!
9//! struct Scroller {
10//!     line_number: u32,
11//!     end: u32,
12//! }
13//!
14//! impl Scrollable for Scroller {
15//!     fn scroll_backwards(&mut self) -> OperationResult {
16//!         if self.line_number > 0 {
17//!             self.line_number -= 1;
18//!             Ok(())
19//!         } else {
20//!             Err(())
21//!         }
22//!     }
23//!     fn scroll_forwards(&mut self) -> OperationResult {
24//!         if self.line_number < self.end - 1 {
25//!             self.line_number += 1;
26//!             Ok(())
27//!         } else {
28//!             Err(())
29//!         }
30//!     }
31//! }
32//!
33//! fn main() {
34//!     let mut scroller = Scroller {
35//!         line_number: 0,
36//!         end: 5,
37//!     };
38//!
39//!     // Read all inputs from something that implements Read
40//!     for input in Input::read_all(&[b'1', b'2', b'3', b'4'][..]) {
41//!         let input = input.unwrap();
42//!
43//!         // Define a chain of handlers for different kinds of events!
44//!         // If a handler (Behavior) cannot process an input, it is passed down the chain.
45//!         let leftover = input
46//!             .chain((Key::Char('1'), || println!("Got a 1!")))
47//!             .chain(
48//!                 ScrollBehavior::new(&mut scroller)
49//!                     .backwards_on(Key::Char('2'))
50//!                     .forwards_on(Key::Char('3')),
51//!             )
52//!             .chain(|i: Input| {
53//!                 if let Event::Key(Key::Char(c)) = i.event {
54//!                     println!("Got some char: {}", c);
55//!                     None // matches! event will be consumed
56//!                 } else {
57//!                     Some(i)
58//!                 }
59//!             })
60//!             .finish();
61//!         if let Some(e) = leftover {
62//!             println!("Could not handle input {:?}", e);
63//!         }
64//!     }
65//!
66//!     // We could not scroll back first, but one line forwards later!
67//!     assert_eq!(scroller.line_number, 1);
68//! }
69//! ```
70
71use std::collections::HashSet;
72pub use termion::event::{Event, Key, MouseButton, MouseEvent};
73use termion::input::{EventsAndRaw, TermReadEventsAndRaw};
74
75use std::io;
76
77/// A structure corresponding to a single input event, e.g., a single keystroke or mouse event.
78///
79/// In addition to the semantic Event enum itself, the raw bytes that created this event are
80/// available, as well. This is useful if the user wants to pass the input on to some other
81/// terminal-like abstraction under certain circumstances (e.g., when writing a terminal
82/// multiplexer).
83#[derive(Eq, PartialEq, Clone, Debug)]
84#[allow(missing_docs)]
85pub struct Input {
86    pub event: Event,
87    pub raw: Vec<u8>,
88}
89
90impl Input {
91    /// Create an iterator that reads from the provided argument and converts the read bytes into
92    /// a stream of `Input`s.
93    ///
94    /// Please note that the iterator blocks when no bytes are available from the `Read` source.
95    pub fn read_all<R: io::Read>(read: R) -> InputIter<R> {
96        InputIter {
97            inner: read.events_and_raw(),
98        }
99    }
100
101    /// Begin matching and processing of the event. See `InputChain`.
102    pub fn chain<B: Behavior>(self, behavior: B) -> InputChain {
103        let chain_begin = InputChain { input: Some(self) };
104        chain_begin.chain(behavior)
105    }
106
107    /// Check whether this event is equal to the provided event-like argument.
108    pub fn matches<T: ToEvent>(&self, e: T) -> bool {
109        self.event == e.to_event()
110    }
111}
112
113/// An iterator of `Input` events.
114pub struct InputIter<R: io::Read> {
115    inner: EventsAndRaw<R>,
116}
117
118impl<R: io::Read> Iterator for InputIter<R> {
119    type Item = Result<Input, io::Error>;
120
121    fn next(&mut self) -> Option<Result<Input, io::Error>> {
122        self.inner.next().map(|tuple| {
123            tuple.map(|(event, raw)| Input {
124                event: event,
125                raw: raw,
126            })
127        })
128    }
129}
130
131/// An intermediate element in a chain of `Behavior`s that are matched against the event and
132/// executed if applicable.
133///
134/// # Examples:
135/// ```
136/// use unsegen::input::*;
137///
138/// let mut triggered_first = false;
139/// let mut triggered_second = false;
140/// let mut triggered_third = false;
141///
142/// let input = Input {
143///     event: Event::Key(Key::Char('g')),
144///     raw: Vec::new(), //Incorrect, but does not matter for this example.
145/// };
146///
147/// let res = input
148///     .chain((Key::Char('f'), || triggered_first = true)) // does not match, passes event on
149///     .chain(|i: Input| if let Event::Key(Key::Char(_)) = i.event {
150///         triggered_second = true;
151///         None // matches! event will be consumed
152///     } else {
153///         Some(i)
154///     })
155///     .chain((Key::Char('g'), || triggered_first = true)) // matches, but is not reached!
156///     .finish();
157///
158/// assert!(!triggered_first);
159/// assert!(triggered_second);
160/// assert!(!triggered_third);
161/// assert!(res.is_none());
162/// ```
163pub struct InputChain {
164    input: Option<Input>,
165}
166
167impl InputChain {
168    /// Add another behavior to the line of input processors that will try to consume the event one
169    /// after another.
170    pub fn chain<B: Behavior>(self, behavior: B) -> InputChain {
171        if let Some(event) = self.input {
172            InputChain {
173                input: behavior.input(event),
174            }
175        } else {
176            InputChain { input: None }
177        }
178    }
179
180    /// Add another behavior to the line of input processors that will try to consume the event one
181    /// after another.
182    ///
183    /// If this chain element consumes the input, `f` is executed.
184    pub fn chain_and_then<B: Behavior>(self, behavior: B, f: impl FnOnce()) -> InputChain {
185        if let Some(event) = self.input {
186            let input = behavior.input(event);
187            if input.is_none() {
188                // Previously present, but now consumed
189                f();
190            }
191            InputChain { input }
192        } else {
193            InputChain { input: None }
194        }
195    }
196
197    /// Unpack the final chain value. If the `Input` was consumed by some `Behavior`, the result
198    /// will be None, otherwise the original `Input` will be returned.
199    pub fn finish(self) -> Option<Input> {
200        self.input
201    }
202
203    /// Execute the provided function only if the input was consumed previously in the chain.
204    pub fn if_consumed(self, f: impl FnOnce()) -> Self {
205        if self.input.is_none() {
206            f()
207        }
208        self
209    }
210
211    /// Execute the provided function only if the input not was consumed previously in the chain.
212    pub fn if_not_consumed(self, f: impl FnOnce()) -> Self {
213        if self.input.is_some() {
214            f()
215        }
216        self
217    }
218}
219impl From<Input> for InputChain {
220    fn from(input: Input) -> Self {
221        InputChain { input: Some(input) }
222    }
223}
224impl From<Option<Input>> for InputChain {
225    fn from(input: Option<Input>) -> Self {
226        InputChain { input }
227    }
228}
229
230/// Used conveniently supply `Event`-like arguments to a number of functions in the input module.
231/// For example, you can supply `Key::Up` instead of `Event::Key(Key::Up)`.
232///
233/// Basically an `Into<Event>`, but we cannot use that as Event is a reexport of termion.
234pub trait ToEvent {
235    /// Convert to an event.
236    fn to_event(self) -> Event;
237}
238
239impl ToEvent for Key {
240    fn to_event(self) -> Event {
241        Event::Key(self)
242    }
243}
244
245impl ToEvent for MouseEvent {
246    fn to_event(self) -> Event {
247        Event::Mouse(self)
248    }
249}
250
251impl ToEvent for Event {
252    fn to_event(self) -> Event {
253        self
254    }
255}
256
257/// Very thin wrapper around HashSet<Event>, mostly to conveniently insert `ToEvent`s.
258struct EventSet {
259    events: HashSet<Event>,
260}
261impl EventSet {
262    fn new() -> Self {
263        EventSet {
264            events: HashSet::new(),
265        }
266    }
267    fn insert<E: ToEvent>(&mut self, event: E) {
268        self.events.insert(event.to_event());
269    }
270    fn contains(&self, event: &Event) -> bool {
271        self.events.contains(event)
272    }
273}
274
275/// Something that reacts to input and possibly consumes it.
276///
277/// An inplementor is free to check the `Input` for arbitrary criteria and return the input if not
278/// consumed. Note that the implementor should not _change_ the input event in that case.
279///
280/// If the implementor somehow reacts to the input, it is generally a good idea to "consume" the
281/// value by returning None. This makes sure that subsequent `Behavior`s will not act.
282///
283/// Another thing of note is that a Behavior is generally constructed on the fly and consumed in
284/// the `input` function!
285/// For specialised behavior that does not fit into the often used abstractions defined in this
286/// module (`Scrollable`, `Writable`, `Navigatable`, ...) the easiest way to construct a behavior
287/// is either using a `FnOnce(Input) -> Option<Input>` where the implementor has to decide whether
288/// the input matches the desired criteria or using a pair `(ToEvent, FnOnce())` where the function
289/// is only iff the `Input` to be processed matches the provided `Event`-like thing.
290pub trait Behavior {
291    /// Receive, process and possibly consume the input.
292    fn input(self, input: Input) -> Option<Input>;
293}
294
295impl<F: FnOnce(Input) -> Option<Input>> Behavior for F {
296    fn input(self, input: Input) -> Option<Input> {
297        self(input)
298    }
299}
300
301impl<E: ToEvent, F: FnOnce()> Behavior for (E, F) {
302    fn input(self, input: Input) -> Option<Input> {
303        let (event, function) = self;
304        if input.matches(event) {
305            function();
306            None
307        } else {
308            Some(input)
309        }
310    }
311}
312
313impl<E: ToEvent + Clone, F: FnOnce()> Behavior for (&[E], F) {
314    fn input(self, input: Input) -> Option<Input> {
315        let (it, function) = self;
316        for event in it {
317            if input.matches(event.clone()) {
318                function();
319                return None;
320            }
321        }
322        Some(input)
323    }
324}
325
326/// A common return type for Operations such as functions of `Scrollable`, `Writable`,
327/// `Navigatable`, etc.
328///
329/// Ok(()) means: The input was processed successfully and should be consumed.
330/// Err(()) means: The input could not be processed and should be passed on to and processed by
331/// some other `Behavior`.
332pub type OperationResult = Result<(), ()>;
333fn pass_on_if_err(res: OperationResult, input: Input) -> Option<Input> {
334    if res.is_err() {
335        Some(input)
336    } else {
337        None
338    }
339}
340
341// ScrollableBehavior -----------------------------------------------
342
343/// Collection of triggers for functions of something `Scrollable` implementing `Behavior`.
344pub struct ScrollBehavior<'a, S: Scrollable + 'a> {
345    scrollable: &'a mut S,
346    to_beginning_on: EventSet,
347    to_end_on: EventSet,
348    backwards_on: EventSet,
349    forwards_on: EventSet,
350}
351
352impl<'a, S: Scrollable> ScrollBehavior<'a, S> {
353    /// Create the behavior to act on the provided ´Scrollable`. Add triggers using other functions!
354    pub fn new(scrollable: &'a mut S) -> Self {
355        ScrollBehavior {
356            scrollable: scrollable,
357            backwards_on: EventSet::new(),
358            forwards_on: EventSet::new(),
359            to_beginning_on: EventSet::new(),
360            to_end_on: EventSet::new(),
361        }
362    }
363    /// Make the behavior trigger the `scroll_to_beginning` function on the provided event.
364    pub fn to_beginning_on<E: ToEvent>(mut self, event: E) -> Self {
365        self.to_beginning_on.insert(event);
366        self
367    }
368    /// Make the behavior trigger the `scroll_to_end` function on the provided event.
369    pub fn to_end_on<E: ToEvent>(mut self, event: E) -> Self {
370        self.to_end_on.insert(event);
371        self
372    }
373    /// Make the behavior trigger the `scroll_backwards` function on the provided event.
374    pub fn backwards_on<E: ToEvent>(mut self, event: E) -> Self {
375        self.backwards_on.insert(event);
376        self
377    }
378    /// Make the behavior trigger the `scroll_forwards` function on the provided event.
379    pub fn forwards_on<E: ToEvent>(mut self, event: E) -> Self {
380        self.forwards_on.insert(event);
381        self
382    }
383}
384
385impl<'a, S: Scrollable> Behavior for ScrollBehavior<'a, S> {
386    fn input(self, input: Input) -> Option<Input> {
387        if self.forwards_on.contains(&input.event) {
388            pass_on_if_err(self.scrollable.scroll_forwards(), input)
389        } else if self.backwards_on.contains(&input.event) {
390            pass_on_if_err(self.scrollable.scroll_backwards(), input)
391        } else if self.to_beginning_on.contains(&input.event) {
392            pass_on_if_err(self.scrollable.scroll_to_beginning(), input)
393        } else if self.to_end_on.contains(&input.event) {
394            pass_on_if_err(self.scrollable.scroll_to_end(), input)
395        } else {
396            Some(input)
397        }
398    }
399}
400
401/// Something that can be scrolled. Use in conjunction with `ScrollBehavior` to manipulate when
402/// input arrives.
403///
404/// Note that `scroll_to_beginning` and `scroll_to_end` should be implemented manually if a fast
405/// pass is available and performance is important. By default these functions call
406/// `scroll_backwards` and `scroll_forwards` respectively until they fail.
407#[allow(missing_docs)]
408pub trait Scrollable {
409    fn scroll_backwards(&mut self) -> OperationResult;
410    fn scroll_forwards(&mut self) -> OperationResult;
411    fn scroll_to_beginning(&mut self) -> OperationResult {
412        if self.scroll_backwards().is_err() {
413            return Err(());
414        } else {
415            while self.scroll_backwards().is_ok() {}
416            Ok(())
417        }
418    }
419    fn scroll_to_end(&mut self) -> OperationResult {
420        if self.scroll_forwards().is_err() {
421            return Err(());
422        } else {
423            while self.scroll_forwards().is_ok() {}
424            Ok(())
425        }
426    }
427}
428
429// WriteBehavior ------------------------------------------
430
431/// Collection of triggers for functions of something `Writable` implementing `Behavior`.
432pub struct WriteBehavior<'a, W: Writable + 'a> {
433    writable: &'a mut W,
434}
435impl<'a, W: Writable + 'a> WriteBehavior<'a, W> {
436    /// Create a new Behavior for the `Writable`.
437    pub fn new(writable: &'a mut W) -> Self {
438        WriteBehavior { writable: writable }
439    }
440}
441
442impl<'a, W: Writable + 'a> Behavior for WriteBehavior<'a, W> {
443    fn input(self, input: Input) -> Option<Input> {
444        if let Event::Key(Key::Char(c)) = input.event {
445            pass_on_if_err(self.writable.write(c), input)
446        } else {
447            Some(input)
448        }
449    }
450}
451
452/// Something that can be written to in the sense of a text box, editor or text input.
453///
454/// All inputs that correspond to keystrokes with a corresponding `char` representation will be
455/// converted and passed to the `Writable`.
456pub trait Writable {
457    /// Process the provided char and report if it was processed successfully.
458    fn write(&mut self, c: char) -> OperationResult;
459}
460
461// NavigateBehavior ------------------------------------------------
462
463/// Collection of triggers for functions of something `Navigatable` implementing `Behavior`.
464pub struct NavigateBehavior<'a, N: Navigatable + 'a> {
465    navigatable: &'a mut N,
466    up_on: EventSet,
467    down_on: EventSet,
468    left_on: EventSet,
469    right_on: EventSet,
470}
471
472impl<'a, N: Navigatable + 'a> NavigateBehavior<'a, N> {
473    /// Create the behavior to act on the provided `Navigatable`. Add triggers using other functions!
474    pub fn new(navigatable: &'a mut N) -> Self {
475        NavigateBehavior {
476            navigatable: navigatable,
477            up_on: EventSet::new(),
478            down_on: EventSet::new(),
479            left_on: EventSet::new(),
480            right_on: EventSet::new(),
481        }
482    }
483
484    /// Make the behavior trigger the `move_up` function on the provided event.
485    ///
486    /// A typical candidate for `event` would be `Key::Up`.
487    pub fn up_on<E: ToEvent>(mut self, event: E) -> Self {
488        self.up_on.insert(event);
489        self
490    }
491    /// Make the behavior trigger the `move_down` function on the provided event.
492    ///
493    /// A typical candidate for `event` would be `Key::Down`.
494    pub fn down_on<E: ToEvent>(mut self, event: E) -> Self {
495        self.down_on.insert(event);
496        self
497    }
498    /// Make the behavior trigger the `move_left` function on the provided event.
499    ///
500    /// A typical candidate for `event` would be `Key::Left`.
501    pub fn left_on<E: ToEvent>(mut self, event: E) -> Self {
502        self.left_on.insert(event);
503        self
504    }
505    /// Make the behavior trigger the `move_right` function on the provided event.
506    ///
507    /// A typical candidate for `event` would be `Key::Right`.
508    pub fn right_on<E: ToEvent>(mut self, event: E) -> Self {
509        self.right_on.insert(event);
510        self
511    }
512}
513
514impl<'a, N: Navigatable + 'a> Behavior for NavigateBehavior<'a, N> {
515    fn input(self, input: Input) -> Option<Input> {
516        if self.up_on.contains(&input.event) {
517            pass_on_if_err(self.navigatable.move_up(), input)
518        } else if self.down_on.contains(&input.event) {
519            pass_on_if_err(self.navigatable.move_down(), input)
520        } else if self.left_on.contains(&input.event) {
521            pass_on_if_err(self.navigatable.move_left(), input)
522        } else if self.right_on.contains(&input.event) {
523            pass_on_if_err(self.navigatable.move_right(), input)
524        } else {
525            Some(input)
526        }
527    }
528}
529
530/// Something that can be navigated like a cursor in a text editor or character in a simple 2D
531/// game.
532#[allow(missing_docs)]
533pub trait Navigatable {
534    fn move_up(&mut self) -> OperationResult;
535    fn move_down(&mut self) -> OperationResult;
536    fn move_left(&mut self) -> OperationResult;
537    fn move_right(&mut self) -> OperationResult;
538}
539
540// EditBehavior ---------------------------------------------------------
541
542/// Collection of triggers for functions of something `Editable` implementing `Behavior`.
543pub struct EditBehavior<'a, E: Editable + 'a> {
544    editable: &'a mut E,
545    up_on: EventSet,
546    down_on: EventSet,
547    left_on: EventSet,
548    right_on: EventSet,
549    delete_forwards_on: EventSet,
550    delete_backwards_on: EventSet,
551    clear_on: EventSet,
552    go_to_beginning_of_line_on: EventSet,
553    go_to_end_of_line_on: EventSet,
554}
555
556impl<'a, E: Editable> EditBehavior<'a, E> {
557    /// Create the behavior to act on the provided `Editable`. Add triggers using other functions!
558    pub fn new(editable: &'a mut E) -> Self {
559        EditBehavior {
560            editable: editable,
561            up_on: EventSet::new(),
562            down_on: EventSet::new(),
563            left_on: EventSet::new(),
564            right_on: EventSet::new(),
565            delete_forwards_on: EventSet::new(),
566            delete_backwards_on: EventSet::new(),
567            clear_on: EventSet::new(),
568            go_to_beginning_of_line_on: EventSet::new(),
569            go_to_end_of_line_on: EventSet::new(),
570        }
571    }
572
573    /// Make the behavior trigger the `move_up` function on the provided event.
574    ///
575    /// A typical candidate for `event` would be `Key::Up`.
576    pub fn up_on<T: ToEvent>(mut self, event: T) -> Self {
577        self.up_on.insert(event);
578        self
579    }
580    /// Make the behavior trigger the `move_down` function on the provided event.
581    ///
582    /// A typical candidate for `event` would be `Key::Down`.
583    pub fn down_on<T: ToEvent>(mut self, event: T) -> Self {
584        self.down_on.insert(event);
585        self
586    }
587    /// Make the behavior trigger the `move_left` function on the provided event.
588    ///
589    /// A typical candidate for `event` would be `Key::Left`.
590    pub fn left_on<T: ToEvent>(mut self, event: T) -> Self {
591        self.left_on.insert(event);
592        self
593    }
594    /// Make the behavior trigger the `move_right` function on the provided event.
595    ///
596    /// A typical candidate for `event` would be `Key::Right`.
597    pub fn right_on<T: ToEvent>(mut self, event: T) -> Self {
598        self.right_on.insert(event);
599        self
600    }
601    /// Make the behavior trigger the `delete_forwards` function on the provided event.
602    ///
603    /// A typical candidate for `event` would be `Key::Delete`.
604    pub fn delete_forwards_on<T: ToEvent>(mut self, event: T) -> Self {
605        self.delete_forwards_on.insert(event);
606        self
607    }
608    /// Make the behavior trigger the `delete_backwards` function on the provided event.
609    ///
610    /// A typical candidate for `event` would be `Key::Backspace`.
611    pub fn delete_backwards_on<T: ToEvent>(mut self, event: T) -> Self {
612        self.delete_backwards_on.insert(event);
613        self
614    }
615    /// Make the behavior trigger the `clear` function on the provided event.
616    pub fn clear_on<T: ToEvent>(mut self, event: T) -> Self {
617        self.clear_on.insert(event);
618        self
619    }
620    /// Make the behavior trigger the `go_to_beginning_of_line` function on the provided event.
621    ///
622    /// A typical candidate for `event` would be `Key::Home`.
623    pub fn go_to_beginning_of_line_on<T: ToEvent>(mut self, event: T) -> Self {
624        self.go_to_beginning_of_line_on.insert(event);
625        self
626    }
627    /// Make the behavior trigger the `go_to_end_of_line_on` function on the provided event.
628    ///
629    /// A typical candidate for `event` would be `Key::End`.
630    pub fn go_to_end_of_line_on<T: ToEvent>(mut self, event: T) -> Self {
631        self.go_to_end_of_line_on.insert(event);
632        self
633    }
634}
635
636impl<'a, E: Editable> Behavior for EditBehavior<'a, E> {
637    fn input(self, input: Input) -> Option<Input> {
638        if self.up_on.contains(&input.event) {
639            pass_on_if_err(self.editable.move_up(), input)
640        } else if self.down_on.contains(&input.event) {
641            pass_on_if_err(self.editable.move_down(), input)
642        } else if self.left_on.contains(&input.event) {
643            pass_on_if_err(self.editable.move_left(), input)
644        } else if self.right_on.contains(&input.event) {
645            pass_on_if_err(self.editable.move_right(), input)
646        } else if self.delete_forwards_on.contains(&input.event) {
647            pass_on_if_err(self.editable.delete_forwards(), input)
648        } else if self.delete_backwards_on.contains(&input.event) {
649            pass_on_if_err(self.editable.delete_backwards(), input)
650        } else if self.clear_on.contains(&input.event) {
651            pass_on_if_err(self.editable.clear(), input)
652        } else if self.go_to_beginning_of_line_on.contains(&input.event) {
653            pass_on_if_err(self.editable.go_to_beginning_of_line(), input)
654        } else if self.go_to_end_of_line_on.contains(&input.event) {
655            pass_on_if_err(self.editable.go_to_end_of_line(), input)
656        } else if let Event::Key(Key::Char(c)) = input.event {
657            pass_on_if_err(self.editable.write(c), input)
658        } else {
659            Some(input)
660        }
661    }
662}
663
664/// Something that acts like a text editor.
665pub trait Editable: Navigatable + Writable {
666    /// In the sense of pressing the "Delete" key.
667    fn delete_forwards(&mut self) -> OperationResult;
668    /// In the sense of pressing the "Backspace" key.
669    fn delete_backwards(&mut self) -> OperationResult;
670    /// In the sense of pressing the "Home" key.
671    fn go_to_beginning_of_line(&mut self) -> OperationResult;
672    /// In the sense of pressing the "End" key.
673    fn go_to_end_of_line(&mut self) -> OperationResult;
674    /// Remove all content.
675    fn clear(&mut self) -> OperationResult;
676}