bevy_window_title_diagnostics/
lib.rs

1use bevy::diagnostic::{Diagnostic, DiagnosticPath, DiagnosticsStore};
2use bevy::prelude::*;
3use bevy::time::{Time, Timer};
4use bevy::window::Window;
5use core::time::Duration;
6// use bevy::window::Windows;
7
8/// An App Plugin that logs diagnostics to the primary window's title
9pub struct WindowTitleLoggerDiagnosticsPlugin {
10    pub wait_duration: Duration,
11    pub filter: Option<Vec<DiagnosticPath>>,
12}
13
14/// State used by the [`WindowTitleLoggerDiagnosticsPlugin`]
15#[derive(Resource)]
16struct WindowTitleLoggerState {
17    timer: Timer,
18    filter: Option<Vec<DiagnosticPath>>,
19}
20
21impl Default for WindowTitleLoggerDiagnosticsPlugin {
22    fn default() -> Self {
23        WindowTitleLoggerDiagnosticsPlugin {
24            wait_duration: Duration::from_secs(1),
25            filter: None,
26        }
27    }
28}
29
30impl Plugin for WindowTitleLoggerDiagnosticsPlugin {
31    fn build(&self, app: &mut App) {
32        app.insert_resource(WindowTitleLoggerState {
33            timer: Timer::new(self.wait_duration, bevy::time::TimerMode::Repeating),
34            filter: self.filter.clone(),
35        });
36
37        app.add_systems(PostUpdate, Self::log_diagnostics_system);
38    }
39}
40
41impl WindowTitleLoggerDiagnosticsPlugin {
42    pub fn filtered(filter: Vec<DiagnosticPath>) -> Self {
43        WindowTitleLoggerDiagnosticsPlugin {
44            filter: Some(filter),
45            ..Default::default()
46        }
47    }
48
49    fn format(diagnostic: &Diagnostic) -> String {
50        if let Some(value) = diagnostic.value() {
51            if let Some(average) = diagnostic.average() {
52                return format!(" {}: {:.2} ({:.2}) |", diagnostic.path(), value, average);
53            } else {
54                return format!(" {}: {:.2} |", diagnostic.path(), value);
55            }
56        }
57
58        "".to_owned()
59    }
60
61    fn log_diagnostics_system(
62        mut state: ResMut<WindowTitleLoggerState>,
63        time: Res<Time>,
64        diagnostics: Res<DiagnosticsStore>,
65        mut windows: Query<&mut Window>,
66    ) {
67        if state.timer.tick(time.delta()).finished() {
68            let mut title = String::new();
69
70            if let Some(ref filter) = state.filter {
71                for diagnostic in filter.iter().map(|path| diagnostics.get(path).unwrap()) {
72                    title = title + &Self::format(diagnostic);
73                }
74            } else {
75                for diagnostic in diagnostics.iter() {
76                    title = title + &Self::format(diagnostic);
77                }
78            }
79
80            if let Some(mut window) = windows.iter_mut().next() {
81                window.title = title[0..title.len() - 1].to_owned();
82            }
83        }
84    }
85}