summaryrefslogtreecommitdiffstats
path: root/src/interactive/widgets/main.rs
blob: 977e79fd9878b7c85cd4ec14355a10122b2dc74a (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
use crate::interactive::{
    react::Component,
    widgets::{
        Entries, Header, ListState, ReactFooter, ReactFooterProps, ReactHelpPane,
        ReactHelpPaneProps,
    },
    FocussedPane, TerminalApp,
};
use dua::traverse::Traversal;
use std::borrow::Borrow;
use tui::style::{Color, Style};
use tui::{
    buffer::Buffer,
    layout::{Constraint, Direction, Layout, Rect},
    style::Modifier,
    widgets::Widget,
};

/// The state that can be mutated while drawing
/// This is easiest compared to alternatives, but at least it's restricted to a subset of the state
#[derive(Default, Clone)] // TODO: remove Clone derive
pub struct DrawState {
    entries_list: ListState,
}

#[derive(Default, Clone)] // TODO: remove clone derive
pub struct ReactMainWindow {
    pub draw_state: DrawState,
    pub help_pane: Option<ReactHelpPane>,
}

impl<'a, 'b> Component for ReactMainWindow {
    type Props = TerminalApp;

    fn render(&mut self, props: impl Borrow<TerminalApp>, area: Rect, buf: &mut Buffer) {
        let TerminalApp {
            traversal:
                Traversal {
                    tree,
                    entries_traversed,
                    total_bytes,
                    ..
                },
            display,
            state,
            ..
        } = props.borrow();
        let draw_state = &mut self.draw_state;

        let regions = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [
                    Constraint::Length(1),
                    Constraint::Max(256),
                    Constraint::Length(1),
                ]
                .as_ref(),
            )
            .split(area);
        let (header_area, entries_area, footer_area) = (regions[0], regions[1], regions[2]);
        let (entries_area, help_pane) = match self.help_pane {
            Some(ref mut pane) => {
                let regions = Layout::default()
                    .direction(Direction::Horizontal)
                    .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
                    .split(entries_area);
                (regions[0], Some((regions[1], pane)))
            }
            None => (entries_area, None),
        };
        let grey = Style {
            fg: Color::DarkGray,
            bg: Color::Reset,
            modifier: Modifier::empty(),
        };
        let white = Style {
            fg: Color::White,
            ..grey
        };
        let (entries_style, help_style) = match state.focussed {
            FocussedPane::Main => (white, grey),
            FocussedPane::Help => (grey, white),
        };

        Header.draw(header_area, buf);
        Entries {
            tree: &tree,
            root: state.root,
            display: *display,
            entries: &state.entries,
            selected: state.selected,
            border_style: entries_style,
            list_state: &mut draw_state.entries_list,
            is_focussed: if let FocussedPane::Main = state.focussed {
                true
            } else {
                false
            },
        }
        .draw(entries_area, buf);

        if let Some((help_area, pane)) = help_pane {
            let props = ReactHelpPaneProps {
                border_style: help_style,
            };
            pane.render(props, help_area, buf);
        }

        ReactFooter.render(
            ReactFooterProps {
                total_bytes: *total_bytes,
                entries_traversed: *entries_traversed,
                format: display.byte_format,
                message: state.message.clone(),
            },
            footer_area,
            buf,
        );
    }
}