Compare commits
3 commits
15b5d5e131
...
b9ade066e7
| Author | SHA1 | Date | |
|---|---|---|---|
| b9ade066e7 | |||
| 713dc8c2de | |||
| 2aede33db6 |
6 changed files with 134 additions and 64 deletions
|
|
@ -17,8 +17,14 @@ use crate::diags;
|
||||||
use core::{
|
use core::{
|
||||||
action::Action,
|
action::Action,
|
||||||
components::{
|
components::{
|
||||||
Component, footer::Footer, fps::FpsCounter, left::Left, popup, right::Right,
|
Component,
|
||||||
state::StatefulList, title::Title,
|
footer::Footer,
|
||||||
|
fps::FpsCounter,
|
||||||
|
left::{Left, SelectionType},
|
||||||
|
popup,
|
||||||
|
right::Right,
|
||||||
|
state::StatefulList,
|
||||||
|
title::Title,
|
||||||
},
|
},
|
||||||
config::Config,
|
config::Config,
|
||||||
line::{DVLine, get_disk_description_right, get_part_description},
|
line::{DVLine, get_disk_description_right, get_part_description},
|
||||||
|
|
@ -740,15 +746,15 @@ fn build_footer_string(cur_mode: Mode) -> String {
|
||||||
fn build_left_items(app: &App) -> Action {
|
fn build_left_items(app: &App) -> Action {
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
let mut labels = vec![String::new(), String::new()];
|
let mut labels = vec![String::new(), String::new()];
|
||||||
let select_num: usize;
|
let select_type: SelectionType;
|
||||||
let title: String;
|
let title: String;
|
||||||
match app.cur_mode {
|
match app.cur_mode {
|
||||||
Mode::Home => {
|
Mode::Home => {
|
||||||
select_num = 0;
|
select_type = SelectionType::Loop;
|
||||||
title = String::from("Home");
|
title = String::from("Home");
|
||||||
}
|
}
|
||||||
Mode::DiagMenu => {
|
Mode::DiagMenu => {
|
||||||
select_num = 0;
|
select_type = SelectionType::Loop;
|
||||||
title = String::from("Troubleshooting");
|
title = String::from("Troubleshooting");
|
||||||
app.list.items.iter().for_each(|mode| {
|
app.list.items.iter().for_each(|mode| {
|
||||||
let (name, _) = get_mode_strings(*mode);
|
let (name, _) = get_mode_strings(*mode);
|
||||||
|
|
@ -756,7 +762,7 @@ fn build_left_items(app: &App) -> Action {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Mode::InstallDrivers => {
|
Mode::InstallDrivers => {
|
||||||
select_num = 1;
|
select_type = SelectionType::One;
|
||||||
title = String::from("Install Drivers");
|
title = String::from("Install Drivers");
|
||||||
app.clone
|
app.clone
|
||||||
.driver_list
|
.driver_list
|
||||||
|
|
@ -764,7 +770,7 @@ fn build_left_items(app: &App) -> Action {
|
||||||
.for_each(|driver| items.push(driver.to_string()));
|
.for_each(|driver| items.push(driver.to_string()));
|
||||||
}
|
}
|
||||||
Mode::InjectDrivers => {
|
Mode::InjectDrivers => {
|
||||||
select_num = 1;
|
select_type = SelectionType::One;
|
||||||
title = String::from("Select Drivers");
|
title = String::from("Select Drivers");
|
||||||
app.clone
|
app.clone
|
||||||
.driver_list
|
.driver_list
|
||||||
|
|
@ -772,7 +778,7 @@ fn build_left_items(app: &App) -> Action {
|
||||||
.for_each(|driver| items.push(driver.to_string()));
|
.for_each(|driver| items.push(driver.to_string()));
|
||||||
}
|
}
|
||||||
Mode::SelectDisks => {
|
Mode::SelectDisks => {
|
||||||
select_num = 1;
|
select_type = SelectionType::One;
|
||||||
title = String::from("Select Disk");
|
title = String::from("Select Disk");
|
||||||
let disk_list = app.clone.disk_list.lock().unwrap();
|
let disk_list = app.clone.disk_list.lock().unwrap();
|
||||||
disk_list
|
disk_list
|
||||||
|
|
@ -780,11 +786,11 @@ fn build_left_items(app: &App) -> Action {
|
||||||
.for_each(|disk| items.push(disk.description.to_string()));
|
.for_each(|disk| items.push(disk.description.to_string()));
|
||||||
}
|
}
|
||||||
Mode::BootScan | Mode::ScanDisks => {
|
Mode::BootScan | Mode::ScanDisks => {
|
||||||
select_num = 0;
|
select_type = SelectionType::Loop;
|
||||||
title = String::from("Processing");
|
title = String::from("Processing");
|
||||||
}
|
}
|
||||||
Mode::SelectParts => {
|
Mode::SelectParts => {
|
||||||
select_num = 2;
|
select_type = SelectionType::Two;
|
||||||
title = String::from("Select Boot and OS Partitions");
|
title = String::from("Select Boot and OS Partitions");
|
||||||
labels[0] = String::from("boot");
|
labels[0] = String::from("boot");
|
||||||
labels[1] = String::from("os");
|
labels[1] = String::from("os");
|
||||||
|
|
@ -798,7 +804,7 @@ fn build_left_items(app: &App) -> Action {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mode::BootDiags => {
|
Mode::BootDiags => {
|
||||||
select_num = 0;
|
select_type = SelectionType::Loop;
|
||||||
let (new_title, _) = get_mode_strings(app.cur_mode);
|
let (new_title, _) = get_mode_strings(app.cur_mode);
|
||||||
title = new_title;
|
title = new_title;
|
||||||
app.diag_groups.get().iter().for_each(|group| {
|
app.diag_groups.get().iter().for_each(|group| {
|
||||||
|
|
@ -810,19 +816,19 @@ fn build_left_items(app: &App) -> Action {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Mode::BootSetup => {
|
Mode::BootSetup => {
|
||||||
select_num = 0;
|
select_type = SelectionType::Loop;
|
||||||
let (new_title, _) = get_mode_strings(app.cur_mode);
|
let (new_title, _) = get_mode_strings(app.cur_mode);
|
||||||
title = new_title;
|
title = new_title;
|
||||||
}
|
}
|
||||||
Mode::SetBootMode => {
|
Mode::SetBootMode => {
|
||||||
select_num = 1;
|
select_type = SelectionType::One;
|
||||||
title = String::from("Set Boot Mode");
|
title = String::from("Set Boot Mode");
|
||||||
app.boot_modes.iter().for_each(|entry| {
|
app.boot_modes.iter().for_each(|entry| {
|
||||||
items.push(format!("{:?} Safe Mode", entry));
|
items.push(format!("{:?} Safe Mode", entry));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Mode::Done | Mode::Failed => {
|
Mode::Done | Mode::Failed => {
|
||||||
select_num = 0;
|
select_type = SelectionType::Loop;
|
||||||
title = String::from("Done");
|
title = String::from("Done");
|
||||||
}
|
}
|
||||||
// Invalid states
|
// Invalid states
|
||||||
|
|
@ -835,7 +841,7 @@ fn build_left_items(app: &App) -> Action {
|
||||||
panic!("This shouldn't happen?")
|
panic!("This shouldn't happen?")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Action::UpdateLeft(title, labels, items, select_num)
|
Action::UpdateLeft(title, labels, items, select_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_right_items(app: &App) -> Action {
|
fn build_right_items(app: &App) -> Action {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,12 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum::Display;
|
use strum::Display;
|
||||||
|
|
||||||
use crate::{components::popup::Type, line::DVLine, state::Mode, system::disk::Disk};
|
use crate::{
|
||||||
|
components::{left::SelectionType, popup::Type as PopupType},
|
||||||
|
line::DVLine,
|
||||||
|
state::Mode,
|
||||||
|
system::disk::Disk,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
|
|
@ -32,11 +37,7 @@ pub enum Action {
|
||||||
TasksComplete,
|
TasksComplete,
|
||||||
UpdateDiskList(Vec<Disk>),
|
UpdateDiskList(Vec<Disk>),
|
||||||
UpdateFooter(String),
|
UpdateFooter(String),
|
||||||
UpdateLeft(String, Vec<String>, Vec<String>, usize), // (title, labels, items, select_num)
|
UpdateLeft(String, Vec<String>, Vec<String>, SelectionType), // (title, labels, items, select_type)
|
||||||
// NOTE: select_num should be set to 0, 1, or 2.
|
|
||||||
// 0: For repeating selections
|
|
||||||
// 1: For a single choice
|
|
||||||
// 2: For two selections (obviously)
|
|
||||||
UpdateRight(Vec<Vec<DVLine>>, usize, Vec<Vec<DVLine>>), // (labels, start_index, items) - items before start are always shown
|
UpdateRight(Vec<Vec<DVLine>>, usize, Vec<Vec<DVLine>>), // (labels, start_index, items) - items before start are always shown
|
||||||
// App (PE-Menu)
|
// App (PE-Menu)
|
||||||
OpenTerminal,
|
OpenTerminal,
|
||||||
|
|
@ -44,7 +45,7 @@ pub enum Action {
|
||||||
Shutdown,
|
Shutdown,
|
||||||
// Screens
|
// Screens
|
||||||
DismissPopup,
|
DismissPopup,
|
||||||
DisplayPopup(Type, String),
|
DisplayPopup(PopupType, String),
|
||||||
NextScreen,
|
NextScreen,
|
||||||
PrevScreen,
|
PrevScreen,
|
||||||
SetMode(Mode),
|
SetMode(Mode),
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ use crate::action::Action;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct FpsCounter {
|
pub struct FpsCounter {
|
||||||
|
mode: String,
|
||||||
|
|
||||||
last_tick_update: Instant,
|
last_tick_update: Instant,
|
||||||
tick_count: u32,
|
tick_count: u32,
|
||||||
ticks_per_second: f64,
|
ticks_per_second: f64,
|
||||||
|
|
@ -49,6 +51,7 @@ impl FpsCounter {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
mode: String::from(""),
|
||||||
last_tick_update: Instant::now(),
|
last_tick_update: Instant::now(),
|
||||||
tick_count: 0,
|
tick_count: 0,
|
||||||
ticks_per_second: 0.0,
|
ticks_per_second: 0.0,
|
||||||
|
|
@ -88,29 +91,44 @@ impl FpsCounter {
|
||||||
impl Component for FpsCounter {
|
impl Component for FpsCounter {
|
||||||
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||||
match action {
|
match action {
|
||||||
Action::Tick => self.app_tick()?,
|
Action::SetMode(mode) => {
|
||||||
|
self.mode = format!("{:?}", mode);
|
||||||
|
}
|
||||||
Action::Render => self.render_tick()?,
|
Action::Render => self.render_tick()?,
|
||||||
|
Action::Tick => self.app_tick()?,
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||||
let [column, _] =
|
|
||||||
Layout::horizontal([Constraint::Min(1), Constraint::Length(2)]).areas(area);
|
|
||||||
let [_, row, _] = Layout::vertical([
|
let [_, row, _] = Layout::vertical([
|
||||||
Constraint::Length(1),
|
Constraint::Length(1),
|
||||||
Constraint::Length(1),
|
Constraint::Length(1),
|
||||||
Constraint::Min(0),
|
Constraint::Min(0),
|
||||||
])
|
])
|
||||||
.areas(column);
|
.areas(area);
|
||||||
|
let [_, left, right, _] = Layout::horizontal([
|
||||||
|
Constraint::Length(2),
|
||||||
|
Constraint::Min(1),
|
||||||
|
Constraint::Min(1),
|
||||||
|
Constraint::Length(2),
|
||||||
|
])
|
||||||
|
.areas(row);
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
let span = Span::styled(format!("Mode: {}", &self.mode), Style::new().dim());
|
||||||
|
let paragraph = Paragraph::new(span).left_aligned();
|
||||||
|
frame.render_widget(paragraph, left);
|
||||||
|
|
||||||
|
// FPS
|
||||||
let message = format!(
|
let message = format!(
|
||||||
"{:.2} ticks/sec, {:.2} FPS",
|
"{:.2} ticks/sec, {:.2} FPS",
|
||||||
self.ticks_per_second, self.frames_per_second
|
self.ticks_per_second, self.frames_per_second
|
||||||
);
|
);
|
||||||
let span = Span::styled(message, Style::new().dim());
|
let span = Span::styled(message, Style::new().dim());
|
||||||
let paragraph = Paragraph::new(span).right_aligned();
|
let paragraph = Paragraph::new(span).right_aligned();
|
||||||
frame.render_widget(paragraph, row);
|
frame.render_widget(paragraph, right);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,18 +19,27 @@ use ratatui::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
widgets::{Block, Borders, HighlightSpacing, List, ListItem, Padding, Paragraph},
|
widgets::{Block, Borders, HighlightSpacing, List, ListItem, Padding, Paragraph},
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use super::{Component, state::StatefulList};
|
use super::{Component, state::StatefulList};
|
||||||
use crate::{action::Action, config::Config};
|
use crate::{action::Action, config::Config};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum SelectionType {
|
||||||
|
#[default]
|
||||||
|
Loop,
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Left {
|
pub struct Left {
|
||||||
command_tx: Option<UnboundedSender<Action>>,
|
command_tx: Option<UnboundedSender<Action>>,
|
||||||
config: Config,
|
config: Config,
|
||||||
labels: Vec<String>,
|
labels: Vec<String>,
|
||||||
list: StatefulList<String>,
|
list: StatefulList<String>,
|
||||||
select_num: usize,
|
select_type: SelectionType,
|
||||||
selections: Vec<Option<usize>>,
|
selections: Vec<Option<usize>>,
|
||||||
selections_saved: Vec<Option<usize>>,
|
selections_saved: Vec<Option<usize>>,
|
||||||
title_text: String,
|
title_text: String,
|
||||||
|
|
@ -40,8 +49,8 @@ impl Left {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
select_num: 0,
|
|
||||||
labels: vec![String::from("one"), String::from("two")],
|
labels: vec![String::from("one"), String::from("two")],
|
||||||
|
select_type: SelectionType::Loop,
|
||||||
selections: vec![None, None],
|
selections: vec![None, None],
|
||||||
selections_saved: vec![None, None],
|
selections_saved: vec![None, None],
|
||||||
title_text: String::from("Home"),
|
title_text: String::from("Home"),
|
||||||
|
|
@ -76,14 +85,14 @@ impl Component for Left {
|
||||||
Action::KeyUp => self.list.previous(),
|
Action::KeyUp => self.list.previous(),
|
||||||
Action::KeyDown => self.list.next(),
|
Action::KeyDown => self.list.next(),
|
||||||
Action::Process => {
|
Action::Process => {
|
||||||
if self.select_num == 0 {
|
if self.select_type == SelectionType::Loop {
|
||||||
// Selections aren't being used so this is a no-op
|
// Selections aren't being used so this is a no-op
|
||||||
} else if let Some(command_tx) = self.command_tx.clone() {
|
} else if let Some(command_tx) = self.command_tx.clone() {
|
||||||
match (self.selections[0], self.selections[1]) {
|
match (self.selections[0], self.selections[1]) {
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
// Making first selection
|
// Making first selection
|
||||||
command_tx.send(Action::Select(self.list.selected(), None))?;
|
command_tx.send(Action::Select(self.list.selected(), None))?;
|
||||||
if self.select_num == 1 {
|
if self.select_type == SelectionType::One {
|
||||||
// Confirm selection
|
// Confirm selection
|
||||||
command_tx.send(Action::NextScreen)?;
|
command_tx.send(Action::NextScreen)?;
|
||||||
}
|
}
|
||||||
|
|
@ -118,14 +127,14 @@ impl Component for Left {
|
||||||
self.selections[0] = None;
|
self.selections[0] = None;
|
||||||
self.selections[1] = None;
|
self.selections[1] = None;
|
||||||
}
|
}
|
||||||
Action::UpdateLeft(title, labels, items, select_num) => {
|
Action::UpdateLeft(title, labels, items, select_type) => {
|
||||||
self.title_text = title;
|
self.title_text = title;
|
||||||
self.labels = labels
|
self.labels = labels
|
||||||
.iter()
|
.iter()
|
||||||
.map(|label| format!(" ~{}~", label.to_lowercase()))
|
.map(|label| format!(" ~{}~", label.to_lowercase()))
|
||||||
.collect();
|
.collect();
|
||||||
self.list.set_items(items);
|
self.list.set_items(items);
|
||||||
self.select_num = select_num;
|
self.select_type = select_type;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,14 @@
|
||||||
use core::{
|
use core::{
|
||||||
action::Action,
|
action::Action,
|
||||||
components::{
|
components::{
|
||||||
Component, footer::Footer, fps::FpsCounter, left::Left, popup, right::Right,
|
Component,
|
||||||
state::StatefulList, title::Title,
|
footer::Footer,
|
||||||
|
fps::FpsCounter,
|
||||||
|
left::{Left, SelectionType},
|
||||||
|
popup,
|
||||||
|
right::Right,
|
||||||
|
state::StatefulList,
|
||||||
|
title::Title,
|
||||||
},
|
},
|
||||||
config::Config,
|
config::Config,
|
||||||
line::{DVLine, get_disk_description_right, get_part_description},
|
line::{DVLine, get_disk_description_right, get_part_description},
|
||||||
|
|
@ -627,17 +633,17 @@ fn build_footer_string(cur_mode: Mode) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_left_items(app: &App, cur_mode: Mode) -> Action {
|
fn build_left_items(app: &App, cur_mode: Mode) -> Action {
|
||||||
let select_num: usize;
|
let select_type: SelectionType;
|
||||||
let title: String;
|
let title: String;
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
let mut labels: Vec<String> = Vec::new();
|
let mut labels: Vec<String> = Vec::new();
|
||||||
match cur_mode {
|
match cur_mode {
|
||||||
Mode::Home => {
|
Mode::Home => {
|
||||||
select_num = 0;
|
select_type = SelectionType::Loop;
|
||||||
title = String::from("Home");
|
title = String::from("Home");
|
||||||
}
|
}
|
||||||
Mode::InstallDrivers => {
|
Mode::InstallDrivers => {
|
||||||
select_num = 1;
|
select_type = SelectionType::One;
|
||||||
title = String::from("Install Drivers");
|
title = String::from("Install Drivers");
|
||||||
app.clone
|
app.clone
|
||||||
.driver_list
|
.driver_list
|
||||||
|
|
@ -645,7 +651,7 @@ fn build_left_items(app: &App, cur_mode: Mode) -> Action {
|
||||||
.for_each(|driver| items.push(driver.to_string()));
|
.for_each(|driver| items.push(driver.to_string()));
|
||||||
}
|
}
|
||||||
Mode::SelectDisks => {
|
Mode::SelectDisks => {
|
||||||
select_num = 2;
|
select_type = SelectionType::Two;
|
||||||
title = String::from("Select Source and Destination Disks");
|
title = String::from("Select Source and Destination Disks");
|
||||||
labels.push(String::from("source"));
|
labels.push(String::from("source"));
|
||||||
labels.push(String::from("dest"));
|
labels.push(String::from("dest"));
|
||||||
|
|
@ -655,21 +661,21 @@ fn build_left_items(app: &App, cur_mode: Mode) -> Action {
|
||||||
.for_each(|disk| items.push(disk.description.to_string()));
|
.for_each(|disk| items.push(disk.description.to_string()));
|
||||||
}
|
}
|
||||||
Mode::SelectTableType => {
|
Mode::SelectTableType => {
|
||||||
select_num = 1;
|
select_type = SelectionType::One;
|
||||||
title = String::from("Select Partition Table Type");
|
title = String::from("Select Partition Table Type");
|
||||||
items.push(format!("{}", PartitionTableType::Guid));
|
items.push(format!("{}", PartitionTableType::Guid));
|
||||||
items.push(format!("{}", PartitionTableType::Legacy));
|
items.push(format!("{}", PartitionTableType::Legacy));
|
||||||
}
|
}
|
||||||
Mode::Confirm => {
|
Mode::Confirm => {
|
||||||
select_num = 0;
|
select_type = SelectionType::Loop;
|
||||||
title = String::from("Confirm Selections");
|
title = String::from("Confirm Selections");
|
||||||
}
|
}
|
||||||
Mode::ScanDisks | Mode::PreClone | Mode::Clone | Mode::PostClone => {
|
Mode::ScanDisks | Mode::PreClone | Mode::Clone | Mode::PostClone => {
|
||||||
select_num = 0;
|
select_type = SelectionType::Loop;
|
||||||
title = String::from("Processing");
|
title = String::from("Processing");
|
||||||
}
|
}
|
||||||
Mode::SelectParts => {
|
Mode::SelectParts => {
|
||||||
select_num = 2;
|
select_type = SelectionType::Two;
|
||||||
title = String::from("Select Boot and OS Partitions");
|
title = String::from("Select Boot and OS Partitions");
|
||||||
labels.push(String::from("boot"));
|
labels.push(String::from("boot"));
|
||||||
labels.push(String::from("os"));
|
labels.push(String::from("os"));
|
||||||
|
|
@ -683,7 +689,7 @@ fn build_left_items(app: &App, cur_mode: Mode) -> Action {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mode::Done | Mode::Failed => {
|
Mode::Done | Mode::Failed => {
|
||||||
select_num = 0;
|
select_type = SelectionType::Loop;
|
||||||
title = String::from("Done");
|
title = String::from("Done");
|
||||||
}
|
}
|
||||||
// Invalid states
|
// Invalid states
|
||||||
|
|
@ -695,7 +701,7 @@ fn build_left_items(app: &App, cur_mode: Mode) -> Action {
|
||||||
| Mode::PEMenu
|
| Mode::PEMenu
|
||||||
| Mode::SetBootMode => panic!("This shouldn't happen?"),
|
| Mode::SetBootMode => panic!("This shouldn't happen?"),
|
||||||
};
|
};
|
||||||
Action::UpdateLeft(title, labels, items, select_num)
|
Action::UpdateLeft(title, labels, items, select_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_right_items(app: &App, cur_mode: Mode) -> Action {
|
fn build_right_items(app: &App, cur_mode: Mode) -> Action {
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,14 @@
|
||||||
use core::{
|
use core::{
|
||||||
action::Action,
|
action::Action,
|
||||||
components::{
|
components::{
|
||||||
Component, footer::Footer, fps::FpsCounter, left::Left, popup, right::Right,
|
Component,
|
||||||
state::StatefulList, title::Title,
|
footer::Footer,
|
||||||
|
fps::FpsCounter,
|
||||||
|
left::{Left, SelectionType},
|
||||||
|
popup,
|
||||||
|
right::Right,
|
||||||
|
state::StatefulList,
|
||||||
|
title::Title,
|
||||||
},
|
},
|
||||||
config::Config,
|
config::Config,
|
||||||
line::DVLine,
|
line::DVLine,
|
||||||
|
|
@ -76,7 +82,7 @@ impl App {
|
||||||
let disk_list_arc = Arc::new(Mutex::new(Vec::new()));
|
let disk_list_arc = Arc::new(Mutex::new(Vec::new()));
|
||||||
let tasks = Tasks::new(action_tx.clone(), disk_list_arc.clone());
|
let tasks = Tasks::new(action_tx.clone(), disk_list_arc.clone());
|
||||||
let mut list = StatefulList::default();
|
let mut list = StatefulList::default();
|
||||||
list.set_items(load_tools());
|
list.set_items(load_tools(action_tx.clone()));
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
// TUI
|
// TUI
|
||||||
action_rx,
|
action_rx,
|
||||||
|
|
@ -411,7 +417,7 @@ fn build_left_items(app: &App) -> Action {
|
||||||
})
|
})
|
||||||
// ─
|
// ─
|
||||||
.collect();
|
.collect();
|
||||||
Action::UpdateLeft(title, labels, items, 0)
|
Action::UpdateLeft(title, labels, items, SelectionType::Loop)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_right_items(app: &App) -> Action {
|
fn build_right_items(app: &App) -> Action {
|
||||||
|
|
@ -441,21 +447,45 @@ fn build_right_items(app: &App) -> Action {
|
||||||
Action::UpdateRight(labels, start_index, items)
|
Action::UpdateRight(labels, start_index, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_tools() -> Vec<Tool> {
|
pub fn load_tools(action_tx: mpsc::UnboundedSender<Action>) -> Vec<Tool> {
|
||||||
|
let mut entries: Vec<PathBuf>;
|
||||||
|
let mut tools: Vec<Tool> = Vec::new();
|
||||||
let exe_path = env::current_exe().expect("Failed to find main executable");
|
let exe_path = env::current_exe().expect("Failed to find main executable");
|
||||||
let tool_config_path = exe_path.parent().unwrap().join("menu_entries");
|
let tool_config_path = exe_path.parent().unwrap().join("menu_entries");
|
||||||
let mut entries: Vec<PathBuf> = std::fs::read_dir(tool_config_path)
|
if let Ok(read_dir) = std::fs::read_dir(tool_config_path) {
|
||||||
.expect("Failed to find any tool configs")
|
entries = read_dir
|
||||||
.map(|res| res.map(|e| e.path()))
|
.map(|res| res.map(|e| e.path()))
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.collect();
|
.collect();
|
||||||
entries.sort();
|
entries.sort();
|
||||||
entries
|
} else {
|
||||||
.iter()
|
action_tx
|
||||||
.map(|entry| {
|
.send(Action::Error(String::from(
|
||||||
let contents = fs::read_to_string(entry).expect("Failed to read tool config file");
|
"Failed to find any tool configs",
|
||||||
let tool: Tool = toml::from_str(&contents).expect("Failed to parse tool config file");
|
)))
|
||||||
tool
|
.unwrap();
|
||||||
})
|
entries = Vec::new();
|
||||||
.collect()
|
}
|
||||||
|
entries.iter().for_each(|entry| {
|
||||||
|
if let Ok(toml_str) = fs::read_to_string(entry) {
|
||||||
|
if let Ok(tool) = toml::from_str(&toml_str) {
|
||||||
|
tools.push(tool);
|
||||||
|
} else {
|
||||||
|
action_tx
|
||||||
|
.send(Action::Error(format!(
|
||||||
|
"Failed to parse tool config file: {:?}",
|
||||||
|
&entry,
|
||||||
|
)))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
action_tx
|
||||||
|
.send(Action::Error(format!(
|
||||||
|
"Failed to read tool config file: {:?}",
|
||||||
|
&entry,
|
||||||
|
)))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tools
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue