rat_fps/
lib.rs

1use std::{error::Error, fmt::Write, time::{SystemTime, UNIX_EPOCH}};
2
3use ratatui::{style::Stylize, widgets::{Block, Paragraph, Widget}};
4
5/// Note: This implements the widget using the mutable reference,
6/// so when rendering one must pass a mut reference to this object
7pub struct FPS {
8    prev_time: u128,
9    fps_text: String
10}
11
12impl FPS {
13    /// Create new widget
14    pub fn new() -> Result<Self, Box<dyn Error>>{
15        Ok(Self{
16            prev_time: FPS::now_millis()?,
17            fps_text: String::with_capacity(1024)
18        })
19    }
20
21    /// get time to wait for in poll for example with crosster::event::poll,
22    /// it is in milliseconds, calculated from using the @expected_fps
23    /// field value
24    pub fn wait_for_fps(&self, expected_fps: u128) -> Result<u64, Box<dyn Error>> {
25        let now = Self::now_millis()?;
26        let dif = now - self.prev_time;
27        if expected_fps>0 && dif > 1000/expected_fps {
28            Ok(0)
29        }else{
30            Ok(1000/expected_fps as u64)
31        }
32    }
33
34    fn now_millis ()-> Result<u128, Box<dyn Error>> {
35        let now = SystemTime::now().duration_since(UNIX_EPOCH)?;
36        Ok(now.as_millis())
37    }
38
39    fn set_fps(&mut self) -> Result<(), Box<dyn Error>> {
40        let now = FPS::now_millis()?;
41        let time_passed = now - self.prev_time;
42        self.prev_time = now;
43        let mut fps = 1;
44        if time_passed >0 {
45            fps = 1000/time_passed;
46        }
47        self.fps_text.clear();
48        write!(&mut self.fps_text, "{} FPS", fps)?;
49        Ok(())
50    }
51}
52
53/// Implements Widget on mutable reference
54impl Widget for &mut FPS {
55    fn render(self,
56        area: ratatui::prelude::Rect,
57        buf: &mut ratatui::prelude::Buffer) where Self: Sized {
58        self.set_fps().unwrap();
59        let text = Paragraph::new(self.fps_text.as_str()).bold()
60            .green();
61
62        let mut new_area = area.clone();
63        if area.height >= 3 {
64            let len_of_str = self.fps_text.len()+2;
65            if (len_of_str as u16) < area.width {
66                new_area.width = len_of_str as u16
67            }
68            new_area.height = 3;
69            text.block(Block::bordered()).render(new_area,buf);
70            return;
71        }
72
73        new_area.width = self.fps_text.len() as u16;
74        text.render(area,buf);
75    }
76}