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}