-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Removed screen flicker when entering TUI command screen
- Loading branch information
1 parent
368badb
commit a536ede
Showing
2 changed files
with
60 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,83 @@ | ||
use anyhow::Result; | ||
use crossterm::{ | ||
event::{DisableMouseCapture, EnableMouseCapture}, | ||
execute, | ||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, | ||
use crossterm::terminal::{ | ||
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, | ||
}; | ||
use ratatui::backend::CrosstermBackend; | ||
use std::io::{stdout, Stdout}; | ||
|
||
pub type Terminal = ratatui::Terminal<CrosstermBackend<Stdout>>; | ||
type Terminal = ratatui::Terminal<CrosstermBackend<Stdout>>; | ||
|
||
pub struct TerminalManager { | ||
/// A Terminal User Interface (TUI). | ||
pub struct Tui { | ||
// TODO: don't make public (means moving the UI::draw! macro here) | ||
pub terminal: Terminal, | ||
} | ||
|
||
impl TerminalManager { | ||
impl Tui { | ||
/// Initialise and show a new TUI. | ||
pub fn new() -> Result<Self> { | ||
let terminal = Self::create_tui()?; | ||
let mut terminal_manager = TerminalManager { terminal }; | ||
terminal_manager.show_tui()?; | ||
let terminal = Self::create_new_terminal()?; | ||
let mut terminal_manager = Tui { terminal }; | ||
|
||
terminal_manager.show()?; | ||
|
||
Ok(terminal_manager) | ||
} | ||
|
||
// TODO: maybe we don't have to create the stdout, backend variables again, only once at creation. Optimize that later | ||
fn create_tui() -> Result<Terminal> { | ||
/// Create a new terminal that will write the TUI to stdout. | ||
fn create_new_terminal() -> Result<Terminal> { | ||
Ok(Terminal::new(CrosstermBackend::new(stdout()))?) | ||
} | ||
|
||
/// Show the TUI. | ||
fn show(&mut self) -> Result<()> { | ||
enable_raw_mode()?; | ||
let mut stdout = stdout(); | ||
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; | ||
let backend = CrosstermBackend::new(stdout); | ||
let mut terminal = Terminal::new(backend)?; | ||
terminal.hide_cursor()?; | ||
crossterm::execute!(self.terminal.backend_mut(), EnterAlternateScreen)?; | ||
self.terminal.hide_cursor()?; | ||
|
||
Ok(terminal) | ||
Ok(()) | ||
} | ||
|
||
pub fn show_tui(&mut self) -> Result<()> { | ||
self.terminal = Self::create_tui()?; | ||
/// Show a TUI which has been painted over by another TUI program. | ||
pub fn restore(&mut self) -> Result<()> { | ||
// Our own TUI was painted over by another program previously, so we | ||
// must create a new one. | ||
self.terminal = Self::create_new_terminal()?; | ||
|
||
self.show()?; | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Hide the TUI, but don't unpaint the TUI we have already drawn. This is | ||
/// useful if another program wants to paint a TUI onto the screen, and we | ||
/// don't want to temporarily return to the user's terminal for a | ||
/// split-second. | ||
pub fn hide(&mut self) -> Result<()> { | ||
disable_raw_mode()?; | ||
// The trick to not unpainting our TUI is to not leave the alternate | ||
// screen like we would normally do when hiding the TUI. | ||
self.terminal.show_cursor()?; | ||
|
||
Ok(()) | ||
} | ||
|
||
pub fn hide_tui(&mut self) -> Result<()> { | ||
/// Exit the TUI, which entails completely unpainting the TUI, and returning | ||
/// to the user's terminal. | ||
fn exit(&mut self) -> Result<()> { | ||
disable_raw_mode()?; | ||
execute!( | ||
self.terminal.backend_mut(), | ||
LeaveAlternateScreen, | ||
DisableMouseCapture | ||
)?; | ||
crossterm::execute!(self.terminal.backend_mut(), LeaveAlternateScreen)?; | ||
self.terminal.show_cursor()?; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl Drop for TerminalManager { | ||
impl Drop for Tui { | ||
fn drop(&mut self) { | ||
// TODO: remove unwrap | ||
self.hide_tui().unwrap(); | ||
if let Err(e) = self.exit() { | ||
// TODO: one shouldn't panic in a drop impl, since a second panic would cause instant termination | ||
panic!("Tearing down the TUI failed with: {}", e); | ||
} | ||
} | ||
} |