1 use ::tui::buffer::Buffer;
2 use ::tui::layout::Rect;
3 use ::tui::style::{Color, Modifier, Style};
4 use ::tui::widgets::Widget;
5
6 use crate::state::tiles::FileType;
7 use crate::state::FileToDelete;
8 use crate::ui::format::truncate_middle;
9 use crate::ui::grid::draw_filled_rect;
10
truncated_file_name_line(file_to_delete: &FileToDelete, max_len: u16) -> String11 fn truncated_file_name_line(file_to_delete: &FileToDelete, max_len: u16) -> String {
12 let full_path = file_to_delete
13 .full_path()
14 .into_os_string()
15 .into_string()
16 .expect("could not convert os string to string");
17 let file_name = file_to_delete
18 .path_to_file
19 .last()
20 .expect("could not find file to delete")
21 .to_string_lossy();
22 #[cfg(test)]
23 let full_path = str::replace(&full_path, "\\", "/");
24 if max_len > full_path.len() as u16 {
25 full_path
26 } else {
27 truncate_middle(&file_name, max_len)
28 }
29 }
30
31 fn render_deletion_prompt(buf: &mut Buffer, message_rect: &Rect, file_to_delete: &FileToDelete) {
32 let max_text_len = message_rect.width - 4;
33 let file_name_line = truncated_file_name_line(file_to_delete, max_text_len);
34 let text_style = Style::default()
35 .bg(Color::Black)
36 .fg(Color::Red)
37 .add_modifier(Modifier::BOLD);
38 let question_line = match file_to_delete.file_type {
39 FileType::File => {
40 if max_text_len >= 17 {
41 "Delete this file?".to_string()
42 } else if max_text_len >= 3 {
43 "Delete?".to_string()
44 } else {
45 unreachable!("should not render if terminal is so small");
46 }
47 }
48 FileType::Folder => {
49 let children = file_to_delete
50 .num_descendants
51 .expect("folder should have descendants");
52 let full_line = format!("Delete folder with {} children?", children);
53 let short_line = "Delete folder?".to_string();
54 if max_text_len >= full_line.len() as u16 {
55 full_line
56 } else if max_text_len >= short_line.len() as u16 {
57 short_line
58 } else {
59 unreachable!("should not render if terminal is so small");
60 }
61 }
62 };
63 let y_n_line = "(y/n)";
64 let question_line_start_position =
65 ((message_rect.width - question_line.len() as u16) as f64 / 2.0).ceil() as u16
66 + message_rect.x;
67 let file_name_line_start_position =
68 ((message_rect.width - file_name_line.len() as u16) as f64 / 2.0).ceil() as u16
69 + message_rect.x;
70 let y_n_line_start_position =
71 ((message_rect.width - y_n_line.len() as u16) as f64 / 2.0).ceil() as u16 + message_rect.x;
72 buf.set_string(
73 question_line_start_position,
74 message_rect.y + message_rect.height / 2 - 3,
75 question_line,
76 text_style,
77 );
78 buf.set_string(
79 file_name_line_start_position,
80 message_rect.y + message_rect.height / 2,
81 file_name_line,
82 text_style,
83 );
84 buf.set_string(
85 y_n_line_start_position,
86 message_rect.y + message_rect.height / 2 + 3,
87 y_n_line,
88 text_style,
89 );
90 }
91
92 fn render_deletion_in_progress(
93 buf: &mut Buffer,
94 message_rect: &Rect,
95 file_to_delete: &FileToDelete,
96 ) {
97 let max_text_len = message_rect.width - 4;
98 let file_name_line = truncated_file_name_line(file_to_delete, max_text_len);
99 let deleting_line = "Deleting";
100 let text_style = Style::default()
101 .bg(Color::Black)
102 .fg(Color::Red)
103 .add_modifier(Modifier::BOLD);
104 let deleting_line_start_position =
105 ((message_rect.width - deleting_line.len() as u16) as f64 / 2.0).ceil() as u16
106 + message_rect.x;
107 let file_line_start_position = ((message_rect.width - file_name_line.len() as u16) as f64 / 2.0)
108 .ceil() as u16
109 + message_rect.x;
110 buf.set_string(
111 deleting_line_start_position,
112 message_rect.y + message_rect.height / 2 - 1,
113 deleting_line,
114 text_style,
115 );
116 buf.set_string(
117 file_line_start_position,
118 message_rect.y + message_rect.height / 2 + 1,
119 file_name_line,
120 text_style,
121 );
122 }
123
124 pub struct MessageBox<'a> {
125 file_to_delete: &'a FileToDelete,
126 deletion_in_progress: bool,
127 }
128
129 impl<'a> MessageBox<'a> {
130 pub fn new(file_to_delete: &'a FileToDelete, deletion_in_progress: bool) -> Self {
131 Self {
132 file_to_delete,
133 deletion_in_progress,
134 }
135 }
136 }
137
138 impl<'a> Widget for MessageBox<'a> {
139 fn render(self, area: Rect, buf: &mut Buffer) {
140 let (width, height) = if area.width > 150 {
141 (150, 10)
142 } else if area.width >= 50 {
143 (area.width / 2, 10)
144 } else {
145 unreachable!("app should not be rendered if window is so small")
146 };
147
148 // position self in the middle of the rect
149 let x = ((area.x + area.width) / 2) - width / 2;
150 let y = ((area.y + area.height) / 2) - height / 2;
151
152 let message_rect = Rect {
153 x,
154 y,
155 width,
156 height,
157 };
158 let fill_style = Style::default()
159 .bg(Color::Black)
160 .fg(Color::Red)
161 .add_modifier(Modifier::BOLD);
162
163 draw_filled_rect(buf, fill_style, &message_rect);
164 if self.deletion_in_progress {
165 render_deletion_in_progress(buf, &message_rect, &self.file_to_delete);
166 } else {
167 render_deletion_prompt(buf, &message_rect, &self.file_to_delete);
168 }
169 }
170 }
171