Add remaining UI section templates
Actual logic needs re-added
This commit is contained in:
parent
b999c1ee90
commit
7458699859
6 changed files with 281 additions and 4 deletions
18
src/app.rs
18
src/app.rs
|
|
@ -12,7 +12,10 @@ use tracing::{debug, info};
|
|||
|
||||
use crate::{
|
||||
action::Action,
|
||||
components::{fps::FpsCounter, title::Title, Component},
|
||||
components::{
|
||||
footer::Footer, fps::FpsCounter, left::Left, popup::Popup, right::Right, title::Title,
|
||||
Component,
|
||||
},
|
||||
config::Config,
|
||||
tui::{Event, Tui},
|
||||
};
|
||||
|
|
@ -42,7 +45,14 @@ impl App {
|
|||
Ok(Self {
|
||||
tick_rate,
|
||||
frame_rate,
|
||||
components: vec![Box::new(Title::new()), Box::new(FpsCounter::default())],
|
||||
components: vec![
|
||||
Box::new(Title::new()),
|
||||
Box::new(FpsCounter::default()),
|
||||
Box::new(Left::new()),
|
||||
Box::new(Right::new()),
|
||||
Box::new(Footer::new()),
|
||||
Box::new(Popup::new()),
|
||||
],
|
||||
should_quit: false,
|
||||
should_suspend: false,
|
||||
config: Config::new()?,
|
||||
|
|
@ -169,11 +179,11 @@ impl App {
|
|||
|
||||
fn render(&mut self, tui: &mut Tui) -> Result<()> {
|
||||
tui.draw(|frame| {
|
||||
if let [header, _body, _footer, _left, _right, _popup] = get_chunks(frame.area())[..] {
|
||||
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,
|
||||
left, right, footer, popup,
|
||||
];
|
||||
for (component, area) in zip(self.components.iter_mut(), component_areas) {
|
||||
if let Err(err) = component.draw(frame, area) {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@ use tokio::sync::mpsc::UnboundedSender;
|
|||
|
||||
use crate::{action::Action, config::Config, tui::Event};
|
||||
|
||||
pub mod footer;
|
||||
pub mod fps;
|
||||
pub mod left;
|
||||
pub mod popup;
|
||||
pub mod right;
|
||||
pub mod title;
|
||||
|
||||
/// `Component` is a trait that represents a visual and interactive element of the user interface.
|
||||
|
|
|
|||
59
src/components/footer.rs
Normal file
59
src/components/footer.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use color_eyre::Result;
|
||||
use ratatui::{prelude::*, widgets::*};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use super::Component;
|
||||
use crate::{action::Action, config::Config};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Footer {
|
||||
command_tx: Option<UnboundedSender<Action>>,
|
||||
config: Config,
|
||||
show_install_driver: bool,
|
||||
}
|
||||
|
||||
impl Footer {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Footer {
|
||||
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 update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::Tick => {
|
||||
// add any logic here that should run on every tick
|
||||
}
|
||||
Action::Render => {
|
||||
// add any logic here that should run on every render
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let footer_text = Span::styled(
|
||||
if self.show_install_driver {
|
||||
"(Enter) to select / (b) to go back / (i) to install driver / (q) to quit"
|
||||
} else {
|
||||
"(Enter) to select / (b) to go back / (q) to quit"
|
||||
},
|
||||
Style::default().fg(Color::DarkGray),
|
||||
);
|
||||
let footer = Paragraph::new(Line::from(footer_text).centered())
|
||||
.block(Block::default().borders(Borders::ALL));
|
||||
frame.render_widget(footer, area);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
74
src/components/left.rs
Normal file
74
src/components/left.rs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
use color_eyre::Result;
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::{prelude::*, widgets::*};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use super::Component;
|
||||
use crate::{action::Action, config::Config};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Left {
|
||||
command_tx: Option<UnboundedSender<Action>>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl Left {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Left {
|
||||
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
|
||||
// TODO
|
||||
let _ = key; // to appease clippy
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
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 update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::Tick => {
|
||||
// add any logic here that should run on every tick
|
||||
}
|
||||
Action::Render => {
|
||||
// add any logic here that should run on every render
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [title_area, body_area] = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Length(1), Constraint::Min(1)])
|
||||
.areas(area);
|
||||
|
||||
// Title
|
||||
let title_text = String::from("Hello world!");
|
||||
let title = Paragraph::new(Line::from(title_text).centered())
|
||||
.block(Block::default().borders(Borders::NONE));
|
||||
frame.render_widget(title, title_area);
|
||||
|
||||
// Body
|
||||
let paragraph = Paragraph::new("Body text...").block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.padding(Padding::new(1, 1, 1, 1)),
|
||||
);
|
||||
frame.render_widget(paragraph, body_area);
|
||||
|
||||
// Done
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
56
src/components/popup.rs
Normal file
56
src/components/popup.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use color_eyre::Result;
|
||||
use ratatui::{prelude::*, widgets::*};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use super::Component;
|
||||
use crate::{action::Action, config::Config};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Popup {
|
||||
command_tx: Option<UnboundedSender<Action>>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl Popup {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Popup {
|
||||
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 update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::Tick => {
|
||||
// add any logic here that should run on every tick
|
||||
}
|
||||
Action::Render => {
|
||||
// add any logic here that should run on every render
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let popup_block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.style(Style::default().cyan().bold());
|
||||
let popup = Paragraph::new(vec![Line::from(Span::raw("Popup text..."))])
|
||||
.block(popup_block)
|
||||
.centered()
|
||||
.wrap(Wrap { trim: false });
|
||||
frame.render_widget(Clear, area);
|
||||
frame.render_widget(popup, area);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
74
src/components/right.rs
Normal file
74
src/components/right.rs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
use color_eyre::Result;
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::{prelude::*, widgets::*};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use super::Component;
|
||||
use crate::{action::Action, config::Config};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Right {
|
||||
command_tx: Option<UnboundedSender<Action>>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl Right {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Right {
|
||||
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
|
||||
// TODO ??
|
||||
let _ = key; // to appease clippy
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
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 update(&mut self, action: Action) -> Result<Option<Action>> {
|
||||
match action {
|
||||
Action::Tick => {
|
||||
// add any logic here that should run on every tick
|
||||
}
|
||||
Action::Render => {
|
||||
// add any logic here that should run on every render
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
|
||||
let [title_area, body_area] = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Length(1), Constraint::Min(1)])
|
||||
.areas(area);
|
||||
|
||||
// Title
|
||||
let title_text = String::from("Info?");
|
||||
let title = Paragraph::new(Line::from(title_text).centered())
|
||||
.block(Block::default().borders(Borders::NONE));
|
||||
frame.render_widget(title, title_area);
|
||||
|
||||
// Body
|
||||
let paragraph = Paragraph::new("Some info...").block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.padding(Padding::new(1, 1, 1, 1)),
|
||||
);
|
||||
frame.render_widget(paragraph, body_area);
|
||||
|
||||
// Done
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue