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
15pub struct RunConfig<Event, Error>
17where
18 Event: 'static,
19 Error: 'static,
20{
21 pub(crate) term: Rc<RefCell<Terminal<CrosstermBackend<Stdout>>>>,
26 pub(crate) poll: Vec<Box<dyn PollEvents<Event, Error>>>,
30 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 #[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 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 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 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 pub fn poll(mut self, poll: impl PollEvents<Event, Error> + 'static) -> Self {
112 self.poll.push(Box::new(poll));
113 self
114 }
115
116 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 pub manual: bool,
128 pub raw_mode: bool,
130 pub alternate_screen: bool,
132 pub mouse_capture: bool,
134 pub bracketed_paste: bool,
136 pub cursor_blinking: bool,
138 pub cursor: SetCursorStyle,
140 pub keyboard_enhancements: KeyboardEnhancementFlags,
142 pub clear_area: bool,
144
145 pub non_exhaustive: NonExhaustive,
146}
147
148impl Debug for TermInit {
149 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
150 f.debug_struct("TermInit")
151 .field("manual", &self.manual)
152 .field("raw_mode", &self.raw_mode)
153 .field("alternate_screen", &self.alternate_screen)
154 .field("mouse_capture", &self.mouse_capture)
155 .field("bracketed_paste", &self.bracketed_paste)
156 .field("cursor_blinking", &self.cursor_blinking)
157 .field(
158 "cursor",
159 &match self.cursor {
160 SetCursorStyle::DefaultUserShape => "DefaultUserShape",
161 SetCursorStyle::BlinkingBlock => "BlinkingBlock",
162 SetCursorStyle::SteadyBlock => "SteadyBlock",
163 SetCursorStyle::BlinkingUnderScore => "BlinkingUnderScore",
164 SetCursorStyle::SteadyUnderScore => "SteadyUnderScore",
165 SetCursorStyle::BlinkingBar => "BlinkingBar",
166 SetCursorStyle::SteadyBar => "SteadyBar",
167 },
168 )
169 .field("keyboard_enhancements", &self.keyboard_enhancements)
170 .finish()
171 }
172}
173
174impl Default for TermInit {
175 fn default() -> Self {
176 Self {
177 manual: false,
178 raw_mode: true,
179 alternate_screen: true,
180 mouse_capture: true,
181 bracketed_paste: true,
182 cursor_blinking: true,
183 cursor: SetCursorStyle::DefaultUserShape,
184 keyboard_enhancements: KeyboardEnhancementFlags::REPORT_EVENT_TYPES
185 | KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
186 | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
187 | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES,
188 clear_area: false,
189 non_exhaustive: NonExhaustive,
190 }
191 }
192}