Add initial results UI layout
This commit is contained in:
parent
2296b8f274
commit
e0823293e2
4 changed files with 114 additions and 9 deletions
|
|
@ -39,6 +39,8 @@ use core::{
|
||||||
tui::{Event, Tui},
|
tui::{Event, Tui},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
array,
|
||||||
|
collections::HashMap,
|
||||||
env,
|
env,
|
||||||
iter::zip,
|
iter::zip,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
|
@ -74,6 +76,7 @@ pub struct App {
|
||||||
boot_modes: Vec<SafeMode>,
|
boot_modes: Vec<SafeMode>,
|
||||||
selections: Vec<Option<usize>>,
|
selections: Vec<Option<usize>>,
|
||||||
system32: String,
|
system32: String,
|
||||||
|
results: Arc<Mutex<HashMap<String, String>>>,
|
||||||
tasks: Tasks,
|
tasks: Tasks,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,6 +84,16 @@ impl App {
|
||||||
pub fn new(tick_rate: f64, frame_rate: f64) -> Result<Self> {
|
pub fn new(tick_rate: f64, frame_rate: f64) -> Result<Self> {
|
||||||
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
||||||
let disk_list_arc = Arc::new(Mutex::new(Vec::new()));
|
let disk_list_arc = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
let mut results_fake = HashMap::new();
|
||||||
|
results_fake.insert(String::from("1. One"), String::from("Line one,\nline two"));
|
||||||
|
results_fake.insert(
|
||||||
|
String::from("2. Two"),
|
||||||
|
String::from("Another example?\n¯\\_(ツ)_/¯"),
|
||||||
|
);
|
||||||
|
let too_many_lines: [usize; 75] = array::from_fn(|i| i + 1);
|
||||||
|
let too_many_lines: Vec<String> = too_many_lines.iter().map(|x| format!("{x}")).collect();
|
||||||
|
results_fake.insert(String::from("3. Three"), too_many_lines.join("\n"));
|
||||||
|
let results_arc = Arc::new(Mutex::new(results_fake));
|
||||||
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(vec![
|
list.set_items(vec![
|
||||||
|
|
@ -101,7 +114,9 @@ impl App {
|
||||||
Box::new(Footer::new()),
|
Box::new(Footer::new()),
|
||||||
Box::new(popup::Popup::new()),
|
Box::new(popup::Popup::new()),
|
||||||
Box::new(crate::components::progress::Progress::new()),
|
Box::new(crate::components::progress::Progress::new()),
|
||||||
Box::new(crate::components::results::Results::new()),
|
Box::new(crate::components::results::Results::new(
|
||||||
|
results_arc.clone(),
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
config: Config::new()?,
|
config: Config::new()?,
|
||||||
diag_groups: diags::Groups::new(),
|
diag_groups: diags::Groups::new(),
|
||||||
|
|
@ -117,6 +132,7 @@ impl App {
|
||||||
boot_modes: vec![SafeMode::Enable, SafeMode::Disable],
|
boot_modes: vec![SafeMode::Enable, SafeMode::Disable],
|
||||||
system32: String::new(),
|
system32: String::new(),
|
||||||
selections: vec![None, None],
|
selections: vec![None, None],
|
||||||
|
results: results_arc,
|
||||||
tasks,
|
tasks,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -582,6 +598,7 @@ impl App {
|
||||||
"Program Files",
|
"Program Files",
|
||||||
"Program Files (x86)",
|
"Program Files (x86)",
|
||||||
"ProgramData",
|
"ProgramData",
|
||||||
|
"Haha.....no",
|
||||||
"Windows\\System32\\config",
|
"Windows\\System32\\config",
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -657,14 +674,14 @@ impl App {
|
||||||
|
|
||||||
fn render(&mut self, tui: &mut Tui) -> Result<()> {
|
fn render(&mut self, tui: &mut Tui) -> Result<()> {
|
||||||
tui.draw(|frame| {
|
tui.draw(|frame| {
|
||||||
if let [header, _body, footer, left, right, popup, progress] =
|
if let [header, _body, footer, left, right, popup, progress, results] =
|
||||||
get_chunks(frame.area())[..]
|
get_chunks(frame.area())[..]
|
||||||
{
|
{
|
||||||
let component_areas = vec![
|
let component_areas = vec![
|
||||||
header, // Title Bar
|
header, // Title Bar
|
||||||
header, // FPS Counter
|
header, // FPS Counter
|
||||||
left, right, footer, popup, // core
|
left, right, footer, popup, // core
|
||||||
progress, header, // boot-diags
|
progress, results, // boot-diags
|
||||||
];
|
];
|
||||||
for (component, area) in zip(self.components.iter_mut(), component_areas) {
|
for (component, area) in zip(self.components.iter_mut(), component_areas) {
|
||||||
if let Err(err) = component.draw(frame, area) {
|
if let Err(err) = component.draw(frame, area) {
|
||||||
|
|
@ -732,6 +749,9 @@ fn get_chunks(r: Rect) -> Vec<Rect> {
|
||||||
// Progress
|
// Progress
|
||||||
chunks.push(centered_rect(60, 80, chunks[1]));
|
chunks.push(centered_rect(60, 80, chunks[1]));
|
||||||
|
|
||||||
|
// Results
|
||||||
|
chunks.push(centered_rect(60, 80, chunks[1]));
|
||||||
|
|
||||||
// Done
|
// Done
|
||||||
chunks
|
chunks
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,20 +14,38 @@
|
||||||
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>.
|
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>.
|
||||||
//
|
//
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use ratatui::{Frame, layout::Rect};
|
use ratatui::{
|
||||||
|
Frame,
|
||||||
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
|
style::{Style, Stylize},
|
||||||
|
widgets::{Block, Clear, Padding, Paragraph, Tabs},
|
||||||
|
};
|
||||||
|
|
||||||
use core::{action::Action, components::Component};
|
use core::{action::Action, components::Component, state::Mode};
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct Results {
|
pub struct Results {
|
||||||
// command_tx: Option<UnboundedSender<Action>>,
|
// command_tx: Option<UnboundedSender<Action>>,
|
||||||
// config: Config,
|
// config: Config,
|
||||||
|
key_index: usize,
|
||||||
|
line_index: u16,
|
||||||
|
results: Arc<Mutex<HashMap<String, String>>>,
|
||||||
|
show: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Results {
|
impl Results {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new(results: Arc<Mutex<HashMap<String, String>>>) -> Self {
|
||||||
Self::default()
|
Results {
|
||||||
|
key_index: 0,
|
||||||
|
line_index: 0,
|
||||||
|
results,
|
||||||
|
show: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
|
// fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
|
||||||
|
|
@ -42,11 +60,74 @@ impl Results {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Results {
|
impl Component for Results {
|
||||||
fn update(&mut self, _action: Action) -> Result<Option<Action>> {
|
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||||
|
match action {
|
||||||
|
Action::KeyUp => {
|
||||||
|
if self.line_index > 0 {
|
||||||
|
self.line_index = self.line_index - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Action::KeyDown => {
|
||||||
|
self.line_index = self.line_index + 1;
|
||||||
|
}
|
||||||
|
Action::KeyLeft => {
|
||||||
|
if self.key_index > 0 {
|
||||||
|
self.key_index -= 1;
|
||||||
|
self.line_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Action::KeyRight => {
|
||||||
|
if self.key_index < 2 {
|
||||||
|
self.key_index += 1;
|
||||||
|
self.line_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Action::SetMode(mode) => {
|
||||||
|
self.show = mode == Mode::BootDiags;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, _frame: &mut Frame, _area: Rect) -> Result<()> {
|
fn draw(&mut self, frame: &mut Frame, rect: Rect) -> Result<()> {
|
||||||
|
if !self.show {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let areas = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([Constraint::Length(3), Constraint::Min(0)])
|
||||||
|
.split(rect)
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
if let Ok(results_hashmap) = self.results.lock() {
|
||||||
|
// Labels
|
||||||
|
let mut tab_labels: Vec<&str> = results_hashmap.keys().map(|k| k.as_str()).collect();
|
||||||
|
tab_labels.sort();
|
||||||
|
let hash_key = tab_labels.get(self.key_index).unwrap().to_string();
|
||||||
|
let tabs = Tabs::new(tab_labels)
|
||||||
|
.block(Block::bordered())
|
||||||
|
.style(Style::default().white())
|
||||||
|
.highlight_style(Style::default().green())
|
||||||
|
.select(self.key_index)
|
||||||
|
.divider("")
|
||||||
|
.padding(" [", "] ");
|
||||||
|
|
||||||
|
// Details
|
||||||
|
let details = if let Some(lines) = results_hashmap.get(&hash_key) {
|
||||||
|
lines.clone()
|
||||||
|
} else {
|
||||||
|
String::from("¯\\_(ツ)_/¯")
|
||||||
|
};
|
||||||
|
let paragraph = Paragraph::new(details)
|
||||||
|
.scroll((self.line_index, 0))
|
||||||
|
.block(Block::bordered().padding(Padding::horizontal(1)));
|
||||||
|
|
||||||
|
// Render
|
||||||
|
frame.render_widget(Clear, rect);
|
||||||
|
frame.render_widget(tabs, areas[0]);
|
||||||
|
frame.render_widget(paragraph, areas[1]);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,8 @@
|
||||||
"<Enter>": "Process",
|
"<Enter>": "Process",
|
||||||
"<Up>": "KeyUp",
|
"<Up>": "KeyUp",
|
||||||
"<Down>": "KeyDown",
|
"<Down>": "KeyDown",
|
||||||
|
"<Left>": "KeyLeft",
|
||||||
|
"<Right>": "KeyRight",
|
||||||
"<r>": "BootScan",
|
"<r>": "BootScan",
|
||||||
"<q>": "Quit",
|
"<q>": "Quit",
|
||||||
"<Ctrl-d>": "Quit",
|
"<Ctrl-d>": "Quit",
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,8 @@ pub enum Action {
|
||||||
Help,
|
Help,
|
||||||
KeyDown,
|
KeyDown,
|
||||||
KeyUp,
|
KeyUp,
|
||||||
|
KeyLeft,
|
||||||
|
KeyRight,
|
||||||
Quit,
|
Quit,
|
||||||
Render,
|
Render,
|
||||||
Resize(u16, u16),
|
Resize(u16, u16),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue