mirror of
https://github.com/veeso/termscp.git
synced 2026-09-27 22:41:19 -07:00
build: migrate to tui-realm 4.0
Upgrade tuirealm (3.x -> 4.0.0), tui-realm-stdlib (3 -> 4), tui-term (0.2 -> 0.3). Apply all breaking changes from the 4.0 migration guide across the termscp UI. Key changes: - Root-level re-exports removed; imports moved to module-qualified paths (`tuirealm::application`, `::component`, `::event`, `::props`, `::state`, `::subscription`, `::listener`, `::ratatui`). Same for stdlib component types (`tui_realm_stdlib::components::*`). - `MockComponent` trait renamed to `Component`; old `Component` trait renamed to `AppComponent`. `#[derive(MockComponent)]` is now `#[derive(Component)]`. `Component::on` now takes `&Event<_>`. - `TextSpan` replaced with `SpanStatic`/`LineStatic`/`TextStatic` (ratatui-based); tuple `(String, Alignment)` titles replaced with the new `Title` builder; `Alignment` split into `HorizontalAlignment`/`VerticalAlignment`; stdlib components use `.alignment_horizontal` instead of `.alignment`. - `State::One`/`PropPayload::One` -> `Single`. `CmdResult::None` -> `NoChange`. `Props::get_or` removed; `Props::get` now returns a borrowed `Option<&AttrValue>` (call sites switched to `.and_then(AttrValue::as_*)`). `Component::query` returns `Option<QueryResult<'a>>`. - `Attribute::HighlightedColor` -> `HighlightStyle` (a full `Style`). `.highlighted_*` helpers renamed to `.highlight_*`. - `PollStrategy::UpTo(n)` now requires a `Duration`; tick timeout moved from `EventListenerCfg::poll_timeout` into `PollStrategy`. - `TerminalBridge` removed; `Context` now holds `CrosstermTerminalAdapter` directly and enables raw mode + alternate screen explicitly. The `TerminalAdapter` trait is imported where its methods are used. - `Update` trait removed; activity `update` methods are plain inherent functions. - `ProgressBar` replaced by stdlib `Gauge`. Paragraph `.wrap` renamed to `.wrap_trim`; `.text` now takes an `Into<Text>`. Stdlib `List` row items are now individual lines (`Vec<Span>` per row) rather than a `Table` of spans; custom `FileList`/`Log` convert between the two models. - Radio builders drop `.foreground(color)` so unselected items render with the terminal default foreground, and set `highlight_style(Style::default().fg(color).add_modifier(REVERSED))` so the selected entry is visibly highlighted only with the theme color. - Custom `FileList` keeps the selected row highlighted with the full highlight style when focused and falls back to a foreground-only style when unfocused. - Theme loading is now backwards compatible: `Theme` uses a custom `Deserialize` through an intermediate `ThemeFile` with optional fields, so missing keys, unknown values or legacy aliases (`transfer_progress_bar_full`/`_partial`) fall back to defaults on a per-field basis instead of failing the whole load.
This commit is contained in:
@@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use remotefs::File;
|
||||
use remotefs::fs::UnixPex;
|
||||
use tuirealm::{State, StateValue};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use super::browser::FileExplorerTab;
|
||||
use super::lib::browser::FoundExplorerTab;
|
||||
@@ -135,7 +135,7 @@ impl FileTransferActivity {
|
||||
|
||||
fn get_selected_index(&self, id: &Id) -> SelectedFileIndex {
|
||||
match self.app.state(id) {
|
||||
Ok(State::One(StateValue::Usize(idx))) => SelectedFileIndex::One(idx),
|
||||
Ok(State::Single(StateValue::Usize(idx))) => SelectedFileIndex::One(idx),
|
||||
_ => SelectedFileIndex::None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use std::time::SystemTime;
|
||||
|
||||
use remotefs::File;
|
||||
use remotefs::fs::Metadata;
|
||||
use tuirealm::terminal::TerminalAdapter;
|
||||
|
||||
use super::super::ui_result;
|
||||
use super::{FileTransferActivity, LogLevel, SelectedFile, TransferPayload};
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
// ext
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use tuirealm::terminal::TerminalAdapter;
|
||||
|
||||
use super::{File, FileTransferActivity, LogLevel, TransferPayload};
|
||||
|
||||
impl FileTransferActivity {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! this little module exposes the routine to create a pending action on the file transfer activity.
|
||||
//! A pending action is an action which blocks the execution of the application in await of a certain `Msg`.
|
||||
|
||||
use tuirealm::{PollStrategy, Update};
|
||||
use tuirealm::application::PollStrategy;
|
||||
|
||||
use super::{FileTransferActivity, Msg};
|
||||
|
||||
@@ -18,7 +18,10 @@ impl FileTransferActivity {
|
||||
self.redraw = true;
|
||||
loop {
|
||||
// Poll
|
||||
match self.app.tick(PollStrategy::Once) {
|
||||
match self
|
||||
.app
|
||||
.tick(PollStrategy::Once(std::time::Duration::from_millis(10)))
|
||||
{
|
||||
Ok(mut messages) => {
|
||||
if !messages.is_empty() {
|
||||
self.redraw = true;
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
//!
|
||||
//! file transfer activity components
|
||||
|
||||
use tui_realm_stdlib::Phantom;
|
||||
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::{Component, MockComponent, NoUserEvent};
|
||||
use tui_realm_stdlib::components::Phantom;
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers, NoUserEvent};
|
||||
|
||||
use super::{Msg, TransferMsg, UiMsg};
|
||||
|
||||
@@ -30,13 +30,13 @@ pub use self::log::Log;
|
||||
pub use self::selected_files::SelectedFilesList;
|
||||
pub use self::terminal::Terminal;
|
||||
|
||||
#[derive(Default, MockComponent)]
|
||||
#[derive(Default, Component)]
|
||||
pub struct GlobalListener {
|
||||
component: Phantom,
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for GlobalListener {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for GlobalListener {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||
Some(Msg::Ui(UiMsg::ShowDisconnectPopup))
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
//! log tab component
|
||||
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, AttrValue, Attribute, Borders, Color, Style, Table};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{
|
||||
AttrValue, Attribute, Borders, Color, HorizontalAlignment, Props, QueryResult, Style, Table,
|
||||
Title,
|
||||
};
|
||||
use tuirealm::ratatui::widgets::{List as TuiList, ListDirection, ListItem, ListState};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, Props, State, StateValue};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use super::{Msg, UiMsg};
|
||||
|
||||
@@ -31,38 +35,47 @@ impl Log {
|
||||
}
|
||||
}
|
||||
|
||||
impl MockComponent for Log {
|
||||
fn view(&mut self, frame: &mut tuirealm::Frame, area: tuirealm::ratatui::layout::Rect) {
|
||||
let width: usize = area.width as usize - 4;
|
||||
impl Component for Log {
|
||||
fn view(
|
||||
&mut self,
|
||||
frame: &mut tuirealm::ratatui::Frame,
|
||||
area: tuirealm::ratatui::layout::Rect,
|
||||
) {
|
||||
let _width: usize = area.width as usize - 4;
|
||||
let focus = self
|
||||
.props
|
||||
.get_or(Attribute::Focus, AttrValue::Flag(false))
|
||||
.unwrap_flag();
|
||||
.get(Attribute::Focus)
|
||||
.and_then(AttrValue::as_flag)
|
||||
.unwrap_or(false);
|
||||
let borders = self
|
||||
.props
|
||||
.get_or(Attribute::Borders, AttrValue::Borders(Borders::default()))
|
||||
.unwrap_borders();
|
||||
.get(Attribute::Borders)
|
||||
.and_then(AttrValue::as_borders)
|
||||
.unwrap_or_default();
|
||||
let bg = self
|
||||
.props
|
||||
.get_or(Attribute::Background, AttrValue::Color(Color::Reset))
|
||||
.unwrap_color();
|
||||
.get(Attribute::Background)
|
||||
.and_then(AttrValue::as_color)
|
||||
.unwrap_or(Color::Reset);
|
||||
// Make list
|
||||
let list_items: Vec<ListItem> = self
|
||||
.props
|
||||
.get(Attribute::Content)
|
||||
.unwrap()
|
||||
.unwrap_table()
|
||||
.iter()
|
||||
.map(|row| {
|
||||
let row_refs = row.iter().collect::<Vec<_>>();
|
||||
ListItem::new(tui_realm_stdlib::utils::wrap_spans(
|
||||
row_refs.as_slice(),
|
||||
width,
|
||||
&self.props,
|
||||
))
|
||||
.and_then(AttrValue::as_table)
|
||||
.map(|table| {
|
||||
table
|
||||
.iter()
|
||||
.map(|row| {
|
||||
let spans = row
|
||||
.iter()
|
||||
.flat_map(|line| line.spans.iter().cloned())
|
||||
.collect::<Vec<_>>();
|
||||
ListItem::new(tuirealm::ratatui::text::Line::from(spans))
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
let title = ("Log".to_string(), Alignment::Left);
|
||||
.unwrap_or_default();
|
||||
let title = Title::from("Log".to_string()).alignment(HorizontalAlignment::Left);
|
||||
let w = TuiList::new(list_items)
|
||||
.block(tui_realm_stdlib::utils::get_block(
|
||||
borders,
|
||||
@@ -79,29 +92,26 @@ impl MockComponent for Log {
|
||||
frame.render_stateful_widget(w, area, &mut state);
|
||||
}
|
||||
|
||||
fn query(&self, attr: Attribute) -> Option<AttrValue> {
|
||||
self.props.get(attr)
|
||||
fn query<'a>(&'a self, attr: Attribute) -> Option<QueryResult<'a>> {
|
||||
self.props.get_for_query(attr)
|
||||
}
|
||||
|
||||
fn attr(&mut self, attr: Attribute, value: AttrValue) {
|
||||
self.props.set(attr, value);
|
||||
if matches!(attr, Attribute::Content) {
|
||||
self.states.set_list_len(
|
||||
match self
|
||||
.props
|
||||
.get(Attribute::Content)
|
||||
.map(AttrValue::unwrap_table)
|
||||
{
|
||||
Some(spans) => spans.len(),
|
||||
_ => 0,
|
||||
},
|
||||
);
|
||||
let len = self
|
||||
.props
|
||||
.get(Attribute::Content)
|
||||
.and_then(AttrValue::as_table)
|
||||
.map(std::vec::Vec::len)
|
||||
.unwrap_or(0);
|
||||
self.states.set_list_len(len);
|
||||
self.states.reset_list_index();
|
||||
}
|
||||
}
|
||||
|
||||
fn state(&self) -> State {
|
||||
State::One(StateValue::Usize(self.states.get_list_index()))
|
||||
State::Single(StateValue::Usize(self.states.get_list_index()))
|
||||
}
|
||||
|
||||
fn perform(&mut self, cmd: Cmd) -> CmdResult {
|
||||
@@ -112,7 +122,7 @@ impl MockComponent for Log {
|
||||
if prev != self.states.get_list_index() {
|
||||
CmdResult::Changed(self.state())
|
||||
} else {
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
}
|
||||
Cmd::Move(Direction::Up) => {
|
||||
@@ -121,7 +131,7 @@ impl MockComponent for Log {
|
||||
if prev != self.states.get_list_index() {
|
||||
CmdResult::Changed(self.state())
|
||||
} else {
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
}
|
||||
Cmd::Scroll(Direction::Down) => {
|
||||
@@ -130,7 +140,7 @@ impl MockComponent for Log {
|
||||
if prev != self.states.get_list_index() {
|
||||
CmdResult::Changed(self.state())
|
||||
} else {
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
}
|
||||
Cmd::Scroll(Direction::Up) => {
|
||||
@@ -139,7 +149,7 @@ impl MockComponent for Log {
|
||||
if prev != self.states.get_list_index() {
|
||||
CmdResult::Changed(self.state())
|
||||
} else {
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
}
|
||||
Cmd::GoTo(Position::Begin) => {
|
||||
@@ -148,7 +158,7 @@ impl MockComponent for Log {
|
||||
if prev != self.states.get_list_index() {
|
||||
CmdResult::Changed(self.state())
|
||||
} else {
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
}
|
||||
Cmd::GoTo(Position::End) => {
|
||||
@@ -157,16 +167,16 @@ impl MockComponent for Log {
|
||||
if prev != self.states.get_list_index() {
|
||||
CmdResult::Changed(self.state())
|
||||
} else {
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
}
|
||||
_ => CmdResult::None,
|
||||
_ => CmdResult::NoChange,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for Log {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for Log {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent { code: Key::Up, .. }) => {
|
||||
self.perform(Cmd::Move(Direction::Down));
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
//!
|
||||
//! file transfer activity components
|
||||
|
||||
use tui_realm_stdlib::Span;
|
||||
use tuirealm::props::{Color, TextSpan};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent};
|
||||
use tui_realm_stdlib::components::Span;
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, NoUserEvent};
|
||||
use tuirealm::props::{Color, SpanStatic};
|
||||
use tuirealm::ratatui::style::Stylize;
|
||||
|
||||
use super::Msg;
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct FooterBar {
|
||||
component: Span,
|
||||
}
|
||||
@@ -17,37 +19,37 @@ impl FooterBar {
|
||||
pub fn new(key_color: Color) -> Self {
|
||||
Self {
|
||||
component: Span::default().spans([
|
||||
TextSpan::from("<F1|H>").bold().fg(key_color),
|
||||
TextSpan::from(" Help "),
|
||||
TextSpan::from("<TAB>").bold().fg(key_color),
|
||||
TextSpan::from(" Change tab "),
|
||||
TextSpan::from("<SPACE>").bold().fg(key_color),
|
||||
TextSpan::from(" Transfer "),
|
||||
TextSpan::from("<ENTER>").bold().fg(key_color),
|
||||
TextSpan::from(" Enter dir "),
|
||||
TextSpan::from("<F2|S>").bold().fg(key_color),
|
||||
TextSpan::from(" Save as "),
|
||||
TextSpan::from("<F3|V>").bold().fg(key_color),
|
||||
TextSpan::from(" View "),
|
||||
TextSpan::from("<F4|O>").bold().fg(key_color),
|
||||
TextSpan::from(" Edit "),
|
||||
TextSpan::from("<F5|C>").bold().fg(key_color),
|
||||
TextSpan::from(" Copy "),
|
||||
TextSpan::from("<F6|R>").bold().fg(key_color),
|
||||
TextSpan::from(" Rename "),
|
||||
TextSpan::from("<F7|D>").bold().fg(key_color),
|
||||
TextSpan::from(" Make dir "),
|
||||
TextSpan::from("<F8|DEL>").bold().fg(key_color),
|
||||
TextSpan::from(" Delete "),
|
||||
TextSpan::from("<F10|Q>").bold().fg(key_color),
|
||||
TextSpan::from(" Quit "),
|
||||
SpanStatic::from("<F1|H>").bold().fg(key_color),
|
||||
SpanStatic::from(" Help "),
|
||||
SpanStatic::from("<TAB>").bold().fg(key_color),
|
||||
SpanStatic::from(" Change tab "),
|
||||
SpanStatic::from("<SPACE>").bold().fg(key_color),
|
||||
SpanStatic::from(" Transfer "),
|
||||
SpanStatic::from("<ENTER>").bold().fg(key_color),
|
||||
SpanStatic::from(" Enter dir "),
|
||||
SpanStatic::from("<F2|S>").bold().fg(key_color),
|
||||
SpanStatic::from(" Save as "),
|
||||
SpanStatic::from("<F3|V>").bold().fg(key_color),
|
||||
SpanStatic::from(" View "),
|
||||
SpanStatic::from("<F4|O>").bold().fg(key_color),
|
||||
SpanStatic::from(" Edit "),
|
||||
SpanStatic::from("<F5|C>").bold().fg(key_color),
|
||||
SpanStatic::from(" Copy "),
|
||||
SpanStatic::from("<F6|R>").bold().fg(key_color),
|
||||
SpanStatic::from(" Rename "),
|
||||
SpanStatic::from("<F7|D>").bold().fg(key_color),
|
||||
SpanStatic::from(" Make dir "),
|
||||
SpanStatic::from("<F8|DEL>").bold().fg(key_color),
|
||||
SpanStatic::from(" Delete "),
|
||||
SpanStatic::from("<F10|Q>").bold().fg(key_color),
|
||||
SpanStatic::from(" Quit "),
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for FooterBar {
|
||||
fn on(&mut self, _: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for FooterBar {
|
||||
fn on(&mut self, _: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
use remotefs::fs::{UnixPex, UnixPexClass};
|
||||
use tui_realm_stdlib::Checkbox;
|
||||
use tui_realm_stdlib::components::Checkbox;
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, AttrValue, Attribute, BorderSides, Borders, Color};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{
|
||||
AttrValue, Attribute, BorderSides, Borders, Color, HorizontalAlignment, Props, Title,
|
||||
};
|
||||
use tuirealm::ratatui::layout::{Constraint, Direction as LayoutDirection, Layout};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, Props, State, StateValue};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, TransferMsg, UiMsg};
|
||||
|
||||
@@ -60,21 +63,21 @@ impl ChmodPopup {
|
||||
user: Checkbox::default()
|
||||
.foreground(color)
|
||||
.choices(["Read", "Write", "Execute"])
|
||||
.title("User", Alignment::Left)
|
||||
.title(Title::from("User").alignment(HorizontalAlignment::Left))
|
||||
.borders(Borders::default().sides(BorderSides::NONE))
|
||||
.values(&make_pex_values(pex.user()))
|
||||
.rewind(true),
|
||||
group: Checkbox::default()
|
||||
.foreground(color)
|
||||
.choices(["Read", "Write", "Execute"])
|
||||
.title("Group", Alignment::Left)
|
||||
.title(Title::from("Group").alignment(HorizontalAlignment::Left))
|
||||
.borders(Borders::default().sides(BorderSides::NONE))
|
||||
.values(&make_pex_values(pex.group()))
|
||||
.rewind(true),
|
||||
others: Checkbox::default()
|
||||
.foreground(color)
|
||||
.choices(["Read", "Write", "Execute"])
|
||||
.title("Others", Alignment::Left)
|
||||
.title(Title::from("Others").alignment(HorizontalAlignment::Left))
|
||||
.borders(Borders::default().sides(BorderSides::NONE))
|
||||
.values(&make_pex_values(pex.others()))
|
||||
.rewind(true),
|
||||
@@ -146,8 +149,8 @@ impl ChmodPopup {
|
||||
}
|
||||
}
|
||||
|
||||
impl MockComponent for ChmodPopup {
|
||||
fn attr(&mut self, attr: tuirealm::Attribute, value: AttrValue) {
|
||||
impl Component for ChmodPopup {
|
||||
fn attr(&mut self, attr: tuirealm::props::Attribute, value: AttrValue) {
|
||||
self.props.set(attr, value.clone());
|
||||
|
||||
if attr == Attribute::Focus {
|
||||
@@ -166,28 +169,40 @@ impl MockComponent for ChmodPopup {
|
||||
}
|
||||
Cmd::Move(Direction::Up) => {
|
||||
self.active_checkbox_up();
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
Cmd::Move(Direction::Down) => {
|
||||
self.active_checkbox_down();
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
Cmd::Toggle => self.get_active_checkbox().perform(cmd),
|
||||
Cmd::Submit => CmdResult::Submit(self.state()),
|
||||
_ => CmdResult::None,
|
||||
_ => CmdResult::NoChange,
|
||||
}
|
||||
}
|
||||
|
||||
fn query(&self, attr: tuirealm::Attribute) -> Option<AttrValue> {
|
||||
self.props.get(attr)
|
||||
fn query<'a>(
|
||||
&'a self,
|
||||
attr: tuirealm::props::Attribute,
|
||||
) -> Option<tuirealm::props::QueryResult<'a>> {
|
||||
self.props.get_for_query(attr)
|
||||
}
|
||||
|
||||
fn state(&self) -> State {
|
||||
State::One(StateValue::U32(self.get_mode().into()))
|
||||
State::Single(StateValue::U32(self.get_mode().into()))
|
||||
}
|
||||
|
||||
fn view(&mut self, frame: &mut tuirealm::Frame, area: tuirealm::ratatui::layout::Rect) {
|
||||
if self.props.get_or(Attribute::Display, AttrValue::Flag(true)) != AttrValue::Flag(true) {
|
||||
fn view(
|
||||
&mut self,
|
||||
frame: &mut tuirealm::ratatui::Frame,
|
||||
area: tuirealm::ratatui::layout::Rect,
|
||||
) {
|
||||
if !self
|
||||
.props
|
||||
.get(Attribute::Display)
|
||||
.and_then(AttrValue::as_flag)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
let chunks = Layout::default()
|
||||
@@ -205,10 +220,11 @@ impl MockComponent for ChmodPopup {
|
||||
|
||||
let focus = self
|
||||
.props
|
||||
.get_or(Attribute::Focus, AttrValue::Flag(false))
|
||||
.unwrap_flag();
|
||||
.get(Attribute::Focus)
|
||||
.and_then(AttrValue::as_flag)
|
||||
.unwrap_or(false);
|
||||
|
||||
let div_title = (self.title.clone(), Alignment::Center);
|
||||
let div_title = Title::from(self.title.clone()).alignment(HorizontalAlignment::Center);
|
||||
|
||||
let div = tui_realm_stdlib::utils::get_block(
|
||||
Borders::default().color(self.color),
|
||||
@@ -225,8 +241,8 @@ impl MockComponent for ChmodPopup {
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for ChmodPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for ChmodPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||
Some(Msg::Ui(UiMsg::CloseChmodPopup))
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use tui_realm_stdlib::Input;
|
||||
use tui_realm_stdlib::components::Input;
|
||||
use tuirealm::command::{Cmd, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, InputType, Style};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{BorderType, Borders, Color, HorizontalAlignment, InputType, Style, Title};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, TransferMsg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct CopyPopup {
|
||||
component: Input,
|
||||
}
|
||||
@@ -22,17 +23,19 @@ impl CopyPopup {
|
||||
)
|
||||
.foreground(color)
|
||||
.input_type(InputType::Text)
|
||||
.placeholder(
|
||||
.placeholder(tuirealm::props::SpanStatic::styled(
|
||||
"destination",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
)
|
||||
.title("Copy file(s) to\u{2026}", Alignment::Center),
|
||||
))
|
||||
.title(
|
||||
Title::from("Copy file(s) to\u{2026}").alignment(HorizontalAlignment::Center),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for CopyPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for CopyPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -73,13 +76,13 @@ impl Component<Msg, NoUserEvent> for CopyPopup {
|
||||
code: Key::Char(ch),
|
||||
..
|
||||
}) => {
|
||||
self.perform(Cmd::Type(ch));
|
||||
self.perform(Cmd::Type(*ch));
|
||||
Some(Msg::None)
|
||||
}
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => match self.state() {
|
||||
State::One(StateValue::String(i)) => {
|
||||
State::Single(StateValue::String(i)) => {
|
||||
Some(Msg::Transfer(TransferMsg::CopyFileTo(i)))
|
||||
}
|
||||
_ => Some(Msg::None),
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use tui_realm_stdlib::Radio;
|
||||
use tui_realm_stdlib::components::Radio;
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction};
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers, NoUserEvent};
|
||||
use tuirealm::props::{
|
||||
BorderType, Borders, Color, HorizontalAlignment, Style, TextModifiers, Title,
|
||||
};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, TransferMsg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct DeletePopup {
|
||||
component: Radio,
|
||||
}
|
||||
@@ -15,21 +18,25 @@ impl DeletePopup {
|
||||
pub fn new(color: Color) -> Self {
|
||||
Self {
|
||||
component: Radio::default()
|
||||
.highlight_style(
|
||||
Style::default()
|
||||
.fg(color)
|
||||
.add_modifier(TextModifiers::REVERSED),
|
||||
)
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.choices(["Yes", "No"])
|
||||
.value(1)
|
||||
.title("Delete file(s)?", Alignment::Center),
|
||||
.title(Title::from("Delete file(s)?").alignment(HorizontalAlignment::Center)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for DeletePopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for DeletePopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -59,7 +66,7 @@ impl Component<Msg, NoUserEvent> for DeletePopup {
|
||||
}) => {
|
||||
if matches!(
|
||||
self.perform(Cmd::Submit),
|
||||
CmdResult::Submit(State::One(StateValue::Usize(0)))
|
||||
CmdResult::Submit(State::Single(StateValue::Usize(0)))
|
||||
) {
|
||||
Some(Msg::Transfer(TransferMsg::DeleteFile))
|
||||
} else {
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use tui_realm_stdlib::Radio;
|
||||
use tui_realm_stdlib::components::Radio;
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction};
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers, NoUserEvent};
|
||||
use tuirealm::props::{
|
||||
BorderType, Borders, Color, HorizontalAlignment, Style, TextModifiers, Title,
|
||||
};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct DisconnectPopup {
|
||||
component: Radio,
|
||||
}
|
||||
@@ -15,20 +18,27 @@ impl DisconnectPopup {
|
||||
pub fn new(color: Color) -> Self {
|
||||
Self {
|
||||
component: Radio::default()
|
||||
.highlight_style(
|
||||
Style::default()
|
||||
.fg(color)
|
||||
.add_modifier(TextModifiers::REVERSED),
|
||||
)
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.choices(["Yes", "No"])
|
||||
.title("Are you sure you want to disconnect?", Alignment::Center),
|
||||
.title(
|
||||
Title::from("Are you sure you want to disconnect?")
|
||||
.alignment(HorizontalAlignment::Center),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for DisconnectPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for DisconnectPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -58,7 +68,7 @@ impl Component<Msg, NoUserEvent> for DisconnectPopup {
|
||||
}) => {
|
||||
if matches!(
|
||||
self.perform(Cmd::Submit),
|
||||
CmdResult::Submit(State::One(StateValue::Usize(0)))
|
||||
CmdResult::Submit(State::Single(StateValue::Usize(0)))
|
||||
) {
|
||||
Some(Msg::Ui(UiMsg::Disconnect))
|
||||
} else {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use tui_realm_stdlib::Paragraph;
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, TextSpan};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent};
|
||||
use tui_realm_stdlib::components::Paragraph;
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{BorderType, Borders, Color, HorizontalAlignment, SpanStatic};
|
||||
use tuirealm::ratatui::text::Text;
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct ErrorPopup {
|
||||
component: Paragraph,
|
||||
}
|
||||
@@ -14,21 +15,23 @@ impl ErrorPopup {
|
||||
pub fn new<S: AsRef<str>>(text: S, color: Color) -> Self {
|
||||
Self {
|
||||
component: Paragraph::default()
|
||||
.alignment(Alignment::Center)
|
||||
.alignment_horizontal(HorizontalAlignment::Center)
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.text([TextSpan::from(text.as_ref())])
|
||||
.wrap(true),
|
||||
.text(Text::from_iter([SpanStatic::from(
|
||||
text.as_ref().to_string(),
|
||||
)]))
|
||||
.wrap_trim(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for ErrorPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for ErrorPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Esc | Key::Enter,
|
||||
@@ -39,7 +42,7 @@ impl Component<Msg, NoUserEvent> for ErrorPopup {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct FatalPopup {
|
||||
component: Paragraph,
|
||||
}
|
||||
@@ -48,21 +51,23 @@ impl FatalPopup {
|
||||
pub fn new<S: AsRef<str>>(text: S, color: Color) -> Self {
|
||||
Self {
|
||||
component: Paragraph::default()
|
||||
.alignment(Alignment::Center)
|
||||
.alignment_horizontal(HorizontalAlignment::Center)
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.text([TextSpan::from(text.as_ref())])
|
||||
.wrap(true),
|
||||
.text(Text::from_iter([SpanStatic::from(
|
||||
text.as_ref().to_string(),
|
||||
)]))
|
||||
.wrap_trim(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for FatalPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for FatalPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Esc | Key::Enter,
|
||||
|
||||
@@ -2,17 +2,20 @@ use std::time::UNIX_EPOCH;
|
||||
|
||||
use bytesize::ByteSize;
|
||||
use remotefs::File;
|
||||
use tui_realm_stdlib::List;
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, TableBuilder, TextSpan};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent};
|
||||
use tui_realm_stdlib::components::List;
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{
|
||||
BorderType, Borders, Color, HorizontalAlignment, SpanStatic, TableBuilder, Title,
|
||||
};
|
||||
use tuirealm::ratatui::style::Stylize;
|
||||
#[cfg(posix)]
|
||||
use uzers::{get_group_by_gid, get_user_by_uid};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, UiMsg};
|
||||
use crate::utils::fmt::fmt_time;
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct FileInfoPopup {
|
||||
component: List,
|
||||
}
|
||||
@@ -28,23 +31,23 @@ impl FileInfoPopup {
|
||||
};
|
||||
// Make texts
|
||||
texts
|
||||
.add_col(TextSpan::from("Path: "))
|
||||
.add_col(TextSpan::new(path.as_str()).fg(Color::Yellow));
|
||||
.add_col(SpanStatic::from("Path: "))
|
||||
.add_col(SpanStatic::raw(path.clone()).fg(Color::Yellow));
|
||||
texts
|
||||
.add_row()
|
||||
.add_col(TextSpan::from("Name: "))
|
||||
.add_col(TextSpan::new(file.name()).fg(Color::Yellow));
|
||||
.add_col(SpanStatic::from("Name: "))
|
||||
.add_col(SpanStatic::raw(file.name().clone()).fg(Color::Yellow));
|
||||
if let Some(filetype) = file.extension() {
|
||||
texts
|
||||
.add_row()
|
||||
.add_col(TextSpan::from("File type: "))
|
||||
.add_col(TextSpan::new(filetype).fg(Color::LightGreen));
|
||||
.add_col(SpanStatic::from("File type: "))
|
||||
.add_col(SpanStatic::raw(filetype.clone()).fg(Color::LightGreen));
|
||||
}
|
||||
let (bsize, size): (ByteSize, u64) = (ByteSize(file.metadata().size), file.metadata().size);
|
||||
texts
|
||||
.add_row()
|
||||
.add_col(TextSpan::from("Size: "))
|
||||
.add_col(TextSpan::new(format!("{bsize} ({size})").as_str()).fg(Color::Cyan));
|
||||
.add_col(SpanStatic::from("Size: "))
|
||||
.add_col(SpanStatic::raw(format!("{bsize} ({size})")).fg(Color::Cyan));
|
||||
let atime: String = fmt_time(
|
||||
file.metadata().accessed.unwrap_or(UNIX_EPOCH),
|
||||
"%b %d %Y %H:%M:%S",
|
||||
@@ -59,16 +62,16 @@ impl FileInfoPopup {
|
||||
);
|
||||
texts
|
||||
.add_row()
|
||||
.add_col(TextSpan::from("Creation time: "))
|
||||
.add_col(TextSpan::new(ctime.as_str()).fg(Color::LightGreen));
|
||||
.add_col(SpanStatic::from("Creation time: "))
|
||||
.add_col(SpanStatic::raw(ctime.clone()).fg(Color::LightGreen));
|
||||
texts
|
||||
.add_row()
|
||||
.add_col(TextSpan::from("Last modified time: "))
|
||||
.add_col(TextSpan::new(mtime.as_str()).fg(Color::LightBlue));
|
||||
.add_col(SpanStatic::from("Last modified time: "))
|
||||
.add_col(SpanStatic::raw(mtime.clone()).fg(Color::LightBlue));
|
||||
texts
|
||||
.add_row()
|
||||
.add_col(TextSpan::from("Last access time: "))
|
||||
.add_col(TextSpan::new(atime.as_str()).fg(Color::LightRed));
|
||||
.add_col(SpanStatic::from("Last access time: "))
|
||||
.add_col(SpanStatic::raw(atime.clone()).fg(Color::LightRed));
|
||||
// User
|
||||
#[cfg(posix)]
|
||||
let username: String = match file.metadata().uid {
|
||||
@@ -93,24 +96,29 @@ impl FileInfoPopup {
|
||||
let group: String = format!("{}", file.metadata().gid.unwrap_or(0));
|
||||
texts
|
||||
.add_row()
|
||||
.add_col(TextSpan::from("User: "))
|
||||
.add_col(TextSpan::new(username.as_str()).fg(Color::LightYellow));
|
||||
.add_col(SpanStatic::from("User: "))
|
||||
.add_col(SpanStatic::raw(username.clone()).fg(Color::LightYellow));
|
||||
texts
|
||||
.add_row()
|
||||
.add_col(TextSpan::from("Group: "))
|
||||
.add_col(TextSpan::new(group.as_str()).fg(Color::Blue));
|
||||
.add_col(SpanStatic::from("Group: "))
|
||||
.add_col(SpanStatic::raw(group.clone()).fg(Color::Blue));
|
||||
Self {
|
||||
component: List::default()
|
||||
.borders(Borders::default().modifiers(BorderType::Rounded))
|
||||
.scroll(false)
|
||||
.title(file.name(), Alignment::Left)
|
||||
.rows(texts.build()),
|
||||
.title(Title::from(file.name().clone()).alignment(HorizontalAlignment::Left))
|
||||
.rows(
|
||||
texts
|
||||
.build()
|
||||
.into_iter()
|
||||
.map(|row| row.into_iter().flat_map(|l| l.spans).collect::<Vec<_>>()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for FileInfoPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for FileInfoPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Esc | Key::Enter,
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use tui_realm_stdlib::Input;
|
||||
use tui_realm_stdlib::components::Input;
|
||||
use tuirealm::command::{Cmd, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, InputType, Style};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{BorderType, Borders, Color, HorizontalAlignment, InputType, Style, Title};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct FilterPopup {
|
||||
component: Input,
|
||||
}
|
||||
@@ -22,20 +23,20 @@ impl FilterPopup {
|
||||
)
|
||||
.foreground(color)
|
||||
.input_type(InputType::Text)
|
||||
.placeholder(
|
||||
.placeholder(tuirealm::props::SpanStatic::styled(
|
||||
"regex or wildmatch",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
)
|
||||
))
|
||||
.title(
|
||||
"Filter files by regex or wildmatch in the current directory",
|
||||
Alignment::Center,
|
||||
Title::from("Filter files by regex or wildmatch in the current directory")
|
||||
.alignment(HorizontalAlignment::Center),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for FilterPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for FilterPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -76,13 +77,15 @@ impl Component<Msg, NoUserEvent> for FilterPopup {
|
||||
code: Key::Char(ch),
|
||||
..
|
||||
}) => {
|
||||
self.perform(Cmd::Type(ch));
|
||||
self.perform(Cmd::Type(*ch));
|
||||
Some(Msg::None)
|
||||
}
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => match self.state() {
|
||||
State::One(StateValue::String(filter)) => Some(Msg::Ui(UiMsg::FilterFiles(filter))),
|
||||
State::Single(StateValue::String(filter)) => {
|
||||
Some(Msg::Ui(UiMsg::FilterFiles(filter)))
|
||||
}
|
||||
_ => Some(Msg::None),
|
||||
},
|
||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use tui_realm_stdlib::Input;
|
||||
use tui_realm_stdlib::components::Input;
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, InputType, PropValue, Style};
|
||||
use tuirealm::{
|
||||
AttrValue, Attribute, Component, Event, MockComponent, NoUserEvent, State, StateValue,
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{
|
||||
AttrValue, Attribute, BorderType, Borders, Color, HorizontalAlignment, InputType, PropValue,
|
||||
Style, Title,
|
||||
};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, TransferMsg, UiMsg};
|
||||
|
||||
@@ -51,14 +53,15 @@ impl From<CmdResult> for Suggestion {
|
||||
fn from(value: CmdResult) -> Self {
|
||||
match value {
|
||||
CmdResult::Batch(v) if v.len() == 1 => {
|
||||
if let CmdResult::Submit(State::One(StateValue::String(s))) = v.first().unwrap() {
|
||||
if let CmdResult::Submit(State::Single(StateValue::String(s))) = v.first().unwrap()
|
||||
{
|
||||
Suggestion::Suggest(s.clone())
|
||||
} else {
|
||||
Suggestion::None
|
||||
}
|
||||
}
|
||||
CmdResult::Batch(v) if v.len() == 2 => {
|
||||
if let CmdResult::Submit(State::One(StateValue::String(s))) = v.get(1).unwrap() {
|
||||
if let CmdResult::Submit(State::Single(StateValue::String(s))) = v.get(1).unwrap() {
|
||||
Suggestion::Rescan(PathBuf::from(s))
|
||||
} else {
|
||||
Suggestion::None
|
||||
@@ -72,13 +75,13 @@ impl From<CmdResult> for Suggestion {
|
||||
impl From<Suggestion> for CmdResult {
|
||||
fn from(value: Suggestion) -> Self {
|
||||
match value {
|
||||
Suggestion::None => CmdResult::None,
|
||||
Suggestion::Suggest(s) => {
|
||||
CmdResult::Batch(vec![CmdResult::Submit(State::One(StateValue::String(s)))])
|
||||
}
|
||||
Suggestion::None => CmdResult::NoChange,
|
||||
Suggestion::Suggest(s) => CmdResult::Batch(vec![CmdResult::Submit(State::Single(
|
||||
StateValue::String(s),
|
||||
))]),
|
||||
Suggestion::Rescan(p) => CmdResult::Batch(vec![
|
||||
CmdResult::None,
|
||||
CmdResult::Submit(State::One(StateValue::String(
|
||||
CmdResult::NoChange,
|
||||
CmdResult::Submit(State::Single(StateValue::String(
|
||||
p.to_string_lossy().to_string(),
|
||||
))),
|
||||
]),
|
||||
@@ -187,18 +190,25 @@ impl GotoPopup {
|
||||
)
|
||||
.foreground(color)
|
||||
.input_type(InputType::Text)
|
||||
.placeholder(
|
||||
.placeholder(tuirealm::props::SpanStatic::styled(
|
||||
"/foo/bar/buzz",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
)
|
||||
.title("Go to… (Press <TAB> for autocompletion)", Alignment::Center),
|
||||
))
|
||||
.title(
|
||||
Title::from("Go to… (Press <TAB> for autocompletion)")
|
||||
.alignment(HorizontalAlignment::Center),
|
||||
),
|
||||
states,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MockComponent for GotoPopup {
|
||||
fn view(&mut self, frame: &mut tuirealm::Frame, area: tuirealm::ratatui::prelude::Rect) {
|
||||
impl Component for GotoPopup {
|
||||
fn view(
|
||||
&mut self,
|
||||
frame: &mut tuirealm::ratatui::Frame,
|
||||
area: tuirealm::ratatui::prelude::Rect,
|
||||
) {
|
||||
self.input.view(frame, area);
|
||||
}
|
||||
|
||||
@@ -220,12 +230,12 @@ impl MockComponent for GotoPopup {
|
||||
}
|
||||
}
|
||||
|
||||
fn query(&self, attr: Attribute) -> Option<AttrValue> {
|
||||
fn query<'a>(&'a self, attr: Attribute) -> Option<tuirealm::props::QueryResult<'a>> {
|
||||
self.input.query(attr)
|
||||
}
|
||||
|
||||
fn state(&self) -> State {
|
||||
State::One(StateValue::String(self.states.computed_search()))
|
||||
State::Single(StateValue::String(self.states.computed_search()))
|
||||
}
|
||||
|
||||
fn perform(&mut self, cmd: Cmd) -> CmdResult {
|
||||
@@ -236,7 +246,7 @@ impl MockComponent for GotoPopup {
|
||||
.search
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| self.input.state().unwrap_one().unwrap_string());
|
||||
.unwrap_or_else(|| self.input.state().unwrap_single().unwrap_string());
|
||||
let suggest = self.states.suggest(&input);
|
||||
if let Suggestion::Suggest(suggestion) = suggest.clone() {
|
||||
self.input
|
||||
@@ -247,7 +257,7 @@ impl MockComponent for GotoPopup {
|
||||
}
|
||||
cmd => {
|
||||
let res = self.input.perform(cmd);
|
||||
if let CmdResult::Changed(State::One(StateValue::String(new_text))) = &res {
|
||||
if let CmdResult::Changed(State::Single(StateValue::String(new_text))) = &res {
|
||||
self.states.search = Some(new_text.clone());
|
||||
}
|
||||
res
|
||||
@@ -256,8 +266,8 @@ impl MockComponent for GotoPopup {
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for GotoPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for GotoPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -298,7 +308,7 @@ impl Component<Msg, NoUserEvent> for GotoPopup {
|
||||
code: Key::Char(ch),
|
||||
..
|
||||
}) => {
|
||||
self.perform(Cmd::Type(ch));
|
||||
self.perform(Cmd::Type(*ch));
|
||||
Some(Msg::None)
|
||||
}
|
||||
Event::Keyboard(KeyEvent { code: Key::Tab, .. }) => {
|
||||
@@ -311,7 +321,7 @@ impl Component<Msg, NoUserEvent> for GotoPopup {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => match self.state() {
|
||||
State::One(StateValue::String(i)) => Some(Msg::Transfer(TransferMsg::GoTo(i))),
|
||||
State::Single(StateValue::String(i)) => Some(Msg::Transfer(TransferMsg::GoTo(i))),
|
||||
_ => Some(Msg::None),
|
||||
},
|
||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use tui_realm_stdlib::List;
|
||||
use tui_realm_stdlib::components::List;
|
||||
use tuirealm::command::{Cmd, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, TableBuilder, TextSpan};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{
|
||||
BorderType, Borders, Color, HorizontalAlignment, SpanStatic, TableBuilder, Title,
|
||||
};
|
||||
use tuirealm::ratatui::style::Stylize;
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct KeybindingsPopup {
|
||||
component: List,
|
||||
}
|
||||
@@ -18,148 +21,152 @@ impl KeybindingsPopup {
|
||||
.borders(Borders::default().modifiers(BorderType::Rounded))
|
||||
.scroll(true)
|
||||
.step(8)
|
||||
.highlighted_str("? ")
|
||||
.title("Keybindings", Alignment::Center)
|
||||
.highlight_str("? ")
|
||||
.title(Title::from("Keybindings").alignment(HorizontalAlignment::Center))
|
||||
.rewind(true)
|
||||
.rows(
|
||||
TableBuilder::default()
|
||||
.add_col(TextSpan::new("<ESC>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Disconnect"))
|
||||
.add_col(SpanStatic::raw("<ESC>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Disconnect"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<BACKSPACE>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Go to previous directory"))
|
||||
.add_col(SpanStatic::raw("<BACKSPACE>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Go to previous directory"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<TAB|RIGHT|LEFT>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Change explorer tab"))
|
||||
.add_col(SpanStatic::raw("<TAB|RIGHT|LEFT>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Change explorer tab"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<UP/DOWN>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Move up/down in list"))
|
||||
.add_col(SpanStatic::raw("<UP/DOWN>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Move up/down in list"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<ENTER>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Enter directory"))
|
||||
.add_col(SpanStatic::raw("<ENTER>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Enter directory"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<SPACE>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Upload/Download file"))
|
||||
.add_col(SpanStatic::raw("<SPACE>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Upload/Download file"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<BACKTAB>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(
|
||||
.add_col(SpanStatic::raw("<BACKTAB>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(
|
||||
" Switch between explorer and log window",
|
||||
))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<A>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Toggle hidden files"))
|
||||
.add_col(SpanStatic::raw("<A>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Toggle hidden files"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<B>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Change file sorting mode"))
|
||||
.add_col(SpanStatic::raw("<B>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Change file sorting mode"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<C|F5>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Copy"))
|
||||
.add_col(SpanStatic::raw("<C|F5>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Copy"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<D|F7>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Make directory"))
|
||||
.add_col(SpanStatic::raw("<D|F7>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Make directory"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<F>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Search files"))
|
||||
.add_col(SpanStatic::raw("<F>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Search files"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<G>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Go to path"))
|
||||
.add_col(SpanStatic::raw("<G>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Go to path"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<H|F1>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Show help"))
|
||||
.add_col(SpanStatic::raw("<H|F1>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Show help"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<I>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(
|
||||
.add_col(SpanStatic::raw("<I>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(
|
||||
" Show info about selected file",
|
||||
))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<K>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(
|
||||
.add_col(SpanStatic::raw("<K>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(
|
||||
" Create symlink pointing to the current selected entry",
|
||||
))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<L>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Reload directory content"))
|
||||
.add_col(SpanStatic::raw("<L>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Reload directory content"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<M>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Select file"))
|
||||
.add_col(SpanStatic::raw("<M>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Select file"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<N>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Create new file"))
|
||||
.add_col(SpanStatic::raw("<N>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Create new file"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<O|F4>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(
|
||||
.add_col(SpanStatic::raw("<O|F4>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(
|
||||
" Open text file with preferred editor",
|
||||
))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<P>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Toggle bottom panel"))
|
||||
.add_col(SpanStatic::raw("<P>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Toggle bottom panel"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<Q|F10>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Quit termscp"))
|
||||
.add_col(SpanStatic::raw("<Q|F10>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Quit termscp"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<R|F6>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Rename file"))
|
||||
.add_col(SpanStatic::raw("<R|F6>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Rename file"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<S|F2>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Save file as"))
|
||||
.add_col(SpanStatic::raw("<S|F2>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Save file as"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<T>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Watch/unwatch file changes"))
|
||||
.add_col(SpanStatic::raw("<T>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(
|
||||
" Watch/unwatch file changes",
|
||||
))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<U>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Go to parent directory"))
|
||||
.add_col(SpanStatic::raw("<U>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Go to parent directory"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<V|F3>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(
|
||||
.add_col(SpanStatic::raw("<V|F3>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(
|
||||
" Open file with default application for file type",
|
||||
))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<W>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(
|
||||
.add_col(SpanStatic::raw("<W>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(
|
||||
" Open file with specified application",
|
||||
))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<X>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Execute shell command"))
|
||||
.add_col(SpanStatic::raw("<X>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Execute shell command"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<Y>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(
|
||||
.add_col(SpanStatic::raw("<Y>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(
|
||||
" Toggle synchronized browsing",
|
||||
))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<Z>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Change file permissions"))
|
||||
.add_col(SpanStatic::raw("<Z>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Change file permissions"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("</>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Filter files"))
|
||||
.add_col(SpanStatic::raw("</>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Filter files"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<DEL|F8|E>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Delete selected file"))
|
||||
.add_col(SpanStatic::raw("<DEL|F8|E>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Delete selected file"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<CTRL+A>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Select all files"))
|
||||
.add_col(SpanStatic::raw("<CTRL+A>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Select all files"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<ALT+A>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Deselect all files"))
|
||||
.add_col(SpanStatic::raw("<ALT+A>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Deselect all files"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<CTRL+C>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Interrupt file transfer"))
|
||||
.add_col(SpanStatic::raw("<CTRL+C>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Interrupt file transfer"))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<CTRL+S>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(
|
||||
.add_col(SpanStatic::raw("<CTRL+S>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(
|
||||
" Get total path size of selected files",
|
||||
))
|
||||
.add_row()
|
||||
.add_col(TextSpan::new("<CTRL+T>").bold().fg(key_color))
|
||||
.add_col(TextSpan::from(" Show watched paths"))
|
||||
.build(),
|
||||
.add_col(SpanStatic::raw("<CTRL+T>").bold().fg(key_color))
|
||||
.add_col(SpanStatic::from(" Show watched paths"))
|
||||
.build()
|
||||
.into_iter()
|
||||
.map(|row| row.into_iter().flat_map(|l| l.spans).collect::<Vec<_>>()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for KeybindingsPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for KeybindingsPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Esc | Key::Enter,
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use tui_realm_stdlib::{Input, Radio};
|
||||
use tui_realm_stdlib::components::{Input, Radio};
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, InputType, Style};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers, NoUserEvent};
|
||||
use tuirealm::props::{
|
||||
BorderType, Borders, Color, HorizontalAlignment, InputType, Style, TextModifiers, Title,
|
||||
};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, PendingActionMsg, TransferMsg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct MkdirPopup {
|
||||
component: Input,
|
||||
}
|
||||
@@ -22,17 +25,17 @@ impl MkdirPopup {
|
||||
)
|
||||
.foreground(color)
|
||||
.input_type(InputType::Text)
|
||||
.placeholder(
|
||||
.placeholder(tuirealm::props::SpanStatic::styled(
|
||||
"New directory name",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
)
|
||||
.title("directory-name", Alignment::Center),
|
||||
))
|
||||
.title(Title::from("directory-name").alignment(HorizontalAlignment::Center)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for MkdirPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for MkdirPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -73,13 +76,13 @@ impl Component<Msg, NoUserEvent> for MkdirPopup {
|
||||
code: Key::Char(ch),
|
||||
..
|
||||
}) => {
|
||||
self.perform(Cmd::Type(ch));
|
||||
self.perform(Cmd::Type(*ch));
|
||||
Some(Msg::None)
|
||||
}
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => match self.state() {
|
||||
State::One(StateValue::String(i)) => Some(Msg::Transfer(TransferMsg::Mkdir(i))),
|
||||
State::Single(StateValue::String(i)) => Some(Msg::Transfer(TransferMsg::Mkdir(i))),
|
||||
_ => Some(Msg::None),
|
||||
},
|
||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||
@@ -90,7 +93,7 @@ impl Component<Msg, NoUserEvent> for MkdirPopup {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct SyncBrowsingMkdirPopup {
|
||||
component: Radio,
|
||||
}
|
||||
@@ -99,25 +102,25 @@ impl SyncBrowsingMkdirPopup {
|
||||
pub fn new(color: Color, dir_name: &str) -> Self {
|
||||
Self {
|
||||
component: Radio::default()
|
||||
.highlight_style(Style::default().fg(color).add_modifier(TextModifiers::REVERSED))
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.choices(["Yes", "No"])
|
||||
.choices(["Yes", "No"])
|
||||
.title(
|
||||
format!(
|
||||
Title::from(format!(
|
||||
r#"Sync browsing: directory "{dir_name}" doesn't exist. Do you want to create it?"#
|
||||
),
|
||||
Alignment::Center,
|
||||
))
|
||||
.alignment(HorizontalAlignment::Center),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for SyncBrowsingMkdirPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for SyncBrowsingMkdirPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -149,7 +152,7 @@ impl Component<Msg, NoUserEvent> for SyncBrowsingMkdirPopup {
|
||||
}) => {
|
||||
if matches!(
|
||||
self.perform(Cmd::Submit),
|
||||
CmdResult::Submit(State::One(StateValue::Usize(0)))
|
||||
CmdResult::Submit(State::Single(StateValue::Usize(0)))
|
||||
) {
|
||||
Some(Msg::PendingAction(PendingActionMsg::MakePendingDirectory))
|
||||
} else {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use tui_realm_stdlib::Input;
|
||||
use tui_realm_stdlib::components::Input;
|
||||
use tuirealm::command::{Cmd, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, InputType, Style};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{BorderType, Borders, Color, HorizontalAlignment, InputType, Style, Title};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, TransferMsg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct NewfilePopup {
|
||||
component: Input,
|
||||
}
|
||||
@@ -22,17 +23,17 @@ impl NewfilePopup {
|
||||
)
|
||||
.foreground(color)
|
||||
.input_type(InputType::Text)
|
||||
.placeholder(
|
||||
.placeholder(tuirealm::props::SpanStatic::styled(
|
||||
"New file name",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
)
|
||||
.title("file.txt", Alignment::Center),
|
||||
))
|
||||
.title(Title::from("file.txt").alignment(HorizontalAlignment::Center)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for NewfilePopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for NewfilePopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -73,13 +74,15 @@ impl Component<Msg, NoUserEvent> for NewfilePopup {
|
||||
code: Key::Char(ch),
|
||||
..
|
||||
}) => {
|
||||
self.perform(Cmd::Type(ch));
|
||||
self.perform(Cmd::Type(*ch));
|
||||
Some(Msg::None)
|
||||
}
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => match self.state() {
|
||||
State::One(StateValue::String(i)) => Some(Msg::Transfer(TransferMsg::NewFile(i))),
|
||||
State::Single(StateValue::String(i)) => {
|
||||
Some(Msg::Transfer(TransferMsg::NewFile(i)))
|
||||
}
|
||||
_ => Some(Msg::None),
|
||||
},
|
||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use tui_realm_stdlib::Input;
|
||||
use tui_realm_stdlib::components::Input;
|
||||
use tuirealm::command::{Cmd, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, InputType, Style};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{BorderType, Borders, Color, HorizontalAlignment, InputType, Style, Title};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, TransferMsg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct OpenWithPopup {
|
||||
component: Input,
|
||||
}
|
||||
@@ -22,17 +23,20 @@ impl OpenWithPopup {
|
||||
)
|
||||
.foreground(color)
|
||||
.input_type(InputType::Text)
|
||||
.placeholder(
|
||||
.placeholder(tuirealm::props::SpanStatic::styled(
|
||||
"Open file with\u{2026}",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
)
|
||||
.title("Type the program to open the file with", Alignment::Center),
|
||||
))
|
||||
.title(
|
||||
Title::from("Type the program to open the file with")
|
||||
.alignment(HorizontalAlignment::Center),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for OpenWithPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for OpenWithPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -73,13 +77,13 @@ impl Component<Msg, NoUserEvent> for OpenWithPopup {
|
||||
code: Key::Char(ch),
|
||||
..
|
||||
}) => {
|
||||
self.perform(Cmd::Type(ch));
|
||||
self.perform(Cmd::Type(*ch));
|
||||
Some(Msg::None)
|
||||
}
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => match self.state() {
|
||||
State::One(StateValue::String(i)) => {
|
||||
State::Single(StateValue::String(i)) => {
|
||||
Some(Msg::Transfer(TransferMsg::OpenFileWith(i)))
|
||||
}
|
||||
_ => Some(Msg::None),
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
use tui_realm_stdlib::ProgressBar;
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent};
|
||||
use tui_realm_stdlib::components::Gauge;
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers, NoUserEvent};
|
||||
use tuirealm::props::{BorderType, Borders, Color, HorizontalAlignment, Title};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, TransferMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct TransferProgressBar {
|
||||
component: ProgressBar,
|
||||
component: Gauge,
|
||||
}
|
||||
|
||||
impl TransferProgressBar {
|
||||
pub fn new<S: Into<String>>(prog: f64, label: S, title: S, color: Color) -> Self {
|
||||
Self {
|
||||
component: ProgressBar::default()
|
||||
component: Gauge::default()
|
||||
.borders(Borders::default().modifiers(BorderType::Rounded))
|
||||
.foreground(color)
|
||||
.label(label)
|
||||
.progress(prog)
|
||||
.title(title, Alignment::Center),
|
||||
.title(Title::from(title.into()).alignment(HorizontalAlignment::Center)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for TransferProgressBar {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for TransferProgressBar {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
if matches!(
|
||||
ev,
|
||||
Event::Keyboard(KeyEvent {
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use tui_realm_stdlib::Radio;
|
||||
use tui_realm_stdlib::components::Radio;
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction};
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers, NoUserEvent};
|
||||
use tuirealm::props::{
|
||||
BorderType, Borders, Color, HorizontalAlignment, Style, TextModifiers, Title,
|
||||
};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct QuitPopup {
|
||||
component: Radio,
|
||||
}
|
||||
@@ -15,20 +18,27 @@ impl QuitPopup {
|
||||
pub fn new(color: Color) -> Self {
|
||||
Self {
|
||||
component: Radio::default()
|
||||
.highlight_style(
|
||||
Style::default()
|
||||
.fg(color)
|
||||
.add_modifier(TextModifiers::REVERSED),
|
||||
)
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.choices(["Yes", "No"])
|
||||
.title("Are you sure you want to quit termscp?", Alignment::Center),
|
||||
.title(
|
||||
Title::from("Are you sure you want to quit termscp?")
|
||||
.alignment(HorizontalAlignment::Center),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for QuitPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for QuitPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -58,7 +68,7 @@ impl Component<Msg, NoUserEvent> for QuitPopup {
|
||||
}) => {
|
||||
if matches!(
|
||||
self.perform(Cmd::Submit),
|
||||
CmdResult::Submit(State::One(StateValue::Usize(0)))
|
||||
CmdResult::Submit(State::Single(StateValue::Usize(0)))
|
||||
) {
|
||||
Some(Msg::Ui(UiMsg::Quit))
|
||||
} else {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use tui_realm_stdlib::Input;
|
||||
use tui_realm_stdlib::components::Input;
|
||||
use tuirealm::command::{Cmd, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, InputType, Style};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{BorderType, Borders, Color, HorizontalAlignment, InputType, Style, Title};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, TransferMsg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct RenamePopup {
|
||||
component: Input,
|
||||
}
|
||||
@@ -22,17 +23,19 @@ impl RenamePopup {
|
||||
)
|
||||
.foreground(color)
|
||||
.input_type(InputType::Text)
|
||||
.placeholder(
|
||||
.placeholder(tuirealm::props::SpanStatic::styled(
|
||||
"/foo/bar/buzz.txt",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
)
|
||||
.title("Move file(s) to\u{2026}", Alignment::Center),
|
||||
))
|
||||
.title(
|
||||
Title::from("Move file(s) to\u{2026}").alignment(HorizontalAlignment::Center),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for RenamePopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for RenamePopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -73,13 +76,13 @@ impl Component<Msg, NoUserEvent> for RenamePopup {
|
||||
code: Key::Char(ch),
|
||||
..
|
||||
}) => {
|
||||
self.perform(Cmd::Type(ch));
|
||||
self.perform(Cmd::Type(*ch));
|
||||
Some(Msg::None)
|
||||
}
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => match self.state() {
|
||||
State::One(StateValue::String(i)) => {
|
||||
State::Single(StateValue::String(i)) => {
|
||||
Some(Msg::Transfer(TransferMsg::RenameFile(i)))
|
||||
}
|
||||
_ => Some(Msg::None),
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use tui_realm_stdlib::Radio;
|
||||
use tui_realm_stdlib::components::Radio;
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction};
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers, NoUserEvent};
|
||||
use tuirealm::props::{
|
||||
BorderType, Borders, Color, HorizontalAlignment, Style, TextModifiers, Title,
|
||||
};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, PendingActionMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct ReplacePopup {
|
||||
component: Radio,
|
||||
}
|
||||
@@ -19,20 +22,24 @@ impl ReplacePopup {
|
||||
};
|
||||
Self {
|
||||
component: Radio::default()
|
||||
.highlight_style(
|
||||
Style::default()
|
||||
.fg(color)
|
||||
.add_modifier(TextModifiers::REVERSED),
|
||||
)
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.choices(["Replace", "Skip", "Replace All", "Skip All", "Cancel"])
|
||||
.title(text, Alignment::Center),
|
||||
.title(Title::from(text).alignment(HorizontalAlignment::Center)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for ReplacePopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for ReplacePopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -60,19 +67,19 @@ impl Component<Msg, NoUserEvent> for ReplacePopup {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => match self.perform(Cmd::Submit) {
|
||||
CmdResult::Submit(State::One(StateValue::Usize(0))) => {
|
||||
CmdResult::Submit(State::Single(StateValue::Usize(0))) => {
|
||||
Some(Msg::PendingAction(PendingActionMsg::ReplaceOverwrite))
|
||||
}
|
||||
CmdResult::Submit(State::One(StateValue::Usize(1))) => {
|
||||
CmdResult::Submit(State::Single(StateValue::Usize(1))) => {
|
||||
Some(Msg::PendingAction(PendingActionMsg::ReplaceSkip))
|
||||
}
|
||||
CmdResult::Submit(State::One(StateValue::Usize(2))) => {
|
||||
CmdResult::Submit(State::Single(StateValue::Usize(2))) => {
|
||||
Some(Msg::PendingAction(PendingActionMsg::ReplaceOverwriteAll))
|
||||
}
|
||||
CmdResult::Submit(State::One(StateValue::Usize(3))) => {
|
||||
CmdResult::Submit(State::Single(StateValue::Usize(3))) => {
|
||||
Some(Msg::PendingAction(PendingActionMsg::ReplaceSkipAll))
|
||||
}
|
||||
CmdResult::Submit(State::One(StateValue::Usize(4))) => {
|
||||
CmdResult::Submit(State::Single(StateValue::Usize(4))) => {
|
||||
Some(Msg::PendingAction(PendingActionMsg::ReplaceCancel))
|
||||
}
|
||||
_ => Some(Msg::None),
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use tui_realm_stdlib::Input;
|
||||
use tui_realm_stdlib::components::Input;
|
||||
use tuirealm::command::{Cmd, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, InputType, Style};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{BorderType, Borders, Color, HorizontalAlignment, InputType, Style, Title};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, TransferMsg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct SaveAsPopup {
|
||||
component: Input,
|
||||
}
|
||||
@@ -22,17 +23,17 @@ impl SaveAsPopup {
|
||||
)
|
||||
.foreground(color)
|
||||
.input_type(InputType::Text)
|
||||
.placeholder(
|
||||
.placeholder(tuirealm::props::SpanStatic::styled(
|
||||
"/foo/bar/buzz.txt",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
)
|
||||
.title("Save as\u{2026}", Alignment::Center),
|
||||
))
|
||||
.title(Title::from("Save as\u{2026}").alignment(HorizontalAlignment::Center)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for SaveAsPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for SaveAsPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -73,13 +74,13 @@ impl Component<Msg, NoUserEvent> for SaveAsPopup {
|
||||
code: Key::Char(ch),
|
||||
..
|
||||
}) => {
|
||||
self.perform(Cmd::Type(ch));
|
||||
self.perform(Cmd::Type(*ch));
|
||||
Some(Msg::None)
|
||||
}
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => match self.state() {
|
||||
State::One(StateValue::String(i)) => {
|
||||
State::Single(StateValue::String(i)) => {
|
||||
Some(Msg::Transfer(TransferMsg::SaveFileAs(i)))
|
||||
}
|
||||
_ => Some(Msg::None),
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
use tui_realm_stdlib::Radio;
|
||||
use tui_realm_stdlib::components::Radio;
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{
|
||||
BorderType, Borders, Color, HorizontalAlignment, Style, TextModifiers, Title,
|
||||
};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::explorer::FileSorting;
|
||||
use crate::ui::activities::filetransfer::{Msg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct SortingPopup {
|
||||
component: Radio,
|
||||
}
|
||||
@@ -16,14 +19,18 @@ impl SortingPopup {
|
||||
pub fn new(value: FileSorting, color: Color) -> Self {
|
||||
Self {
|
||||
component: Radio::default()
|
||||
.highlight_style(
|
||||
Style::default()
|
||||
.fg(color)
|
||||
.add_modifier(TextModifiers::REVERSED),
|
||||
)
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.choices(["Name", "Modify time", "Creation time", "Size"])
|
||||
.title("Sort files by\u{2026}", Alignment::Center)
|
||||
.title(Title::from("Sort files by\u{2026}").alignment(HorizontalAlignment::Center))
|
||||
.value(match value {
|
||||
FileSorting::CreationTime => 2,
|
||||
FileSorting::ModifyTime => 1,
|
||||
@@ -35,8 +42,8 @@ impl SortingPopup {
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for SortingPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for SortingPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
let result = match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -50,7 +57,7 @@ impl Component<Msg, NoUserEvent> for SortingPopup {
|
||||
}) => return Some(Msg::Ui(UiMsg::CloseFileSortingPopup)),
|
||||
_ => return None,
|
||||
};
|
||||
if let CmdResult::Changed(State::One(StateValue::Usize(i))) = result {
|
||||
if let CmdResult::Changed(State::Single(StateValue::Usize(i))) = result {
|
||||
Some(Msg::Ui(UiMsg::ChangeFileSorting(match i {
|
||||
0 => FileSorting::Name,
|
||||
1 => FileSorting::ModifyTime,
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use tui_realm_stdlib::Span;
|
||||
use tuirealm::props::{Color, TextSpan};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent};
|
||||
use tui_realm_stdlib::components::Span;
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, NoUserEvent};
|
||||
use tuirealm::props::{Color, SpanStatic};
|
||||
use tuirealm::ratatui::style::Stylize;
|
||||
|
||||
use crate::explorer::FileSorting;
|
||||
use crate::ui::activities::filetransfer::Msg;
|
||||
use crate::ui::activities::filetransfer::lib::browser::Browser;
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct StatusBarLocal {
|
||||
component: Span,
|
||||
}
|
||||
@@ -17,22 +19,22 @@ impl StatusBarLocal {
|
||||
let hidden_files = hidden_files_label(browser.host_bridge().hidden_files_visible());
|
||||
Self {
|
||||
component: Span::default().spans([
|
||||
TextSpan::new("File sorting: ").fg(sorting_color),
|
||||
TextSpan::new(file_sorting).fg(sorting_color).reversed(),
|
||||
TextSpan::new(" Hidden files: ").fg(hidden_color),
|
||||
TextSpan::new(hidden_files).fg(hidden_color).reversed(),
|
||||
SpanStatic::raw("File sorting: ").fg(sorting_color),
|
||||
SpanStatic::raw(file_sorting).fg(sorting_color).reversed(),
|
||||
SpanStatic::raw(" Hidden files: ").fg(hidden_color),
|
||||
SpanStatic::raw(hidden_files).fg(hidden_color).reversed(),
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for StatusBarLocal {
|
||||
fn on(&mut self, _ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for StatusBarLocal {
|
||||
fn on(&mut self, _ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct StatusBarRemote {
|
||||
component: Span,
|
||||
}
|
||||
@@ -52,19 +54,19 @@ impl StatusBarRemote {
|
||||
};
|
||||
Self {
|
||||
component: Span::default().spans([
|
||||
TextSpan::new("File sorting: ").fg(sorting_color),
|
||||
TextSpan::new(file_sorting).fg(sorting_color).reversed(),
|
||||
TextSpan::new(" Hidden files: ").fg(hidden_color),
|
||||
TextSpan::new(hidden_files).fg(hidden_color).reversed(),
|
||||
TextSpan::new(" Sync browsing: ").fg(sync_color),
|
||||
TextSpan::new(sync_browsing).fg(sync_color).reversed(),
|
||||
SpanStatic::raw("File sorting: ").fg(sorting_color),
|
||||
SpanStatic::raw(file_sorting).fg(sorting_color).reversed(),
|
||||
SpanStatic::raw(" Hidden files: ").fg(hidden_color),
|
||||
SpanStatic::raw(hidden_files).fg(hidden_color).reversed(),
|
||||
SpanStatic::raw(" Sync browsing: ").fg(sync_color),
|
||||
SpanStatic::raw(sync_browsing).fg(sync_color).reversed(),
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for StatusBarRemote {
|
||||
fn on(&mut self, _ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for StatusBarRemote {
|
||||
fn on(&mut self, _ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use tui_realm_stdlib::Input;
|
||||
use tui_realm_stdlib::components::Input;
|
||||
use tuirealm::command::{Cmd, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, InputType, Style};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{BorderType, Borders, Color, HorizontalAlignment, InputType, Style, Title};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, TransferMsg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct SymlinkPopup {
|
||||
component: Input,
|
||||
}
|
||||
@@ -22,20 +23,20 @@ impl SymlinkPopup {
|
||||
)
|
||||
.foreground(color)
|
||||
.input_type(InputType::Text)
|
||||
.placeholder(
|
||||
.placeholder(tuirealm::props::SpanStatic::styled(
|
||||
"Symlink name",
|
||||
Style::default().fg(Color::Rgb(128, 128, 128)),
|
||||
)
|
||||
))
|
||||
.title(
|
||||
"Create a symlink pointing to the selected entry",
|
||||
Alignment::Center,
|
||||
Title::from("Create a symlink pointing to the selected entry")
|
||||
.alignment(HorizontalAlignment::Center),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for SymlinkPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for SymlinkPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -76,13 +77,13 @@ impl Component<Msg, NoUserEvent> for SymlinkPopup {
|
||||
code: Key::Char(ch),
|
||||
..
|
||||
}) => {
|
||||
self.perform(Cmd::Type(ch));
|
||||
self.perform(Cmd::Type(*ch));
|
||||
Some(Msg::None)
|
||||
}
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => match self.state() {
|
||||
State::One(StateValue::String(i)) => {
|
||||
State::Single(StateValue::String(i)) => {
|
||||
Some(Msg::Transfer(TransferMsg::CreateSymlink(i)))
|
||||
}
|
||||
_ => Some(Msg::None),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use tui_realm_stdlib::Paragraph;
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, TextSpan};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent};
|
||||
use tui_realm_stdlib::components::Paragraph;
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers, NoUserEvent};
|
||||
use tuirealm::props::{BorderType, Borders, Color, HorizontalAlignment, SpanStatic};
|
||||
use tuirealm::ratatui::text::Text;
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, TransferMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct WaitPopup {
|
||||
component: Paragraph,
|
||||
}
|
||||
@@ -14,26 +15,28 @@ impl WaitPopup {
|
||||
pub fn new<S: AsRef<str>>(text: S, color: Color) -> Self {
|
||||
Self {
|
||||
component: Paragraph::default()
|
||||
.alignment(Alignment::Center)
|
||||
.alignment_horizontal(HorizontalAlignment::Center)
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.text([TextSpan::from(text.as_ref())])
|
||||
.wrap(true),
|
||||
.text(Text::from_iter([SpanStatic::from(
|
||||
text.as_ref().to_string(),
|
||||
)]))
|
||||
.wrap_trim(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for WaitPopup {
|
||||
fn on(&mut self, _ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for WaitPopup {
|
||||
fn on(&mut self, _ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct WalkdirWaitPopup {
|
||||
component: Paragraph,
|
||||
}
|
||||
@@ -42,24 +45,24 @@ impl WalkdirWaitPopup {
|
||||
pub fn new<S: AsRef<str>>(text: S, color: Color) -> Self {
|
||||
Self {
|
||||
component: Paragraph::default()
|
||||
.alignment(Alignment::Center)
|
||||
.alignment_horizontal(HorizontalAlignment::Center)
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.text([
|
||||
TextSpan::from(text.as_ref()),
|
||||
TextSpan::from("Press 'CTRL+C' to abort"),
|
||||
])
|
||||
.wrap(true),
|
||||
.text(Text::from_iter([
|
||||
SpanStatic::from(text.as_ref().to_string()),
|
||||
SpanStatic::from("Press 'CTRL+C' to abort"),
|
||||
]))
|
||||
.wrap_trim(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for WalkdirWaitPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for WalkdirWaitPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
if matches!(
|
||||
ev,
|
||||
Event::Keyboard(KeyEvent {
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use tui_realm_stdlib::{List, Radio};
|
||||
use tui_realm_stdlib::components::{List, Radio};
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, TextSpan};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers, NoUserEvent};
|
||||
use tuirealm::props::{
|
||||
BorderType, Borders, Color, HorizontalAlignment, SpanStatic, Style, TextModifiers, Title,
|
||||
};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{Msg, TransferMsg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct WatchedPathsList {
|
||||
component: List,
|
||||
}
|
||||
@@ -23,24 +26,24 @@ impl WatchedPathsList {
|
||||
.rewind(true)
|
||||
.scroll(true)
|
||||
.step(4)
|
||||
.highlighted_color(color)
|
||||
.highlighted_str("\u{27a4} ")
|
||||
.highlight_style(Style::default().fg(color))
|
||||
.highlight_str("\u{27a4} ")
|
||||
.title(
|
||||
"These files are currently synched with the remote host",
|
||||
Alignment::Center,
|
||||
Title::from("These files are currently synched with the remote host")
|
||||
.alignment(HorizontalAlignment::Center),
|
||||
)
|
||||
.rows(
|
||||
paths
|
||||
.iter()
|
||||
.map(|x| vec![TextSpan::from(x.to_string_lossy().to_string())])
|
||||
.collect(),
|
||||
.map(|x| vec![SpanStatic::from(x.to_string_lossy().to_string())])
|
||||
.collect::<Vec<Vec<SpanStatic>>>(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for WatchedPathsList {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for WatchedPathsList {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||
Some(Msg::Ui(UiMsg::CloseWatchedPathsList))
|
||||
@@ -82,7 +85,7 @@ impl Component<Msg, NoUserEvent> for WatchedPathsList {
|
||||
code: Key::Enter, ..
|
||||
}) => {
|
||||
// get state
|
||||
if let State::One(StateValue::Usize(idx)) = self.component.state() {
|
||||
if let State::Single(StateValue::Usize(idx)) = self.component.state() {
|
||||
Some(Msg::Transfer(TransferMsg::ToggleWatchFor(idx)))
|
||||
} else {
|
||||
Some(Msg::None)
|
||||
@@ -93,7 +96,7 @@ impl Component<Msg, NoUserEvent> for WatchedPathsList {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct WatcherPopup {
|
||||
component: Radio,
|
||||
}
|
||||
@@ -106,20 +109,24 @@ impl WatcherPopup {
|
||||
};
|
||||
Self {
|
||||
component: Radio::default()
|
||||
.highlight_style(
|
||||
Style::default()
|
||||
.fg(color)
|
||||
.add_modifier(TextModifiers::REVERSED),
|
||||
)
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.choices(["Yes", "No"])
|
||||
.title(text, Alignment::Center),
|
||||
.title(Title::from(text).alignment(HorizontalAlignment::Center)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for WatcherPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for WatcherPopup {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -149,7 +156,7 @@ impl Component<Msg, NoUserEvent> for WatcherPopup {
|
||||
}) => {
|
||||
if matches!(
|
||||
self.perform(Cmd::Submit),
|
||||
CmdResult::Submit(State::One(StateValue::Usize(0)))
|
||||
CmdResult::Submit(State::Single(StateValue::Usize(0)))
|
||||
) {
|
||||
Some(Msg::Transfer(TransferMsg::ToggleWatch))
|
||||
} else {
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use tui_realm_stdlib::List;
|
||||
use tui_realm_stdlib::components::List;
|
||||
use tuirealm::command::{Cmd, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::{Alignment, BorderType, Borders, Color, TextSpan};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{BorderType, Borders, Color, HorizontalAlignment, SpanStatic, Style, Title};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use crate::ui::activities::filetransfer::{MarkQueue, Msg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct SelectedFilesList {
|
||||
component: List,
|
||||
paths: Vec<PathBuf>,
|
||||
@@ -35,20 +36,20 @@ impl SelectedFilesList {
|
||||
.rewind(true)
|
||||
.scroll(true)
|
||||
.step(4)
|
||||
.highlighted_color(color)
|
||||
.highlighted_str("➤ ")
|
||||
.title(title, Alignment::Left)
|
||||
.highlight_style(Style::default().fg(color))
|
||||
.highlight_str("➤ ")
|
||||
.title(Title::from(title.to_string()).alignment(HorizontalAlignment::Left))
|
||||
.rows(
|
||||
paths
|
||||
.iter()
|
||||
.map(|(src, dest)| {
|
||||
vec![
|
||||
TextSpan::from(Self::filename(src)),
|
||||
TextSpan::from(" -> "),
|
||||
TextSpan::from(Self::filename(dest)),
|
||||
SpanStatic::from(Self::filename(src)),
|
||||
SpanStatic::from(" -> "),
|
||||
SpanStatic::from(Self::filename(dest)),
|
||||
]
|
||||
})
|
||||
.collect(),
|
||||
.collect::<Vec<Vec<SpanStatic>>>(),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -61,8 +62,8 @@ impl SelectedFilesList {
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for SelectedFilesList {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for SelectedFilesList {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Down, ..
|
||||
@@ -112,7 +113,7 @@ impl Component<Msg, NoUserEvent> for SelectedFilesList {
|
||||
..
|
||||
}) => {
|
||||
// unmark the selected file
|
||||
let State::One(StateValue::Usize(idx)) = self.state() else {
|
||||
let State::Single(StateValue::Usize(idx)) = self.state() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,16 +3,16 @@ mod history;
|
||||
mod line;
|
||||
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent};
|
||||
use tuirealm::props::Color;
|
||||
use tuirealm::{AttrValue, Attribute, Component, Event, MockComponent, NoUserEvent};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
|
||||
use tuirealm::props::{AttrValue, Attribute, Color};
|
||||
|
||||
use self::component::TerminalComponent;
|
||||
use self::line::Line;
|
||||
use super::Msg;
|
||||
use crate::ui::activities::filetransfer::{TransferMsg, UiMsg};
|
||||
|
||||
#[derive(MockComponent, Default)]
|
||||
#[derive(Component, Default)]
|
||||
pub struct Terminal {
|
||||
component: TerminalComponent,
|
||||
}
|
||||
@@ -45,8 +45,8 @@ impl Terminal {
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for Terminal {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for Terminal {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => {
|
||||
Some(Msg::Ui(UiMsg::CloseExecPopup))
|
||||
@@ -55,7 +55,7 @@ impl Component<Msg, NoUserEvent> for Terminal {
|
||||
code: Key::Enter, ..
|
||||
}) => match self.component.perform(Cmd::Submit) {
|
||||
CmdResult::Submit(state) => {
|
||||
let cmd = state.unwrap_one().unwrap_string();
|
||||
let cmd = state.unwrap_single().unwrap_string();
|
||||
Some(Msg::Transfer(TransferMsg::ExecuteCmd(cmd)))
|
||||
}
|
||||
_ => None,
|
||||
@@ -127,7 +127,7 @@ impl Component<Msg, NoUserEvent> for Terminal {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Char(c), ..
|
||||
}) => {
|
||||
self.component.perform(Cmd::Type(c));
|
||||
self.component.perform(Cmd::Type(*c));
|
||||
Some(Msg::None)
|
||||
}
|
||||
_ => None,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use tui_term::vt100::Parser;
|
||||
use tui_term::widget::PseudoTerminal;
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction, Position};
|
||||
use tuirealm::props::{BorderSides, BorderType, Style};
|
||||
use tuirealm::component::Component;
|
||||
use tuirealm::props::{AttrValue, Attribute, BorderSides, BorderType, Props, QueryResult, Style};
|
||||
use tuirealm::ratatui::layout::Rect;
|
||||
use tuirealm::ratatui::widgets::Block;
|
||||
use tuirealm::{AttrValue, Attribute, MockComponent, Props, State, StateValue};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use super::Line;
|
||||
use super::history::History;
|
||||
@@ -45,8 +46,7 @@ impl TerminalComponent {
|
||||
}
|
||||
|
||||
pub fn write_prompt(&mut self) {
|
||||
if let Some(value) = self.query(Attribute::Content) {
|
||||
let prompt = value.unwrap_string();
|
||||
if let Some(AttrValue::String(prompt)) = self.props.get(Attribute::Content).cloned() {
|
||||
self.parser.process(prompt.as_bytes());
|
||||
}
|
||||
}
|
||||
@@ -83,8 +83,8 @@ impl TerminalComponent {
|
||||
}
|
||||
}
|
||||
|
||||
impl MockComponent for TerminalComponent {
|
||||
fn view(&mut self, frame: &mut tuirealm::Frame, area: Rect) {
|
||||
impl Component for TerminalComponent {
|
||||
fn view(&mut self, frame: &mut tuirealm::ratatui::Frame, area: Rect) {
|
||||
let width = area.width.saturating_sub(2);
|
||||
let height = area.height.saturating_sub(2);
|
||||
|
||||
@@ -95,23 +95,30 @@ impl MockComponent for TerminalComponent {
|
||||
}
|
||||
|
||||
let title = self
|
||||
.query(Attribute::Title)
|
||||
.map(AttrValue::unwrap_string)
|
||||
.props
|
||||
.get(Attribute::Title)
|
||||
.and_then(|v| match v {
|
||||
AttrValue::String(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_else(|| "Terminal".to_string());
|
||||
|
||||
let fg = self
|
||||
.query(Attribute::Foreground)
|
||||
.map(AttrValue::unwrap_color)
|
||||
.props
|
||||
.get(Attribute::Foreground)
|
||||
.and_then(AttrValue::as_color)
|
||||
.unwrap_or(tuirealm::ratatui::style::Color::Reset);
|
||||
|
||||
let bg = self
|
||||
.query(Attribute::Background)
|
||||
.map(AttrValue::unwrap_color)
|
||||
.props
|
||||
.get(Attribute::Background)
|
||||
.and_then(AttrValue::as_color)
|
||||
.unwrap_or(tuirealm::ratatui::style::Color::Reset);
|
||||
|
||||
let border_color = self
|
||||
.query(Attribute::Borders)
|
||||
.map(AttrValue::unwrap_color)
|
||||
.props
|
||||
.get(Attribute::Borders)
|
||||
.and_then(AttrValue::as_color)
|
||||
.unwrap_or(tuirealm::ratatui::style::Color::Reset);
|
||||
|
||||
let terminal = PseudoTerminal::new(self.parser.screen())
|
||||
@@ -128,13 +135,13 @@ impl MockComponent for TerminalComponent {
|
||||
frame.render_widget(terminal, area);
|
||||
}
|
||||
|
||||
fn query(&self, attr: tuirealm::Attribute) -> Option<tuirealm::AttrValue> {
|
||||
self.props.get(attr)
|
||||
fn query<'a>(&'a self, attr: Attribute) -> Option<QueryResult<'a>> {
|
||||
self.props.get_for_query(attr)
|
||||
}
|
||||
|
||||
fn attr(&mut self, attr: tuirealm::Attribute, value: AttrValue) {
|
||||
fn attr(&mut self, attr: Attribute, value: AttrValue) {
|
||||
if attr == Attribute::Text {
|
||||
if let tuirealm::AttrValue::String(s) = value {
|
||||
if let AttrValue::String(s) = value {
|
||||
self.parser.process(b"\r");
|
||||
self.parser.process(s.as_bytes());
|
||||
self.parser.process(b"\r");
|
||||
@@ -146,14 +153,14 @@ impl MockComponent for TerminalComponent {
|
||||
}
|
||||
|
||||
fn state(&self) -> State {
|
||||
State::One(StateValue::String(self.line.content().to_string()))
|
||||
State::Single(StateValue::String(self.line.content().to_string()))
|
||||
}
|
||||
|
||||
fn perform(&mut self, cmd: Cmd) -> CmdResult {
|
||||
match cmd {
|
||||
Cmd::Type(s) => {
|
||||
if !s.is_ascii() || self.scroll > 0 {
|
||||
return CmdResult::None; // Ignore non-ASCII characters or if scrolled
|
||||
return CmdResult::NoChange; // Ignore non-ASCII characters or if scrolled
|
||||
}
|
||||
self.parser.process(&[s as u8]);
|
||||
self.line.push(s);
|
||||
@@ -161,46 +168,46 @@ impl MockComponent for TerminalComponent {
|
||||
}
|
||||
Cmd::Move(Direction::Down) => {
|
||||
if self.scroll > 0 {
|
||||
return CmdResult::None; // Cannot move down if not scrolled
|
||||
return CmdResult::NoChange; // Cannot move down if not scrolled
|
||||
}
|
||||
|
||||
self.history_next();
|
||||
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
Cmd::Move(Direction::Left) => {
|
||||
if self.scroll > 0 {
|
||||
return CmdResult::None; // Cannot move up if not scrolled
|
||||
return CmdResult::NoChange; // Cannot move up if not scrolled
|
||||
}
|
||||
|
||||
if self.line.left() {
|
||||
self.parser.process(&[27, 91, 68]);
|
||||
}
|
||||
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
Cmd::Move(Direction::Right) => {
|
||||
if self.scroll > 0 {
|
||||
return CmdResult::None; // Cannot move up if not scrolled
|
||||
return CmdResult::NoChange; // Cannot move up if not scrolled
|
||||
}
|
||||
|
||||
if self.line.right() {
|
||||
self.parser.process(&[27, 91, 67]);
|
||||
}
|
||||
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
Cmd::Move(Direction::Up) => {
|
||||
if self.scroll > 0 {
|
||||
return CmdResult::None; // Cannot move up if not scrolled
|
||||
return CmdResult::NoChange; // Cannot move up if not scrolled
|
||||
}
|
||||
|
||||
self.history_prev();
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
Cmd::Cancel => {
|
||||
if self.scroll > 0 {
|
||||
return CmdResult::None; // Cannot move to the beginning if scrolled
|
||||
return CmdResult::NoChange; // Cannot move to the beginning if scrolled
|
||||
}
|
||||
|
||||
if !self.line.is_empty() {
|
||||
@@ -215,7 +222,7 @@ impl MockComponent for TerminalComponent {
|
||||
}
|
||||
Cmd::Delete => {
|
||||
if self.scroll > 0 {
|
||||
return CmdResult::None; // Cannot move to the beginning if scrolled
|
||||
return CmdResult::NoChange; // Cannot move to the beginning if scrolled
|
||||
}
|
||||
|
||||
if !self.line.is_empty() {
|
||||
@@ -231,40 +238,40 @@ impl MockComponent for TerminalComponent {
|
||||
self.scroll = self.scroll.saturating_sub(8);
|
||||
self.parser.set_scrollback(self.scroll);
|
||||
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
Cmd::Scroll(Direction::Up) => {
|
||||
self.parser.set_scrollback(self.scroll.saturating_add(8));
|
||||
let scrollback = self.parser.screen().scrollback();
|
||||
self.scroll = scrollback;
|
||||
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
Cmd::Toggle => {
|
||||
// insert
|
||||
self.parser.process(&[27, 91, 50, 126]); // Toggle insert mode
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
Cmd::GoTo(Position::Begin) => {
|
||||
if self.scroll > 0 {
|
||||
return CmdResult::None; // Cannot move to the beginning if scrolled
|
||||
return CmdResult::NoChange; // Cannot move to the beginning if scrolled
|
||||
}
|
||||
|
||||
for _ in 0..self.line.begin() {
|
||||
self.parser.process(&[27, 91, 68]); // Move cursor to the left
|
||||
}
|
||||
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
Cmd::GoTo(Position::End) => {
|
||||
if self.scroll > 0 {
|
||||
return CmdResult::None; // Cannot move to the beginning if scrolled
|
||||
return CmdResult::NoChange; // Cannot move to the beginning if scrolled
|
||||
}
|
||||
|
||||
for _ in 0..self.line.end() {
|
||||
self.parser.process(&[27, 91, 67]); // Move cursor to the right
|
||||
}
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
Cmd::Submit => {
|
||||
self.scroll = 0; // Reset scroll on submit
|
||||
@@ -281,9 +288,9 @@ impl MockComponent for TerminalComponent {
|
||||
self.history.push(&line);
|
||||
}
|
||||
|
||||
CmdResult::Submit(State::One(StateValue::String(line)))
|
||||
CmdResult::Submit(State::Single(StateValue::String(line)))
|
||||
}
|
||||
_ => CmdResult::None,
|
||||
_ => CmdResult::NoChange,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,16 @@ mod file_list;
|
||||
mod file_list_with_search;
|
||||
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction, Position};
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::props::{Alignment, Borders, Color, TextSpan};
|
||||
use tuirealm::{Component, Event, MockComponent, NoUserEvent, State, StateValue};
|
||||
use tuirealm::component::{AppComponent, Component};
|
||||
use tuirealm::event::{Event, Key, KeyEvent, KeyModifiers, NoUserEvent};
|
||||
use tuirealm::props::{Borders, Color, HorizontalAlignment, SpanStatic, Title};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use self::file_list::FileList;
|
||||
use self::file_list_with_search::FileListWithSearch;
|
||||
use super::{Msg, TransferMsg, UiMsg};
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct ExplorerFuzzy {
|
||||
component: FileListWithSearch,
|
||||
}
|
||||
@@ -26,13 +27,22 @@ impl ExplorerFuzzy {
|
||||
.background(bg)
|
||||
.borders(Borders::default().color(hg))
|
||||
.foreground(fg)
|
||||
.highlighted_color(hg)
|
||||
.title(title, Alignment::Left)
|
||||
.rows(files.iter().map(|x| vec![TextSpan::from(*x)]).collect()),
|
||||
.highlight_color(hg)
|
||||
.title(Title::from(title.as_ref().to_string()).alignment(HorizontalAlignment::Left))
|
||||
.rows(
|
||||
files
|
||||
.iter()
|
||||
.map(|x| {
|
||||
vec![tuirealm::props::LineStatic::from(SpanStatic::from(
|
||||
(*x).to_string(),
|
||||
))]
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_search(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
fn on_search(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
@@ -79,8 +89,8 @@ impl ExplorerFuzzy {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Char(ch),
|
||||
..
|
||||
}) => match self.perform(Cmd::Type(ch)) {
|
||||
CmdResult::Changed(State::One(StateValue::String(search))) => {
|
||||
}) => match self.perform(Cmd::Type(*ch)) {
|
||||
CmdResult::Changed(State::Single(StateValue::String(search))) => {
|
||||
Some(Msg::Ui(UiMsg::FuzzySearch(search)))
|
||||
}
|
||||
_ => Some(Msg::None),
|
||||
@@ -92,7 +102,7 @@ impl ExplorerFuzzy {
|
||||
}
|
||||
}
|
||||
|
||||
fn on_file_list(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
fn on_file_list(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Down, ..
|
||||
@@ -145,7 +155,7 @@ impl ExplorerFuzzy {
|
||||
code: Key::Char('m'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
}) => {
|
||||
let CmdResult::Changed(State::One(StateValue::Usize(index))) =
|
||||
let CmdResult::Changed(State::Single(StateValue::Usize(index))) =
|
||||
self.perform(Cmd::Toggle)
|
||||
else {
|
||||
return Some(Msg::None);
|
||||
@@ -217,8 +227,8 @@ impl ExplorerFuzzy {
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for ExplorerFuzzy {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for ExplorerFuzzy {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match self.component.focus() {
|
||||
file_list_with_search::Focus::List => self.on_file_list(ev),
|
||||
file_list_with_search::Focus::Search => self.on_search(ev),
|
||||
@@ -226,7 +236,7 @@ impl Component<Msg, NoUserEvent> for ExplorerFuzzy {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct ExplorerFind {
|
||||
component: FileList,
|
||||
}
|
||||
@@ -238,15 +248,24 @@ impl ExplorerFind {
|
||||
.background(bg)
|
||||
.borders(Borders::default().color(hg))
|
||||
.foreground(fg)
|
||||
.highlighted_color(hg)
|
||||
.title(title, Alignment::Left)
|
||||
.rows(files.iter().map(|x| vec![TextSpan::from(*x)]).collect()),
|
||||
.highlight_color(hg)
|
||||
.title(Title::from(title.as_ref().to_string()).alignment(HorizontalAlignment::Left))
|
||||
.rows(
|
||||
files
|
||||
.iter()
|
||||
.map(|x| {
|
||||
vec![tuirealm::props::LineStatic::from(SpanStatic::from(
|
||||
(*x).to_string(),
|
||||
))]
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for ExplorerFind {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for ExplorerFind {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Down, ..
|
||||
@@ -299,7 +318,7 @@ impl Component<Msg, NoUserEvent> for ExplorerFind {
|
||||
code: Key::Char('m'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
}) => {
|
||||
let CmdResult::Changed(State::One(StateValue::Usize(index))) =
|
||||
let CmdResult::Changed(State::Single(StateValue::Usize(index))) =
|
||||
self.perform(Cmd::Toggle)
|
||||
else {
|
||||
return Some(Msg::None);
|
||||
@@ -367,7 +386,7 @@ impl Component<Msg, NoUserEvent> for ExplorerFind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct ExplorerLocal {
|
||||
component: FileList,
|
||||
}
|
||||
@@ -379,16 +398,25 @@ impl ExplorerLocal {
|
||||
.background(bg)
|
||||
.borders(Borders::default().color(hg))
|
||||
.foreground(fg)
|
||||
.highlighted_color(hg)
|
||||
.title(title, Alignment::Left)
|
||||
.rows(files.iter().map(|x| vec![TextSpan::from(*x)]).collect())
|
||||
.highlight_color(hg)
|
||||
.title(Title::from(title.as_ref().to_string()).alignment(HorizontalAlignment::Left))
|
||||
.rows(
|
||||
files
|
||||
.iter()
|
||||
.map(|x| {
|
||||
vec![tuirealm::props::LineStatic::from(SpanStatic::from(
|
||||
(*x).to_string(),
|
||||
))]
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.dot_dot(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for ExplorerLocal {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for ExplorerLocal {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Down, ..
|
||||
@@ -441,7 +469,7 @@ impl Component<Msg, NoUserEvent> for ExplorerLocal {
|
||||
code: Key::Char('m'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
}) => {
|
||||
let CmdResult::Changed(State::One(StateValue::Usize(index))) =
|
||||
let CmdResult::Changed(State::Single(StateValue::Usize(index))) =
|
||||
self.perform(Cmd::Toggle)
|
||||
else {
|
||||
return Some(Msg::None);
|
||||
@@ -464,7 +492,7 @@ impl Component<Msg, NoUserEvent> for ExplorerLocal {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => {
|
||||
if matches!(self.component.state(), State::One(StateValue::String(_))) {
|
||||
if matches!(self.component.state(), State::Single(StateValue::String(_))) {
|
||||
Some(Msg::Transfer(TransferMsg::GoToParentDirectory))
|
||||
} else {
|
||||
Some(Msg::Transfer(TransferMsg::EnterDirectory))
|
||||
@@ -474,7 +502,7 @@ impl Component<Msg, NoUserEvent> for ExplorerLocal {
|
||||
code: Key::Char(' '),
|
||||
..
|
||||
}) => {
|
||||
if matches!(self.component.state(), State::One(StateValue::String(_))) {
|
||||
if matches!(self.component.state(), State::Single(StateValue::String(_))) {
|
||||
Some(Msg::None)
|
||||
} else {
|
||||
Some(Msg::Transfer(TransferMsg::TransferFile))
|
||||
@@ -585,7 +613,7 @@ impl Component<Msg, NoUserEvent> for ExplorerLocal {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MockComponent)]
|
||||
#[derive(Component)]
|
||||
pub struct ExplorerRemote {
|
||||
component: FileList,
|
||||
}
|
||||
@@ -597,16 +625,25 @@ impl ExplorerRemote {
|
||||
.background(bg)
|
||||
.borders(Borders::default().color(hg))
|
||||
.foreground(fg)
|
||||
.highlighted_color(hg)
|
||||
.title(title, Alignment::Left)
|
||||
.rows(files.iter().map(|x| vec![TextSpan::from(*x)]).collect())
|
||||
.highlight_color(hg)
|
||||
.title(Title::from(title.as_ref().to_string()).alignment(HorizontalAlignment::Left))
|
||||
.rows(
|
||||
files
|
||||
.iter()
|
||||
.map(|x| {
|
||||
vec![tuirealm::props::LineStatic::from(SpanStatic::from(
|
||||
(*x).to_string(),
|
||||
))]
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.dot_dot(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for ExplorerRemote {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
impl AppComponent<Msg, NoUserEvent> for ExplorerRemote {
|
||||
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Down, ..
|
||||
@@ -659,7 +696,7 @@ impl Component<Msg, NoUserEvent> for ExplorerRemote {
|
||||
code: Key::Char('m'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
}) => {
|
||||
let CmdResult::Changed(State::One(StateValue::Usize(index))) =
|
||||
let CmdResult::Changed(State::Single(StateValue::Usize(index))) =
|
||||
self.perform(Cmd::Toggle)
|
||||
else {
|
||||
return Some(Msg::None);
|
||||
@@ -682,7 +719,7 @@ impl Component<Msg, NoUserEvent> for ExplorerRemote {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => {
|
||||
if matches!(self.component.state(), State::One(StateValue::String(_))) {
|
||||
if matches!(self.component.state(), State::Single(StateValue::String(_))) {
|
||||
Some(Msg::Transfer(TransferMsg::GoToParentDirectory))
|
||||
} else {
|
||||
Some(Msg::Transfer(TransferMsg::EnterDirectory))
|
||||
@@ -692,7 +729,7 @@ impl Component<Msg, NoUserEvent> for ExplorerRemote {
|
||||
code: Key::Char(' '),
|
||||
..
|
||||
}) => {
|
||||
if matches!(self.component.state(), State::One(StateValue::String(_))) {
|
||||
if matches!(self.component.state(), State::Single(StateValue::String(_))) {
|
||||
Some(Msg::None)
|
||||
} else {
|
||||
Some(Msg::Transfer(TransferMsg::TransferFile))
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
//! `FileList` component renders a file list tab
|
||||
|
||||
use tuirealm::command::{Cmd, CmdResult, Direction, Position};
|
||||
use tuirealm::component::Component;
|
||||
use tuirealm::props::{
|
||||
Alignment, AttrValue, Attribute, Borders, Color, Style, Table, TextModifiers, TextSpan,
|
||||
AttrValue, Attribute, Borders, Color, HorizontalAlignment, Props, QueryResult, SpanStatic,
|
||||
Style, Table, TextModifiers, Title,
|
||||
};
|
||||
use tuirealm::ratatui::text::{Line, Span};
|
||||
use tuirealm::ratatui::widgets::{List as TuiList, ListDirection, ListItem, ListState};
|
||||
use tuirealm::{MockComponent, Props, State, StateValue};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
pub const FILE_LIST_CMD_SELECT_ALL: &str = "A";
|
||||
pub const FILE_LIST_CMD_DESELECT_ALL: &str = "D";
|
||||
@@ -108,16 +110,16 @@ impl FileList {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn title<S: AsRef<str>>(mut self, t: S, a: Alignment) -> Self {
|
||||
self.attr(
|
||||
Attribute::Title,
|
||||
AttrValue::Title((t.as_ref().to_string(), a)),
|
||||
);
|
||||
pub fn title(mut self, t: Title) -> Self {
|
||||
self.attr(Attribute::Title, AttrValue::Title(t));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn highlighted_color(mut self, c: Color) -> Self {
|
||||
self.attr(Attribute::HighlightedColor, AttrValue::Color(c));
|
||||
pub fn highlight_color(mut self, c: Color) -> Self {
|
||||
self.attr(
|
||||
Attribute::HighlightStyle,
|
||||
AttrValue::Style(Style::default().fg(c).add_modifier(TextModifiers::REVERSED)),
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -136,40 +138,57 @@ impl FileList {
|
||||
fn has_dot_dot(&self) -> bool {
|
||||
self.props
|
||||
.get(Attribute::Custom(PROP_DOT_DOT))
|
||||
.map(AttrValue::unwrap_flag)
|
||||
.and_then(AttrValue::as_flag)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl MockComponent for FileList {
|
||||
fn view(&mut self, frame: &mut tuirealm::Frame, area: tuirealm::ratatui::layout::Rect) {
|
||||
impl Component for FileList {
|
||||
fn view(
|
||||
&mut self,
|
||||
frame: &mut tuirealm::ratatui::Frame,
|
||||
area: tuirealm::ratatui::layout::Rect,
|
||||
) {
|
||||
let title = self
|
||||
.props
|
||||
.get_or(
|
||||
Attribute::Title,
|
||||
AttrValue::Title((String::default(), Alignment::Left)),
|
||||
)
|
||||
.unwrap_title();
|
||||
.get(Attribute::Title)
|
||||
.and_then(AttrValue::as_title)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Title::from(String::default()).alignment(HorizontalAlignment::Left));
|
||||
let borders = self
|
||||
.props
|
||||
.get_or(Attribute::Borders, AttrValue::Borders(Borders::default()))
|
||||
.unwrap_borders();
|
||||
.get(Attribute::Borders)
|
||||
.and_then(AttrValue::as_borders)
|
||||
.unwrap_or_default();
|
||||
let focus = self
|
||||
.props
|
||||
.get_or(Attribute::Focus, AttrValue::Flag(false))
|
||||
.unwrap_flag();
|
||||
.get(Attribute::Focus)
|
||||
.and_then(AttrValue::as_flag)
|
||||
.unwrap_or(false);
|
||||
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("..")]]
|
||||
vec![vec![tuirealm::props::LineStatic::from(SpanStatic::from(
|
||||
"..",
|
||||
))]]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let foreground = self
|
||||
.props
|
||||
.get(Attribute::Foreground)
|
||||
.and_then(AttrValue::as_color)
|
||||
.unwrap_or(Color::Reset);
|
||||
let background = self
|
||||
.props
|
||||
.get(Attribute::Background)
|
||||
.and_then(AttrValue::as_color)
|
||||
.unwrap_or(Color::Reset);
|
||||
let list_items: Vec<ListItem> = match self
|
||||
.props
|
||||
.get(Attribute::Content)
|
||||
.map(AttrValue::unwrap_table)
|
||||
.and_then(AttrValue::as_table)
|
||||
{
|
||||
Some(table) => init_table_iter
|
||||
.iter()
|
||||
@@ -177,39 +196,44 @@ impl MockComponent for FileList {
|
||||
.map(|row| {
|
||||
let columns: Vec<Span> = row
|
||||
.iter()
|
||||
.map(|col| {
|
||||
let (fg, bg, modifiers) =
|
||||
tui_realm_stdlib::utils::use_or_default_styles(&self.props, col);
|
||||
|
||||
Span::styled(
|
||||
col.content.clone(),
|
||||
Style::default().add_modifier(modifiers).fg(fg).bg(bg),
|
||||
)
|
||||
.flat_map(|line| line.spans.iter())
|
||||
.map(|span| {
|
||||
let mut style = span.style;
|
||||
if style.fg.is_none() {
|
||||
style.fg = Some(foreground);
|
||||
}
|
||||
if style.bg.is_none() {
|
||||
style.bg = Some(background);
|
||||
}
|
||||
Span::styled(span.content.clone(), style)
|
||||
})
|
||||
.collect();
|
||||
ListItem::new(Line::from(columns))
|
||||
})
|
||||
.collect(), // Make List item from TextSpan
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
let highlighted_color = self
|
||||
let highlight_style = self
|
||||
.props
|
||||
.get(Attribute::HighlightedColor)
|
||||
.map(AttrValue::unwrap_color);
|
||||
let modifiers = match focus {
|
||||
true => TextModifiers::REVERSED,
|
||||
false => TextModifiers::empty(),
|
||||
};
|
||||
.get(Attribute::HighlightStyle)
|
||||
.and_then(AttrValue::as_style);
|
||||
// Make list
|
||||
let mut list = TuiList::new(list_items)
|
||||
.block(div)
|
||||
.direction(ListDirection::TopToBottom);
|
||||
if let Some(highlighted_color) = highlighted_color {
|
||||
list = list.highlight_style(
|
||||
Style::default()
|
||||
.fg(highlighted_color)
|
||||
.add_modifier(modifiers),
|
||||
);
|
||||
if let Some(style) = highlight_style {
|
||||
let applied = if focus {
|
||||
style
|
||||
} else {
|
||||
// On blur keep the highlight color on the foreground but drop the
|
||||
// REVERSED modifier so the selected row just stands out via fg only.
|
||||
let mut s = Style::default();
|
||||
if let Some(fg) = style.fg {
|
||||
s = s.fg(fg);
|
||||
}
|
||||
s
|
||||
};
|
||||
list = list.highlight_style(applied);
|
||||
}
|
||||
let mut state: ListState = ListState::default();
|
||||
state.select(Some(self.states.list_index));
|
||||
@@ -219,31 +243,27 @@ impl MockComponent for FileList {
|
||||
fn attr(&mut self, attr: Attribute, value: AttrValue) {
|
||||
self.props.set(attr, value);
|
||||
if matches!(attr, Attribute::Content) {
|
||||
self.states.init_list_states(
|
||||
match self
|
||||
.props
|
||||
.get(Attribute::Content)
|
||||
.map(AttrValue::unwrap_table)
|
||||
{
|
||||
Some(line) => line.len(),
|
||||
_ => 0,
|
||||
},
|
||||
self.has_dot_dot(),
|
||||
);
|
||||
let len = self
|
||||
.props
|
||||
.get(Attribute::Content)
|
||||
.and_then(AttrValue::as_table)
|
||||
.map(std::vec::Vec::len)
|
||||
.unwrap_or(0);
|
||||
self.states.init_list_states(len, self.has_dot_dot());
|
||||
self.states.fix_list_index();
|
||||
}
|
||||
}
|
||||
|
||||
fn query(&self, attr: Attribute) -> Option<AttrValue> {
|
||||
self.props.get(attr)
|
||||
fn query<'a>(&'a self, attr: Attribute) -> Option<QueryResult<'a>> {
|
||||
self.props.get_for_query(attr)
|
||||
}
|
||||
|
||||
fn state(&self) -> State {
|
||||
if self.has_dot_dot() && self.states.list_index == 0 {
|
||||
return State::One(StateValue::String("..".to_string()));
|
||||
return State::Single(StateValue::String("..".to_string()));
|
||||
}
|
||||
|
||||
State::One(StateValue::Usize(if self.has_dot_dot() {
|
||||
State::Single(StateValue::Usize(if self.has_dot_dot() {
|
||||
self.states.list_index.saturating_sub(1)
|
||||
} else {
|
||||
self.states.list_index
|
||||
@@ -258,7 +278,7 @@ impl MockComponent for FileList {
|
||||
if prev != self.states.list_index {
|
||||
CmdResult::Changed(self.state())
|
||||
} else {
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
}
|
||||
Cmd::Move(Direction::Up) => {
|
||||
@@ -267,7 +287,7 @@ impl MockComponent for FileList {
|
||||
if prev != self.states.list_index {
|
||||
CmdResult::Changed(self.state())
|
||||
} else {
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
}
|
||||
Cmd::Scroll(Direction::Down) => {
|
||||
@@ -276,7 +296,7 @@ impl MockComponent for FileList {
|
||||
if prev != self.states.list_index {
|
||||
CmdResult::Changed(self.state())
|
||||
} else {
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
}
|
||||
Cmd::Scroll(Direction::Up) => {
|
||||
@@ -285,7 +305,7 @@ impl MockComponent for FileList {
|
||||
if prev != self.states.list_index {
|
||||
CmdResult::Changed(self.state())
|
||||
} else {
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
}
|
||||
Cmd::GoTo(Position::Begin) => {
|
||||
@@ -294,7 +314,7 @@ impl MockComponent for FileList {
|
||||
if prev != self.states.list_index {
|
||||
CmdResult::Changed(self.state())
|
||||
} else {
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
}
|
||||
Cmd::GoTo(Position::End) => {
|
||||
@@ -303,12 +323,12 @@ impl MockComponent for FileList {
|
||||
if prev != self.states.list_index {
|
||||
CmdResult::Changed(self.state())
|
||||
} else {
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
}
|
||||
Cmd::Toggle => {
|
||||
if self.states.list_index == 0 && self.has_dot_dot() {
|
||||
return CmdResult::None;
|
||||
return CmdResult::NoChange;
|
||||
}
|
||||
|
||||
let index = self.states.real_index();
|
||||
@@ -317,9 +337,9 @@ impl MockComponent for FileList {
|
||||
.list_index
|
||||
.saturating_add(1)
|
||||
.min(self.states.list_len.saturating_sub(1));
|
||||
CmdResult::Changed(State::One(StateValue::Usize(index)))
|
||||
CmdResult::Changed(State::Single(StateValue::Usize(index)))
|
||||
}
|
||||
_ => CmdResult::None,
|
||||
_ => CmdResult::NoChange,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
use tui_realm_stdlib::Input;
|
||||
use tui_realm_stdlib::components::Input;
|
||||
use tuirealm::command::{Cmd, CmdResult};
|
||||
use tuirealm::props::{Alignment, AttrValue, Attribute, Borders, Color, Table};
|
||||
use tuirealm::component::Component;
|
||||
use tuirealm::props::{
|
||||
AttrValue, Attribute, Borders, Color, HorizontalAlignment, QueryResult, Style, Table,
|
||||
TextModifiers, Title,
|
||||
};
|
||||
use tuirealm::ratatui::layout::{Constraint, Direction, Layout};
|
||||
use tuirealm::{MockComponent, State};
|
||||
use tuirealm::state::State;
|
||||
|
||||
use super::file_list::FileList;
|
||||
|
||||
@@ -62,21 +66,21 @@ impl FileListWithSearch {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn title<S: AsRef<str>>(mut self, t: S, a: Alignment) -> Self {
|
||||
self.file_list.attr(
|
||||
Attribute::Title,
|
||||
AttrValue::Title((t.as_ref().to_string(), a)),
|
||||
);
|
||||
pub fn title(mut self, t: Title) -> Self {
|
||||
let align = t.content.alignment.unwrap_or(HorizontalAlignment::Left);
|
||||
self.file_list.attr(Attribute::Title, AttrValue::Title(t));
|
||||
self.search.attr(
|
||||
Attribute::Title,
|
||||
AttrValue::Title(("Fuzzy search".to_string(), a)),
|
||||
AttrValue::Title(Title::from("Fuzzy search".to_string()).alignment(align)),
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn highlighted_color(mut self, c: Color) -> Self {
|
||||
self.file_list
|
||||
.attr(Attribute::HighlightedColor, AttrValue::Color(c));
|
||||
pub fn highlight_color(mut self, c: Color) -> Self {
|
||||
self.file_list.attr(
|
||||
Attribute::HighlightStyle,
|
||||
AttrValue::Style(Style::default().fg(c).add_modifier(TextModifiers::REVERSED)),
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -87,8 +91,12 @@ impl FileListWithSearch {
|
||||
}
|
||||
}
|
||||
|
||||
impl MockComponent for FileListWithSearch {
|
||||
fn view(&mut self, frame: &mut tuirealm::Frame, area: tuirealm::ratatui::layout::Rect) {
|
||||
impl Component for FileListWithSearch {
|
||||
fn view(
|
||||
&mut self,
|
||||
frame: &mut tuirealm::ratatui::Frame,
|
||||
area: tuirealm::ratatui::layout::Rect,
|
||||
) {
|
||||
// split the area in two
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
@@ -107,7 +115,7 @@ impl MockComponent for FileListWithSearch {
|
||||
self.file_list.view(frame, chunks[1]);
|
||||
}
|
||||
|
||||
fn query(&self, attr: Attribute) -> Option<AttrValue> {
|
||||
fn query<'a>(&'a self, attr: Attribute) -> Option<QueryResult<'a>> {
|
||||
self.file_list.query(attr)
|
||||
}
|
||||
|
||||
@@ -151,7 +159,7 @@ impl MockComponent for FileListWithSearch {
|
||||
AttrValue::Flag(self.states.focus == Focus::List),
|
||||
);
|
||||
|
||||
CmdResult::None
|
||||
CmdResult::NoChange
|
||||
}
|
||||
cmd if self.states.focus == Focus::Search => self.search.perform(cmd),
|
||||
cmd => self.file_list.perform(cmd),
|
||||
|
||||
@@ -3,14 +3,17 @@ mod host;
|
||||
mod log;
|
||||
mod notify;
|
||||
|
||||
use tuirealm::{PollStrategy, Update};
|
||||
use tuirealm::application::PollStrategy;
|
||||
|
||||
use super::FileTransferActivity;
|
||||
|
||||
impl FileTransferActivity {
|
||||
/// Call `Application::tick()` and process messages in `Update`
|
||||
pub(super) fn tick(&mut self) {
|
||||
match self.app.tick(PollStrategy::UpTo(1)) {
|
||||
match self
|
||||
.app
|
||||
.tick(PollStrategy::UpTo(1, std::time::Duration::from_millis(10)))
|
||||
{
|
||||
Ok(messages) => {
|
||||
if !messages.is_empty() {
|
||||
self.redraw = true;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use tuirealm::props::{
|
||||
Alignment, AttrValue, Attribute, PropPayload, PropValue, TextModifiers, TextSpan,
|
||||
AttrValue, Attribute, HorizontalAlignment, LineStatic, PropPayload, PropValue, SpanStatic,
|
||||
TextModifiers, Title,
|
||||
};
|
||||
use tuirealm::terminal::TerminalAdapter;
|
||||
|
||||
use super::super::browser::FileExplorerTab;
|
||||
use super::super::{FileTransferActivity, Id, ui_result};
|
||||
@@ -33,17 +35,18 @@ impl FileTransferActivity {
|
||||
hostname.len() + 3
|
||||
) // 3 because of '/…/'
|
||||
);
|
||||
let files: Vec<Vec<TextSpan>> = self
|
||||
let files: Vec<Vec<LineStatic>> = self
|
||||
.host_bridge()
|
||||
.iter_files()
|
||||
.map(|x| {
|
||||
let mut span = TextSpan::from(self.host_bridge().fmt_file(x));
|
||||
let mut span = SpanStatic::from(self.host_bridge().fmt_file(x));
|
||||
if self.host_bridge().enqueued().contains_key(x.path()) {
|
||||
span.modifiers |=
|
||||
TextModifiers::REVERSED | TextModifiers::UNDERLINED | TextModifiers::ITALIC;
|
||||
span.style = span.style.add_modifier(
|
||||
TextModifiers::REVERSED | TextModifiers::UNDERLINED | TextModifiers::ITALIC,
|
||||
);
|
||||
}
|
||||
|
||||
vec![span]
|
||||
vec![LineStatic::from(span)]
|
||||
})
|
||||
.collect();
|
||||
// Update content and title
|
||||
@@ -55,7 +58,7 @@ impl FileTransferActivity {
|
||||
ui_result(self.app.attr(
|
||||
&Id::ExplorerHostBridge,
|
||||
Attribute::Title,
|
||||
AttrValue::Title((hostname, Alignment::Left)),
|
||||
AttrValue::Title(Title::from(hostname).alignment(HorizontalAlignment::Left)),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -147,17 +150,18 @@ impl FileTransferActivity {
|
||||
hostname.len() + 3 // 3 because of '/…/'
|
||||
)
|
||||
);
|
||||
let files: Vec<Vec<TextSpan>> = self
|
||||
let files: Vec<Vec<LineStatic>> = self
|
||||
.remote()
|
||||
.iter_files()
|
||||
.map(|x| {
|
||||
let mut span = TextSpan::from(self.remote().fmt_file(x));
|
||||
let mut span = SpanStatic::from(self.remote().fmt_file(x));
|
||||
if self.remote().enqueued().contains_key(x.path()) {
|
||||
span.modifiers |=
|
||||
TextModifiers::REVERSED | TextModifiers::UNDERLINED | TextModifiers::ITALIC;
|
||||
span.style = span.style.add_modifier(
|
||||
TextModifiers::REVERSED | TextModifiers::UNDERLINED | TextModifiers::ITALIC,
|
||||
);
|
||||
}
|
||||
|
||||
vec![span]
|
||||
vec![LineStatic::from(span)]
|
||||
})
|
||||
.collect();
|
||||
// Update content and title
|
||||
@@ -169,7 +173,7 @@ impl FileTransferActivity {
|
||||
ui_result(self.app.attr(
|
||||
&Id::ExplorerRemote,
|
||||
Attribute::Title,
|
||||
AttrValue::Title((hostname, Alignment::Left)),
|
||||
AttrValue::Title(Title::from(hostname).alignment(HorizontalAlignment::Left)),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -185,7 +189,7 @@ impl FileTransferActivity {
|
||||
ui_result(self.app.attr(
|
||||
&Id::TransferProgressBar,
|
||||
Attribute::Value,
|
||||
AttrValue::Payload(PropPayload::One(PropValue::F64(
|
||||
AttrValue::Payload(PropPayload::Single(PropValue::F64(
|
||||
self.transfer.progress.calc_progress(),
|
||||
))),
|
||||
));
|
||||
@@ -201,7 +205,7 @@ impl FileTransferActivity {
|
||||
ui_result(self.app.attr(
|
||||
&Id::TransferProgressBar,
|
||||
Attribute::Title,
|
||||
AttrValue::Title((title, Alignment::Center)),
|
||||
AttrValue::Title(Title::from(title).alignment(HorizontalAlignment::Center)),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -231,17 +235,18 @@ impl FileTransferActivity {
|
||||
}
|
||||
|
||||
pub(in crate::ui::activities::filetransfer) fn update_find_list(&mut self) {
|
||||
let files: Vec<Vec<TextSpan>> = self
|
||||
let files: Vec<Vec<LineStatic>> = self
|
||||
.found()
|
||||
.unwrap()
|
||||
.iter_files()
|
||||
.map(|x| {
|
||||
let mut span = TextSpan::from(self.found().unwrap().fmt_file(x));
|
||||
let mut span = SpanStatic::from(self.found().unwrap().fmt_file(x));
|
||||
if self.found().unwrap().enqueued().contains_key(x.path()) {
|
||||
span.modifiers |=
|
||||
TextModifiers::REVERSED | TextModifiers::UNDERLINED | TextModifiers::ITALIC;
|
||||
span.style = span.style.add_modifier(
|
||||
TextModifiers::REVERSED | TextModifiers::UNDERLINED | TextModifiers::ITALIC,
|
||||
);
|
||||
}
|
||||
vec![span]
|
||||
vec![LineStatic::from(span)]
|
||||
})
|
||||
.collect();
|
||||
ui_result(self.app.attr(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use tuirealm::props::{AttrValue, Attribute, Color, TableBuilder, TextSpan};
|
||||
use tuirealm::props::{AttrValue, Attribute, Color, SpanStatic, TableBuilder};
|
||||
use tuirealm::ratatui::style::Stylize;
|
||||
|
||||
use super::super::{FileTransferActivity, Id, LogLevel, LogRecord, ui_result};
|
||||
|
||||
@@ -52,28 +53,20 @@ impl FileTransferActivity {
|
||||
LogLevel::Warn => Color::Yellow,
|
||||
LogLevel::Info => Color::Green,
|
||||
};
|
||||
let level_label = match record.level {
|
||||
LogLevel::Error => "ERROR",
|
||||
LogLevel::Warn => "WARN",
|
||||
LogLevel::Info => "INFO",
|
||||
};
|
||||
table
|
||||
.add_col(TextSpan::from(format!(
|
||||
.add_col(SpanStatic::from(format!(
|
||||
"{}",
|
||||
record.time.format("%Y-%m-%dT%H:%M:%S%Z")
|
||||
)))
|
||||
.add_col(TextSpan::from(" ["))
|
||||
.add_col(
|
||||
TextSpan::new(
|
||||
format!(
|
||||
"{:5}",
|
||||
match record.level {
|
||||
LogLevel::Error => "ERROR",
|
||||
LogLevel::Warn => "WARN",
|
||||
LogLevel::Info => "INFO",
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
.fg(fg),
|
||||
)
|
||||
.add_col(TextSpan::from("]: "))
|
||||
.add_col(TextSpan::from(record.msg.as_str()));
|
||||
.add_col(SpanStatic::from(" ["))
|
||||
.add_col(SpanStatic::raw(format!("{level_label:5}")).fg(fg))
|
||||
.add_col(SpanStatic::from("]: "))
|
||||
.add_col(SpanStatic::from(record.msg.clone()));
|
||||
}
|
||||
ui_result(self.app.attr(
|
||||
&Id::Log,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// locals
|
||||
// externals
|
||||
use remotefs::fs::File;
|
||||
use tuirealm::{State, StateValue, Update};
|
||||
use tuirealm::state::{State, StateValue};
|
||||
|
||||
use super::actions::SelectedFile;
|
||||
use super::actions::walkdir::WalkdirError;
|
||||
@@ -15,8 +15,8 @@ use super::{
|
||||
ui_result,
|
||||
};
|
||||
|
||||
impl Update<Msg> for FileTransferActivity {
|
||||
fn update(&mut self, msg: Option<Msg>) -> Option<Msg> {
|
||||
impl FileTransferActivity {
|
||||
pub(super) fn update(&mut self, msg: Option<Msg>) -> Option<Msg> {
|
||||
match msg.unwrap_or(Msg::None) {
|
||||
Msg::None => None,
|
||||
Msg::PendingAction(_) => {
|
||||
@@ -78,7 +78,7 @@ impl FileTransferActivity {
|
||||
self.action_find_delete();
|
||||
// Remove deleted entries from the find-result list
|
||||
match self.app.state(&Id::ExplorerFind) {
|
||||
Ok(State::One(StateValue::Usize(idx))) => {
|
||||
Ok(State::Single(StateValue::Usize(idx))) => {
|
||||
self.found_mut().unwrap().del_entry(idx);
|
||||
}
|
||||
Ok(State::Vec(values)) => {
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
//! `filetransfer_activity` is the module which implements the Filetransfer activity, which is the main activity afterall
|
||||
|
||||
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
|
||||
use tuirealm::props::Attribute;
|
||||
use tuirealm::ratatui::layout::{Constraint, Direction, Layout};
|
||||
use tuirealm::ratatui::widgets::Clear;
|
||||
use tuirealm::{Attribute, Sub, SubClause, SubEventClause};
|
||||
use tuirealm::subscription::{EventClause, Sub, SubClause};
|
||||
use tuirealm::terminal::TerminalAdapter;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use crate::ui::activities::filetransfer::browser::FoundExplorerTab;
|
||||
@@ -215,7 +217,12 @@ impl FileTransferActivity {
|
||||
let lines = self
|
||||
.app
|
||||
.query(&Id::WaitPopup, Attribute::Text)
|
||||
.map(|x| x.map(|x| x.unwrap_payload().unwrap_vec().len()))
|
||||
.map(|x| {
|
||||
x.map(|x| match x.into_attr() {
|
||||
tuirealm::props::AttrValue::Text(t) => t.lines.len(),
|
||||
_ => 1,
|
||||
})
|
||||
})
|
||||
.unwrap_or_default()
|
||||
.unwrap_or(1) as u16;
|
||||
let popup =
|
||||
@@ -270,21 +277,19 @@ impl FileTransferActivity {
|
||||
// Get current text width
|
||||
let text_width = self
|
||||
.app
|
||||
.query(&id, tuirealm::Attribute::Text)
|
||||
.query(&id, tuirealm::props::Attribute::Text)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|x| {
|
||||
if x.as_payload().is_none() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
x.unwrap_payload()
|
||||
.unwrap_vec()
|
||||
.into_iter()
|
||||
.map(|x| x.unwrap_text_span().content)
|
||||
.collect::<Vec<String>>()
|
||||
.map(|x| match x.into_attr() {
|
||||
tuirealm::props::AttrValue::Text(t) => t
|
||||
.lines
|
||||
.iter()
|
||||
.flat_map(|line| line.spans.iter())
|
||||
.map(|span| span.content.as_ref())
|
||||
.collect::<Vec<&str>>()
|
||||
.join("")
|
||||
.width() as u16
|
||||
.width() as u16,
|
||||
_ => 0,
|
||||
})
|
||||
.unwrap_or(0);
|
||||
// Calc real width of a row in the popup
|
||||
@@ -305,41 +310,41 @@ impl FileTransferActivity {
|
||||
Box::<components::GlobalListener>::default(),
|
||||
vec![
|
||||
Sub::new(
|
||||
SubEventClause::Keyboard(KeyEvent {
|
||||
EventClause::Keyboard(KeyEvent {
|
||||
code: Key::Esc,
|
||||
modifiers: KeyModifiers::NONE,
|
||||
}),
|
||||
Self::no_popup_mounted_clause(),
|
||||
),
|
||||
Sub::new(
|
||||
SubEventClause::Keyboard(KeyEvent {
|
||||
EventClause::Keyboard(KeyEvent {
|
||||
code: Key::Char('h'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
}),
|
||||
Self::no_popup_mounted_clause(),
|
||||
),
|
||||
Sub::new(
|
||||
SubEventClause::Keyboard(KeyEvent {
|
||||
EventClause::Keyboard(KeyEvent {
|
||||
code: Key::Function(1),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
}),
|
||||
Self::no_popup_mounted_clause(),
|
||||
),
|
||||
Sub::new(
|
||||
SubEventClause::Keyboard(KeyEvent {
|
||||
EventClause::Keyboard(KeyEvent {
|
||||
code: Key::Function(10),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
}),
|
||||
Self::no_popup_mounted_clause(),
|
||||
),
|
||||
Sub::new(
|
||||
SubEventClause::Keyboard(KeyEvent {
|
||||
EventClause::Keyboard(KeyEvent {
|
||||
code: Key::Char('q'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
}),
|
||||
Self::no_popup_mounted_clause(),
|
||||
),
|
||||
Sub::new(SubEventClause::WindowResize, SubClause::Always),
|
||||
Sub::new(EventClause::WindowResize, SubClause::Always),
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
//! `filetransfer_activity` is the module which implements the Filetransfer activity, which is the main activity afterall
|
||||
|
||||
use remotefs::fs::{File, UnixPex};
|
||||
use tuirealm::props::{PropPayload, PropValue, TextSpan};
|
||||
use tuirealm::{AttrValue, Attribute};
|
||||
use tuirealm::props::{AttrValue, Attribute, PropPayload, PropValue, SpanStatic};
|
||||
|
||||
use crate::explorer::FileSorting;
|
||||
use crate::ui::activities::filetransfer::browser::FileExplorerTab;
|
||||
@@ -93,9 +92,9 @@ impl FileTransferActivity {
|
||||
let _ = self.app.attr(
|
||||
&Id::WaitPopup,
|
||||
Attribute::Text,
|
||||
AttrValue::Payload(PropPayload::Vec(vec![
|
||||
PropValue::TextSpan(TextSpan::from(text)),
|
||||
PropValue::TextSpan(TextSpan::from("Press 'CTRL+C' to abort")),
|
||||
AttrValue::Text(tuirealm::ratatui::text::Text::from_iter([
|
||||
SpanStatic::from(text),
|
||||
SpanStatic::from("Press 'CTRL+C' to abort"),
|
||||
])),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user