prototty_unix/
context.rs

1pub use ansi_colour::Colour as AnsiColour;
2use error::*;
3use prototty_grid::*;
4use prototty_input::*;
5use prototty_render::*;
6use std::time::Duration;
7use terminal::*;
8
9const DEFAULT_FG: AnsiColour = AnsiColour::from_rgb24(grey24(255));
10const DEFAULT_BG: AnsiColour = AnsiColour::from_rgb24(grey24(0));
11
12pub trait ColourConfig {
13    fn convert_foreground_rgb24(&mut self, rgb24: Rgb24) -> AnsiColour;
14    fn convert_background_rgb24(&mut self, rgb24: Rgb24) -> AnsiColour {
15        self.convert_foreground_rgb24(rgb24)
16    }
17    fn default_foreground(&mut self) -> AnsiColour {
18        DEFAULT_FG
19    }
20    fn default_background(&mut self) -> AnsiColour {
21        DEFAULT_BG
22    }
23}
24
25pub struct DefaultColourConfig;
26
27impl ColourConfig for DefaultColourConfig {
28    fn convert_foreground_rgb24(&mut self, rgb24: Rgb24) -> AnsiColour {
29        AnsiColour::from_rgb24(rgb24)
30    }
31}
32
33impl<F: FnMut(Rgb24) -> AnsiColour> ColourConfig for F {
34    fn convert_foreground_rgb24(&mut self, rgb24: Rgb24) -> AnsiColour {
35        (self)(rgb24)
36    }
37}
38
39struct UnixColourConversion<C>(C);
40
41impl<C: ColourConfig> ColourConversion for UnixColourConversion<C> {
42    type Colour = AnsiColour;
43    fn default_foreground(&mut self) -> Self::Colour {
44        self.0.default_foreground()
45    }
46    fn default_background(&mut self) -> Self::Colour {
47        self.0.default_background()
48    }
49    fn convert_foreground_rgb24(&mut self, rgb24: Rgb24) -> Self::Colour {
50        self.0.convert_foreground_rgb24(rgb24)
51    }
52    fn convert_background_rgb24(&mut self, rgb24: Rgb24) -> Self::Colour {
53        self.0.convert_background_rgb24(rgb24)
54    }
55}
56
57/// An interface to a terminal for rendering `View`s, and getting input.
58pub struct Context<C: ColourConfig = DefaultColourConfig> {
59    terminal: Terminal,
60    grid: Grid<UnixColourConversion<C>>,
61}
62
63impl Context<DefaultColourConfig> {
64    /// Initialise a new context using the current terminal.
65    pub fn new() -> Result<Self> {
66        Self::with_colour_config(DefaultColourConfig)
67    }
68
69    pub fn from_terminal(terminal: Terminal) -> Result<Self> {
70        Self::from_terminal_with_colour_config(terminal, DefaultColourConfig)
71    }
72}
73
74impl<C: ColourConfig> Context<C> {
75    pub fn with_colour_config(mut colour_config: C) -> Result<Self> {
76        Terminal::new(
77            colour_config.default_foreground(),
78            colour_config.default_background(),
79        )
80        .and_then(|terminal| {
81            Self::from_terminal_with_colour_config(terminal, colour_config)
82        })
83    }
84
85    pub fn from_terminal_with_colour_config(
86        mut terminal: Terminal,
87        mut colour_config: C,
88    ) -> Result<Self> {
89        let size = terminal.resize_if_necessary(
90            colour_config.default_foreground(),
91            colour_config.default_background(),
92        )?;
93        let grid = Grid::new(size, UnixColourConversion(colour_config));
94        Ok(Self { terminal, grid })
95    }
96
97    fn resize_if_necessary(&mut self) -> Result<()> {
98        let size = self.terminal.resize_if_necessary(
99            self.grid.default_foreground(),
100            self.grid.default_background(),
101        )?;
102        if size != self.grid.size() {
103            self.grid.resize(size);
104        }
105        Ok(())
106    }
107
108    pub fn drain_input(&mut self) -> Result<DrainInput> {
109        self.terminal.drain_input()
110    }
111
112    /// Gets an input event from the terminal if one is present,
113    /// returning immediately.
114    pub fn poll_input(&mut self) -> Result<Option<Input>> {
115        self.terminal.poll_input()
116    }
117
118    /// Gets an input event from the terminal, waiting until
119    /// an event occurs.
120    pub fn wait_input(&mut self) -> Result<Input> {
121        self.terminal.wait_input()
122    }
123
124    /// Gets an input event from the terminal, waiting until
125    /// either an event occurs, or the timeout expires, in which
126    /// case this method returns `None`.
127    pub fn wait_input_timeout(&mut self, timeout: Duration) -> Result<Option<Input>> {
128        self.terminal.wait_input_timeout(timeout)
129    }
130
131    pub fn render<V: View<T>, T>(&mut self, view: &mut V, data: T) -> Result<()> {
132        let size = self.size()?;
133        self.render_at(view, data, ViewContext::default_with_size(size))
134    }
135
136    pub fn render_at<V: View<T>, T, R: ViewTransformRgb24>(
137        &mut self,
138        view: &mut V,
139        data: T,
140        context: ViewContext<R>,
141    ) -> Result<()> {
142        self.resize_if_necessary()?;
143        self.grid.clear();
144        view.view(data, context, &mut self.grid);
145        self.terminal.draw_grid(&mut self.grid)
146    }
147
148    pub fn size(&self) -> Result<Size> {
149        self.terminal.size()
150    }
151}