1use crate::input::Input;
2use crossterm::{
3 execute,
4 terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
5};
6use ratatui::{
7 layout::Rect, prelude::CrosstermBackend, DefaultTerminal, Frame, Terminal, TerminalOptions,
8 Viewport,
9};
10use std::{io::stdout, panic, sync::Once};
11
12pub use draw::{draw, draw_fuzzy_finder};
13pub use scroller::Scroller;
14
15pub mod confirmation;
16mod draw;
17mod popup;
18mod scroller;
19
20static ALTERNATE_SCREEN: Once = Once::new();
21
22pub struct Options {
23 pub viewport: Viewport,
24 pub raw_mode: bool,
25 pub alternate_screen: bool,
26}
27
28impl Default for Options {
29 fn default() -> Self {
30 Self {
31 viewport: Viewport::Fullscreen,
32 raw_mode: true,
33 alternate_screen: true,
34 }
35 }
36}
37
38impl From<Options> for TerminalOptions {
39 fn from(value: Options) -> Self {
40 TerminalOptions {
41 viewport: value.viewport,
42 }
43 }
44}
45
46pub fn init() -> DefaultTerminal {
47 init_with_options(Options::default())
48}
49
50pub fn init_inline(height: u16) -> DefaultTerminal {
51 init_with_options(Options {
52 viewport: Viewport::Inline(height),
53 raw_mode: true,
54 alternate_screen: false,
55 })
56}
57
58fn init_with_options(options: Options) -> DefaultTerminal {
59 set_panic_hook();
60
61 if options.raw_mode {
62 enable_raw_mode().expect("Failed to enable raw mode.");
63 }
64
65 if options.alternate_screen {
66 ALTERNATE_SCREEN.call_once(|| {
67 execute!(stdout(), EnterAlternateScreen).expect("Failed to enter alternate screen.");
68 });
69 }
70
71 let backend = CrosstermBackend::new(stdout());
72 Terminal::with_options(backend, options.into()).expect("Failed to initialize terminal.")
73}
74
75fn set_panic_hook() {
76 let default_hook = panic::take_hook();
77
78 panic::set_hook(Box::new(move |info| {
79 restore();
80 default_hook(info);
81 }));
82}
83
84pub fn restore() {
85 disable_raw_mode().expect("Failed to disable raw mode.");
86
87 if ALTERNATE_SCREEN.is_completed() {
88 execute!(stdout(), LeaveAlternateScreen).expect("Failed to leave alternate screen.");
89 }
90}
91
92fn set_cursor(f: &mut Frame, input: &Input, area: Rect) {
93 f.set_cursor_position((area.x + input.cursor_position(), area.y));
94}