Add initial UI sections from previous build

This commit is contained in:
2Shirt 2024-10-29 23:46:23 -07:00
parent 4ef4096930
commit b999c1ee90
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
4 changed files with 96 additions and 16 deletions

View file

@ -1,13 +1,18 @@
use std::iter::zip;
use color_eyre::Result;
use crossterm::event::KeyEvent;
use ratatui::prelude::Rect;
use ratatui::{
layout::{Constraint, Direction, Layout},
prelude::Rect,
};
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc;
use tracing::{debug, info};
use crate::{
action::Action,
components::{fps::FpsCounter, home::Home, Component},
components::{fps::FpsCounter, title::Title, Component},
config::Config,
tui::{Event, Tui},
};
@ -37,7 +42,7 @@ impl App {
Ok(Self {
tick_rate,
frame_rate,
components: vec![Box::new(Home::new()), Box::new(FpsCounter::default())],
components: vec![Box::new(Title::new()), Box::new(FpsCounter::default())],
should_quit: false,
should_suspend: false,
config: Config::new()?,
@ -164,14 +169,75 @@ impl App {
fn render(&mut self, tui: &mut Tui) -> Result<()> {
tui.draw(|frame| {
for component in self.components.iter_mut() {
if let Err(err) = component.draw(frame, frame.area()) {
let _ = self
.action_tx
.send(Action::Error(format!("Failed to draw: {:?}", err)));
if let [header, _body, _footer, _left, _right, _popup] = get_chunks(frame.area())[..] {
let component_areas = vec![
header, // Title Bar
header, // FPS Counter
// left, right, footer, popup,
];
for (component, area) in zip(self.components.iter_mut(), component_areas) {
if let Err(err) = component.draw(frame, area) {
let _ = self
.action_tx
.send(Action::Error(format!("Failed to draw: {:?}", err)));
}
}
}
};
})?;
Ok(())
}
}
fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
// Cut the given rectangle into three vertical pieces
let popup_layout = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Percentage(percent_y),
Constraint::Percentage((100 - percent_y) / 2),
])
.split(r);
// Then cut the middle vertical piece into three width-wise pieces
Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2),
])
.split(popup_layout[1])[1] // Return the middle chunk
}
fn get_chunks(r: Rect) -> Vec<Rect> {
let mut chunks: Vec<Rect> = Vec::with_capacity(6);
// Main sections
chunks.extend(
Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(3),
Constraint::Min(1),
Constraint::Length(3),
])
.split(r)
.to_vec(),
);
// Left/Right
chunks.extend(
Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(centered_rect(90, 90, chunks[1]))
.to_vec(),
);
// Popup
chunks.push(centered_rect(60, 25, r));
// Done
chunks
}

View file

@ -9,7 +9,7 @@ use tokio::sync::mpsc::UnboundedSender;
use crate::{action::Action, config::Config, tui::Event};
pub mod fps;
pub mod home;
pub mod title;
/// `Component` is a trait that represents a visual and interactive element of the user interface.
///

View file

@ -78,14 +78,21 @@ impl Component for FpsCounter {
}
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
let [top, _] = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]).areas(area);
let [column, _] =
Layout::horizontal([Constraint::Min(1), Constraint::Length(2)]).areas(area);
let [_, row, _] = Layout::vertical([
Constraint::Length(1),
Constraint::Length(1),
Constraint::Min(0),
])
.areas(column);
let message = format!(
"{:.2} ticks/sec, {:.2} FPS",
self.ticks_per_second, self.frames_per_second
);
let span = Span::styled(message, Style::new().dim());
let paragraph = Paragraph::new(span).right_aligned();
frame.render_widget(paragraph, top);
frame.render_widget(paragraph, row);
Ok(())
}
}

View file

@ -6,18 +6,18 @@ use super::Component;
use crate::{action::Action, config::Config};
#[derive(Default)]
pub struct Home {
pub struct Title {
command_tx: Option<UnboundedSender<Action>>,
config: Config,
}
impl Home {
impl Title {
pub fn new() -> Self {
Self::default()
}
}
impl Component for Home {
impl Component for Title {
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
self.command_tx = Some(tx);
Ok(())
@ -42,7 +42,14 @@ impl Component for Home {
}
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
frame.render_widget(Paragraph::new("hello world"), area);
// Title Block
let title_text = Span::styled(
"WizardKit: Clone Tool",
Style::default().fg(Color::LightCyan),
);
let title = Paragraph::new(Line::from(title_text).centered())
.block(Block::default().borders(Borders::ALL));
frame.render_widget(title, area);
Ok(())
}
}