feat(ux): implement a warning modal on deleting a file/folder while s… (#60)

* feat(app) implement a warning modal on deleting a file/folder while scanning

add keypress handler for warning_modal

update apps ui state of warning modal

implement autoclose functionality for warningmodal

* remove auto close functionality from warning modal

* fix(controls): use delete key when loading

Co-authored-by: Aram Drevekenin <aram@poor.dev>
This commit is contained in:
Mehdi Mohseni
2020-07-04 16:06:26 +04:30
committed by GitHub
parent f63760d2df
commit 356904fa04
6 changed files with 147 additions and 1 deletions

View File

@@ -19,6 +19,7 @@ pub enum UiMode {
DeleteFile(FileToDelete),
ErrorMessage(String),
Exiting { app_loaded: bool },
WarningMessage(FileToDelete),
}
pub struct App<B>
@@ -120,6 +121,12 @@ where
}
};
}
pub fn show_warning_modal(&mut self) {
if let Some(file_to_delete) = self.get_file_to_delete() {
self.ui_mode = UiMode::WarningMessage(file_to_delete);
self.render();
}
}
pub fn prompt_exit(&mut self) {
self.ui_mode = UiMode::Exiting {
app_loaded: self.loaded,

View File

@@ -52,6 +52,9 @@ pub fn handle_keypress_loading_mode<B: Backend>(evt: Event, app: &mut App<B>) {
key!(char '\n') => {
app.handle_enter();
}
key!(Delete) => {
app.show_warning_modal();
}
key!(Esc) | key!(Backspace) => {
app.go_up();
}
@@ -137,3 +140,11 @@ pub fn handle_keypress_exiting_mode<B: Backend>(evt: Event, app: &mut App<B>) {
_ => (),
};
}
pub fn handle_keypress_warning_message<B: Backend>(evt: Event, app: &mut App<B>) {
match evt {
_ => {
app.reset_ui_mode();
}
}
}

View File

@@ -7,6 +7,7 @@ use ::tui::backend::Backend;
use crate::input::{
handle_keypress_delete_file_mode, handle_keypress_error_message, handle_keypress_exiting_mode,
handle_keypress_loading_mode, handle_keypress_normal_mode, handle_keypress_screen_too_small,
handle_keypress_warning_message,
};
use crate::{App, UiMode};
@@ -85,6 +86,9 @@ where
UiMode::Exiting { app_loaded: _ } => {
handle_keypress_exiting_mode(evt, app);
}
UiMode::WarningMessage(_) => {
handle_keypress_warning_message(evt, app);
}
}
if !app.is_running {
break;

View File

@@ -7,7 +7,7 @@ use crate::state::files::FileTree;
use crate::state::tiles::Board;
use crate::state::UiEffects;
use crate::ui::grid::RectangleGrid;
use crate::ui::modals::{ConfirmBox, ErrorBox, MessageBox};
use crate::ui::modals::{ConfirmBox, ErrorBox, MessageBox, WarningBox};
use crate::ui::title::TitleLine;
use crate::ui::{BottomLine, TermTooSmall};
use crate::UiMode;
@@ -246,6 +246,36 @@ where
);
f.render_widget(ConfirmBox::new(), full_screen);
}
UiMode::WarningMessage(_) => {
f.render_widget(
TitleLine::new(
base_path_info,
current_path_info,
file_tree.space_freed,
)
.progress_indicator(ui_effects.loading_progress_indicator)
.path_error(ui_effects.current_path_is_red)
.read_errors(file_tree.failed_to_read)
.show_loading(),
chunks[0],
);
f.render_widget(
RectangleGrid::new(
&board.tiles,
board.unrenderable_tile_coordinates,
board.selected_index,
),
chunks[1],
);
f.render_widget(
BottomLine::new()
.currently_selected(board.currently_selected())
.last_read_path(ui_effects.last_read_path.as_ref())
.hide_delete(),
chunks[2],
);
f.render_widget(WarningBox::new(), full_screen);
}
};
})
.expect("failed to draw");

View File

@@ -1,7 +1,9 @@
mod confirm_box;
mod error_box;
mod message_box;
mod warning_box;
pub use confirm_box::*;
pub use error_box::*;
pub use message_box::*;
pub use warning_box::*;

View File

@@ -0,0 +1,92 @@
use tui::buffer::Buffer;
use tui::layout::Rect;
use tui::style::{Color, Modifier, Style};
use tui::widgets::Widget;
use crate::ui::format::truncate_end;
use crate::ui::grid::draw_filled_rect;
pub struct WarningBox {}
impl<'a> WarningBox {
pub fn new() -> Self {
Self {}
}
}
impl<'a> Widget for WarningBox {
fn render(self, area: Rect, buf: &mut Buffer) {
let (width, height) = if area.width > 150 {
(150, 10)
} else if area.width >= 50 {
(area.width / 2, 10)
} else {
unreachable!("app should not be rendered if window is so small")
};
// position self in the middle of the rect
let x = ((area.x + area.width) / 2) - width / 2;
let y = ((area.y + area.height) / 2) - height / 2;
let warning_rect = Rect {
x,
y,
width,
height,
};
let fill_style = Style::default()
.bg(Color::Black)
.fg(Color::Yellow)
.modifier(Modifier::BOLD);
let text_max_length = warning_rect.width - 4;
let mut warning_text_start_position: u16 = 0;
let possible_warning_texts = [
"Sorry, deletion is only allowed once the scanning has completed",
"Deletion is not allowed while scanning",
"Can't delete while scanning",
"Can't delete now",
];
// set default value of the warning_text
// to the longest one from possible_warning_texts array
let mut warning_text = String::from(possible_warning_texts[0]);
for line in possible_warning_texts.iter() {
// "+5" here is to make sure confirm message has always some padding
if warning_rect.width >= (line.chars().count() as u16) + 5 {
// here we truncate the end and not the middle because
// when dealing with warning messages, the beginning tends
// to be the important part
warning_text = truncate_end(line, text_max_length);
warning_text_start_position =
((warning_rect.width - warning_text.len() as u16) as f64 / 2.0).ceil() as u16
+ warning_rect.x;
break;
}
}
let controls_text = ["(Press any key to dismiss)", "(any key to dismiss)"];
draw_filled_rect(buf, fill_style, &warning_rect);
buf.set_string(
warning_text_start_position,
warning_rect.y + warning_rect.height / 2 - 2,
warning_text,
fill_style,
);
for line in controls_text.iter() {
if text_max_length >= line.chars().count() as u16 {
let start_position =
((warning_rect.width - line.chars().count() as u16) as f64 / 2.0).ceil() as u16
+ warning_rect.x;
buf.set_string(
start_position,
warning_rect.y + warning_rect.height / 2 + 2,
line,
fill_style,
);
break;
}
}
}
}