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 alternate_screen: bool,
130 pub mouse_capture: bool,
132 pub bracketed_paste: bool,
134 pub cursor_blinking: bool,
136 pub cursor: SetCursorStyle,
138 pub keyboard_enhancements: KeyboardEnhancementFlags,
140 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}