Add initial UI sections from previous build
This commit is contained in:
parent
4ef4096930
commit
b999c1ee90
4 changed files with 96 additions and 16 deletions
84
src/app.rs
84
src/app.rs
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue