Skip to content

Commit

Permalink
Merge pull request #17 from drdo/confirm-clear-all-marks
Browse files Browse the repository at this point in the history
Add confirmation dialog when clearing all marks
  • Loading branch information
drdo authored Jun 3, 2024
2 parents fc58eb7 + 7ad77e5 commit 451873f
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 9 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ humansize = "2"
indicatif = "0.17"
log = "0.4"
nix = { version = "0.28", features = ["process"] }
ratatui = { version = "0.26", features = ["unstable-widget-ref"] }
ratatui = { version = "0.26", features = ["unstable-rendered-line-info", "unstable-widget-ref"] }
rusqlite = { version = "0.31", features = ["bundled", "functions", "trace"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Expand Down
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,8 @@ fn convert_event(event: crossterm::event::Event) -> Option<Event> {
((KeyModifiers::CONTROL, KeyCode::Char('b')), PageUp),
((KeyModifiers::empty(), KeyCode::PageDown), PageDown),
((KeyModifiers::CONTROL, KeyCode::Char('f')), PageDown),
((KeyModifiers::empty(), KeyCode::Enter), Enter),
((KeyModifiers::empty(), KeyCode::Esc), Exit),
((KeyModifiers::empty(), KeyCode::Char('m')), Mark),
((KeyModifiers::empty(), KeyCode::Char('u')), Unmark),
((KeyModifiers::empty(), KeyCode::Char('c')), UnmarkAll),
Expand Down
153 changes: 145 additions & 8 deletions src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use ratatui::layout::{Constraint, Direction, Layout, Position, Rect, Size};
use ratatui::prelude::Line;
use ratatui::style::{Style, Stylize};
use ratatui::text::Span;
use ratatui::widgets::{List, ListItem, Paragraph, WidgetRef};
use ratatui::widgets::{
Block, BorderType, Clear, List, ListItem, Padding, Paragraph, Widget,
WidgetRef, Wrap,
};
use redu::types::{Directory, Entry, File};
use unicode_segmentation::UnicodeSegmentation;

Expand All @@ -22,6 +25,8 @@ pub enum Event {
Down,
PageUp,
PageDown,
Enter,
Exit,
Mark,
Unmark,
UnmarkAll,
Expand Down Expand Up @@ -55,6 +60,7 @@ pub struct App {
selected: usize,
offset: usize,
footer_extra: Vec<Span<'static>>,
confirm_dialog: Option<ConfirmDialog>,
}

impl App {
Expand All @@ -78,6 +84,7 @@ impl App {
selected: 0,
offset: 0,
footer_extra,
confirm_dialog: None,
}
}

Expand All @@ -86,17 +93,58 @@ impl App {
use Event::*;
match event {
Resize(new_size) => self.resize(new_size),
Left => self.left(),
Right => self.right(),
Left =>
if let Some(ref mut confirm_dialog) = self.confirm_dialog {
confirm_dialog.yes_selected = false;
Action::Render
} else {
self.left()
},
Right =>
if let Some(ref mut confirm_dialog) = self.confirm_dialog {
confirm_dialog.yes_selected = true;
Action::Render
} else {
self.right()
},
Up => self.move_selection(-1, true),
Down => self.move_selection(1, true),
PageUp =>
self.move_selection(-(self.list_size.height as isize), false),
PageDown =>
self.move_selection(self.list_size.height as isize, false),
Enter =>
if let Some(confirm_dialog) = self.confirm_dialog.take() {
if confirm_dialog.yes_selected {
confirm_dialog.action
} else {
Action::Render
}
} else {
Action::Nothing
},
Exit =>
if self.confirm_dialog.take().is_some() {
Action::Render
} else {
Action::Nothing
},
Mark => self.mark_selection(),
Unmark => self.unmark_selection(),
UnmarkAll => self.unmark_all(),
UnmarkAll =>
if self.confirm_dialog.is_none() {
self.confirm_dialog = Some(ConfirmDialog {
text: "Are you sure you want to delete all marks?"
.into(),
yes: "Yes".into(),
no: "No".into(),
yes_selected: false,
action: Action::DeleteAllMarks,
});
Action::Render
} else {
Action::Nothing
},
Quit => Action::Quit,
Generate => self.generate(),
Entries { parent, children } => self.set_entries(parent, children),
Expand Down Expand Up @@ -157,10 +205,6 @@ impl App {
self.selected_entry().map(Action::DeleteMark).unwrap_or(Action::Nothing)
}

fn unmark_all(&self) -> Action {
Action::DeleteAllMarks
}

fn generate(&self) -> Action {
let mut lines = self
.marks
Expand Down Expand Up @@ -239,6 +283,83 @@ fn path_extended<'a>(
}
}

/// ConfirmDialog //////////////////////////////////////////////////////////////
struct ConfirmDialog {
text: String,
yes: String,
no: String,
yes_selected: bool,
action: Action,
}

impl WidgetRef for ConfirmDialog {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
let main_text = Paragraph::new(self.text.clone())
.centered()
.wrap(Wrap { trim: false });

let padding = Padding { left: 2, right: 2, top: 1, bottom: 0 };
let horiz_padding = padding.left + padding.right;
let vert_padding = padding.top + padding.bottom;
let dialog_area = {
let max_text_width = min(80, area.width - 2 - horiz_padding); // take out the border and padding
let text_width =
min(self.text.graphemes(true).count() as u16, max_text_width);
let text_height = main_text.line_count(max_text_width) as u16;
let max_width = text_width + 2 + horiz_padding; // text + border + padding
let max_height = text_height + 2 + vert_padding + 1 + 2 + 1; // text + border + padding + empty line + buttons
centered(max_width, max_height, area)
};

let block = Block::bordered().title("Confirm").padding(padding);

let (main_text_area, buttons_area) = {
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Fill(100), Constraint::Length(3)])
.split(block.inner(dialog_area));
(layout[0], layout[1])
};
let (no_button_area, yes_button_area) = {
let layout = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Fill(1),
Constraint::Min(self.no.graphemes(true).count() as u16),
Constraint::Fill(1),
Constraint::Min(self.yes.graphemes(true).count() as u16),
Constraint::Fill(1),
])
.split(buttons_area);
(layout[1], layout[3])
};

fn render_button(
label: &String,
selected: bool,
area: Rect,
buf: &mut Buffer,
) {
let mut block = Block::bordered().border_type(BorderType::Plain);
let mut button = Paragraph::new(label.clone())
.centered()
.wrap(Wrap { trim: false });
if selected {
block = block.border_type(BorderType::QuadrantInside);
button = button.black().on_white();
}
button.render(block.inner(area), buf);
block.render(area, buf);
}

Clear.render(dialog_area, buf);
block.render(dialog_area, buf);
main_text.render(main_text_area, buf);
render_button(&self.no, !self.yes_selected, no_button_area, buf);
render_button(&self.yes, self.yes_selected, yes_button_area, buf);
}
}

/// Render /////////////////////////////////////////////////////////////////////
struct ListEntry {
Expand Down Expand Up @@ -392,6 +513,10 @@ impl WidgetRef for App {
.on_light_blue()
.render_ref(footer_rect, buf);
}

if let Some(confirm_dialog) = &self.confirm_dialog {
confirm_dialog.render_ref(area, buf);
}
}
}

Expand Down Expand Up @@ -465,6 +590,18 @@ fn compute_layout(area: Rect) -> (Rect, Rect, Rect) {
(layout[0], layout[1], layout[2])
}

/// Returns a `Rect` centered in `area` with a maximum width and height.
fn centered(max_width: u16, max_height: u16, area: Rect) -> Rect {
let width = min(max_width, area.width);
let height = min(max_height, area.height);
Rect {
x: area.width / 2 - width / 2,
y: area.height / 2 - height / 2,
width,
height,
}
}

/// Tests //////////////////////////////////////////////////////////////////////
#[cfg(test)]
Expand Down

0 comments on commit 451873f

Please sign in to comment.