summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/interactive/app/eventloop.rs8
-rw-r--r--src/interactive/app/handlers.rs33
-rw-r--r--src/interactive/app_test.rs28
-rw-r--r--src/interactive/widgets/entries.rs8
-rw-r--r--src/interactive/widgets/footer.rs6
-rw-r--r--src/interactive/widgets/main.rs47
-rw-r--r--src/interactive/widgets/mark.rs63
7 files changed, 104 insertions, 89 deletions
diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs
index 6227448..adf0955 100644
--- a/src/interactive/app/eventloop.rs
+++ b/src/interactive/app/eventloop.rs
@@ -40,7 +40,6 @@ pub struct AppState {
pub sorting: SortMode,
pub message: Option<String>,
pub focussed: FocussedPane,
- pub marked: EntryMarkMap,
}
/// State and methods representing the interactive disk usage analyser for the terminal
@@ -110,12 +109,7 @@ impl TerminalApp {
}
match self.state.focussed {
- FocussedPane::Mark => self
- .window
- .mark_pane
- .as_mut()
- .expect("mark pane")
- .key(key, &self.state.marked),
+ FocussedPane::Mark => self.window.mark_pane.as_mut().expect("mark pane").key(key),
FocussedPane::Help => {
self.window.help_pane.as_mut().expect("help pane").key(key);
}
diff --git a/src/interactive/app/handlers.rs b/src/interactive/app/handlers.rs
index 11becc1..850ce12 100644
--- a/src/interactive/app/handlers.rs
+++ b/src/interactive/app/handlers.rs
@@ -1,5 +1,5 @@
use crate::interactive::{
- app::{EntryMark, FocussedPane, TerminalApp},
+ app::{FocussedPane, TerminalApp},
sorted_entries,
widgets::{HelpPane, MarkPane},
};
@@ -125,29 +125,18 @@ impl TerminalApp {
}
pub fn mark_entry(&mut self, advance_cursor: bool) {
- if let Some(index) = self.state.selected {
- // TODO: consider using the Entry::Occupied/Vacant API to remove things
- if self.state.marked.get(&index).is_some() {
- self.state.marked.remove(&index);
- } else {
- if let Some(e) = self.state.entries.iter().find(|e| e.index == index) {
- self.state.marked.insert(
- index,
- EntryMark {
- size: e.data.size,
- path: path_of(&self.traversal.tree, index),
- },
- );
- }
- }
- if self.state.marked.is_empty() {
- self.window.mark_pane = None;
- } else {
- self.window.mark_pane = Some(MarkPane::new(&self.state.marked));
+ match (self.state.selected, self.window.mark_pane.take()) {
+ (Some(index), Some(pane)) => {
+ self.window.mark_pane = pane.toggle_index(index, &self.traversal.tree);
}
- if advance_cursor {
- self.change_entry_selection(CursorDirection::Down)
+ (Some(index), None) => {
+ self.window.mark_pane =
+ MarkPane::default().toggle_index(index, &self.traversal.tree)
}
+ _ => {}
+ };
+ if advance_cursor {
+ self.change_entry_selection(CursorDirection::Down)
}
}
}
diff --git a/src/interactive/app_test.rs b/src/interactive/app_test.rs
index fb413e6..6215c85 100644
--- a/src/interactive/app_test.rs
+++ b/src/interactive/app_test.rs
@@ -224,9 +224,15 @@ fn simple_user_journey() -> Result<(), Error> {
let previously_selected_index = *app.state.selected.as_ref().unwrap();
app.process_events(&mut terminal, b"d".keys())?;
{
- assert_eq!(1, app.state.marked.len(), "it marks only a single node",);
+ assert_eq!(
+ Some(1),
+ app.window.mark_pane.as_ref().map(|p| p.marked().len()),
+ "it marks only a single node",
+ );
assert!(
- app.state.marked.contains_key(&previously_selected_index),
+ app.window.mark_pane.as_ref().map_or(false, |p| p
+ .marked()
+ .contains_key(&previously_selected_index)),
"it marks the selected node"
);
assert_eq!(
@@ -241,8 +247,8 @@ fn simple_user_journey() -> Result<(), Error> {
app.process_events(&mut terminal, b"d".keys())?;
assert_eq!(
- 2,
- app.state.marked.len(),
+ Some(2),
+ app.window.mark_pane.as_ref().map(|p| p.marked().len()),
"it marks the currently selected, second node",
);
@@ -258,13 +264,15 @@ fn simple_user_journey() -> Result<(), Error> {
app.process_events(&mut terminal, b"d".keys())?;
assert_eq!(
- 1,
- app.state.marked.len(),
+ Some(1),
+ app.window.mark_pane.as_ref().map(|p| p.marked().len()),
"it toggled the previous selected entry off",
);
assert!(
- app.state.marked.contains_key(&previously_selected_index),
+ app.window.mark_pane.as_ref().map_or(false, |p| p
+ .marked()
+ .contains_key(&previously_selected_index)),
"it leaves the first selected entry marked"
);
}
@@ -272,7 +280,11 @@ fn simple_user_journey() -> Result<(), Error> {
{
app.process_events(&mut terminal, b"k ".keys())?;
- assert_eq!(0, app.state.marked.len(), "it toggles the item off",);
+ assert_eq!(
+ Some(0),
+ app.window.mark_pane.as_ref().map(|p| p.marked().len()),
+ "it toggles the item off",
+ );
assert_eq!(
node_by_index(&app, previously_selected_index),
diff --git a/src/interactive/widgets/entries.rs b/src/interactive/widgets/entries.rs
index 29affa5..bef91cd 100644
--- a/src/interactive/widgets/entries.rs
+++ b/src/interactive/widgets/entries.rs
@@ -19,7 +19,7 @@ pub struct EntriesProps<'a> {
pub display: DisplayOptions,
pub selected: Option<TreeIndex>,
pub entries: &'a [EntryDataBundle],
- pub marked: &'a EntryMarkMap,
+ pub marked: Option<&'a EntryMarkMap>,
pub border_style: Style,
pub is_focussed: bool,
}
@@ -152,7 +152,11 @@ impl Entries {
)
.into(),
Style {
- fg: match (!is_dir, exists, marked.contains_key(node_idx)) {
+ fg: match (
+ !is_dir,
+ exists,
+ marked.map(|m| m.contains_key(node_idx)).unwrap_or(false),
+ ) {
(true, true, false) if !is_selected => Color::DarkGray,
(true, true, false) => style.fg,
(false, true, false) => style.fg,
diff --git a/src/interactive/widgets/footer.rs b/src/interactive/widgets/footer.rs
index 56d6657..f961412 100644
--- a/src/interactive/widgets/footer.rs
+++ b/src/interactive/widgets/footer.rs
@@ -17,7 +17,7 @@ pub struct FooterProps<'a> {
pub total_bytes: Option<u64>,
pub entries_traversed: u64,
pub format: ByteFormat,
- pub marked: &'a EntryMarkMap,
+ pub marked: Option<&'a EntryMarkMap>,
pub message: Option<String>,
}
@@ -45,7 +45,7 @@ impl Footer {
)
.into(),
)),
- match marked.is_empty() {
+ marked.and_then(|marked| match marked.is_empty() {
true => None,
false => Some(Text::Styled(
format!(
@@ -60,7 +60,7 @@ impl Footer {
modifier: Modifier::BOLD | Modifier::RAPID_BLINK,
},
)),
- },
+ }),
message.as_ref().map(|m| {
Text::Styled(
m.into(),
diff --git a/src/interactive/widgets/main.rs b/src/interactive/widgets/main.rs
index 6eab313..a5640a1 100644
--- a/src/interactive/widgets/main.rs
+++ b/src/interactive/widgets/main.rs
@@ -92,28 +92,34 @@ impl MainWindow {
Mark => (grey, grey, white),
};
- let bg_color = match (state.marked.is_empty(), state.focussed) {
- (false, FocussedPane::Mark) => Color::LightRed,
- (false, _) => COLOR_MARKED_LIGHT,
- (true, _) => Color::White,
+ let bg_color = {
+ let marked = mark_pane.as_ref().map(|(_, p)| p.marked());
+ match (marked.map(|m| m.is_empty()), state.focussed) {
+ (Some(false), FocussedPane::Mark) => Color::LightRed,
+ (Some(false), _) | (None, _) => COLOR_MARKED_LIGHT,
+ (_, _) => Color::White,
+ }
};
Header.render(bg_color, header_area, buf);
- let props = EntriesProps {
- tree: &tree,
- root: state.root,
- display: *display,
- entries: &state.entries,
- marked: &state.marked,
- selected: state.selected,
- border_style: entries_style,
- is_focussed: if let Main = state.focussed {
- true
- } else {
- false
- },
- };
- self.entries_pane.render(props, entries_area, buf);
+ {
+ let marked = mark_pane.as_ref().map(|(_, p)| p.marked());
+ let props = EntriesProps {
+ tree: &tree,
+ root: state.root,
+ display: *display,
+ entries: &state.entries,
+ marked,
+ selected: state.selected,
+ border_style: entries_style,
+ is_focussed: if let Main = state.focussed {
+ true
+ } else {
+ false
+ },
+ };
+ self.entries_pane.render(props, entries_area, buf);
+ }
if let Some((help_area, pane)) = help_pane {
let props = HelpPaneProps {
@@ -124,7 +130,6 @@ impl MainWindow {
if let Some((mark_area, pane)) = mark_pane {
let props = MarkPaneProps {
border_style: mark_style,
- marked: &state.marked,
};
pane.render(props, mark_area, buf);
}
@@ -133,7 +138,7 @@ impl MainWindow {
FooterProps {
total_bytes: *total_bytes,
entries_traversed: *entries_traversed,
- marked: &state.marked,
+ marked: self.mark_pane.as_ref().map(|p| p.marked()),
format: display.byte_format,
message: state.message.clone(),
},
diff --git a/src/interactive/widgets/mark.rs b/src/interactive/widgets/mark.rs
index 42650aa..d600b34 100644
--- a/src/interactive/widgets/mark.rs
+++ b/src/interactive/widgets/mark.rs
@@ -1,5 +1,6 @@
-use crate::interactive::{widgets::COLOR_MARKED_LIGHT, CursorDirection, EntryMarkMap};
-use dua::traverse::TreeIndex;
+use crate::interactive::{widgets::COLOR_MARKED_LIGHT, CursorDirection, EntryMark, EntryMarkMap};
+use dua::path_of;
+use dua::traverse::{Tree, TreeIndex};
use itertools::Itertools;
use std::borrow::Borrow;
use termion::{event::Key, event::Key::*};
@@ -13,47 +14,57 @@ use tui::{
};
use tui_react::{List, ListProps};
+#[derive(Default)]
pub struct MarkPane {
- list: List,
selected: Option<TreeIndex>,
+ marked: EntryMarkMap,
+ list: List,
}
-pub struct MarkPaneProps<'a> {
+pub struct MarkPaneProps {
pub border_style: Style,
- pub marked: &'a EntryMarkMap,
}
impl MarkPane {
- pub fn new(_marked: &EntryMarkMap) -> MarkPane {
- MarkPane {
- list: Default::default(),
- selected: None,
+ pub fn toggle_index(mut self, index: TreeIndex, tree: &Tree) -> Option<Self> {
+ if self.marked.get(&index).is_some() {
+ self.marked.remove(&index);
+ } else {
+ if let Some(e) = tree.node_weight(index) {
+ self.marked.insert(
+ index,
+ EntryMark {
+ size: e.size,
+ path: path_of(tree, index),
+ },
+ );
+ }
+ }
+ if self.marked.is_empty() {
+ None
+ } else {
+ Some(self)
}
}
-
- pub fn key(&mut self, key: Key, marked: &EntryMarkMap) {
+ pub fn marked(&self) -> &EntryMarkMap {
+ &self.marked
+ }
+ pub fn key(&mut self, key: Key) {
match key {
- Ctrl('u') | PageUp => self.change_selection(CursorDirection::PageUp, marked),
- Char('k') | Up => self.change_selection(CursorDirection::Up, marked),
- Char('j') | Down => self.change_selection(CursorDirection::Down, marked),
- Ctrl('d') | PageDown => self.change_selection(CursorDirection::PageDown, marked),
+ Ctrl('u') | PageUp => self.change_selection(CursorDirection::PageUp),
+ Char('k') | Up => self.change_selection(CursorDirection::Up),
+ Char('j') | Down => self.change_selection(CursorDirection::Down),
+ Ctrl('d') | PageDown => self.change_selection(CursorDirection::PageDown),
_ => {}
};
}
- fn change_selection(&mut self, _direction: CursorDirection, _marked: &EntryMarkMap) {}
+ fn change_selection(&mut self, _direction: CursorDirection) {}
- pub fn render<'a>(
- &mut self,
- props: impl Borrow<MarkPaneProps<'a>>,
- area: Rect,
- buf: &mut Buffer,
- ) {
- let MarkPaneProps {
- border_style,
- marked,
- } = props.borrow();
+ pub fn render(&mut self, props: impl Borrow<MarkPaneProps>, area: Rect, buf: &mut Buffer) {
+ let MarkPaneProps { border_style } = props.borrow();
+ let marked: &_ = &self.marked;
let block = Block::default()
.title("Marked Entries")
.border_style(*border_style)