Replace Results component with LogView
Required ignoring KeyUp/KeyDown handling in some cases so a new Mode was added to accomodate
This commit is contained in:
parent
a9e929585f
commit
92f7874584
10 changed files with 184 additions and 167 deletions
|
|
@ -38,8 +38,6 @@ use core::{
|
|||
tui::{Event, Tui},
|
||||
};
|
||||
use std::{
|
||||
array,
|
||||
collections::HashMap,
|
||||
env,
|
||||
iter::zip,
|
||||
path::PathBuf,
|
||||
|
|
@ -83,17 +81,8 @@ pub struct App {
|
|||
impl App {
|
||||
pub fn new(tick_rate: f64, frame_rate: f64) -> Result<Self> {
|
||||
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
||||
let diag_groups_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 mut list = StatefulList::default();
|
||||
list.set_items(vec![
|
||||
|
|
@ -114,8 +103,8 @@ impl App {
|
|||
Box::new(Footer::new()),
|
||||
Box::new(popup::Popup::new()),
|
||||
Box::new(crate::components::progress::Progress::new()),
|
||||
Box::new(crate::components::results::Results::new(
|
||||
results_arc.clone(),
|
||||
Box::new(crate::components::logview::LogView::new(
|
||||
diag_groups_arc.clone(),
|
||||
)),
|
||||
],
|
||||
config: Config::new()?,
|
||||
|
|
@ -127,7 +116,7 @@ impl App {
|
|||
// App
|
||||
clone: CloneSettings::new(disk_list_arc),
|
||||
cur_mode: Mode::Home,
|
||||
diag_groups: Arc::new(Mutex::new(Vec::new())),
|
||||
diag_groups: diag_groups_arc,
|
||||
list,
|
||||
boot_modes: vec![SafeMode::Enable, SafeMode::Disable],
|
||||
system32: String::new(),
|
||||
|
|
@ -356,6 +345,9 @@ impl App {
|
|||
self.action_tx.send(Action::SetMode(next_mode))?;
|
||||
}
|
||||
Action::Process => match self.cur_mode {
|
||||
Mode::BootDiags => {
|
||||
self.action_tx.send(Action::SetMode(Mode::LogView))?;
|
||||
}
|
||||
Mode::DiagMenu => {
|
||||
// Use highlighted entry
|
||||
if let Some(new_mode) = self.list.get_selected() {
|
||||
|
|
@ -365,7 +357,7 @@ impl App {
|
|||
Mode::Done => {
|
||||
self.action_tx.send(Action::NextScreen)?;
|
||||
}
|
||||
Mode::BootDiags | Mode::BootSetup => {
|
||||
Mode::BootSetup => {
|
||||
//let new_mode = self.next_mode();
|
||||
//self.action_tx.send(Action::SetMode(new_mode))?;
|
||||
}
|
||||
|
|
@ -736,7 +728,7 @@ fn get_chunks(r: Rect) -> Vec<Rect> {
|
|||
|
||||
fn build_footer_string(cur_mode: Mode) -> String {
|
||||
match cur_mode {
|
||||
Mode::BootDiags => {
|
||||
Mode::BootDiags | Mode::LogView => {
|
||||
String::from("(Enter) to select / (m) for menu / (s) to start over / (q) to quit")
|
||||
}
|
||||
Mode::BootScan | Mode::BootSetup | Mode::Home | Mode::ScanDisks => {
|
||||
|
|
@ -825,7 +817,7 @@ fn build_left_items(app: &App) -> Action {
|
|||
}
|
||||
}
|
||||
}
|
||||
Mode::BootDiags => {
|
||||
Mode::BootDiags | Mode::LogView => {
|
||||
select_type = SelectionType::Loop;
|
||||
let (new_title, _) = get_mode_strings(app.cur_mode);
|
||||
title = new_title;
|
||||
|
|
@ -1030,7 +1022,7 @@ fn build_right_items(app: &App) -> Action {
|
|||
|
||||
fn get_mode_strings(mode: Mode) -> (String, String) {
|
||||
match mode {
|
||||
Mode::BootScan | Mode::BootDiags => (
|
||||
Mode::BootScan | Mode::BootDiags | Mode::LogView => (
|
||||
String::from("Boot Diagnostics"),
|
||||
String::from("Check for common Windows boot issues"),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
pub mod logview;
|
||||
pub mod progress;
|
||||
pub mod results;
|
||||
|
|
|
|||
125
boot_diags/src/components/logview.rs
Normal file
125
boot_diags/src/components/logview.rs
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// This file is part of Deja-Vu.
|
||||
//
|
||||
// Deja-Vu is free software: you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Deja-Vu is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// 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 color_eyre::Result;
|
||||
use ratatui::{
|
||||
Frame,
|
||||
layout::Rect,
|
||||
widgets::{Block, Clear, Padding, Paragraph, Wrap},
|
||||
};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use core::{
|
||||
action::Action,
|
||||
components::{Component, state::StatefulList},
|
||||
config::Config,
|
||||
state::Mode,
|
||||
};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::diags::DiagGroup;
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct LogView {
|
||||
command_tx: Option<UnboundedSender<Action>>,
|
||||
config: Config,
|
||||
line_index: u16,
|
||||
list: StatefulList<String>,
|
||||
mode: Mode,
|
||||
diag_groups: Arc<Mutex<Vec<DiagGroup>>>,
|
||||
}
|
||||
|
||||
impl LogView {
|
||||
#[must_use]
|
||||
pub fn new(diag_groups: Arc<Mutex<Vec<DiagGroup>>>) -> Self {
|
||||
LogView {
|
||||
diag_groups,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for LogView {
|
||||
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
|
||||
self.command_tx = Some(tx);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
self.config = config;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::KeyUp => {
|
||||
if self.mode == Mode::LogView {
|
||||
if self.line_index > 0 {
|
||||
self.line_index = self.line_index - 1;
|
||||
}
|
||||
} else {
|
||||
self.list.previous();
|
||||
}
|
||||
}
|
||||
Action::KeyDown => {
|
||||
if self.mode == Mode::LogView {
|
||||
self.line_index = self.line_index + 1;
|
||||
} else {
|
||||
self.list.next();
|
||||
}
|
||||
}
|
||||
Action::Process => {
|
||||
if self.mode == Mode::LogView {
|
||||
if let Some(command_tx) = self.command_tx.clone() {
|
||||
command_tx.send(Action::SetMode(Mode::BootDiags))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Action::SetMode(new_mode) => {
|
||||
self.line_index = 0;
|
||||
self.mode = new_mode;
|
||||
if self.mode == Mode::BootDiags {
|
||||
self.list.clear_items();
|
||||
if let Ok(diag_groups) = self.diag_groups.lock() {
|
||||
let raw_logs = diag_groups
|
||||
.iter()
|
||||
.map(|group| group.get_logs_raw())
|
||||
.collect();
|
||||
self.list.set_items(raw_logs);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, rect: Rect) -> Result<()> {
|
||||
if self.mode != Mode::LogView {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(log_text) = self.list.get_selected() {
|
||||
let paragraph = Paragraph::new(log_text)
|
||||
.wrap(Wrap { trim: true })
|
||||
.scroll((self.line_index, 0))
|
||||
.block(Block::bordered().padding(Padding::horizontal(1)));
|
||||
frame.render_widget(Clear, rect);
|
||||
frame.render_widget(paragraph, rect);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -123,7 +123,7 @@ impl Component for Progress {
|
|||
DiagResult::Warn => Color::Yellow,
|
||||
};
|
||||
let text = if line.running || line.text.is_empty() {
|
||||
String::from("...")
|
||||
String::from("..")
|
||||
} else {
|
||||
line.text.clone()
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,136 +0,0 @@
|
|||
// This file is part of Deja-Vu.
|
||||
//
|
||||
// Deja-Vu is free software: you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Deja-Vu is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// 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 color_eyre::Result;
|
||||
use ratatui::{
|
||||
Frame,
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
style::{Style, Stylize},
|
||||
widgets::{Block, Clear, Padding, Paragraph, Tabs},
|
||||
};
|
||||
|
||||
use core::{action::Action, components::Component, state::Mode};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Results {
|
||||
// command_tx: Option<UnboundedSender<Action>>,
|
||||
// config: Config,
|
||||
key_index: usize,
|
||||
line_index: u16,
|
||||
results: Arc<Mutex<HashMap<String, String>>>,
|
||||
show: bool,
|
||||
}
|
||||
|
||||
impl Results {
|
||||
#[must_use]
|
||||
pub fn new(results: Arc<Mutex<HashMap<String, String>>>) -> Self {
|
||||
Results {
|
||||
key_index: 0,
|
||||
line_index: 0,
|
||||
results,
|
||||
show: false,
|
||||
}
|
||||
}
|
||||
|
||||
// fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
|
||||
// self.command_tx = Some(tx);
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// fn register_config_handler(&mut self, config: Config) -> Result<()> {
|
||||
// self.config = config;
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
||||
|
||||
impl Component for Results {
|
||||
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;
|
||||
self.key_index = 0;
|
||||
self.line_index = 0;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, rect: Rect) -> Result<()> {
|
||||
return Ok(());
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -109,8 +109,6 @@
|
|||
"<Enter>": "Process",
|
||||
"<Up>": "KeyUp",
|
||||
"<Down>": "KeyDown",
|
||||
"<Left>": "KeyLeft",
|
||||
"<Right>": "KeyRight",
|
||||
"<r>": "BootScan",
|
||||
"<s>": "ScanDisks",
|
||||
"<q>": "Quit",
|
||||
|
|
@ -133,6 +131,15 @@
|
|||
"<Ctrl-c>": "Quit",
|
||||
"<Ctrl-z>": "Suspend"
|
||||
},
|
||||
"LogView": {
|
||||
"<Enter>": "Process",
|
||||
"<Up>": "KeyUp",
|
||||
"<Down>": "KeyDown",
|
||||
"<q>": "Quit",
|
||||
"<Ctrl-d>": "Quit",
|
||||
"<Ctrl-c>": "Quit",
|
||||
"<Ctrl-z>": "Suspend"
|
||||
},
|
||||
"InjectDrivers": {
|
||||
"<Enter>": "Process",
|
||||
"<Up>": "KeyUp",
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use serde::{Deserialize, Serialize};
|
|||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use super::{Component, state::StatefulList};
|
||||
use crate::{action::Action, config::Config};
|
||||
use crate::{action::Action, config::Config, state::Mode};
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum SelectionType {
|
||||
|
|
@ -39,6 +39,7 @@ pub struct Left {
|
|||
config: Config,
|
||||
labels: Vec<String>,
|
||||
list: StatefulList<String>,
|
||||
mode: Mode,
|
||||
select_type: SelectionType,
|
||||
selections: Vec<Option<usize>>,
|
||||
selections_saved: Vec<Option<usize>>,
|
||||
|
|
@ -82,9 +83,21 @@ impl Component for Left {
|
|||
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::Highlight(index) => self.set_highlight(index),
|
||||
Action::KeyUp => self.list.previous(),
|
||||
Action::KeyDown => self.list.next(),
|
||||
Action::KeyUp => {
|
||||
if self.mode != Mode::LogView {
|
||||
self.list.previous();
|
||||
}
|
||||
}
|
||||
Action::KeyDown => {
|
||||
if self.mode != Mode::LogView {
|
||||
self.list.next();
|
||||
}
|
||||
}
|
||||
Action::Process => {
|
||||
if self.mode == Mode::LogView {
|
||||
// Avoid updating selections/etc while log is open
|
||||
return Ok(None);
|
||||
}
|
||||
if self.select_type == SelectionType::Loop {
|
||||
// Selections aren't being used so this is a no-op
|
||||
} else if let Some(command_tx) = self.command_tx.clone() {
|
||||
|
|
@ -123,7 +136,8 @@ impl Component for Left {
|
|||
self.selections_saved[0] = one;
|
||||
self.selections_saved[1] = two;
|
||||
}
|
||||
Action::SetMode(_) => {
|
||||
Action::SetMode(new_mode) => {
|
||||
self.mode = new_mode;
|
||||
self.selections[0] = None;
|
||||
self.selections[1] = None;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use ratatui::{
|
|||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use super::{Component, state::StatefulList};
|
||||
use crate::{action::Action, config::Config, line::DVLine};
|
||||
use crate::{action::Action, config::Config, line::DVLine, state::Mode};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Right {
|
||||
|
|
@ -31,6 +31,7 @@ pub struct Right {
|
|||
list_header: Vec<DVLine>,
|
||||
list_labels: Vec<Vec<DVLine>>,
|
||||
list: StatefulList<Vec<DVLine>>,
|
||||
mode: Mode,
|
||||
selections: Vec<Option<usize>>,
|
||||
selections_saved: Vec<Option<usize>>,
|
||||
title: String,
|
||||
|
|
@ -93,8 +94,16 @@ impl Component for Right {
|
|||
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::Highlight(index) => self.set_highlight(index),
|
||||
Action::KeyUp => self.list.previous(),
|
||||
Action::KeyDown => self.list.next(),
|
||||
Action::KeyUp => {
|
||||
if self.mode != Mode::LogView {
|
||||
self.list.previous();
|
||||
}
|
||||
}
|
||||
Action::KeyDown => {
|
||||
if self.mode != Mode::LogView {
|
||||
self.list.next();
|
||||
}
|
||||
}
|
||||
Action::Select(one, two) => {
|
||||
self.selections[0] = one;
|
||||
self.selections[1] = two;
|
||||
|
|
@ -109,7 +118,8 @@ impl Component for Right {
|
|||
self.selections_saved[0] = one;
|
||||
self.selections_saved[1] = two;
|
||||
}
|
||||
Action::SetMode(_) => {
|
||||
Action::SetMode(new_mode) => {
|
||||
self.mode = new_mode;
|
||||
self.selections[0] = None;
|
||||
self.selections[1] = None;
|
||||
self.selections_saved[0] = None;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ pub enum Mode {
|
|||
BootDiags,
|
||||
BootScan,
|
||||
BootSetup,
|
||||
LogView,
|
||||
InjectDrivers,
|
||||
SetBootMode,
|
||||
// Clone
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ impl App {
|
|||
| Mode::BootSetup
|
||||
| Mode::DiagMenu
|
||||
| Mode::InjectDrivers
|
||||
| Mode::LogView
|
||||
| Mode::PEMenu
|
||||
| Mode::SetBootMode => panic!("This shouldn't happen?"),
|
||||
}
|
||||
|
|
@ -149,6 +150,7 @@ impl App {
|
|||
| Mode::BootSetup
|
||||
| Mode::DiagMenu
|
||||
| Mode::InjectDrivers
|
||||
| Mode::LogView
|
||||
| Mode::PEMenu
|
||||
| Mode::SetBootMode => panic!("This shouldn't happen?"),
|
||||
};
|
||||
|
|
@ -627,6 +629,7 @@ fn build_footer_string(cur_mode: Mode) -> String {
|
|||
| Mode::BootSetup
|
||||
| Mode::DiagMenu
|
||||
| Mode::InjectDrivers
|
||||
| Mode::LogView
|
||||
| Mode::PEMenu
|
||||
| Mode::SetBootMode => panic!("This shouldn't happen?"),
|
||||
}
|
||||
|
|
@ -698,6 +701,7 @@ fn build_left_items(app: &App, cur_mode: Mode) -> Action {
|
|||
| Mode::BootSetup
|
||||
| Mode::DiagMenu
|
||||
| Mode::InjectDrivers
|
||||
| Mode::LogView
|
||||
| Mode::PEMenu
|
||||
| Mode::SetBootMode => panic!("This shouldn't happen?"),
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue