symtropy_devconsole/
lib.rs1use bevy::prelude::*;
14use bevy_egui::{egui, EguiContexts, EguiPlugin, EguiPrimaryContextPass};
15
16#[derive(Resource, Debug, Clone, Copy)]
18pub struct DevConsoleVisible(pub bool);
19
20impl Default for DevConsoleVisible {
21 fn default() -> Self {
22 Self(true)
23 }
24}
25
26#[derive(Resource, Debug, Clone, Copy)]
28pub struct DevConsolePaused(pub bool);
29
30impl Default for DevConsolePaused {
31 fn default() -> Self {
32 Self(false)
33 }
34}
35
36#[derive(Default, Clone)]
38pub struct SymtropyDevConsolePlugin;
39
40impl Plugin for SymtropyDevConsolePlugin {
41 fn build(&self, app: &mut App) {
42 if !app.is_plugin_added::<EguiPlugin>() {
43 app.add_plugins(EguiPlugin::default());
44 }
45 app.init_resource::<DevConsoleVisible>()
46 .init_resource::<DevConsolePaused>()
47 .add_systems(Update, toggle_console_keybind)
48 .add_systems(
49 EguiPrimaryContextPass,
50 (
51 scene_controls_panel,
52 #[cfg(feature = "phi-panel")]
53 phi_inspector_panel,
54 )
55 .chain(),
56 );
57 }
58}
59
60fn toggle_console_keybind(keys: Res<ButtonInput<KeyCode>>, mut vis: ResMut<DevConsoleVisible>) {
61 if keys.just_pressed(KeyCode::F1) {
62 vis.0 = !vis.0;
63 }
64}
65
66fn scene_controls_panel(
67 mut contexts: EguiContexts,
68 vis: Res<DevConsoleVisible>,
69 mut paused: ResMut<DevConsolePaused>,
70 mut time: ResMut<Time<Virtual>>,
71) {
72 if !vis.0 {
73 return;
74 }
75 let Ok(ctx) = contexts.ctx_mut() else {
76 return;
77 };
78 egui::SidePanel::left("dev_console_scene_controls")
79 .default_width(220.0)
80 .show(ctx, |ui| {
81 ui.heading("Scene");
82 ui.separator();
83 let label = if paused.0 { "▶ Resume" } else { "⏸ Pause" };
84 if ui.button(label).clicked() {
85 paused.0 = !paused.0;
86 if paused.0 {
87 time.pause();
88 } else {
89 time.unpause();
90 }
91 }
92 ui.separator();
93 ui.label("F1: Toggle Console");
94 });
95}
96
97#[cfg(feature = "phi-panel")]
98fn phi_inspector_panel(
99 mut contexts: EguiContexts,
100 vis: Res<DevConsoleVisible>,
101 query: Query<(Entity, &symtropy_bevy_core::PhysicsBody)>,
102) {
103 if !vis.0 {
104 return;
105 }
106 let Ok(ctx) = contexts.ctx_mut() else {
107 return;
108 };
109 egui::SidePanel::left("dev_console_phi")
110 .default_width(220.0)
111 .show(ctx, |ui| {
112 ui.heading("Φ Inspector");
113 ui.label(format!(
114 "{} body(ies)",
115 query.iter().count()
116 ));
117 ui.separator();
118 ui.label("Phi tracking decoupled to break cycles.");
119 });
120}