rat_salsa/
run_config.rs

1use crate::_private::NonExhaustive;
2use crate::poll::PollEvents;
3use crossbeam::channel::TryRecvError;
4use ratatui_core::layout::Rect;
5use ratatui_core::terminal::{Terminal, TerminalOptions, Viewport};
6use ratatui_crossterm::CrosstermBackend;
7use ratatui_crossterm::crossterm::cursor::SetCursorStyle;
8use ratatui_crossterm::crossterm::event::KeyboardEnhancementFlags;
9use std::cell::RefCell;
10use std::fmt::{Debug, Formatter};
11use std::io;
12use std::io::{Stdout, stdout};
13use std::rc::Rc;
14
15/// Captures some parameters for [crate::run_tui()].
16pub struct RunConfig<Event, Error>
17where
18    Event: 'static,
19    Error: 'static,
20{
21    /// This is the renderer that connects to the backend, and calls out
22    /// for rendering the application.
23    ///
24    /// Defaults to RenderCrossterm.
25    pub(crate) term: Rc<RefCell<Terminal<CrosstermBackend<Stdout>>>>,
26    /// List of all event-handlers for the application.
27    ///
28    /// Defaults to PollTimers, PollCrossterm, PollTasks. Add yours here.
29    pub(crate) poll: Vec<Box<dyn PollEvents<Event, Error>>>,
30    /// Terminal init flags.
31    pub(crate) term_init: TermInit,
32}
33
34impl<Event, Error> Debug for RunConfig<Event, Error>
35where
36    Event: 'static,
37    Error: 'static,
38{
39    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
40        f.debug_struct("RunConfig")
41            .field("term", &self.term)
42            .field("poll", &"...")
43            .field("term_init", &self.term_init)
44            .finish()
45    }
46}
47
48impl<Event, Error> RunConfig<Event, Error>
49where
50    Event: 'static,
51    Error: 'static + From<io::Error> + From<TryRecvError>,
52{
53    /// Defaults.
54    #[allow(clippy::should_implement_trait)]
55    pub fn default() -> Result<Self, Error> {
56        Ok(Self {
57            term: Rc::new(RefCell::new(Terminal::new(
58                CrosstermBackend::new(stdout()),
59            )?)),
60            poll: Default::default(),
61            term_init: Default::default(),
62        })
63    }
64
65    /// New with terminal.
66    pub fn new(term: Terminal<CrosstermBackend<Stdout>>) -> Self {
67        Self {
68            term: Rc::new(RefCell::new(term)),
69            poll: Default::default(),
70            term_init: Default::default(),
71        }
72    }
73
74    /// Initialize as an inline terminal
75    pub fn inline(lines: u16, clear_on_shutdown: bool) -> Result<Self, io::Error> {
76        Ok(Self {
77            term: Rc::new(RefCell::new(Terminal::with_options(
78                CrosstermBackend::new(stdout()),
79                TerminalOptions {
80                    viewport: Viewport::Inline(lines),
81                },
82            )?)),
83            poll: Default::default(),
84            term_init: TermInit {
85                alternate_screen: false,
86                clear_area: clear_on_shutdown,
87                ..Default::default()
88            },
89        })
90    }
91
92    /// Initialize for a fixed portion of the actual terminal.
93    pub fn fixed(area: Rect, clear_on_shutdown: bool) -> Result<Self, io::Error> {
94        Ok(Self {
95            term: Rc::new(RefCell::new(Terminal::with_options(
96                CrosstermBackend::new(stdout()),
97                TerminalOptions {
98                    viewport: Viewport::Fixed(area),
99                },
100            )?)),
101            poll: Default::default(),
102            term_init: TermInit {
103                alternate_screen: false,
104                clear_area: clear_on_shutdown,
105                ..Default::default()
106            },
107        })
108    }
109
110    /// Add one more poll impl.
111    pub fn poll(mut self, poll: impl PollEvents<Event, Error> + 'static) -> Self {
112        self.poll.push(Box::new(poll));
113        self
114    }
115
116    /// Set flags for terminal init/shutdown.
117    pub fn term_init(mut self, term_init: TermInit) -> Self {
118        self.term_init = term_init;
119        self
120    }
121}
122
123#[derive(Clone, Copy)]
124pub struct TermInit {
125    /// Don't do any init/shutdown.
126    /// Will be done by main().
127    pub manual: bool,
128    /// Switch to alternate screen.
129    pub alternate_screen: bool,
130    /// Enable mouse.
131    pub mouse_capture: bool,
132    /// See [wikipedia](https://en.wikipedia.org/wiki/Bracketed-paste)
133    pub bracketed_paste: bool,
134    /// Enable blinking cursor.
135    pub cursor_blinking: bool,
136    /// Set the cursor-style.
137    pub cursor: SetCursorStyle,
138    /// Key encoding options.
139    pub keyboard_enhancements: KeyboardEnhancementFlags,
140    /// Clear the configured terminal at shutdown.
141    pub clear_area: bool,
142
143    pub non_exhaustive: NonExhaustive,
144}
145
146impl Debug for TermInit {
147    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
148        f.debug_struct("TermInit")
149            .field("manual", &self.manual)
150            .field("alternate_screen", &self.alternate_screen)
151            .field("mouse_capture", &self.mouse_capture)
152            .field("bracketed_paste", &self.bracketed_paste)
153            .field("cursor_blinking", &self.cursor_blinking)
154            .field(
155                "cursor",
156                &match self.cursor {
157                    SetCursorStyle::DefaultUserShape => "DefaultUserShape",
158                    SetCursorStyle::BlinkingBlock => "BlinkingBlock",
159                    SetCursorStyle::SteadyBlock => "SteadyBlock",
160                    SetCursorStyle::BlinkingUnderScore => "BlinkingUnderScore",
161                    SetCursorStyle::SteadyUnderScore => "SteadyUnderScore",
162                    SetCursorStyle::BlinkingBar => "BlinkingBar",
163                    SetCursorStyle::SteadyBar => "SteadyBar",
164                },
165            )
166            .field("keyboard_enhancements", &self.keyboard_enhancements)
167            .finish()
168    }
169}
170
171impl Default for TermInit {
172    fn default() -> Self {
173        Self {
174            manual: false,
175            alternate_screen: true,
176            mouse_capture: true,
177            bracketed_paste: true,
178            cursor_blinking: true,
179            cursor: SetCursorStyle::DefaultUserShape,
180            keyboard_enhancements: KeyboardEnhancementFlags::REPORT_EVENT_TYPES
181                | KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
182                | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
183                | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES,
184            clear_area: false,
185            non_exhaustive: NonExhaustive,
186        }
187    }
188}