219 lines
8.4 KiB
Rust
219 lines
8.4 KiB
Rust
#![allow(non_snake_case)]
|
|
mod centeredRect;
|
|
mod entryTable;
|
|
mod exitPrompt;
|
|
mod textHints;
|
|
|
|
use ratatui::{
|
|
Frame,
|
|
layout::{Constraint, Direction, Layout},
|
|
style::{Color, Style},
|
|
text::{Line, Span, Text},
|
|
widgets::{Block, Borders, Clear, Padding, Paragraph},
|
|
};
|
|
|
|
use crate::app::status::{AppStatus, CurrentScreen, EntryValError};
|
|
use crate::app::{AppState, status::EditingField};
|
|
use crate::ui::centeredRect::centered_rect;
|
|
use crate::ui::textHints::hints;
|
|
|
|
pub fn ui(frame: &mut Frame, app: &mut AppState) {
|
|
// Split entire body into left title+body and right screen+keybinds chunks
|
|
let screenChunks = Layout::default()
|
|
.direction(ratatui::layout::Direction::Horizontal)
|
|
.constraints([Constraint::Fill(5), Constraint::Fill(2)])
|
|
.split(frame.area());
|
|
|
|
// Split left chunk into title and body
|
|
let titleBodyChunks = Layout::default()
|
|
.direction(ratatui::layout::Direction::Vertical)
|
|
.constraints([Constraint::Length(3), Constraint::Min(3)])
|
|
.split(screenChunks[0]);
|
|
|
|
// Split right chunk into header and keybind area
|
|
let headerKeybindChunks = Layout::default()
|
|
.direction(ratatui::layout::Direction::Vertical)
|
|
.constraints([Constraint::Length(3), Constraint::Min(3)])
|
|
.split(screenChunks[1]);
|
|
|
|
// ------------------------
|
|
// Create and add title block
|
|
let titleBlock = Block::default()
|
|
.borders(Borders::all())
|
|
.style(Style::default());
|
|
|
|
let title = Paragraph::new(Text::styled(
|
|
"SteckBrett, a network ports plugboard",
|
|
Style::default().fg(Color::Green),
|
|
))
|
|
.block(titleBlock);
|
|
|
|
frame.render_widget(title, titleBodyChunks[0]);
|
|
|
|
// table drawing
|
|
let table = entryTable::getTableElement(
|
|
&app.entries,
|
|
&app.screen,
|
|
&app.appStatus,
|
|
app.isHashDifferent(),
|
|
);
|
|
|
|
frame.render_stateful_widget(table, titleBodyChunks[1], &mut app.tableState);
|
|
|
|
// Renter exit prompt
|
|
if let CurrentScreen::Exit = app.screen {
|
|
let area = centered_rect(60, 25, titleBodyChunks[1]);
|
|
frame.render_widget(Clear, area);
|
|
frame.render_widget(exitPrompt::getExitPara(), area);
|
|
}
|
|
|
|
// Editing screen popup
|
|
if let Some(edit) = &app.currentlyEditing {
|
|
let popup = Block::default()
|
|
.title("Add an entry")
|
|
.borders(Borders::all())
|
|
.style(Style::default().bg(Color::Rgb(42, 61, 69)).fg(Color::Green));
|
|
let area = centered_rect(75, 30, titleBodyChunks[1]);
|
|
frame.render_widget(Clear, area);
|
|
frame.render_widget(popup, area);
|
|
|
|
let fields = Layout::default()
|
|
.direction(Direction::Horizontal)
|
|
.margin(1)
|
|
.constraints([
|
|
Constraint::Percentage(30),
|
|
Constraint::Percentage(20),
|
|
Constraint::Percentage(30),
|
|
Constraint::Percentage(25),
|
|
])
|
|
.split(area);
|
|
|
|
let mut fromIPBlock = Block::default()
|
|
.title("From IP")
|
|
.borders(Borders::ALL)
|
|
.style(Style::default().fg(Color::White));
|
|
let mut toIPBlock = Block::default()
|
|
.title("To IP")
|
|
.borders(Borders::ALL)
|
|
.style(Style::default().fg(Color::White));
|
|
let mut fromPortBlock = Block::default()
|
|
.title("From Port")
|
|
.borders(Borders::ALL)
|
|
.style(Style::default().fg(Color::White));
|
|
let mut toPortBlock = Block::default()
|
|
.title("To Port")
|
|
.borders(Borders::ALL)
|
|
.style(Style::default().fg(Color::White));
|
|
|
|
let activeStyle = Style::default().bg(Color::LightYellow).fg(Color::Black);
|
|
|
|
match edit {
|
|
EditingField::FromIP => fromIPBlock = fromIPBlock.style(activeStyle),
|
|
EditingField::ToIP => toIPBlock = toIPBlock.style(activeStyle),
|
|
EditingField::FromPort => fromPortBlock = fromPortBlock.style(activeStyle),
|
|
EditingField::ToPort => toPortBlock = toPortBlock.style(activeStyle),
|
|
}
|
|
|
|
let fromIPPara = Paragraph::new(app.fromIP.clone()).block(fromIPBlock);
|
|
let toIPPara = Paragraph::new(app.toIP.clone()).block(toIPBlock);
|
|
let fromPortPara = Paragraph::new(app.fromPort.clone()).block(fromPortBlock);
|
|
let toPortPara = Paragraph::new(app.toPort.clone()).block(toPortBlock);
|
|
|
|
frame.render_widget(fromIPPara, fields[0]);
|
|
frame.render_widget(fromPortPara, fields[1]);
|
|
frame.render_widget(toIPPara, fields[2]);
|
|
frame.render_widget(toPortPara, fields[3]);
|
|
};
|
|
|
|
// --------------------------------
|
|
// Current page title and status
|
|
let mut borderColor = Color::White;
|
|
let _ = borderColor; // to suppress warning, remove later
|
|
let screenHeader = vec![
|
|
match app.screen {
|
|
CurrentScreen::Main => {
|
|
borderColor = Color::LightBlue;
|
|
Span::styled("Main Screen", Style::default().fg(Color::LightBlue))
|
|
}
|
|
CurrentScreen::Add => {
|
|
borderColor = Color::LightGreen;
|
|
Span::styled("Add Window", Style::default().fg(Color::Green))
|
|
}
|
|
CurrentScreen::Exit => {
|
|
borderColor = Color::LightRed;
|
|
Span::styled("Exit Screen", Style::default().fg(Color::Red))
|
|
}
|
|
CurrentScreen::Settings => {
|
|
borderColor = Color::LightBlue;
|
|
Span::styled("Settings", Style::default().fg(Color::Blue))
|
|
}
|
|
CurrentScreen::Delete => {
|
|
borderColor = Color::Magenta;
|
|
Span::styled("Delete", Style::default().fg(Color::Magenta))
|
|
}
|
|
}
|
|
.to_owned(),
|
|
Span::styled(" | ", Style::default().fg(Color::White)),
|
|
{
|
|
match &app.appStatus {
|
|
AppStatus::Welcome => Span::styled("Welcome", Style::default().fg(Color::White)),
|
|
AppStatus::Added => Span::styled("Added", Style::default().fg(Color::Green)),
|
|
AppStatus::Deleted => Span::styled("Deleted", Style::default().fg(Color::Magenta)),
|
|
AppStatus::Editing => {
|
|
if let Some(editing) = &app.currentlyEditing {
|
|
let curEdit = match editing {
|
|
EditingField::FromIP => "From IP",
|
|
EditingField::ToIP => "To IP",
|
|
EditingField::FromPort => "From Port",
|
|
EditingField::ToPort => "To Port",
|
|
};
|
|
Span::styled(
|
|
format!("Editing: {curEdit}"),
|
|
Style::default().fg(Color::Green),
|
|
)
|
|
} else {
|
|
Span::styled("Not Editing", Style::default().fg(Color::White))
|
|
}
|
|
}
|
|
AppStatus::Error(e) => {
|
|
let errString = match e {
|
|
EntryValError::ToPortValError => "To Port Invalid",
|
|
EntryValError::FromPortValError => "From Port Invalid",
|
|
EntryValError::ToIPValError => "To IP Invalid",
|
|
EntryValError::FromIPValError => "From IP Invalid",
|
|
EntryValError::None => "",
|
|
};
|
|
Span::styled(errString, Style::default().fg(Color::Red))
|
|
}
|
|
AppStatus::Saved => Span::styled("Saved", Style::default().fg(Color::Green)),
|
|
}
|
|
},
|
|
];
|
|
|
|
let screenHeaderPara = Paragraph::new(Line::from(screenHeader)).block(
|
|
Block::default()
|
|
.borders(Borders::TOP | Borders::LEFT | Borders::RIGHT)
|
|
.style(Style::default().fg(borderColor)),
|
|
);
|
|
|
|
// Keybinds Entry
|
|
let keybinds = {
|
|
match app.screen {
|
|
CurrentScreen::Main => hints::mainHints(),
|
|
CurrentScreen::Add => hints::addHints(),
|
|
CurrentScreen::Settings => hints::settingsHints(),
|
|
CurrentScreen::Delete => hints::delHints(),
|
|
CurrentScreen::Exit => hints::exitHints(),
|
|
}
|
|
};
|
|
|
|
let keyBindFooter = Paragraph::new(keybinds).block(
|
|
Block::default()
|
|
.borders(Borders::BOTTOM | Borders::LEFT | Borders::RIGHT)
|
|
.style(Style::default().fg(borderColor)),
|
|
);
|
|
|
|
frame.render_widget(screenHeaderPara, headerKeybindChunks[0]);
|
|
frame.render_widget(keyBindFooter, headerKeybindChunks[1]);
|
|
}
|