mirror of
https://github.com/veeso/termscp.git
synced 2026-09-27 06:21:22 -07:00
fix: removed selected files logic from ui; using queue transfer
This commit is contained in:
+61
-9
@@ -7,7 +7,7 @@ pub(crate) mod builder;
|
|||||||
mod formatter;
|
mod formatter;
|
||||||
// Locals
|
// Locals
|
||||||
use std::cmp::Reverse;
|
use std::cmp::Reverse;
|
||||||
use std::collections::VecDeque;
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@@ -42,14 +42,24 @@ pub enum GroupDirs {
|
|||||||
|
|
||||||
/// File explorer states
|
/// File explorer states
|
||||||
pub struct FileExplorer {
|
pub struct FileExplorer {
|
||||||
pub wrkdir: PathBuf, // Current directory
|
/// Current working directory
|
||||||
pub(crate) dirstack: VecDeque<PathBuf>, // Stack of visited directory (max 16)
|
pub wrkdir: PathBuf,
|
||||||
pub(crate) stack_size: usize, // Directory stack size
|
/// Stack of visited directories
|
||||||
pub(crate) file_sorting: FileSorting, // File sorting criteria
|
pub(crate) dirstack: VecDeque<PathBuf>,
|
||||||
pub(crate) group_dirs: Option<GroupDirs>, // If Some, defines how to group directories
|
/// Stack size
|
||||||
pub(crate) opts: ExplorerOpts, // Explorer options
|
pub(crate) stack_size: usize,
|
||||||
pub(crate) fmt: Formatter, // File formatter
|
/// Criteria to sort file
|
||||||
files: Vec<File>, // Files in directory
|
pub(crate) file_sorting: FileSorting,
|
||||||
|
/// defines how to group directories in the explorer
|
||||||
|
pub(crate) group_dirs: Option<GroupDirs>,
|
||||||
|
/// Explorer options
|
||||||
|
pub(crate) opts: ExplorerOpts,
|
||||||
|
/// Formatter for file entries
|
||||||
|
pub(crate) fmt: Formatter,
|
||||||
|
/// Files in directory
|
||||||
|
files: Vec<File>,
|
||||||
|
/// files enqueued for transfer. Map between source and destination
|
||||||
|
transfer_queue: HashMap<PathBuf, PathBuf>, // transfer queue
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FileExplorer {
|
impl Default for FileExplorer {
|
||||||
@@ -63,6 +73,7 @@ impl Default for FileExplorer {
|
|||||||
opts: ExplorerOpts::empty(),
|
opts: ExplorerOpts::empty(),
|
||||||
fmt: Formatter::default(),
|
fmt: Formatter::default(),
|
||||||
files: Vec::new(),
|
files: Vec::new(),
|
||||||
|
transfer_queue: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,6 +150,27 @@ impl FileExplorer {
|
|||||||
filtered.get(idx).copied()
|
filtered.get(idx).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enqueue a file for transfer
|
||||||
|
pub fn enqueue(&mut self, src: &Path, dst: &Path) {
|
||||||
|
self.transfer_queue
|
||||||
|
.insert(PathBuf::from(src), PathBuf::from(dst));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get enqueued files
|
||||||
|
pub fn enqueued(&self) -> &HashMap<PathBuf, PathBuf> {
|
||||||
|
&self.transfer_queue
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dequeue a file
|
||||||
|
pub fn dequeue(&mut self, src: &Path) {
|
||||||
|
self.transfer_queue.remove(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear transfer queue
|
||||||
|
pub fn clear_queue(&mut self) {
|
||||||
|
self.transfer_queue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// Formatting
|
// Formatting
|
||||||
|
|
||||||
/// Format a file entry
|
/// Format a file entry
|
||||||
@@ -586,6 +618,26 @@ mod tests {
|
|||||||
assert_eq!(explorer.files.len(), 3);
|
assert_eq!(explorer.files.len(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_should_enqueue_and_dequeue_files() {
|
||||||
|
let mut explorer: FileExplorer = FileExplorer::default();
|
||||||
|
// Create files (files are then sorted by name)
|
||||||
|
explorer.set_files(vec![
|
||||||
|
make_fs_entry("CONTRIBUTING.md", false),
|
||||||
|
make_fs_entry("docs", true),
|
||||||
|
make_fs_entry("src", true),
|
||||||
|
make_fs_entry("README.md", false),
|
||||||
|
]);
|
||||||
|
// Enqueue
|
||||||
|
explorer.enqueue(Path::new("CONTRIBUTING.md"), Path::new("CONTRIBUTING.md"));
|
||||||
|
explorer.enqueue(Path::new("docs"), Path::new("docs"));
|
||||||
|
// Dequeue
|
||||||
|
explorer.dequeue(Path::new("CONTRIBUTING.md"));
|
||||||
|
assert_eq!(explorer.enqueued().len(), 1);
|
||||||
|
explorer.dequeue(Path::new("docs"));
|
||||||
|
assert_eq!(explorer.enqueued().len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
fn make_fs_entry(name: &str, is_dir: bool) -> File {
|
fn make_fs_entry(name: &str, is_dir: bool) -> File {
|
||||||
let t: SystemTime = SystemTime::now();
|
let t: SystemTime = SystemTime::now();
|
||||||
let metadata = Metadata {
|
let metadata = Metadata {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ pub(crate) mod edit;
|
|||||||
pub(crate) mod exec;
|
pub(crate) mod exec;
|
||||||
pub(crate) mod filter;
|
pub(crate) mod filter;
|
||||||
pub(crate) mod find;
|
pub(crate) mod find;
|
||||||
|
pub(crate) mod mark;
|
||||||
pub(crate) mod mkdir;
|
pub(crate) mod mkdir;
|
||||||
pub(crate) mod newfile;
|
pub(crate) mod newfile;
|
||||||
pub(crate) mod open;
|
pub(crate) mod open;
|
||||||
@@ -64,7 +65,7 @@ impl SelectedFile {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum SelectedFileIndex {
|
enum SelectedFileIndex {
|
||||||
One(usize),
|
One(usize),
|
||||||
Many(Vec<usize>),
|
Many(Vec<usize>), // TODO: remove
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,20 +17,17 @@ const PROP_DOT_DOT: &str = "dot_dot";
|
|||||||
/// OwnStates contains states for this component
|
/// OwnStates contains states for this component
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
struct OwnStates {
|
struct OwnStates {
|
||||||
list_index: usize, // Index of selected element in list
|
list_index: usize, // Index of selected element in list
|
||||||
selected: Vec<usize>, // Selected files
|
list_len: usize, // Length of the list
|
||||||
|
dot_dot: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OwnStates {
|
impl OwnStates {
|
||||||
/// Initialize list states
|
/// Initialize list states
|
||||||
pub fn init_list_states(&mut self, len: usize, has_dot_dot: bool) {
|
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.list_len = len + if has_dot_dot { 1 } else { 0 };
|
||||||
self.fix_list_index();
|
self.fix_list_index();
|
||||||
}
|
self.dot_dot = has_dot_dot;
|
||||||
|
|
||||||
/// Return current value for list index
|
|
||||||
pub fn list_index(&self) -> usize {
|
|
||||||
self.list_index
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Incremenet list index.
|
/// Incremenet list index.
|
||||||
@@ -44,6 +41,14 @@ impl OwnStates {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn real_index(&self) -> usize {
|
||||||
|
if self.dot_dot {
|
||||||
|
self.list_index.saturating_sub(1)
|
||||||
|
} else {
|
||||||
|
self.list_index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Decrement list index
|
/// Decrement list index
|
||||||
/// If `can_rewind` is `true` the index rewinds when boundary is reached
|
/// If `can_rewind` is `true` the index rewinds when boundary is reached
|
||||||
pub fn decr_list_index(&mut self, can_rewind: bool) {
|
pub fn decr_list_index(&mut self, can_rewind: bool) {
|
||||||
@@ -68,22 +73,7 @@ impl OwnStates {
|
|||||||
|
|
||||||
/// Returns the length of the file list, which is actually the capacity of the selection vector
|
/// Returns the length of the file list, which is actually the capacity of the selection vector
|
||||||
pub fn list_len(&self) -> usize {
|
pub fn list_len(&self) -> usize {
|
||||||
self.selected.capacity()
|
self.list_len
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the file with index `entry` is selected
|
|
||||||
pub fn is_selected(&self, entry: usize) -> bool {
|
|
||||||
self.selected.contains(&entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the selection is currently empty
|
|
||||||
pub fn is_selection_empty(&self) -> bool {
|
|
||||||
self.selected.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns current file selection
|
|
||||||
pub fn get_selection(&self) -> Vec<usize> {
|
|
||||||
self.selected.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Keep index if possible, otherwise set to lenght - 1
|
/// Keep index if possible, otherwise set to lenght - 1
|
||||||
@@ -94,44 +84,6 @@ impl OwnStates {
|
|||||||
self.list_index = 0;
|
self.list_index = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- select manipulation
|
|
||||||
|
|
||||||
/// Select or deselect file with provided entry index
|
|
||||||
pub fn toggle_file(&mut self, entry: usize) {
|
|
||||||
match self.is_selected(entry) {
|
|
||||||
true => self.deselect(entry),
|
|
||||||
false => self.select(entry),
|
|
||||||
}
|
|
||||||
// increment index
|
|
||||||
self.incr_list_index(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select all files
|
|
||||||
pub fn select_all(&mut self, has_dot_dot: bool) {
|
|
||||||
for i in 0..self.list_len() {
|
|
||||||
self.select(i + if has_dot_dot { 1 } else { 0 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select all files
|
|
||||||
pub fn deselect_all(&mut self) {
|
|
||||||
self.selected.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Select provided index if not selected yet
|
|
||||||
fn select(&mut self, entry: usize) {
|
|
||||||
if !self.is_selected(entry) {
|
|
||||||
self.selected.push(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove element file with associated index
|
|
||||||
fn deselect(&mut self, entry: usize) {
|
|
||||||
if self.is_selected(entry) {
|
|
||||||
self.selected.retain(|&x| x != entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -222,27 +174,12 @@ impl MockComponent for FileList {
|
|||||||
Some(table) => init_table_iter
|
Some(table) => init_table_iter
|
||||||
.iter()
|
.iter()
|
||||||
.chain(table.iter())
|
.chain(table.iter())
|
||||||
.enumerate()
|
.map(|row| {
|
||||||
.map(|(num, row)| {
|
|
||||||
let real_num = num;
|
|
||||||
let num = if self.has_dot_dot() {
|
|
||||||
num.checked_sub(1).unwrap_or_default()
|
|
||||||
} else {
|
|
||||||
num
|
|
||||||
};
|
|
||||||
|
|
||||||
let columns: Vec<Span> = row
|
let columns: Vec<Span> = row
|
||||||
.iter()
|
.iter()
|
||||||
.map(|col| {
|
.map(|col| {
|
||||||
let (fg, bg, mut modifiers) =
|
let (fg, bg, modifiers) =
|
||||||
tui_realm_stdlib::utils::use_or_default_styles(&self.props, col);
|
tui_realm_stdlib::utils::use_or_default_styles(&self.props, col);
|
||||||
if !(self.has_dot_dot() && real_num == 0)
|
|
||||||
&& self.states.is_selected(num)
|
|
||||||
{
|
|
||||||
modifiers |= TextModifiers::REVERSED
|
|
||||||
| TextModifiers::UNDERLINED
|
|
||||||
| TextModifiers::ITALIC;
|
|
||||||
}
|
|
||||||
|
|
||||||
Span::styled(
|
Span::styled(
|
||||||
col.content.clone(),
|
col.content.clone(),
|
||||||
@@ -302,20 +239,11 @@ impl MockComponent for FileList {
|
|||||||
return State::One(StateValue::String("..".to_string()));
|
return State::One(StateValue::String("..".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.states.is_selection_empty() {
|
State::One(StateValue::Usize(if self.has_dot_dot() {
|
||||||
true => State::One(StateValue::Usize(if self.has_dot_dot() {
|
self.states.list_index.checked_sub(1).unwrap_or_default()
|
||||||
self.states.list_index.checked_sub(1).unwrap_or_default()
|
} else {
|
||||||
} else {
|
self.states.list_index
|
||||||
self.states.list_index
|
}))
|
||||||
})),
|
|
||||||
false => State::Vec(
|
|
||||||
self.states
|
|
||||||
.get_selection()
|
|
||||||
.into_iter()
|
|
||||||
.map(StateValue::Usize)
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perform(&mut self, cmd: Cmd) -> CmdResult {
|
fn perform(&mut self, cmd: Cmd) -> CmdResult {
|
||||||
@@ -374,25 +302,18 @@ impl MockComponent for FileList {
|
|||||||
CmdResult::None
|
CmdResult::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cmd::Custom(FILE_LIST_CMD_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 => {
|
Cmd::Toggle => {
|
||||||
if self.has_dot_dot() && self.states.list_index() == 0 {
|
if self.states.list_index == 0 && self.has_dot_dot() {
|
||||||
return CmdResult::None;
|
return CmdResult::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.states.toggle_file(if self.has_dot_dot() {
|
let index = self.states.real_index();
|
||||||
self.states.list_index().checked_sub(1).unwrap_or_default()
|
self.states.list_index = self
|
||||||
} else {
|
.states
|
||||||
self.states.list_index()
|
.list_index
|
||||||
});
|
.saturating_add(1)
|
||||||
CmdResult::None
|
.min(self.states.list_len.saturating_sub(1));
|
||||||
|
CmdResult::Changed(State::One(StateValue::Usize(index)))
|
||||||
}
|
}
|
||||||
_ => CmdResult::None,
|
_ => CmdResult::None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,21 +132,26 @@ impl ExplorerFuzzy {
|
|||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkAll))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('a'),
|
code: Key::Char('a'),
|
||||||
modifiers: KeyModifiers::ALT,
|
modifiers: KeyModifiers::ALT,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkClear))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('m'),
|
code: Key::Char('m'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Toggle);
|
let CmdResult::Changed(State::One(StateValue::Usize(index))) =
|
||||||
Some(Msg::None)
|
self.perform(Cmd::Toggle)
|
||||||
|
else {
|
||||||
|
return Some(Msg::None);
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Msg::Ui(UiMsg::MarkFile(index)))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent { code: Key::Tab, .. }) => {
|
Event::Keyboard(KeyEvent { code: Key::Tab, .. }) => {
|
||||||
self.perform(Cmd::Change);
|
self.perform(Cmd::Change);
|
||||||
@@ -277,21 +282,26 @@ impl Component<Msg, NoUserEvent> for ExplorerFind {
|
|||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkAll))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('a'),
|
code: Key::Char('a'),
|
||||||
modifiers: KeyModifiers::ALT,
|
modifiers: KeyModifiers::ALT,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkClear))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('m'),
|
code: Key::Char('m'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Toggle);
|
let CmdResult::Changed(State::One(StateValue::Usize(index))) =
|
||||||
Some(Msg::None)
|
self.perform(Cmd::Toggle)
|
||||||
|
else {
|
||||||
|
return Some(Msg::None);
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Msg::Ui(UiMsg::MarkFile(index)))
|
||||||
}
|
}
|
||||||
// -- comp msg
|
// -- comp msg
|
||||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||||
@@ -410,21 +420,26 @@ impl Component<Msg, NoUserEvent> for ExplorerLocal {
|
|||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkAll))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('a'),
|
code: Key::Char('a'),
|
||||||
modifiers: KeyModifiers::ALT,
|
modifiers: KeyModifiers::ALT,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkClear))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('m'),
|
code: Key::Char('m'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Toggle);
|
let CmdResult::Changed(State::One(StateValue::Usize(index))) =
|
||||||
Some(Msg::None)
|
self.perform(Cmd::Toggle)
|
||||||
|
else {
|
||||||
|
return Some(Msg::None);
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Msg::Ui(UiMsg::MarkFile(index)))
|
||||||
}
|
}
|
||||||
// -- comp msg
|
// -- comp msg
|
||||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||||
@@ -619,21 +634,26 @@ impl Component<Msg, NoUserEvent> for ExplorerRemote {
|
|||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_SELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkAll))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('a'),
|
code: Key::Char('a'),
|
||||||
modifiers: KeyModifiers::ALT,
|
modifiers: KeyModifiers::ALT,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
let _ = self.perform(Cmd::Custom(file_list::FILE_LIST_CMD_DESELECT_ALL));
|
||||||
Some(Msg::None)
|
Some(Msg::Ui(UiMsg::MarkClear))
|
||||||
}
|
}
|
||||||
Event::Keyboard(KeyEvent {
|
Event::Keyboard(KeyEvent {
|
||||||
code: Key::Char('m'),
|
code: Key::Char('m'),
|
||||||
modifiers: KeyModifiers::NONE,
|
modifiers: KeyModifiers::NONE,
|
||||||
}) => {
|
}) => {
|
||||||
let _ = self.perform(Cmd::Toggle);
|
let CmdResult::Changed(State::One(StateValue::Usize(index))) =
|
||||||
Some(Msg::None)
|
self.perform(Cmd::Toggle)
|
||||||
|
else {
|
||||||
|
return Some(Msg::None);
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Msg::Ui(UiMsg::MarkFile(index)))
|
||||||
}
|
}
|
||||||
// -- comp msg
|
// -- comp msg
|
||||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||||
|
|||||||
@@ -60,6 +60,35 @@ impl Browser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn other_explorer(&self) -> &FileExplorer {
|
||||||
|
let found_set = self.found.is_some();
|
||||||
|
match (self.tab, found_set) {
|
||||||
|
(FileExplorerTab::HostBridge, false) => &self.remote,
|
||||||
|
(FileExplorerTab::Remote, false) => &self.host_bridge,
|
||||||
|
(FileExplorerTab::HostBridge, true) => &self.found.as_ref().unwrap().explorer,
|
||||||
|
(FileExplorerTab::Remote, true) => &self.found.as_ref().unwrap().explorer,
|
||||||
|
(FileExplorerTab::FindHostBridge, _) => &self.remote,
|
||||||
|
(FileExplorerTab::FindRemote, _) => &self.host_bridge,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn other_explorer_no_found(&self) -> &FileExplorer {
|
||||||
|
match self.tab {
|
||||||
|
FileExplorerTab::HostBridge | FileExplorerTab::FindHostBridge => &self.remote,
|
||||||
|
FileExplorerTab::Remote | FileExplorerTab::FindRemote => &self.host_bridge,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn explorer_mut(&mut self) -> &mut FileExplorer {
|
||||||
|
match self.tab {
|
||||||
|
FileExplorerTab::HostBridge => &mut self.host_bridge,
|
||||||
|
FileExplorerTab::Remote => &mut self.remote,
|
||||||
|
FileExplorerTab::FindHostBridge | FileExplorerTab::FindRemote => {
|
||||||
|
self.found.as_mut().map(|x| &mut x.explorer).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn host_bridge(&self) -> &FileExplorer {
|
pub fn host_bridge(&self) -> &FileExplorer {
|
||||||
&self.host_bridge
|
&self.host_bridge
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use bytesize::ByteSize;
|
use bytesize::ByteSize;
|
||||||
use tuirealm::props::{
|
use tuirealm::props::{
|
||||||
Alignment, AttrValue, Attribute, Color, PropPayload, PropValue, TableBuilder, TextSpan,
|
Alignment, AttrValue, Attribute, Color, PropPayload, PropValue, TableBuilder, TextModifiers,
|
||||||
|
TextSpan,
|
||||||
};
|
};
|
||||||
use tuirealm::{PollStrategy, Update};
|
use tuirealm::{PollStrategy, Update};
|
||||||
|
|
||||||
@@ -240,6 +241,11 @@ impl FileTransferActivity {
|
|||||||
/// Update host bridge file list
|
/// Update host bridge file list
|
||||||
pub(super) fn update_host_bridge_filelist(&mut self) {
|
pub(super) fn update_host_bridge_filelist(&mut self) {
|
||||||
self.reload_host_bridge_dir();
|
self.reload_host_bridge_dir();
|
||||||
|
self.reload_host_bridge_filelist();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update host bridge file list
|
||||||
|
pub(super) fn reload_host_bridge_filelist(&mut self) {
|
||||||
// Get width
|
// Get width
|
||||||
let width = self
|
let width = self
|
||||||
.context_mut()
|
.context_mut()
|
||||||
@@ -261,7 +267,15 @@ impl FileTransferActivity {
|
|||||||
let files: Vec<Vec<TextSpan>> = self
|
let files: Vec<Vec<TextSpan>> = self
|
||||||
.host_bridge()
|
.host_bridge()
|
||||||
.iter_files()
|
.iter_files()
|
||||||
.map(|x| vec![TextSpan::from(self.host_bridge().fmt_file(x))])
|
.map(|x| {
|
||||||
|
let mut span = TextSpan::from(self.host_bridge().fmt_file(x));
|
||||||
|
if self.host_bridge().enqueued().contains_key(x.path()) {
|
||||||
|
span.modifiers |=
|
||||||
|
TextModifiers::REVERSED | TextModifiers::UNDERLINED | TextModifiers::ITALIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![span]
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
// Update content and title
|
// Update content and title
|
||||||
assert!(
|
assert!(
|
||||||
@@ -287,7 +301,10 @@ impl FileTransferActivity {
|
|||||||
/// Update remote file list
|
/// Update remote file list
|
||||||
pub(super) fn update_remote_filelist(&mut self) {
|
pub(super) fn update_remote_filelist(&mut self) {
|
||||||
self.reload_remote_dir();
|
self.reload_remote_dir();
|
||||||
|
self.reload_remote_filelist();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn reload_remote_filelist(&mut self) {
|
||||||
let width = self
|
let width = self
|
||||||
.context_mut()
|
.context_mut()
|
||||||
.terminal()
|
.terminal()
|
||||||
@@ -308,7 +325,15 @@ impl FileTransferActivity {
|
|||||||
let files: Vec<Vec<TextSpan>> = self
|
let files: Vec<Vec<TextSpan>> = self
|
||||||
.remote()
|
.remote()
|
||||||
.iter_files()
|
.iter_files()
|
||||||
.map(|x| vec![TextSpan::from(self.remote().fmt_file(x))])
|
.map(|x| {
|
||||||
|
let mut span = TextSpan::from(self.remote().fmt_file(x));
|
||||||
|
if self.remote().enqueued().contains_key(x.path()) {
|
||||||
|
span.modifiers |=
|
||||||
|
TextModifiers::REVERSED | TextModifiers::UNDERLINED | TextModifiers::ITALIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![span]
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
// Update content and title
|
// Update content and title
|
||||||
assert!(
|
assert!(
|
||||||
@@ -482,6 +507,15 @@ impl FileTransferActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn reload_browser_file_list(&mut self) {
|
||||||
|
match self.browser.tab() {
|
||||||
|
FileExplorerTab::HostBridge | FileExplorerTab::FindHostBridge => {
|
||||||
|
self.reload_host_bridge_filelist()
|
||||||
|
}
|
||||||
|
FileExplorerTab::Remote | FileExplorerTab::FindRemote => self.reload_remote_filelist(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn update_browser_file_list_swapped(&mut self) {
|
pub(super) fn update_browser_file_list_swapped(&mut self) {
|
||||||
match self.browser.tab() {
|
match self.browser.tab() {
|
||||||
FileExplorerTab::HostBridge | FileExplorerTab::FindHostBridge => {
|
FileExplorerTab::HostBridge | FileExplorerTab::FindHostBridge => {
|
||||||
|
|||||||
@@ -153,6 +153,12 @@ enum UiMsg {
|
|||||||
FilterFiles(String),
|
FilterFiles(String),
|
||||||
FuzzySearch(String),
|
FuzzySearch(String),
|
||||||
LogBackTabbed,
|
LogBackTabbed,
|
||||||
|
/// Mark file on the list; usize is the index of the file
|
||||||
|
MarkFile(usize),
|
||||||
|
/// Mark all file at tab
|
||||||
|
MarkAll,
|
||||||
|
/// Clear all marks
|
||||||
|
MarkClear,
|
||||||
Quit,
|
Quit,
|
||||||
ReplacePopupTabbed,
|
ReplacePopupTabbed,
|
||||||
ShowChmodPopup,
|
ShowChmodPopup,
|
||||||
|
|||||||
@@ -479,6 +479,15 @@ impl FileTransferActivity {
|
|||||||
UiMsg::LogBackTabbed => {
|
UiMsg::LogBackTabbed => {
|
||||||
assert!(self.app.active(&Id::ExplorerHostBridge).is_ok());
|
assert!(self.app.active(&Id::ExplorerHostBridge).is_ok());
|
||||||
}
|
}
|
||||||
|
UiMsg::MarkFile(index) => {
|
||||||
|
self.action_mark_file(index);
|
||||||
|
}
|
||||||
|
UiMsg::MarkAll => {
|
||||||
|
self.action_mark_all();
|
||||||
|
}
|
||||||
|
UiMsg::MarkClear => {
|
||||||
|
self.action_mark_clear();
|
||||||
|
}
|
||||||
UiMsg::Quit => {
|
UiMsg::Quit => {
|
||||||
self.disconnect_and_quit();
|
self.disconnect_and_quit();
|
||||||
self.umount_quit();
|
self.umount_quit();
|
||||||
|
|||||||
Reference in New Issue
Block a user