Context as Activity member; on_destroy context is released

This commit is contained in:
ChristianVisintin
2020-11-29 11:33:59 +01:00
parent 00c81634ed
commit 7e085096c8
4 changed files with 168 additions and 87 deletions

View File

@@ -51,7 +51,7 @@ pub enum NextActivity {
/// ///
/// The activity manager takes care of running activities and handling them until the application has ended /// The activity manager takes care of running activities and handling them until the application has ended
pub struct ActivityManager { pub struct ActivityManager {
context: Context, context: Option<Context>,
ftparams: Option<FileTransferParams>, ftparams: Option<FileTransferParams>,
interval: Duration, interval: Duration,
} }
@@ -71,7 +71,7 @@ impl ActivityManager {
}; };
let ctx: Context = Context::new(host); let ctx: Context = Context::new(host);
Ok(ActivityManager { Ok(ActivityManager {
context: ctx, context: Some(ctx),
ftparams: None, ftparams: None,
interval: interval, interval: interval,
}) })
@@ -127,11 +127,16 @@ impl ActivityManager {
let mut activity: AuthActivity = AuthActivity::new(); let mut activity: AuthActivity = AuthActivity::new();
// Prepare result // Prepare result
let result: Option<NextActivity>; let result: Option<NextActivity>;
// Get context
let ctx: Context = match self.context.take() {
Some(ctx) => ctx,
None => return None
};
// Create activity // Create activity
activity.on_create(&mut self.context); activity.on_create(ctx);
loop { loop {
// Draw activity // Draw activity
activity.on_draw(&mut self.context); activity.on_draw();
// Check if has to be terminated // Check if has to be terminated
if activity.quit { if activity.quit {
// Quit activities // Quit activities
@@ -161,7 +166,7 @@ impl ActivityManager {
sleep(self.interval); sleep(self.interval);
} }
// Destroy activity // Destroy activity
activity.on_destroy(&mut self.context); self.context = activity.on_destroy();
result result
} }
@@ -179,11 +184,16 @@ impl ActivityManager {
FileTransferActivity::new(self.ftparams.take().unwrap()); FileTransferActivity::new(self.ftparams.take().unwrap());
// Prepare result // Prepare result
let result: Option<NextActivity>; let result: Option<NextActivity>;
// Get context
let ctx: Context = match self.context.take() {
Some(ctx) => ctx,
None => return None
};
// Create activity // Create activity
activity.on_create(&mut self.context); activity.on_create(ctx);
loop { loop {
// Draw activity // Draw activity
activity.on_draw(&mut self.context); activity.on_draw();
// Check if has to be terminated // Check if has to be terminated
if activity.quit { if activity.quit {
// Quit activities // Quit activities
@@ -199,7 +209,7 @@ impl ActivityManager {
sleep(self.interval); sleep(self.interval);
} }
// Destroy activity // Destroy activity
activity.on_destroy(&mut self.context); self.context = activity.on_destroy();
result result
} }
} }

View File

@@ -77,6 +77,7 @@ pub struct AuthActivity {
pub password: String, pub password: String,
pub submit: bool, // becomes true after user has submitted fields pub submit: bool, // becomes true after user has submitted fields
pub quit: bool, // Becomes true if user has pressed esc pub quit: bool, // Becomes true if user has pressed esc
context: Option<Context>,
selected_field: InputField, selected_field: InputField,
input_mode: InputMode, input_mode: InputMode,
popup_message: Option<String>, popup_message: Option<String>,
@@ -96,6 +97,7 @@ impl AuthActivity {
password: String::new(), password: String::new(),
submit: false, submit: false,
quit: false, quit: false,
context: None,
selected_field: InputField::Address, selected_field: InputField::Address,
input_mode: InputMode::Text, input_mode: InputMode::Text,
popup_message: None, popup_message: None,
@@ -409,20 +411,27 @@ impl Activity for AuthActivity {
/// ///
/// `on_create` is the function which must be called to initialize the activity. /// `on_create` is the function which must be called to initialize the activity.
/// `on_create` must initialize all the data structures used by the activity /// `on_create` must initialize all the data structures used by the activity
fn on_create(&mut self, context: &mut Context) { /// Context is taken from activity manager and will be released only when activity is destroyed
fn on_create(&mut self, context: Context) {
// Set context
self.context = Some(context);
// Clear terminal
let _ = self.context.as_mut().unwrap().terminal.clear();
// Put raw mode on enabled // Put raw mode on enabled
let _ = enable_raw_mode(); let _ = enable_raw_mode();
// Clear terminal
let _ = context.terminal.clear();
} }
/// ### on_draw /// ### on_draw
/// ///
/// `on_draw` is the function which draws the graphical interface. /// `on_draw` is the function which draws the graphical interface.
/// This function must be called at each tick to refresh the interface /// This function must be called at each tick to refresh the interface
fn on_draw(&mut self, context: &mut Context) { fn on_draw(&mut self) {
// Context must be something
if self.context.is_none() {
return;
}
// Start catching Input Events // Start catching Input Events
if let Ok(input_events) = context.input_hnd.fetch_events() { if let Ok(input_events) = self.context.as_ref().unwrap().input_hnd.fetch_events() {
// Iterate over input events // Iterate over input events
for event in input_events.iter() { for event in input_events.iter() {
self.handle_input_event(event); self.handle_input_event(event);
@@ -431,7 +440,8 @@ impl Activity for AuthActivity {
// Determine input mode // Determine input mode
self.input_mode = self.select_input_mode(); self.input_mode = self.select_input_mode();
// draw interface // draw interface
let _ = context.terminal.draw(|f| { let mut ctx: Context = self.context.take().unwrap();
let _ = ctx.terminal.draw(|f| {
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.margin(2) .margin(2)
@@ -483,16 +493,28 @@ impl Activity for AuthActivity {
_ => {} _ => {}
} }
}); });
// Reset ctx
self.context = Some(ctx);
} }
/// ### on_destroy /// ### on_destroy
/// ///
/// `on_destroy` is the function which cleans up runtime variables and data before terminating the activity. /// `on_destroy` is the function which cleans up runtime variables and data before terminating the activity.
/// This function must be called once before terminating the activity. /// This function must be called once before terminating the activity.
fn on_destroy(&mut self, context: &mut Context) { /// This function finally releases the context
fn on_destroy(&mut self) -> Option<Context> {
// Disable raw mode // Disable raw mode
let _ = disable_raw_mode(); let _ = disable_raw_mode();
// Clear terminal if self.context.is_none() {
let _ = context.terminal.clear(); return None;
}
// Clear terminal and return
match self.context.take() {
Some(mut ctx) => {
let _ = ctx.terminal.clear();
Some(ctx)
},
None => None
}
} }
} }

View File

@@ -51,7 +51,7 @@ use tui::{
style::{Color, Modifier, Style}, style::{Color, Modifier, Style},
terminal::Frame, terminal::Frame,
text::{Span, Spans, Text}, text::{Span, Spans, Text},
widgets::{Block, Borders, Clear, Paragraph, Tabs}, widgets::{Block, Borders, List, ListItem, Paragraph, Tabs}
}; };
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
@@ -59,8 +59,8 @@ use unicode_width::UnicodeWidthStr;
static mut UPLOAD_PROGRESS: f64 = 0.0; // VERY VERY BAD CODING static mut UPLOAD_PROGRESS: f64 = 0.0; // VERY VERY BAD CODING
// Types // Types
type DialogCallback = fn(&mut FileTransferActivity, &mut Context); type DialogCallback = fn(&mut FileTransferActivity);
type OnInputSubmitCallback = fn(&mut FileTransferActivity, &mut Context, String); type OnInputSubmitCallback = fn(&mut FileTransferActivity, String);
/// ### FileTransferParams /// ### FileTransferParams
/// ///
@@ -200,6 +200,7 @@ impl LogRecord {
pub struct FileTransferActivity { pub struct FileTransferActivity {
pub disconnected: bool, // Has disconnected from remote? pub disconnected: bool, // Has disconnected from remote?
pub quit: bool, // Has quit term scp? pub quit: bool, // Has quit term scp?
context: Option<Context>, // Context holder
params: FileTransferParams, // FT connection params params: FileTransferParams, // FT connection params
client: Box<dyn FileTransfer>, // File transfer client client: Box<dyn FileTransfer>, // File transfer client
local: FileExplorer, // Local File explorer state local: FileExplorer, // Local File explorer state
@@ -223,6 +224,7 @@ impl FileTransferActivity {
FileTransferActivity { FileTransferActivity {
disconnected: false, disconnected: false,
quit: false, quit: false,
context: None,
params: params, params: params,
client: match protocol { client: match protocol {
FileTransferProtocol::Sftp => Box::new(SftpFileTransfer::new()), FileTransferProtocol::Sftp => Box::new(SftpFileTransfer::new()),
@@ -269,7 +271,7 @@ impl FileTransferActivity {
/// ### disconnect /// ### disconnect
/// ///
/// disconnect from remote /// disconnect from remote
fn disconnect(&mut self, context: &mut Context) { fn disconnect(&mut self) {
// Show popup disconnecting // Show popup disconnecting
self.input_mode = InputMode::Popup(PopupType::Alert( self.input_mode = InputMode::Popup(PopupType::Alert(
Color::Red, Color::Red,
@@ -307,7 +309,7 @@ impl FileTransferActivity {
/// Send fs entry to remote. /// Send fs entry to remote.
/// If dst_name is Some, entry will be saved with a different name. /// If dst_name is Some, entry will be saved with a different name.
/// If entry is a directory, this applies to directory only /// If entry is a directory, this applies to directory only
fn filetransfer_send(&mut self, ctx: &mut Context, entry: &FsEntry, dst_name: Option<String>) { fn filetransfer_send(&mut self, entry: &FsEntry, dst_name: Option<String>) {
// Write popup // Write popup
let file_name: String = match entry { let file_name: String = match entry {
FsEntry::Directory(dir) => dir.name.clone(), FsEntry::Directory(dir) => dir.name.clone(),
@@ -334,7 +336,7 @@ impl FileTransferActivity {
FsEntry::File(file) => { FsEntry::File(file) => {
// Upload file // Upload file
// Try to open local file // Try to open local file
match ctx.local.open_file_read(file.abs_path.as_path()) { match self.context.as_ref().unwrap().local.open_file_read(file.abs_path.as_path()) {
Ok(mut f) => { Ok(mut f) => {
match self match self
.client .client
@@ -383,12 +385,12 @@ impl FileTransferActivity {
format!("Created directory \"{}\"", dir.abs_path.display()).as_ref(), format!("Created directory \"{}\"", dir.abs_path.display()).as_ref(),
); );
// Get files in dir // Get files in dir
match ctx.local.scan_dir(dir.abs_path.as_path()) { match self.context.as_ref().unwrap().local.scan_dir(dir.abs_path.as_path()) {
Ok(entries) => { Ok(entries) => {
// Iterate over files // Iterate over files
for entry in entries.iter() { for entry in entries.iter() {
// Send entry; name is always None after first call // Send entry; name is always None after first call
self.filetransfer_send(ctx, &entry, None); self.filetransfer_send(&entry, None);
} }
} }
Err(err) => self.log( Err(err) => self.log(
@@ -423,7 +425,6 @@ impl FileTransferActivity {
/// If entry is a directory, this applies to directory only /// If entry is a directory, this applies to directory only
fn filetransfer_recv( fn filetransfer_recv(
&mut self, &mut self,
ctx: &mut Context,
entry: &FsEntry, entry: &FsEntry,
local_path: &Path, local_path: &Path,
dst_name: Option<String>, dst_name: Option<String>,
@@ -456,7 +457,7 @@ impl FileTransferActivity {
}; };
local_file_path.push(local_file_name.as_str()); local_file_path.push(local_file_name.as_str());
// Try to open local file // Try to open local file
match ctx.local.open_file_write(local_file_path.as_path()) { match self.context.as_ref().unwrap().local.open_file_write(local_file_path.as_path()) {
Ok(mut local_file) => { Ok(mut local_file) => {
// Download file from remote // Download file from remote
match self.client.recv_file( match self.client.recv_file(
@@ -506,7 +507,7 @@ impl FileTransferActivity {
None => local_dir_path.push(dir.name.as_str()), None => local_dir_path.push(dir.name.as_str()),
} }
// Create directory on local // Create directory on local
match ctx.local.mkdir_ex(local_dir_path.as_path(), true) { match self.context.as_mut().unwrap().local.mkdir_ex(local_dir_path.as_path(), true) {
Ok(_) => { Ok(_) => {
self.log( self.log(
LogLevel::Info, LogLevel::Info,
@@ -520,7 +521,6 @@ impl FileTransferActivity {
// Receive entry; name is always None after first call // Receive entry; name is always None after first call
// Local path becomes local_dir_path // Local path becomes local_dir_path
self.filetransfer_recv( self.filetransfer_recv(
ctx,
&entry, &entry,
local_dir_path.as_path(), local_dir_path.as_path(),
None, None,
@@ -594,11 +594,11 @@ impl FileTransferActivity {
/// ### handle_input_event /// ### handle_input_event
/// ///
/// Handle input event based on current input mode /// Handle input event based on current input mode
fn handle_input_event(&mut self, context: &mut Context, ev: &InputEvent) { fn handle_input_event(&mut self, ev: &InputEvent) {
match &self.input_mode { match &self.input_mode {
InputMode::Explorer => self.handle_input_event_mode_explorer(context, ev), InputMode::Explorer => self.handle_input_event_mode_explorer(ev),
InputMode::Popup(ptype) => { InputMode::Popup(ptype) => {
self.handle_input_event_mode_popup(ev, ptype.clone(), context) self.handle_input_event_mode_popup(ev, ptype.clone())
} }
} }
} }
@@ -606,16 +606,16 @@ impl FileTransferActivity {
/// ### handle_input_event_mode_explorer /// ### handle_input_event_mode_explorer
/// ///
/// Input event handler for explorer mode /// Input event handler for explorer mode
fn handle_input_event_mode_explorer(&mut self, context: &mut Context, ev: &InputEvent) { fn handle_input_event_mode_explorer(&mut self, ev: &InputEvent) {
// Match input field // Match input field
match self.input_field { match self.input_field {
InputField::Explorer => match self.tab { InputField::Explorer => match self.tab {
// Match current selected tab // Match current selected tab
FileExplorerTab::Local => { FileExplorerTab::Local => {
self.handle_input_event_mode_explorer_tab_local(context, ev) self.handle_input_event_mode_explorer_tab_local(ev)
} }
FileExplorerTab::Remote => { FileExplorerTab::Remote => {
self.handle_input_event_mode_explorer_tab_remote(context, ev) self.handle_input_event_mode_explorer_tab_remote(ev)
} }
}, },
InputField::Logs => self.handle_input_event_mode_explorer_log(ev), InputField::Logs => self.handle_input_event_mode_explorer_log(ev),
@@ -627,7 +627,6 @@ impl FileTransferActivity {
/// Input event handler for explorer mode when localhost tab is selected /// Input event handler for explorer mode when localhost tab is selected
fn handle_input_event_mode_explorer_tab_local( fn handle_input_event_mode_explorer_tab_local(
&mut self, &mut self,
context: &mut Context,
ev: &InputEvent, ev: &InputEvent,
) { ) {
// Match events // Match events
@@ -658,9 +657,9 @@ impl FileTransferActivity {
if let Some(entry) = self.local.files.get(self.local.index) { if let Some(entry) = self.local.files.get(self.local.index) {
if let FsEntry::Directory(dir) = entry { if let FsEntry::Directory(dir) = entry {
// Get current directory // Get current directory
let prev_dir: PathBuf = context.local.pwd(); let prev_dir: PathBuf = self.context.as_ref().unwrap().local.pwd();
// Change directory // Change directory
match context.local.change_wrkdir(dir.abs_path.clone()) { match self.context.as_mut().unwrap().local.change_wrkdir(dir.abs_path.clone()) {
Ok(_) => self.local.pushd(prev_dir.as_path()), // Push prev_dir to stack Ok(_) => self.local.pushd(prev_dir.as_path()), // Push prev_dir to stack
Err(err) => { Err(err) => {
// Report err // Report err
@@ -671,7 +670,7 @@ impl FileTransferActivity {
} }
} }
// Update files // Update files
self.local.files = context.local.list_dir(); self.local.files = self.context.as_ref().unwrap().local.list_dir();
} }
} }
} }
@@ -681,10 +680,10 @@ impl FileTransferActivity {
// Till a valid directory is found // Till a valid directory is found
match self.local.popd() { match self.local.popd() {
Some(d) => { Some(d) => {
match context.local.change_wrkdir(d) { match self.context.as_mut().unwrap().local.change_wrkdir(d) {
Ok(_) => { Ok(_) => {
// Update files // Update files
self.local.files = context.local.list_dir(); self.local.files = self.context.as_ref().unwrap().local.list_dir();
// Break, directory has changed // Break, directory has changed
break; break;
} }
@@ -769,7 +768,7 @@ impl FileTransferActivity {
// Get file at index // Get file at index
if let Some(entry) = files.get(self.local.index) { if let Some(entry) = files.get(self.local.index) {
// Call upload // Call upload
self.filetransfer_send(context, entry, None); self.filetransfer_send(entry, None);
} }
} }
_ => { /* Nothing to do */ } _ => { /* Nothing to do */ }
@@ -786,7 +785,6 @@ impl FileTransferActivity {
/// Input event handler for explorer mode when remote tab is selected /// Input event handler for explorer mode when remote tab is selected
fn handle_input_event_mode_explorer_tab_remote( fn handle_input_event_mode_explorer_tab_remote(
&mut self, &mut self,
context: &mut Context,
ev: &InputEvent, ev: &InputEvent,
) { ) {
// Match events // Match events
@@ -961,9 +959,8 @@ impl FileTransferActivity {
if let Some(entry) = files.get(self.remote.index) { if let Some(entry) = files.get(self.remote.index) {
// Call upload // Call upload
self.filetransfer_recv( self.filetransfer_recv(
context,
entry, entry,
context.local.pwd().as_path(), self.context.as_ref().unwrap().local.pwd().as_path(),
None, None,
); );
} }
@@ -1035,16 +1032,15 @@ impl FileTransferActivity {
&mut self, &mut self,
ev: &InputEvent, ev: &InputEvent,
popup: PopupType, popup: PopupType,
ctx: &mut Context,
) { ) {
match popup { match popup {
PopupType::Alert(_, _) => self.handle_input_event_mode_popup_alert(ev), PopupType::Alert(_, _) => self.handle_input_event_mode_popup_alert(ev),
PopupType::Fatal(_) => self.handle_input_event_mode_popup_fatal(ev), PopupType::Fatal(_) => self.handle_input_event_mode_popup_fatal(ev),
PopupType::Input(_, cb) => self.handle_input_event_mode_popup_input(ev, ctx, cb), PopupType::Input(_, cb) => self.handle_input_event_mode_popup_input(ev, cb),
PopupType::Progress(_) => self.handle_input_event_mode_popup_progress(ev), PopupType::Progress(_) => self.handle_input_event_mode_popup_progress(ev),
PopupType::Wait(_) => self.handle_input_event_mode_popup_wait(ev), PopupType::Wait(_) => self.handle_input_event_mode_popup_wait(ev),
PopupType::YesNo(_, yes_cb, no_cb) => { PopupType::YesNo(_, yes_cb, no_cb) => {
self.handle_input_event_mode_popup_yesno(ev, ctx, yes_cb, no_cb) self.handle_input_event_mode_popup_yesno(ev, yes_cb, no_cb)
} }
} }
} }
@@ -1093,7 +1089,6 @@ impl FileTransferActivity {
fn handle_input_event_mode_popup_input( fn handle_input_event_mode_popup_input(
&mut self, &mut self,
ev: &InputEvent, ev: &InputEvent,
ctx: &mut Context,
cb: OnInputSubmitCallback, cb: OnInputSubmitCallback,
) { ) {
// If enter, close popup, otherwise push chars to input // If enter, close popup, otherwise push chars to input
@@ -1115,7 +1110,7 @@ impl FileTransferActivity {
// Set mode back to explorer BEFORE CALLBACKS!!! Callback can then overwrite this, clever uh? // Set mode back to explorer BEFORE CALLBACKS!!! Callback can then overwrite this, clever uh?
self.input_mode = InputMode::Explorer; self.input_mode = InputMode::Explorer;
// Call cb // Call cb
cb(self, ctx, input_text); cb(self, input_text);
} }
KeyCode::Char(ch) => self.input_txt.push(ch), KeyCode::Char(ch) => self.input_txt.push(ch),
KeyCode::Backspace => { KeyCode::Backspace => {
@@ -1154,7 +1149,6 @@ impl FileTransferActivity {
fn handle_input_event_mode_popup_yesno( fn handle_input_event_mode_popup_yesno(
&mut self, &mut self,
ev: &InputEvent, ev: &InputEvent,
ctx: &mut Context,
yes_cb: DialogCallback, yes_cb: DialogCallback,
no_cb: DialogCallback, no_cb: DialogCallback,
) { ) {
@@ -1167,8 +1161,8 @@ impl FileTransferActivity {
self.input_mode = InputMode::Explorer; self.input_mode = InputMode::Explorer;
// Check if user selected yes or not // Check if user selected yes or not
match self.choice_opt { match self.choice_opt {
DialogYesNoOption::No => no_cb(self, ctx), DialogYesNoOption::No => no_cb(self),
DialogYesNoOption::Yes => yes_cb(self, ctx), DialogYesNoOption::Yes => yes_cb(self),
} }
} }
KeyCode::Right => self.choice_opt = DialogYesNoOption::No, // Set to NO KeyCode::Right => self.choice_opt = DialogYesNoOption::No, // Set to NO
@@ -1185,20 +1179,20 @@ impl FileTransferActivity {
/// ### callback_nothing_to_do /// ### callback_nothing_to_do
/// ///
/// Self titled /// Self titled
fn callback_nothing_to_do(&mut self, _context: &mut Context) {} fn callback_nothing_to_do(&mut self) {}
/// ### callback_force_input_mode_to_explorer /// ### callback_force_input_mode_to_explorer
/// ///
/// force input mode to explorer /// force input mode to explorer
fn callback_force_input_mode_to_explorer(&mut self, _context: &mut Context) { fn callback_force_input_mode_to_explorer(&mut self) {
self.input_mode = InputMode::Explorer; self.input_mode = InputMode::Explorer;
} }
/// ### callback_change_directory /// ### callback_change_directory
/// ///
/// Callback for GOTO command /// Callback for GOTO command
fn callback_change_directory(&mut self, context: &mut Context, input: String) { fn callback_change_directory(&mut self, input: String) {
match context.local.change_wrkdir(PathBuf::from(input.as_str())) { match self.context.as_mut().unwrap().local.change_wrkdir(PathBuf::from(input.as_str())) {
Err(err) => { Err(err) => {
// Report err // Report err
self.input_mode = InputMode::Popup(PopupType::Alert( self.input_mode = InputMode::Popup(PopupType::Alert(
@@ -1206,24 +1200,24 @@ impl FileTransferActivity {
format!("Could not change working directory: {}", err), format!("Could not change working directory: {}", err),
)); ));
} }
Ok(_) => self.local.files = context.local.list_dir(), // Update files Ok(_) => self.local.files = self.context.as_ref().unwrap().local.list_dir(), // Update files
} }
} }
/// ### callback_mkdir /// ### callback_mkdir
/// ///
/// Callback for MKDIR command (supports both local and remote) /// Callback for MKDIR command (supports both local and remote)
fn callback_mkdir(&mut self, context: &mut Context, input: String) { fn callback_mkdir(&mut self, input: String) {
match self.tab { match self.tab {
FileExplorerTab::Local => { FileExplorerTab::Local => {
match context.local.mkdir(PathBuf::from(input.as_str()).as_path()) { match self.context.as_mut().unwrap().local.mkdir(PathBuf::from(input.as_str()).as_path()) {
Ok(_) => { Ok(_) => {
// Reload files // Reload files
self.log( self.log(
LogLevel::Info, LogLevel::Info,
format!("Created directory \"{}\"", input).as_ref(), format!("Created directory \"{}\"", input).as_ref(),
); );
self.local.files = context.local.list_dir(); self.local.files = self.context.as_ref().unwrap().local.list_dir();
} }
Err(err) => { Err(err) => {
// Report err // Report err
@@ -1267,13 +1261,13 @@ impl FileTransferActivity {
/// ### callback_rename /// ### callback_rename
/// ///
/// Callback for RENAME command (supports borth local and remote) /// Callback for RENAME command (supports borth local and remote)
fn callback_rename(&mut self, context: &mut Context, input: String) { fn callback_rename(&mut self, input: String) {
match self.tab { match self.tab {
FileExplorerTab::Local => { FileExplorerTab::Local => {
let mut dst_path: PathBuf = PathBuf::from(input); let mut dst_path: PathBuf = PathBuf::from(input);
// Check if path is relative // Check if path is relative
if dst_path.as_path().is_relative() { if dst_path.as_path().is_relative() {
let mut wrkdir: PathBuf = context.local.pwd(); let mut wrkdir: PathBuf = self.context.as_ref().unwrap().local.pwd();
wrkdir.push(dst_path); wrkdir.push(dst_path);
dst_path = wrkdir; dst_path = wrkdir;
} }
@@ -1284,10 +1278,10 @@ impl FileTransferActivity {
FsEntry::File(file) => file.abs_path.clone(), FsEntry::File(file) => file.abs_path.clone(),
}; };
// Rename file or directory and report status as popup // Rename file or directory and report status as popup
match context.local.rename(entry, dst_path.as_path()) { match self.context.as_mut().unwrap().local.rename(entry, dst_path.as_path()) {
Ok(_) => { Ok(_) => {
// Reload files // Reload files
self.local.files = context.local.list_dir(); self.local.files = self.context.as_ref().unwrap().local.list_dir();
// Log // Log
self.log( self.log(
LogLevel::Info, LogLevel::Info,
@@ -1329,7 +1323,7 @@ impl FileTransferActivity {
match self.client.rename(entry, dst_path.as_path()) { match self.client.rename(entry, dst_path.as_path()) {
Ok(_) => { Ok(_) => {
// Reload files // Reload files
self.local.files = context.local.list_dir(); self.local.files = self.context.as_ref().unwrap().local.list_dir();
// Log // Log
self.log( self.log(
LogLevel::Info, LogLevel::Info,
@@ -1365,7 +1359,7 @@ impl FileTransferActivity {
/// ### callback_delete_fsentry /// ### callback_delete_fsentry
/// ///
/// Delete current selected fsentry in the currently selected TAB /// Delete current selected fsentry in the currently selected TAB
fn callback_delete_fsentry(&mut self, context: &mut Context) { fn callback_delete_fsentry(&mut self) {
// Match current selected tab // Match current selected tab
match self.tab { match self.tab {
FileExplorerTab::Local => { FileExplorerTab::Local => {
@@ -1376,10 +1370,10 @@ impl FileTransferActivity {
FsEntry::File(file) => file.abs_path.clone(), FsEntry::File(file) => file.abs_path.clone(),
}; };
// Delete file or directory and report status as popup // Delete file or directory and report status as popup
match context.local.remove(entry) { match self.context.as_mut().unwrap().local.remove(entry) {
Ok(_) => { Ok(_) => {
// Reload files // Reload files
self.local.files = context.local.list_dir(); self.local.files = self.context.as_ref().unwrap().local.list_dir();
// Log // Log
self.log( self.log(
LogLevel::Info, LogLevel::Info,
@@ -1445,14 +1439,14 @@ impl FileTransferActivity {
/// ///
/// Call file upload, but save with input as name /// Call file upload, but save with input as name
/// Handled both local and remote tab /// Handled both local and remote tab
fn callback_save_as(&mut self, ctx: &mut Context, input: String) { fn callback_save_as(&mut self, input: String) {
match self.tab { match self.tab {
FileExplorerTab::Local => { FileExplorerTab::Local => {
let files: Vec<FsEntry> = self.local.files.clone(); let files: Vec<FsEntry> = self.local.files.clone();
// Get file at index // Get file at index
if let Some(entry) = files.get(self.local.index) { if let Some(entry) = files.get(self.local.index) {
// Call send (upload) // Call send (upload)
self.filetransfer_send(ctx, entry, Some(input)); self.filetransfer_send(entry, Some(input));
} }
} }
FileExplorerTab::Remote => { FileExplorerTab::Remote => {
@@ -1460,7 +1454,7 @@ impl FileTransferActivity {
// Get file at index // Get file at index
if let Some(entry) = files.get(self.remote.index) { if let Some(entry) = files.get(self.remote.index) {
// Call receive (download) // Call receive (download)
self.filetransfer_recv(ctx, entry, ctx.local.pwd().as_path(), Some(input)); self.filetransfer_recv(entry, self.context.as_ref().unwrap().local.pwd().as_path(), Some(input));
} }
} }
} }
@@ -1483,6 +1477,43 @@ impl FileTransferActivity {
.style(Style::default().fg(Color::LightYellow).add_modifier(Modifier::BOLD)) .style(Style::default().fg(Color::LightYellow).add_modifier(Modifier::BOLD))
} }
/*
/// ### draw_log_list
///
/// Draw log list
fn draw_log_list(&self) -> List {
let events: Vec<ListItem> = self.log_records
.iter()
.map(|&(evt, level)| {
let s = match level {
"CRITICAL" => Style::default().fg(Color::Red),
"ERROR" => Style::default().fg(Color::Magenta),
"WARNING" => Style::default().fg(Color::Yellow),
"INFO" => Style::default().fg(Color::Blue),
_ => Style::default(),
};
let header = Spans::from(vec![
Span::styled(format!("{:<9}", level), s),
Span::raw(" "),
Span::styled(
"2020-01-01 10:00:00",
Style::default().add_modifier(Modifier::ITALIC),
),
]);
let log = Spans::from(vec![Span::raw(evt)]);
ListItem::new(vec![
Spans::from("-".repeat(chunks[1].width as usize)),
header,
Spans::from(""),
log,
])
})
.collect();
let events_list = List::new(events)
.block(Block::default().borders(Borders::ALL).title("List"))
.start_corner(Corner::BottomLeft);
}*/
/// ### draw_footer /// ### draw_footer
/// ///
/// Draw authentication page footer /// Draw authentication page footer
@@ -1577,20 +1608,26 @@ impl Activity for FileTransferActivity {
/// ///
/// `on_create` is the function which must be called to initialize the activity. /// `on_create` is the function which must be called to initialize the activity.
/// `on_create` must initialize all the data structures used by the activity /// `on_create` must initialize all the data structures used by the activity
fn on_create(&mut self, context: &mut Context) { fn on_create(&mut self, context: Context) {
// Set context
self.context = Some(context);
// Clear terminal
let _ = self.context.as_mut().unwrap().terminal.clear();
// Put raw mode on enabled // Put raw mode on enabled
let _ = enable_raw_mode(); let _ = enable_raw_mode();
// Clear terminal
let _ = context.terminal.clear();
// Get files at current wd // Get files at current wd
self.local.files = context.local.list_dir(); self.local.files = self.context.as_ref().unwrap().local.list_dir();
} }
/// ### on_draw /// ### on_draw
/// ///
/// `on_draw` is the function which draws the graphical interface. /// `on_draw` is the function which draws the graphical interface.
/// This function must be called at each tick to refresh the interface /// This function must be called at each tick to refresh the interface
fn on_draw(&mut self, context: &mut Context) { fn on_draw(&mut self) {
// Context must be something
if self.context.is_none() {
return;
}
// Check if connected // Check if connected
if !self.client.is_connected() { if !self.client.is_connected() {
// Set init state to connecting popup // Set init state to connecting popup
@@ -1599,37 +1636,47 @@ impl Activity for FileTransferActivity {
self.params.address, self.params.port self.params.address, self.params.port
))); )));
// Force ui draw // Force ui draw
let _ = context.terminal.draw(|f| { let mut ctx: Context = self.context.take().unwrap();
let _ = ctx.terminal.draw(|f| {
self.draw(f); self.draw(f);
}); });
self.context = Some(ctx);
// Connect to remote // Connect to remote
self.connect(); self.connect();
} }
// Handle input events FIXME: read one or multiple? // Handle input events FIXME: read one or multiple?
if let Ok(event) = context.input_hnd.read_event() { if let Ok(event) = self.context.as_ref().unwrap().input_hnd.read_event() {
// Iterate over input events // Iterate over input events
if let Some(event) = event { if let Some(event) = event {
self.handle_input_event(context, &event); self.handle_input_event(&event);
} }
} }
// @! draw interface // @! draw interface
let _ = context.terminal.draw(|f| { let mut ctx: Context = self.context.take().unwrap();
let _ = ctx.terminal.draw(|f| {
self.draw(f); self.draw(f);
}); });
self.context = Some(ctx);
} }
/// ### on_destroy /// ### on_destroy
/// ///
/// `on_destroy` is the function which cleans up runtime variables and data before terminating the activity. /// `on_destroy` is the function which cleans up runtime variables and data before terminating the activity.
/// This function must be called once before terminating the activity. /// This function must be called once before terminating the activity.
fn on_destroy(&mut self, context: &mut Context) { fn on_destroy(&mut self) -> Option<Context> {
// Disable raw mode // Disable raw mode
let _ = disable_raw_mode(); let _ = disable_raw_mode();
// Clear terminal
let _ = context.terminal.clear();
// Disconnect client // Disconnect client
if self.client.is_connected() { if self.client.is_connected() {
let _ = self.client.disconnect(); let _ = self.client.disconnect();
} }
// Clear terminal and return
match self.context.take() {
Some(mut ctx) => {
let _ = ctx.terminal.clear();
Some(ctx)
},
None => None
}
} }
} }

View File

@@ -38,17 +38,19 @@ pub trait Activity {
/// ///
/// `on_create` is the function which must be called to initialize the activity. /// `on_create` is the function which must be called to initialize the activity.
/// `on_create` must initialize all the data structures used by the activity /// `on_create` must initialize all the data structures used by the activity
fn on_create(&mut self, context: &mut Context); /// Context is taken from activity manager and will be released only when activity is destroyed
fn on_create(&mut self, context: Context);
/// ### on_draw /// ### on_draw
/// ///
/// `on_draw` is the function which draws the graphical interface. /// `on_draw` is the function which draws the graphical interface.
/// This function must be called at each tick to refresh the interface /// This function must be called at each tick to refresh the interface
fn on_draw(&mut self, context: &mut Context); fn on_draw(&mut self);
/// ### on_destroy /// ### on_destroy
/// ///
/// `on_destroy` is the function which cleans up runtime variables and data before terminating the activity. /// `on_destroy` is the function which cleans up runtime variables and data before terminating the activity.
/// This function must be called once before terminating the activity. /// This function must be called once before terminating the activity.
fn on_destroy(&mut self, context: &mut Context); /// This function finally releases the context
fn on_destroy(&mut self) -> Option<Context>;
} }