Skip to content

Commit

Permalink
feat: Show .. directory before all the others in the explorer to na…
Browse files Browse the repository at this point in the history
…vigate to the parent dir (#301)
  • Loading branch information
veeso authored Oct 14, 2024
1 parent c05ef32 commit 3cde067
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Released on
- [Issue 298](https://github.com/veeso/termscp/issues/298): tuirealm 2.x
- Fixed some performance issues where sometimes the app froze for a couple of seconds, thanks to this <https://github.com/veeso/tui-realm/pull/78>.
- [Issue 292](https://github.com/veeso/termscp/issues/292): New version alert was not displayed due to a semver regex issue.
- [Issue 291](https://github.com/veeso/termscp/issues/291): Show `..` directory before all the others in the explorer. If you click on it you'll go the parent directory (same as pressing `<U>`). No, you can't select it for transfers and it's actually been implemented in the worse way possible, because this little change would require a huge refactoring of the explorer component. I promise I will do it one day, but I dunno when.
- Logging: filter out messages not related to termscp or remotefs

## 0.15.0
Expand Down
63 changes: 54 additions & 9 deletions src/ui/activities/filetransfer/components/transfer/file_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

use tuirealm::command::{Cmd, CmdResult, Direction, Position};
use tuirealm::props::{
Alignment, AttrValue, Attribute, Borders, Color, Style, Table, TextModifiers,
Alignment, AttrValue, Attribute, Borders, Color, Style, Table, TextModifiers, TextSpan,
};
use tuirealm::ratatui::text::{Line, Span};
use tuirealm::ratatui::widgets::{List as TuiList, ListDirection, ListItem, ListState};
use tuirealm::{MockComponent, Props, State, StateValue};

pub const FILE_LIST_CMD_SELECT_ALL: &str = "A";
pub const FILE_LIST_CMD_DESELECT_ALL: &str = "D";
const PROP_DOT_DOT: &str = "dot_dot";

/// OwnStates contains states for this component
#[derive(Clone, Default)]
Expand All @@ -22,8 +23,8 @@ struct OwnStates {

impl OwnStates {
/// Initialize list states
pub fn init_list_states(&mut self, len: usize) {
self.selected = Vec::with_capacity(len);
pub fn init_list_states(&mut self, len: usize, has_dot_dot: bool) {
self.selected = Vec::with_capacity(len + if has_dot_dot { 1 } else { 0 });
self.fix_list_index();
}

Expand Down Expand Up @@ -107,9 +108,9 @@ impl OwnStates {
}

/// Select all files
pub fn select_all(&mut self) {
pub fn select_all(&mut self, has_dot_dot: bool) {
for i in 0..self.list_len() {
self.select(i);
self.select(i + if has_dot_dot { 1 } else { 0 });
}
}

Expand Down Expand Up @@ -172,6 +173,20 @@ impl FileList {
self.attr(Attribute::Content, AttrValue::Table(rows));
self
}

/// If enabled, show `..` entry at the beginning of the list
pub fn dot_dot(mut self, show: bool) -> Self {
self.attr(Attribute::Custom(PROP_DOT_DOT), AttrValue::Flag(show));
self
}

/// Returns the value of the `dot_dot` property
fn has_dot_dot(&self) -> bool {
self.props
.get(Attribute::Custom(PROP_DOT_DOT))
.map(|x| x.unwrap_flag())
.unwrap_or(false)
}
}

impl MockComponent for FileList {
Expand All @@ -193,15 +208,28 @@ impl MockComponent for FileList {
.unwrap_flag();
let div = tui_realm_stdlib::utils::get_block(borders, Some(title), focus, None);
// Make list entries
let init_table_iter = if self.has_dot_dot() {
vec![vec![TextSpan::from("..")]]
} else {
vec![]
};

let list_items: Vec<ListItem> = match self
.props
.get(Attribute::Content)
.map(|x| x.unwrap_table())
{
Some(table) => table
Some(table) => init_table_iter
.iter()
.chain(table.iter())
.enumerate()
.map(|(num, row)| {
let num = if self.has_dot_dot() {
num.checked_sub(1).unwrap_or_default()
} else {
num
};

let columns: Vec<Span> = row
.iter()
.map(|col| {
Expand Down Expand Up @@ -255,6 +283,7 @@ impl MockComponent for FileList {
Some(line) => line.len(),
_ => 0,
},
self.has_dot_dot(),
);
self.states.fix_list_index();
}
Expand All @@ -265,8 +294,16 @@ impl MockComponent for FileList {
}

fn state(&self) -> State {
if self.has_dot_dot() && self.states.list_index == 0 {
return State::One(StateValue::String("..".to_string()));
}

match self.states.is_selection_empty() {
true => State::One(StateValue::Usize(self.states.list_index())),
true => State::One(StateValue::Usize(if self.has_dot_dot() {
self.states.list_index.checked_sub(1).unwrap_or_default()
} else {
self.states.list_index
})),
false => State::Vec(
self.states
.get_selection()
Expand Down Expand Up @@ -334,15 +371,23 @@ impl MockComponent for FileList {
}
}
Cmd::Custom(FILE_LIST_CMD_SELECT_ALL) => {
self.states.select_all();
self.states.select_all(self.has_dot_dot());
CmdResult::None
}
Cmd::Custom(FILE_LIST_CMD_DESELECT_ALL) => {
self.states.deselect_all();
CmdResult::None
}
Cmd::Toggle => {
self.states.toggle_file(self.states.list_index());
if self.has_dot_dot() && self.states.list_index() == 0 {
return CmdResult::None;
}

self.states.toggle_file(if self.has_dot_dot() {
self.states.list_index().checked_sub(1).unwrap_or_default()
} else {
self.states.list_index()
});
CmdResult::None
}
_ => CmdResult::None,
Expand Down
38 changes: 32 additions & 6 deletions src/ui/activities/filetransfer/components/transfer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,8 @@ impl ExplorerLocal {
.foreground(fg)
.highlighted_color(hg)
.title(title, Alignment::Left)
.rows(files.iter().map(|x| vec![TextSpan::from(x)]).collect()),
.rows(files.iter().map(|x| vec![TextSpan::from(x)]).collect())
.dot_dot(true),
}
}
}
Expand Down Expand Up @@ -439,11 +440,23 @@ impl Component<Msg, NoUserEvent> for ExplorerLocal {
}) => Some(Msg::Transfer(TransferMsg::GoToPreviousDirectory)),
Event::Keyboard(KeyEvent {
code: Key::Enter, ..
}) => Some(Msg::Transfer(TransferMsg::EnterDirectory)),
}) => {
if matches!(self.component.state(), State::One(StateValue::String(_))) {
Some(Msg::Transfer(TransferMsg::GoToParentDirectory))
} else {
Some(Msg::Transfer(TransferMsg::EnterDirectory))
}
}
Event::Keyboard(KeyEvent {
code: Key::Char(' '),
..
}) => Some(Msg::Transfer(TransferMsg::TransferFile)),
}) => {
if matches!(self.component.state(), State::One(StateValue::String(_))) {
Some(Msg::None)
} else {
Some(Msg::Transfer(TransferMsg::TransferFile))
}
}
Event::Keyboard(KeyEvent {
code: Key::Char('a'),
modifiers: KeyModifiers::NONE,
Expand Down Expand Up @@ -559,7 +572,8 @@ impl ExplorerRemote {
.foreground(fg)
.highlighted_color(hg)
.title(title, Alignment::Left)
.rows(files.iter().map(|x| vec![TextSpan::from(x)]).collect()),
.rows(files.iter().map(|x| vec![TextSpan::from(x)]).collect())
.dot_dot(true),
}
}
}
Expand Down Expand Up @@ -635,11 +649,23 @@ impl Component<Msg, NoUserEvent> for ExplorerRemote {
}) => Some(Msg::Transfer(TransferMsg::GoToPreviousDirectory)),
Event::Keyboard(KeyEvent {
code: Key::Enter, ..
}) => Some(Msg::Transfer(TransferMsg::EnterDirectory)),
}) => {
if matches!(self.component.state(), State::One(StateValue::String(_))) {
Some(Msg::Transfer(TransferMsg::GoToParentDirectory))
} else {
Some(Msg::Transfer(TransferMsg::EnterDirectory))
}
}
Event::Keyboard(KeyEvent {
code: Key::Char(' '),
..
}) => Some(Msg::Transfer(TransferMsg::TransferFile)),
}) => {
if matches!(self.component.state(), State::One(StateValue::String(_))) {
Some(Msg::None)
} else {
Some(Msg::Transfer(TransferMsg::TransferFile))
}
}
Event::Keyboard(KeyEvent {
code: Key::Char('a'),
modifiers: KeyModifiers::NONE,
Expand Down

0 comments on commit 3cde067

Please sign in to comment.