1 use ::std::path::PathBuf;
2 use ::tui::backend::Backend;
3 use ::tui::layout::{Constraint, Direction, Layout, Rect};
4 use ::tui::Terminal;
5 
6 use crate::state::files::FileTree;
7 use crate::state::tiles::Board;
8 use crate::state::UiEffects;
9 use crate::ui::grid::RectangleGrid;
10 use crate::ui::modals::{ConfirmBox, ErrorBox, MessageBox, WarningBox};
11 use crate::ui::title::TitleLine;
12 use crate::ui::{BottomLine, TermTooSmall};
13 use crate::UiMode;
14 
15 pub struct FolderInfo<'a> {
16     pub path: &'a PathBuf,
17     pub size: u128,
18     pub num_descendants: u64,
19 }
20 
21 pub struct Display<B>
22 where
23     B: Backend,
24 {
25     terminal: Terminal<B>,
26 }
27 
28 impl<B> Display<B>
29 where
30     B: Backend,
31 {
new(terminal_backend: B) -> Self32     pub fn new(terminal_backend: B) -> Self {
33         let mut terminal = Terminal::new(terminal_backend).expect("failed to create terminal");
34         terminal.clear().expect("failed to clear terminal");
35         terminal.hide_cursor().expect("failed to hide cursor");
36         Display { terminal }
37     }
size(&self) -> Rect38     pub fn size(&self) -> Rect {
39         self.terminal.size().expect("could not get terminal size")
40     }
render( &mut self, file_tree: &mut FileTree, board: &mut Board, ui_mode: &UiMode, ui_effects: &UiEffects, )41     pub fn render(
42         &mut self,
43         file_tree: &mut FileTree,
44         board: &mut Board,
45         ui_mode: &UiMode,
46         ui_effects: &UiEffects,
47     ) {
48         self.terminal
49             .draw(|f| {
50                 let full_screen = f.size();
51                 let current_path = file_tree.get_current_path();
52                 let current_path_size = file_tree.get_current_folder_size();
53                 let current_path_descendants = file_tree.get_current_folder().num_descendants;
54                 let base_path_size = file_tree.get_total_size();
55                 let base_path_descendants = file_tree.get_total_descendants();
56                 let current_path_info = FolderInfo {
57                     path: &current_path,
58                     size: current_path_size,
59                     num_descendants: current_path_descendants,
60                 };
61                 let path_in_filesystem = &file_tree.path_in_filesystem;
62                 let base_path_info = FolderInfo {
63                     path: &path_in_filesystem,
64                     size: base_path_size,
65                     num_descendants: base_path_descendants,
66                 };
67                 let mut chunks = Layout::default()
68                     .direction(Direction::Vertical)
69                     .margin(0)
70                     .constraints(
71                         [
72                             Constraint::Length(1),
73                             Constraint::Min(10),
74                             Constraint::Length(2),
75                         ]
76                         .as_ref(),
77                     )
78                     .split(full_screen);
79 
80                 // -1 cos we draw starting at offset 1 in both x and y directions
81 
82                 chunks[1].width -= 1;
83                 chunks[1].height -= 1;
84                 board.change_area(&chunks[1]);
85                 match ui_mode {
86                     UiMode::Loading => {
87                         f.render_widget(
88                             TitleLine::new(
89                                 base_path_info,
90                                 current_path_info,
91                                 file_tree.space_freed,
92                             )
93                             .progress_indicator(ui_effects.loading_progress_indicator)
94                             .path_error(ui_effects.current_path_is_red)
95                             .read_errors(file_tree.failed_to_read)
96                             .zoom_level(board.zoom_level)
97                             .show_loading(),
98                             chunks[0],
99                         );
100                         f.render_widget(
101                             RectangleGrid::new(
102                                 &board.tiles,
103                                 board.unrenderable_tile_coordinates,
104                                 board.selected_index,
105                             ),
106                             chunks[1],
107                         );
108                         f.render_widget(
109                             BottomLine::new()
110                                 .currently_selected(board.currently_selected())
111                                 .last_read_path(ui_effects.last_read_path.as_ref())
112                                 .hide_delete(),
113                             chunks[2],
114                         );
115                     }
116                     UiMode::Normal => {
117                         f.render_widget(
118                             TitleLine::new(
119                                 base_path_info,
120                                 current_path_info,
121                                 file_tree.space_freed,
122                             )
123                             .path_error(ui_effects.current_path_is_red)
124                             .flash_space(ui_effects.flash_space_freed)
125                             .zoom_level(board.zoom_level)
126                             .read_errors(file_tree.failed_to_read),
127                             chunks[0],
128                         );
129                         f.render_widget(
130                             RectangleGrid::new(
131                                 &board.tiles,
132                                 board.unrenderable_tile_coordinates,
133                                 board.selected_index,
134                             ),
135                             chunks[1],
136                         );
137                         f.render_widget(
138                             BottomLine::new().currently_selected(board.currently_selected()),
139                             chunks[2],
140                         );
141                     }
142                     UiMode::ScreenTooSmall => {
143                         f.render_widget(TermTooSmall::new(), full_screen);
144                     }
145                     UiMode::DeleteFile(file_to_delete) => {
146                         f.render_widget(
147                             TitleLine::new(
148                                 base_path_info,
149                                 current_path_info,
150                                 file_tree.space_freed,
151                             )
152                             .path_error(ui_effects.current_path_is_red)
153                             .zoom_level(board.zoom_level)
154                             .read_errors(file_tree.failed_to_read),
155                             chunks[0],
156                         );
157                         f.render_widget(
158                             RectangleGrid::new(
159                                 &board.tiles,
160                                 board.unrenderable_tile_coordinates,
161                                 board.selected_index,
162                             ),
163                             chunks[1],
164                         );
165                         f.render_widget(
166                             BottomLine::new().currently_selected(board.currently_selected()),
167                             chunks[2],
168                         );
169                         f.render_widget(
170                             MessageBox::new(file_to_delete, ui_effects.deletion_in_progress),
171                             full_screen,
172                         );
173                     }
174                     UiMode::ErrorMessage(message) => {
175                         f.render_widget(
176                             TitleLine::new(
177                                 base_path_info,
178                                 current_path_info,
179                                 file_tree.space_freed,
180                             )
181                             .path_error(ui_effects.current_path_is_red)
182                             .flash_space(ui_effects.flash_space_freed)
183                             .zoom_level(board.zoom_level)
184                             .read_errors(file_tree.failed_to_read),
185                             chunks[0],
186                         );
187                         f.render_widget(
188                             RectangleGrid::new(
189                                 &board.tiles,
190                                 board.unrenderable_tile_coordinates,
191                                 board.selected_index,
192                             ),
193                             chunks[1],
194                         );
195                         f.render_widget(
196                             BottomLine::new().currently_selected(board.currently_selected()),
197                             chunks[2],
198                         );
199                         f.render_widget(ErrorBox::new(message), full_screen);
200                     }
201                     UiMode::Exiting { app_loaded } => {
202                         if *app_loaded {
203                             // render normal ui mode
204                             f.render_widget(
205                                 TitleLine::new(
206                                     base_path_info,
207                                     current_path_info,
208                                     file_tree.space_freed,
209                                 )
210                                 .path_error(ui_effects.current_path_is_red)
211                                 .flash_space(ui_effects.flash_space_freed)
212                                 .zoom_level(board.zoom_level)
213                                 .read_errors(file_tree.failed_to_read),
214                                 chunks[0],
215                             );
216                             f.render_widget(
217                                 BottomLine::new().currently_selected(board.currently_selected()),
218                                 chunks[2],
219                             );
220                         } else {
221                             // render loading ui mode
222                             f.render_widget(
223                                 TitleLine::new(
224                                     base_path_info,
225                                     current_path_info,
226                                     file_tree.space_freed,
227                                 )
228                                 .progress_indicator(ui_effects.loading_progress_indicator)
229                                 .path_error(ui_effects.current_path_is_red)
230                                 .zoom_level(board.zoom_level)
231                                 .read_errors(file_tree.failed_to_read)
232                                 .show_loading(),
233                                 chunks[0],
234                             );
235                             f.render_widget(
236                                 BottomLine::new()
237                                     .currently_selected(board.currently_selected())
238                                     .last_read_path(ui_effects.last_read_path.as_ref())
239                                     .hide_delete(),
240                                 chunks[2],
241                             );
242                         }
243                         // render common widgets
244                         f.render_widget(
245                             RectangleGrid::new(
246                                 &board.tiles,
247                                 board.unrenderable_tile_coordinates,
248                                 board.selected_index,
249                             ),
250                             chunks[1],
251                         );
252                         f.render_widget(ConfirmBox::new(), full_screen);
253                     }
254                     UiMode::WarningMessage(_) => {
255                         f.render_widget(
256                             TitleLine::new(
257                                 base_path_info,
258                                 current_path_info,
259                                 file_tree.space_freed,
260                             )
261                             .progress_indicator(ui_effects.loading_progress_indicator)
262                             .path_error(ui_effects.current_path_is_red)
263                             .read_errors(file_tree.failed_to_read)
264                             .show_loading(),
265                             chunks[0],
266                         );
267                         f.render_widget(
268                             RectangleGrid::new(
269                                 &board.tiles,
270                                 board.unrenderable_tile_coordinates,
271                                 board.selected_index,
272                             ),
273                             chunks[1],
274                         );
275                         f.render_widget(
276                             BottomLine::new()
277                                 .currently_selected(board.currently_selected())
278                                 .last_read_path(ui_effects.last_read_path.as_ref())
279                                 .hide_delete(),
280                             chunks[2],
281                         );
282                         f.render_widget(WarningBox::new(), full_screen);
283                     }
284                 };
285             })
286             .expect("failed to draw");
287     }
clear(&mut self)288     pub fn clear(&mut self) {
289         self.terminal.clear().expect("failed to clear terminal");
290         self.terminal.show_cursor().expect("failed to show cursor");
291     }
292 }
293