// 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::from(""), 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(()) } }