twm 0.13.0

A customizable workspace manager for tmux
Documentation
use std::{io, panic};

use anyhow::Result;
use crossterm::{
    event::{DisableMouseCapture, EnableMouseCapture},
    terminal::{self, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::Terminal;
use ratatui::backend::CrosstermBackend;
use std::time::Duration;

use crate::ui::picker::Picker;

use super::EventHandler;
pub type CrosstermTerminal = ratatui::Terminal<ratatui::backend::CrosstermBackend<std::io::Stderr>>;

pub struct Tui {
    running: bool,
    terminal: CrosstermTerminal,
    pub events: EventHandler,
}

impl Tui {
    pub fn start() -> Result<Self> {
        let backend = CrosstermBackend::new(std::io::stderr());
        let terminal = Terminal::new(backend)?;
        let events = EventHandler::new(Duration::from_millis(15));
        let tui = Self::new(terminal, events);
        Ok(tui)
    }

    pub fn new(terminal: CrosstermTerminal, events: EventHandler) -> Self {
        Self {
            terminal,
            events,
            running: false,
        }
    }

    pub fn enter(&mut self) -> Result<()> {
        if self.running {
            return Ok(());
        }
        self.running = true;
        terminal::enable_raw_mode()?;
        crossterm::execute!(io::stderr(), EnterAlternateScreen, EnableMouseCapture)?;

        let panic_hook = panic::take_hook();
        panic::set_hook(Box::new(move |panic| {
            Self::reset().expect("Failed to reset the terminal");
            panic_hook(panic);
        }));

        self.terminal.hide_cursor()?;
        self.terminal.clear()?;
        Ok(())
    }

    fn reset() -> Result<()> {
        terminal::disable_raw_mode()?;
        crossterm::execute!(io::stderr(), LeaveAlternateScreen, DisableMouseCapture)?;
        Ok(())
    }

    pub fn exit(&mut self) -> Result<()> {
        if !self.running {
            return Ok(());
        }
        Self::reset()?;
        self.terminal.show_cursor()?;
        self.running = false;
        Ok(())
    }

    pub fn draw(&mut self, picker: &mut Picker) -> Result<()> {
        self.terminal.draw(|frame| picker.render(frame))?;
        Ok(())
    }
}

impl Drop for Tui {
    fn drop(&mut self) {
        self.exit()
            .unwrap_or_else(|e| eprintln!("Failed to exit TUI: {}", e));
    }
}