requestty_ui/backend/
mod.rs

1//! A module to represent a terminal and operations on it.
2
3use std::{fmt::Display, io};
4
5/// Gets the default [`Backend`] based on the features enabled.
6#[cfg(any(feature = "crossterm", feature = "termion"))]
7#[cfg_attr(docsrs, doc(cfg(any(feature = "crossterm", feature = "termion"))))]
8pub fn get_backend<W: io::Write>(buf: W) -> impl Backend {
9    #[cfg(feature = "crossterm")]
10    return CrosstermBackend::new(buf);
11
12    // XXX: Only works when crossterm and termion are the only two available backends
13    //
14    // Instead of directly checking for termion, we check for not crossterm so that compiling
15    // (documentation) with both features enabled will not error
16    #[cfg(not(feature = "crossterm"))]
17    return TermionBackend::new(buf);
18}
19
20mod test_backend;
21pub use test_backend::TestBackend;
22
23#[cfg(feature = "termion")]
24mod termion;
25
26#[cfg(feature = "termion")]
27pub use self::termion::TermionBackend;
28
29#[cfg(feature = "crossterm")]
30mod crossterm;
31
32#[cfg(feature = "crossterm")]
33pub use self::crossterm::CrosstermBackend;
34
35use crate::style::{Attributes, Color, Styled};
36
37/// A 2D size.
38#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Default)]
39#[allow(missing_docs)]
40pub struct Size {
41    pub width: u16,
42    pub height: u16,
43}
44
45impl Size {
46    /// The area of the size
47    pub fn area(self) -> u16 {
48        self.width * self.height
49    }
50}
51
52impl From<(u16, u16)> for Size {
53    fn from((width, height): (u16, u16)) -> Self {
54        Size { width, height }
55    }
56}
57
58/// The different parts of the terminal that can be cleared at once.
59#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
60pub enum ClearType {
61    /// All cells.
62    All,
63    /// All cells from the cursor position downwards.
64    FromCursorDown,
65    /// All cells from the cursor position upwards.
66    FromCursorUp,
67    /// All cells at the cursor row.
68    CurrentLine,
69    /// All cells from the cursor position until the new line.
70    UntilNewLine,
71}
72
73/// The directions the terminal cursor can be moved relative to the current position.
74#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
75pub enum MoveDirection {
76    /// Moves a given number of rows up.
77    Up(u16),
78    /// Moves a given number of rows down.
79    Down(u16),
80    /// Moves a given number of columns left.
81    Left(u16),
82    /// Moves a given number of columns right.
83    Right(u16),
84    /// Moves a given number of rows down and goes to the start of the line.
85    NextLine(u16),
86    /// Moves a given number of rows up and goes to the start of the line.
87    PrevLine(u16),
88    /// Goes to a given column.
89    Column(u16),
90}
91
92/// A trait to represent a terminal that can be rendered to.
93pub trait Backend: io::Write {
94    /// Enables raw mode.
95    fn enable_raw_mode(&mut self) -> io::Result<()>;
96    /// Disables raw mode.
97    fn disable_raw_mode(&mut self) -> io::Result<()>;
98    /// Hides the cursor.
99    fn hide_cursor(&mut self) -> io::Result<()>;
100    /// Shows the cursor.
101    fn show_cursor(&mut self) -> io::Result<()>;
102
103    /// Gets the cursor position as (col, row). The top-left cell is (0, 0).
104    fn get_cursor_pos(&mut self) -> io::Result<(u16, u16)>;
105    /// Moves the cursor to given position. The top-left cell is (0, 0).
106    fn move_cursor_to(&mut self, x: u16, y: u16) -> io::Result<()>;
107    /// Moves the cursor relative to the current position as per the `direction`.
108    fn move_cursor(&mut self, direction: MoveDirection) -> io::Result<()> {
109        default_move_cursor(self, direction)
110    }
111    /// Scrolls the terminal the given number of rows.
112    ///
113    /// A negative number means the terminal scrolls upwards, while a positive number means the
114    /// terminal scrolls downwards.
115    fn scroll(&mut self, dist: i16) -> io::Result<()>;
116
117    /// Sets the given `attributes` removing ones which were previous applied.
118    fn set_attributes(&mut self, attributes: Attributes) -> io::Result<()>;
119    /// Sets the foreground color.
120    fn set_fg(&mut self, color: Color) -> io::Result<()>;
121    /// Sets the background color.
122    fn set_bg(&mut self, color: Color) -> io::Result<()>;
123    /// Write a styled object to the backend.
124    ///
125    /// See also [`Styled`] and [`Stylize`].
126    ///
127    /// [`Stylize`]: crate::style::Stylize
128    fn write_styled(&mut self, styled: &Styled<dyn Display + '_>) -> io::Result<()> {
129        styled.write(self)
130    }
131
132    /// Clears the cells given by clear_type
133    fn clear(&mut self, clear_type: ClearType) -> io::Result<()>;
134    /// Gets the size of the terminal in rows and columns.
135    fn size(&self) -> io::Result<Size>;
136}
137
138fn default_move_cursor<B: Backend + ?Sized>(
139    backend: &mut B,
140    direction: MoveDirection,
141) -> io::Result<()> {
142    let (mut x, mut y) = backend.get_cursor_pos()?;
143
144    match direction {
145        MoveDirection::Up(dy) => y = y.saturating_sub(dy),
146        MoveDirection::Down(dy) => y = y.saturating_add(dy),
147        MoveDirection::Left(dx) => x = x.saturating_sub(dx),
148        MoveDirection::Right(dx) => x = x.saturating_add(dx),
149        MoveDirection::NextLine(dy) => {
150            x = 0;
151            y = y.saturating_add(dy);
152        }
153        MoveDirection::Column(new_x) => x = new_x,
154        MoveDirection::PrevLine(dy) => {
155            x = 0;
156            y = y.saturating_sub(dy);
157        }
158    }
159
160    backend.move_cursor_to(x, y)
161}
162
163impl<'a, B: Backend> Backend for &'a mut B {
164    fn enable_raw_mode(&mut self) -> io::Result<()> {
165        (**self).enable_raw_mode()
166    }
167    fn disable_raw_mode(&mut self) -> io::Result<()> {
168        (**self).disable_raw_mode()
169    }
170    fn hide_cursor(&mut self) -> io::Result<()> {
171        (**self).hide_cursor()
172    }
173    fn show_cursor(&mut self) -> io::Result<()> {
174        (**self).show_cursor()
175    }
176    fn get_cursor_pos(&mut self) -> io::Result<(u16, u16)> {
177        (**self).get_cursor_pos()
178    }
179    fn move_cursor_to(&mut self, x: u16, y: u16) -> io::Result<()> {
180        (**self).move_cursor_to(x, y)
181    }
182    fn move_cursor(&mut self, direction: MoveDirection) -> io::Result<()> {
183        (**self).move_cursor(direction)
184    }
185    fn scroll(&mut self, dist: i16) -> io::Result<()> {
186        (**self).scroll(dist)
187    }
188    fn set_attributes(&mut self, attributes: Attributes) -> io::Result<()> {
189        (**self).set_attributes(attributes)
190    }
191    fn set_fg(&mut self, color: Color) -> io::Result<()> {
192        (**self).set_fg(color)
193    }
194    fn set_bg(&mut self, color: Color) -> io::Result<()> {
195        (**self).set_bg(color)
196    }
197    fn write_styled(&mut self, styled: &Styled<dyn Display + '_>) -> io::Result<()> {
198        (**self).write_styled(styled)
199    }
200    fn clear(&mut self, clear_type: ClearType) -> io::Result<()> {
201        (**self).clear(clear_type)
202    }
203    fn size(&self) -> io::Result<Size> {
204        (**self).size()
205    }
206}