From ee7de8f355e552dd9d3c45888e26da6ccfd61d39 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Sat, 1 Nov 2025 22:22:58 -0700 Subject: [PATCH] Combine FPS and Title components --- boot_diags/src/app.rs | 6 +- core/src/components.rs | 1 - core/src/components/fps.rs | 134 ----------------------------------- core/src/components/title.rs | 107 ++++++++++++++++++++++------ deja_vu/src/app.rs | 8 +-- pe_menu/src/app.rs | 8 +-- 6 files changed, 90 insertions(+), 174 deletions(-) delete mode 100644 core/src/components/fps.rs diff --git a/boot_diags/src/app.rs b/boot_diags/src/app.rs index 54b2d87..16f095f 100644 --- a/boot_diags/src/app.rs +++ b/boot_diags/src/app.rs @@ -18,7 +18,6 @@ use core::{ components::{ Component, footer::Footer, - fps::FpsCounter, left::{Left, SelectionType}, popup, right::Right, @@ -102,7 +101,6 @@ impl App { action_tx, components: vec![ Box::new(Title::new("Boot Diagnostics")), - Box::new(FpsCounter::new()), Box::new(Left::new()), Box::new(Right::new()), Box::new(Footer::new()), @@ -556,9 +554,7 @@ impl App { get_chunks(frame.area())[..] { let component_areas = vec![ - header, // Title Bar - header, // FPS Counter - left, right, footer, popup, // core + header, left, right, footer, popup, // core progress, results, // boot-diags ]; for (component, area) in zip(self.components.iter_mut(), component_areas) { diff --git a/core/src/components.rs b/core/src/components.rs index 212155a..f1369f7 100644 --- a/core/src/components.rs +++ b/core/src/components.rs @@ -25,7 +25,6 @@ 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; diff --git a/core/src/components/fps.rs b/core/src/components/fps.rs deleted file mode 100644 index a8a6dcf..0000000 --- a/core/src/components/fps.rs +++ /dev/null @@ -1,134 +0,0 @@ -// This file is part of Deja-Vu. -// -// Deja-Vu is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Deja-Vu is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Deja-Vu. If not, see . -// -use std::time::Instant; - -use color_eyre::Result; -use ratatui::{ - Frame, - layout::{Constraint, Layout, Rect}, - style::{Style, Stylize}, - text::Span, - widgets::Paragraph, -}; - -use super::Component; - -use crate::action::Action; - -#[derive(Debug, Clone, PartialEq)] -pub struct FpsCounter { - mode: String, - - last_tick_update: Instant, - tick_count: u32, - ticks_per_second: f64, - - last_frame_update: Instant, - frame_count: u32, - frames_per_second: f64, -} - -impl Default for FpsCounter { - fn default() -> Self { - Self::new() - } -} - -impl FpsCounter { - #[must_use] - pub fn new() -> Self { - Self { - mode: String::new(), - last_tick_update: Instant::now(), - tick_count: 0, - ticks_per_second: 0.0, - last_frame_update: Instant::now(), - frame_count: 0, - frames_per_second: 0.0, - } - } - - #[allow(clippy::unnecessary_wraps)] - fn app_tick(&mut self) -> Result<()> { - self.tick_count += 1; - let now = Instant::now(); - let elapsed = (now - self.last_tick_update).as_secs_f64(); - if elapsed >= 1.0 { - self.ticks_per_second = f64::from(self.tick_count) / elapsed; - self.last_tick_update = now; - self.tick_count = 0; - } - Ok(()) - } - - #[allow(clippy::unnecessary_wraps)] - fn render_tick(&mut self) -> Result<()> { - self.frame_count += 1; - let now = Instant::now(); - let elapsed = (now - self.last_frame_update).as_secs_f64(); - if elapsed >= 1.0 { - self.frames_per_second = f64::from(self.frame_count) / elapsed; - self.last_frame_update = now; - self.frame_count = 0; - } - Ok(()) - } -} - -impl Component for FpsCounter { - fn update(&mut self, action: Action) -> Result> { - match action { - Action::SetMode(mode) => { - self.mode = format!("{mode:?}"); - } - Action::Render => self.render_tick()?, - Action::Tick => self.app_tick()?, - _ => {} - }; - Ok(None) - } - - fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { - let [_, row, _] = Layout::vertical([ - Constraint::Length(1), - Constraint::Length(1), - Constraint::Min(0), - ]) - .areas(area); - let [_, left, right, _] = Layout::horizontal([ - Constraint::Length(2), - Constraint::Min(1), - Constraint::Min(1), - Constraint::Length(2), - ]) - .areas(row); - - // Debug - let span = Span::styled(format!("Mode: {}", &self.mode), Style::new().dim()); - let paragraph = Paragraph::new(span).left_aligned(); - frame.render_widget(paragraph, left); - - // FPS - 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, right); - Ok(()) - } -} diff --git a/core/src/components/title.rs b/core/src/components/title.rs index 9c4a95b..e3bb36e 100644 --- a/core/src/components/title.rs +++ b/core/src/components/title.rs @@ -15,59 +15,126 @@ // use color_eyre::Result; use ratatui::{ + Frame, + layout::{Constraint, Layout, Rect}, prelude::*, + style::{Style, Stylize}, + text::Span, widgets::{Block, Borders, Paragraph}, }; -use tokio::sync::mpsc::UnboundedSender; +use std::time::Instant; use super::Component; -use crate::{action::Action, config::Config}; +use crate::action::Action; -#[derive(Default)] +#[derive(Debug, Clone, PartialEq)] pub struct Title { - command_tx: Option>, - config: Config, - text: String, + mode: String, + title: String, + + last_tick_update: Instant, + tick_count: u32, + ticks_per_second: f64, + + last_frame_update: Instant, + frame_count: u32, + frames_per_second: f64, } impl Title { #[must_use] - pub fn new(text: &str) -> Self { + pub fn new(title: &str) -> Self { Self { - text: String::from(text), - ..Default::default() + mode: String::new(), + title: String::from(title), + last_tick_update: Instant::now(), + tick_count: 0, + ticks_per_second: 0.0, + last_frame_update: Instant::now(), + frame_count: 0, + frames_per_second: 0.0, } } + + #[allow(clippy::unnecessary_wraps)] + fn app_tick(&mut self) -> Result<()> { + self.tick_count += 1; + let now = Instant::now(); + let elapsed = (now - self.last_tick_update).as_secs_f64(); + if elapsed >= 1.0 { + self.ticks_per_second = f64::from(self.tick_count) / elapsed; + self.last_tick_update = now; + self.tick_count = 0; + } + Ok(()) + } + + #[allow(clippy::unnecessary_wraps)] + fn render_tick(&mut self) -> Result<()> { + self.frame_count += 1; + let now = Instant::now(); + let elapsed = (now - self.last_frame_update).as_secs_f64(); + if elapsed >= 1.0 { + self.frames_per_second = f64::from(self.frame_count) / elapsed; + self.last_frame_update = now; + self.frame_count = 0; + } + Ok(()) + } } impl Component for Title { - fn register_action_handler(&mut self, tx: UnboundedSender) -> 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> { #[allow(clippy::match_single_binding)] match action { + Action::SetMode(mode) => { + self.mode = format!("{mode:?}"); + } + Action::Render => self.render_tick()?, + Action::Tick => self.app_tick()?, _ => {} } Ok(None) } fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { + let [_, row, _] = Layout::vertical([ + Constraint::Length(1), + Constraint::Length(1), + Constraint::Min(0), + ]) + .areas(area); + let [_, left, right, _] = Layout::horizontal([ + Constraint::Length(2), + Constraint::Min(1), + Constraint::Min(1), + Constraint::Length(2), + ]) + .areas(row); + // Title Block let title_text = Span::styled( - format!("{}: {}", self.config.app_title.as_str(), self.text), + format!("Deja-Vu: {}", self.title), 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); + + // Mode + let span = Span::styled(format!("Mode: {}", &self.mode), Style::new().dim()); + let paragraph = Paragraph::new(span).left_aligned(); + frame.render_widget(paragraph, left); + + // FPS + 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, right); + Ok(()) } } diff --git a/deja_vu/src/app.rs b/deja_vu/src/app.rs index 375ce74..5fc447f 100644 --- a/deja_vu/src/app.rs +++ b/deja_vu/src/app.rs @@ -18,7 +18,6 @@ use core::{ components::{ Component, footer::Footer, - fps::FpsCounter, left::{Left, SelectionType}, popup, right::Right, @@ -83,7 +82,6 @@ impl App { action_tx, components: vec![ Box::new(Title::new("Clone Tool")), - Box::new(FpsCounter::new()), Box::new(Left::new()), Box::new(Right::new()), Box::new(Footer::new()), @@ -544,11 +542,7 @@ 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())[..] { - let component_areas = vec![ - header, // Title Bar - header, // FPS Counter - left, right, footer, popup, - ]; + let component_areas = vec![header, 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 diff --git a/pe_menu/src/app.rs b/pe_menu/src/app.rs index 99e323f..dd7df87 100644 --- a/pe_menu/src/app.rs +++ b/pe_menu/src/app.rs @@ -18,7 +18,6 @@ use core::{ components::{ Component, footer::Footer, - fps::FpsCounter, left::{Left, SelectionType}, popup, right::Right, @@ -89,7 +88,6 @@ impl App { action_tx, components: vec![ Box::new(Title::new("PE Menu")), - Box::new(FpsCounter::new()), Box::new(Left::new()), Box::new(Right::new()), Box::new(Footer::new()), @@ -305,11 +303,7 @@ 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())[..] { - let component_areas = vec![ - header, // Title Bar - header, // FPS Counter - left, right, footer, popup, - ]; + let component_areas = vec![header, 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