Run get_disks() in the background using a thread
Allows app interaction while the scan is running
This commit is contained in:
parent
7d4f28f950
commit
c62c6c751c
7 changed files with 55 additions and 40 deletions
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"keybindings": {
|
||||
"ScanDisks": {
|
||||
"<Enter>": "Process",
|
||||
"<q>": "Quit", // Quit the application
|
||||
"<Ctrl-d>": "Quit", // Another way to quit
|
||||
"<Ctrl-c>": "Quit", // Yet another way to quit
|
||||
|
|
|
|||
36
src/app.rs
36
src/app.rs
|
|
@ -13,7 +13,11 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
use std::iter::zip;
|
||||
use std::{
|
||||
iter::zip,
|
||||
sync::{Arc, Mutex},
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::KeyEvent;
|
||||
|
|
@ -32,6 +36,7 @@ use crate::{
|
|||
Component,
|
||||
},
|
||||
config::Config,
|
||||
system::disk::{get_disks, Disk},
|
||||
tui::{Event, Tui},
|
||||
};
|
||||
|
||||
|
|
@ -40,6 +45,7 @@ pub struct App {
|
|||
tick_rate: f64,
|
||||
frame_rate: f64,
|
||||
components: Vec<Box<dyn Component>>,
|
||||
disks: Arc<Mutex<Vec<Disk>>>,
|
||||
should_quit: bool,
|
||||
should_suspend: bool,
|
||||
cur_mode: Mode,
|
||||
|
|
@ -74,6 +80,7 @@ impl App {
|
|||
Box::new(Footer::new()),
|
||||
Box::new(Popup::new()),
|
||||
],
|
||||
disks: Arc::new(Mutex::new(Vec::new())),
|
||||
should_quit: false,
|
||||
should_suspend: false,
|
||||
config: Config::new()?,
|
||||
|
|
@ -114,6 +121,8 @@ impl App {
|
|||
}
|
||||
|
||||
pub async fn run(&mut self) -> Result<()> {
|
||||
let disk_wrapper = Arc::clone(&self.disks);
|
||||
let _ = lazy_get_disks(disk_wrapper);
|
||||
let mut tui = Tui::new()?
|
||||
// .mouse(true) // uncomment this line to enable mouse support
|
||||
.tick_rate(self.tick_rate)
|
||||
|
|
@ -203,6 +212,12 @@ impl App {
|
|||
match action {
|
||||
Action::Tick => {
|
||||
self.last_tick_key_events.drain(..);
|
||||
// Continue to next screen if shared disks has been set
|
||||
if self.cur_mode == Mode::ScanDisks {
|
||||
if let Ok(_) = &self.disks.try_lock() {
|
||||
self.action_tx.send(Action::NextScreen)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::Quit => self.should_quit = true,
|
||||
Action::Suspend => self.should_suspend = true,
|
||||
|
|
@ -212,13 +227,17 @@ impl App {
|
|||
Action::Render => self.render(tui)?,
|
||||
Action::PrevScreen => {
|
||||
self.action_tx.send(Action::SetMode(self.prev_mode))?;
|
||||
self.action_tx.send(Action::Select(None, None))?;
|
||||
}
|
||||
Action::NextScreen => {
|
||||
if let Some(mode) = self.next_mode() {
|
||||
self.action_tx.send(Action::SetMode(mode))?;
|
||||
}
|
||||
}
|
||||
Action::ScanDisks => {
|
||||
let disk_wrapper = Arc::clone(&self.disks);
|
||||
let _ = lazy_get_disks(disk_wrapper);
|
||||
self.action_tx.send(Action::SetMode(Mode::ScanDisks))?;
|
||||
}
|
||||
Action::Select(one, two) => {
|
||||
self.selections[0] = one;
|
||||
self.selections[1] = two;
|
||||
|
|
@ -228,8 +247,10 @@ impl App {
|
|||
match new_mode {
|
||||
Mode::ScanDisks => {
|
||||
self.prev_mode = self.cur_mode;
|
||||
self.action_tx.send(Action::Select(None, None))?;
|
||||
self.action_tx.send(Action::ScanDisks)?;
|
||||
}
|
||||
Mode::SelectDisks | Mode::SelectParts => {
|
||||
let disks = self.disks.lock().unwrap();
|
||||
self.action_tx.send(Action::UpdateDiskList(disks.clone()))?;
|
||||
}
|
||||
Mode::Done => {
|
||||
self.action_tx.send(Action::DisplayPopup(String::from(
|
||||
|
|
@ -330,3 +351,10 @@ fn get_chunks(r: Rect) -> Vec<Rect> {
|
|||
// Done
|
||||
chunks
|
||||
}
|
||||
|
||||
fn lazy_get_disks(disk_wrapper: Arc<Mutex<Vec<Disk>>>) -> JoinHandle<()> {
|
||||
thread::spawn(move || {
|
||||
let mut disks = disk_wrapper.lock().unwrap();
|
||||
*disks = get_disks();
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub struct Footer {
|
|||
impl Footer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
footer_text: String::from("(Enter) to select / (b) to go back / (q) to quit"),
|
||||
footer_text: String::from("(q) to quit"),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ impl Component for Footer {
|
|||
}
|
||||
Action::SetMode(new_mode) => {
|
||||
self.footer_text = match new_mode {
|
||||
Mode::ScanDisks => String::from("(Enter) to start / (q) to quit"),
|
||||
Mode::ScanDisks => String::from("(q) to quit"),
|
||||
Mode::SelectDisks => String::from(
|
||||
"(Enter) to select / / (i) to install driver / (r) to rescan / (q) to quit",
|
||||
),
|
||||
|
|
|
|||
|
|
@ -69,11 +69,6 @@ impl Component for Left {
|
|||
Action::KeyUp => self.item_list.previous(),
|
||||
Action::KeyDown => self.item_list.next(),
|
||||
Action::Process => match self.mode {
|
||||
Mode::ScanDisks => {
|
||||
if let Some(command_tx) = self.command_tx.clone() {
|
||||
command_tx.send(Action::ScanDisks)?;
|
||||
}
|
||||
}
|
||||
Mode::Confirm => {
|
||||
if let Some(command_tx) = self.command_tx.clone() {
|
||||
command_tx.send(Action::NextScreen)?;
|
||||
|
|
@ -123,17 +118,22 @@ impl Component for Left {
|
|||
}
|
||||
_ => {}
|
||||
},
|
||||
Action::Select(None, None) => self.selections = vec![None, None],
|
||||
Action::Select(Some(index), None) => self.selections[0] = Some(index),
|
||||
Action::SetMode(new_mode) => {
|
||||
let prev_mode = self.mode;
|
||||
self.mode = new_mode;
|
||||
match new_mode {
|
||||
Mode::ScanDisks => self.title_text = String::from(""),
|
||||
Mode::ScanDisks => {
|
||||
self.title_text = String::from("");
|
||||
self.item_list.clear_items();
|
||||
}
|
||||
Mode::SelectDisks => {
|
||||
self.selections[0] = None;
|
||||
self.selections[1] = None;
|
||||
self.title_text = String::from("Select Source and Destination Disks")
|
||||
self.title_text = String::from("Select Source and Destination Disks");
|
||||
if let Some(command_tx) = self.command_tx.clone() {
|
||||
command_tx.send(Action::DismissPopup)?;
|
||||
}
|
||||
}
|
||||
Mode::SelectParts => {
|
||||
self.selections[0] = None;
|
||||
|
|
@ -171,7 +171,7 @@ impl Component for Left {
|
|||
|
||||
// Body (scan disks)
|
||||
if self.item_list.items.is_empty() {
|
||||
let paragraph = Paragraph::new(String::from("Press Enter to start!")).block(
|
||||
let paragraph = Paragraph::new(String::new()).block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.padding(Padding::new(1, 1, 1, 1)),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use clap::command;
|
||||
// This file is part of Deja-vu.
|
||||
//
|
||||
// Deja-vu is free software: you can redistribute it and/or modify it
|
||||
|
|
@ -19,18 +18,12 @@ use ratatui::{prelude::*, widgets::*};
|
|||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use super::Component;
|
||||
use crate::{
|
||||
action::Action,
|
||||
app::Mode,
|
||||
config::Config,
|
||||
system::disk::{get_disks, Disk},
|
||||
};
|
||||
use crate::{action::Action, app::Mode, config::Config};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Popup {
|
||||
command_tx: Option<UnboundedSender<Action>>,
|
||||
config: Config,
|
||||
disks: Vec<Disk>,
|
||||
popup_text: String,
|
||||
}
|
||||
|
||||
|
|
@ -64,18 +57,7 @@ impl Component for Popup {
|
|||
}
|
||||
Action::DismissPopup => self.popup_text.clear(),
|
||||
Action::DisplayPopup(new_text) => self.popup_text = String::from(new_text),
|
||||
Action::ScanDisks => {
|
||||
if let Some(command_tx) = self.command_tx.clone() {
|
||||
self.disks = get_disks();
|
||||
command_tx.send(Action::NextScreen)?;
|
||||
}
|
||||
}
|
||||
Action::SetMode(Mode::SelectDisks) => {
|
||||
self.popup_text.clear();
|
||||
if let Some(command_tx) = self.command_tx.clone() {
|
||||
command_tx.send(Action::UpdateDiskList(self.disks.clone()))?;
|
||||
}
|
||||
}
|
||||
Action::SetMode(Mode::ScanDisks) => self.popup_text = String::from("Scanning Disks..."),
|
||||
_ => {}
|
||||
}
|
||||
Ok(None)
|
||||
|
|
|
|||
|
|
@ -19,13 +19,14 @@ use ratatui::{prelude::*, widgets::*};
|
|||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use super::Component;
|
||||
use crate::{action::Action, app::Mode, config::Config};
|
||||
use crate::{action::Action, app::Mode, config::Config, system::disk::Disk};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Right {
|
||||
command_tx: Option<UnboundedSender<Action>>,
|
||||
config: Config,
|
||||
cur_mode: Mode,
|
||||
disks: Vec<Disk>,
|
||||
prev_mode: Mode,
|
||||
all_modes: Vec<Mode>,
|
||||
selections: Vec<Option<usize>>,
|
||||
|
|
@ -76,6 +77,7 @@ impl Component for Right {
|
|||
self.cur_mode = new_mode;
|
||||
self.all_modes.push(new_mode);
|
||||
}
|
||||
Action::UpdateDiskList(disks) => self.disks = disks,
|
||||
_ => {}
|
||||
}
|
||||
Ok(None)
|
||||
|
|
@ -95,8 +97,12 @@ impl Component for Right {
|
|||
|
||||
// Body
|
||||
let paragraph = Paragraph::new(format!(
|
||||
"Prev Mode: {:?} // Cur Mode: {:?}\n{:?}\n{:?}",
|
||||
self.prev_mode, self.cur_mode, self.all_modes, self.selections,
|
||||
"Prev Mode: {:?} // Cur Mode: {:?}\n{:?}\n{:?}\nDisks: {}",
|
||||
self.prev_mode,
|
||||
self.cur_mode,
|
||||
self.all_modes,
|
||||
self.selections,
|
||||
self.disks.len(),
|
||||
))
|
||||
.block(
|
||||
Block::default()
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ impl fmt::Display for PartitionTableType {
|
|||
}
|
||||
|
||||
pub fn get_disks() -> Vec<Disk> {
|
||||
let disks;
|
||||
let disks: Vec<Disk>;
|
||||
if cfg!(windows) {
|
||||
disks = diskpart::get_disks();
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue