Add initial results UI layout

This commit is contained in:
2Shirt 2025-05-26 21:49:09 -07:00
parent 2296b8f274
commit e0823293e2
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
4 changed files with 114 additions and 9 deletions

View file

@ -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
} }

View file

@ -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(())
} }
} }

View file

@ -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",

View file

@ -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),