Add username section
This commit is contained in:
parent
88dfe52b07
commit
dd4733c991
8 changed files with 206 additions and 21 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
|
@ -689,7 +689,7 @@ dependencies = [
|
|||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2669,7 +2669,7 @@ dependencies = [
|
|||
"getrandom 0.3.2",
|
||||
"once_cell",
|
||||
"rustix 1.0.3",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2933,6 +2933,16 @@ version = "0.1.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc"
|
||||
|
||||
[[package]]
|
||||
name = "tui-input"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "911e93158bf80bbc94bad533b2b16e3d711e1132d69a6a6980c3920a63422c19"
|
||||
dependencies = [
|
||||
"ratatui",
|
||||
"unicode-width 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
|
|
@ -3144,6 +3154,7 @@ dependencies = [
|
|||
"tracing",
|
||||
"tracing-error",
|
||||
"tracing-subscriber",
|
||||
"tui-input",
|
||||
"vergen-gix",
|
||||
"windows-sys 0.61.2",
|
||||
"xml",
|
||||
|
|
|
|||
|
|
@ -215,7 +215,6 @@
|
|||
"<Ctrl-z>": "Suspend"
|
||||
},
|
||||
"SetUserName": {
|
||||
"<Enter>": "Process",
|
||||
"<Esc>": "PrevScreen",
|
||||
"<Ctrl-d>": "Quit",
|
||||
"<Ctrl-c>": "Quit",
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ pub enum Action {
|
|||
Shutdown,
|
||||
// App (Win-Installer)
|
||||
FindWimNetwork,
|
||||
SetUserName(String),
|
||||
// Screens
|
||||
DismissPopup,
|
||||
DisplayPopup(PopupType, String),
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter", "serde"] }
|
|||
tempfile = "3.23.0"
|
||||
windows-sys = { version = "0.61.1", features = ["Win32_NetworkManagement_WNet"] }
|
||||
xml = "1.1.0"
|
||||
tui-input = "0.14.0"
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.86"
|
||||
|
|
|
|||
|
|
@ -45,7 +45,11 @@ use ratatui::{
|
|||
use tokio::sync::mpsc;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::{components::wim_scan::WimScan, state::State};
|
||||
use crate::{
|
||||
components::{set_username::InputUsername, wim_scan::WimScan},
|
||||
state::State,
|
||||
wim::WimImage,
|
||||
};
|
||||
|
||||
pub struct App {
|
||||
// TUI
|
||||
|
|
@ -81,6 +85,7 @@ impl App {
|
|||
Box::new(Left::new()),
|
||||
Box::new(Right::new()),
|
||||
Box::new(WimScan::new(wim_sources)),
|
||||
Box::new(InputUsername::new()),
|
||||
Box::new(Footer::new()),
|
||||
Box::new(popup::Popup::new()),
|
||||
],
|
||||
|
|
@ -356,6 +361,10 @@ impl App {
|
|||
self.action_tx.send(build_right_items(self))?;
|
||||
self.action_tx.send(Action::Select(None, None))?;
|
||||
}
|
||||
Action::SetUserName(ref name) => {
|
||||
self.state.username = Some(name.clone());
|
||||
self.action_tx.send(Action::NextScreen)?;
|
||||
}
|
||||
Action::TasksComplete => self.action_tx.send(Action::NextScreen)?,
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -376,10 +385,10 @@ impl App {
|
|||
|
||||
fn render(&mut self, tui: &mut Tui) -> Result<()> {
|
||||
tui.draw(|frame| {
|
||||
if let [header, _body, footer, center, left, right, popup] =
|
||||
if let [header, _body, footer, center, left, right, username, popup] =
|
||||
get_chunks(frame.area())[..]
|
||||
{
|
||||
let component_areas = vec![header, center, left, right, footer, popup];
|
||||
let component_areas = vec![header, center, left, right, username, footer, popup];
|
||||
for (component, area) in zip(self.components.iter_mut(), component_areas) {
|
||||
if let Err(err) = component.draw(frame, area) {
|
||||
let _ = self
|
||||
|
|
@ -464,7 +473,7 @@ fn build_left_items(app: &App) -> Action {
|
|||
.for_each(|wim_file| items.push(wim_file.path.clone()));
|
||||
}
|
||||
}
|
||||
Mode::SelectWinImage => {
|
||||
Mode::SelectWinImage | Mode::SetUserName => {
|
||||
select_type = SelectionType::One;
|
||||
title = String::from("Select Windows Image");
|
||||
if let Ok(wim_sources) = app.state.wim_sources.lock()
|
||||
|
|
@ -491,11 +500,6 @@ fn build_left_items(app: &App) -> Action {
|
|||
items.push(format!("{}", PartitionTableType::Guid));
|
||||
items.push(format!("{}", PartitionTableType::Legacy));
|
||||
}
|
||||
Mode::SetUserName => {
|
||||
select_type = SelectionType::Loop;
|
||||
title = String::from("Customize");
|
||||
// TODO: FIXME
|
||||
}
|
||||
Mode::Confirm => {
|
||||
select_type = SelectionType::Loop;
|
||||
title = String::from("Confirm Selections");
|
||||
|
|
@ -627,7 +631,8 @@ fn build_right_items(app: &App) -> Action {
|
|||
});
|
||||
}
|
||||
}
|
||||
Mode::SelectWinImage => {
|
||||
Mode::SelectWinImage | Mode::SetUserName | Mode::Confirm => {
|
||||
info!("Building right items for: {:?}", &app.cur_mode);
|
||||
let source;
|
||||
if let Ok(wim_sources) = app.state.wim_sources.lock()
|
||||
&& let Some(index) = app.state.wim_file_index
|
||||
|
|
@ -679,17 +684,45 @@ fn build_right_items(app: &App) -> Action {
|
|||
line_colors: vec![Color::Blue],
|
||||
},
|
||||
]);
|
||||
labels.push(label_dv_lines);
|
||||
|
||||
// WIM Info
|
||||
source.images.iter().for_each(|image| {
|
||||
items.push(vec![DVLine {
|
||||
line_parts: vec![format!("{image}")],
|
||||
line_colors: vec![Color::Reset],
|
||||
}])
|
||||
});
|
||||
match app.cur_mode {
|
||||
Mode::SelectWinImage => {
|
||||
source.images.iter().for_each(|image| {
|
||||
items.push(vec![DVLine {
|
||||
line_parts: vec![format!("{image}")],
|
||||
line_colors: vec![Color::Reset],
|
||||
}])
|
||||
});
|
||||
}
|
||||
Mode::Confirm => {
|
||||
if let Some(index) = app.state.wim_image_index
|
||||
&& let Some(image) = source.images.get(index)
|
||||
{
|
||||
label_dv_lines.append(&mut vec![
|
||||
DVLine {
|
||||
line_parts: vec![format!("{image}")],
|
||||
line_colors: vec![Color::Reset],
|
||||
},
|
||||
DVLine::blank(),
|
||||
]);
|
||||
}
|
||||
if let Some(username) = &app.state.username {
|
||||
label_dv_lines.append(&mut vec![DVLine {
|
||||
line_parts: vec![String::from("Username: "), username.clone()],
|
||||
line_colors: vec![Color::Green, Color::Reset],
|
||||
}]);
|
||||
}
|
||||
items.push(vec![DVLine::blank()]);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Done
|
||||
info!("label_dv_lines: {:?}", &label_dv_lines);
|
||||
labels.push(label_dv_lines);
|
||||
}
|
||||
Mode::SelectTableType | Mode::SetUserName | Mode::Confirm => {
|
||||
Mode::SelectTableType => {
|
||||
// Labels
|
||||
let dest_dv_line = DVLine {
|
||||
line_parts: vec![
|
||||
|
|
@ -777,6 +810,9 @@ fn get_chunks(r: Rect) -> Vec<Rect> {
|
|||
// Center
|
||||
chunks.push(center);
|
||||
|
||||
// Set username
|
||||
chunks.push(centered_rect(60, 20, r));
|
||||
|
||||
// Popup
|
||||
chunks.push(centered_rect(60, 25, r));
|
||||
|
||||
|
|
|
|||
|
|
@ -13,4 +13,5 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
pub mod set_username;
|
||||
pub mod wim_scan;
|
||||
|
|
|
|||
135
win_installer/src/components/set_username.rs
Normal file
135
win_installer/src/components/set_username.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
// 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 core::{action::Action, components::Component, config::Config, state::Mode, tui::Event};
|
||||
use crossterm::event::KeyCode;
|
||||
use ratatui::{
|
||||
prelude::*,
|
||||
widgets::{Block, Borders, Clear, Paragraph},
|
||||
};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use tui_input::{Input, InputRequest};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InputUsername {
|
||||
command_tx: Option<UnboundedSender<Action>>,
|
||||
config: Config,
|
||||
input: Input,
|
||||
mode: Mode,
|
||||
}
|
||||
|
||||
impl InputUsername {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
input: Input::new(String::from("")),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for InputUsername {
|
||||
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 handle_events(&mut self, event: Option<Event>) -> Result<Option<Action>> {
|
||||
if self.mode != Mode::SetUserName {
|
||||
return Ok(None);
|
||||
}
|
||||
let action = match event {
|
||||
Some(Event::Key(key_event)) => match key_event.code {
|
||||
KeyCode::Backspace => {
|
||||
self.input.handle(InputRequest::DeletePrevChar);
|
||||
None
|
||||
}
|
||||
KeyCode::Char(c) => {
|
||||
let ok_chars: Vec<char> = vec![' ', '-', '_'];
|
||||
if c.is_ascii_alphanumeric() || ok_chars.contains(&c) {
|
||||
self.input.handle(InputRequest::InsertChar(c));
|
||||
}
|
||||
None
|
||||
}
|
||||
KeyCode::Enter => {
|
||||
let username = self.input.value();
|
||||
Some(Action::SetUserName(String::from(username)))
|
||||
}
|
||||
KeyCode::Esc => Some(Action::SetMode(Mode::Home)),
|
||||
_ => None,
|
||||
},
|
||||
Some(Event::Mouse(_)) => None,
|
||||
_ => None,
|
||||
};
|
||||
Ok(action)
|
||||
}
|
||||
|
||||
fn update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::SetMode(mode) => self.mode = mode.clone(),
|
||||
_ => {}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
if self.mode != Mode::SetUserName {
|
||||
// Bail early
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Set areas
|
||||
let [_, center_area, _] = Layout::horizontal([
|
||||
Constraint::Length(1),
|
||||
Constraint::Min(1),
|
||||
Constraint::Length(1),
|
||||
])
|
||||
.areas(area);
|
||||
let [_, input_area, _] = Layout::vertical([
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(1),
|
||||
])
|
||||
.areas(center_area);
|
||||
|
||||
frame.render_widget(Clear, area);
|
||||
let outer_block = Block::bordered().cyan().bold();
|
||||
frame.render_widget(outer_block, area);
|
||||
|
||||
// Input Box
|
||||
let width = input_area.width.max(3) - 3; // keep 2 for borders and 1 for cursor
|
||||
let scroll = self.input.visual_scroll(width as usize);
|
||||
let input = Paragraph::new(self.input.value())
|
||||
.scroll((0, scroll as u16))
|
||||
.white()
|
||||
.block(Block::new().borders(Borders::ALL).title("Enter Username"));
|
||||
frame.render_widget(input, input_area);
|
||||
|
||||
// Ratatui hides the cursor unless it's explicitly set. Position the cursor past the
|
||||
// end of the input text and one line down from the border to the input line
|
||||
let x = self.input.visual_cursor().max(scroll) - scroll + 1;
|
||||
frame.set_cursor_position((input_area.x + x as u16, input_area.y + 1));
|
||||
|
||||
// Done
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@ pub struct State {
|
|||
pub driver: Option<drivers::Driver>,
|
||||
pub driver_list: Vec<drivers::Driver>,
|
||||
pub table_type: Option<PartitionTableType>,
|
||||
pub username: Option<String>,
|
||||
pub wim_file_index: Option<usize>,
|
||||
pub wim_image_index: Option<usize>,
|
||||
pub wim_sources: Arc<Mutex<WimSources>>,
|
||||
|
|
|
|||
Loading…
Reference in a new issue