Files
Steckbrett/src/ui/mod.rs
Phani Pavan K 8e7143d1a7
Some checks failed
/ Quality Check (push) Failing after 1m51s
/ Build (push) Failing after 1m29s
fixed #5, added colors.
2025-10-31 11:32:14 +05:30

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]);
}