summaryrefslogtreecommitdiffstats
path: root/src/ui/tui_backend.rs
blob: dc15f03af79983237a49c90643eca722ba7643f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use std::io::stdout;
use std::io::Write;

use termion::raw::{IntoRawMode, RawTerminal};
use termion::screen::AlternateScreen;
use tui::backend::TermionBackend;
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::widgets::{Block, Borders, Widget};

#[cfg(feature = "mouse")]
use termion::input::MouseTerminal;

use crate::config::option::DisplayOption;

trait New {
    fn new() -> std::io::Result<Self>
    where
        Self: Sized;
}

#[cfg(feature = "mouse")]
type Screen = MouseTerminal<AlternateScreen<RawTerminal<std::io::Stdout>>>;
#[cfg(feature = "mouse")]
impl New for Screen {
    fn new() -> std::io::Result<Self> {
        let stdout = std::io::stdout().into_raw_mode()?;
        let alt_screen = MouseTerminal::from(AlternateScreen::from(stdout));
        return Ok(alt_screen);
    }
}
#[cfg(not(feature = "mouse"))]
type Screen = AlternateScreen<RawTerminal<std::io::Stdout>>;
#[cfg(not(feature = "mouse"))]
impl New for Screen {
    fn new() -> std::io::Result<Self> {
        let stdout = std::io::stdout().into_raw_mode()?;
        let alt_screen = AlternateScreen::from(stdout);
        Ok(alt_screen)
    }
}

pub type JoshutoTerminal = tui::Terminal<TermionBackend<Screen>>;

pub struct TuiBackend {
    pub terminal: Option<JoshutoTerminal>,
}

impl TuiBackend {
    pub fn new() -> std::io::Result<Self> {
        let mut alt_screen = Screen::new()?;
        // clears the screen of artifacts
        write!(alt_screen, "{}", termion::clear::All)?;

        let backend = TermionBackend::new(alt_screen);
        let mut terminal = tui::Terminal::new(backend)?;
        terminal.hide_cursor()?;
        Ok(Self {
            terminal: Some(terminal),
        })
    }

    pub fn render<W>(&mut self, widget: W)
    where
        W: Widget,
    {
        let _ = self.terminal_mut().draw(|frame| {
            let rect = frame.size();
            frame.render_widget(widget, rect);
        });
    }

    pub fn terminal_mut(&mut self) -> &mut JoshutoTerminal {
        self.terminal.as_mut().unwrap()
    }

    pub fn terminal_drop(&mut self) {
        let _ = self.terminal.take();
        let _ = stdout().flush();
    }

    pub fn terminal_restore(&mut self) -> std::io::Result<()> {
        let mut new_backend = TuiBackend::new()?;
        std::mem::swap(&mut self.terminal, &mut new_backend.terminal);
        Ok(())
    }
}

pub fn build_layout(
    area: Rect,
    constraints: &[Constraint; 3],
    display_options: &DisplayOption,
) -> Vec<Rect> {
    let layout_rect = if display_options.show_borders() {
        let area = Rect {
            y: area.top() + 1,
            height: area.height - 2,
            ..area
        };

        let block = Block::default().borders(Borders::ALL);
        let inner = block.inner(area);

        let layout_rect = Layout::default()
            .direction(Direction::Horizontal)
            .constraints(constraints.as_ref())
            .split(inner);

        let block = Block::default().borders(Borders::RIGHT);
        let inner1 = block.inner(layout_rect[0]);

        let block = Block::default().borders(Borders::LEFT);
        let inner3 = block.inner(layout_rect[2]);

        vec![inner1, layout_rect[1], inner3]
    } else {
        let mut layout_rect = Layout::default()
            .direction(Direction::Horizontal)
            .vertical_margin(1)
            .constraints(constraints.as_ref())
            .split(area);

        layout_rect[0] = Rect {
            width: layout_rect[0].width - 1,
            ..layout_rect[0]
        };
        layout_rect[1] = Rect {
            width: layout_rect[1].width - 1,
            ..layout_rect[1]
        };
        layout_rect
    };
    layout_rect
}