kitgit

tirbofish/dropbear · commit

4667c90d3e54534095bb4f6cfb654aecd9886cfc

fix: fixed some editor and project settings stuff, which means HistoricalOption was implemented as a new type. Unverified

Thribhu K <4tkbytes@pm.me> · 2026-01-12 15:20

view full diff

diff --git a/.sdkmanrc b/.sdkmanrc
new file mode 100644
index 0000000..5a6900d
--- /dev/null
+++ b/.sdkmanrc
@@ -0,0 +1,3 @@
+# Enable auto-env through the sdkman_auto_env config
+# Add key=value pairs of SDKs to use below
+java=21.0.9-tem
diff --git a/dropbear-engine/src/lib.rs b/dropbear-engine/src/lib.rs
index 20e5cd0..f9f6107 100644
--- a/dropbear-engine/src/lib.rs
+++ b/dropbear-engine/src/lib.rs
@@ -931,6 +931,9 @@ impl ApplicationHandler for App {
                                 self.windows.remove(&target_window_id);
                             }
                         }
+                        scene::SceneCommand::SetFPS(new_fps) => {
+                            self.set_target_fps(new_fps);
+                        }
                         _ => {}
                     }
                 }
diff --git a/dropbear-engine/src/scene.rs b/dropbear-engine/src/scene.rs
index 21647b8..9058d3e 100644
--- a/dropbear-engine/src/scene.rs
+++ b/dropbear-engine/src/scene.rs
@@ -33,6 +33,7 @@ pub enum SceneCommand {
     DebugMessage(String),
     RequestWindow(WindowData),
     CloseWindow(WindowId),
+    SetFPS(u32),
 }
 
 impl Default for SceneCommand {
@@ -145,7 +146,7 @@ impl Manager {
                 }
                 SceneCommand::None => {}
                 SceneCommand::DebugMessage(msg) => log::debug!("{}", msg),
-                SceneCommand::RequestWindow(_) | SceneCommand::CloseWindow(_) => {
+                SceneCommand::RequestWindow(_) | SceneCommand::CloseWindow(_) | SceneCommand::SetFPS(_) => {
                     return vec![command];
                 }
             }
diff --git a/eucalyptus-core/src/runtime.rs b/eucalyptus-core/src/runtime.rs
index e2cceee..9532ce5 100644
--- a/eucalyptus-core/src/runtime.rs
+++ b/eucalyptus-core/src/runtime.rs
@@ -4,6 +4,7 @@ use crate::scene::SceneConfig;
 use crate::states::{PROJECT, SCENES};
 use anyhow::Context;
 use semver::Version;
+use crate::utils::option::HistoricalOption;
 
 /// The settings of a project in its runtime.
 ///
@@ -16,8 +17,10 @@ pub struct RuntimeSettings {
     /// 
     /// The first scene is not set is expected to be the first scene out of the
     /// projects scene list, or just a normal anyhow error. 
+    #[serde(default)]
     pub initial_scene: Option<String>,
-    pub target_fps: u32,
+    #[serde(default)]
+    pub target_fps: HistoricalOption<u32>,
 }
 
 impl RuntimeSettings {
@@ -25,7 +28,7 @@ impl RuntimeSettings {
     pub fn new() -> Self {
         Self {
             initial_scene: None,
-            target_fps: u32::MAX,
+            target_fps: HistoricalOption::none(),
         }
     }
 }
diff --git a/eucalyptus-core/src/utils.rs b/eucalyptus-core/src/utils.rs
index 5744646..4f65b4f 100644
--- a/eucalyptus-core/src/utils.rs
+++ b/eucalyptus-core/src/utils.rs
@@ -1,5 +1,7 @@
 //! Utility functions and helpers
 
+pub mod option;
+
 use crate::states::Node;
 use dropbear_engine::utils::{ResourceReference, ResourceReferenceType, relative_path_from_euca};
 use std::path::{Path, PathBuf};
diff --git a/eucalyptus-core/src/utils/option.rs b/eucalyptus-core/src/utils/option.rs
new file mode 100644
index 0000000..2b5ec32
--- /dev/null
+++ b/eucalyptus-core/src/utils/option.rs
@@ -0,0 +1,120 @@
+//! Custom optional implementations
+
+use serde::{Deserialize, Serialize};
+
+/// An optional value that when replaced can store the old value. 
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
+pub struct HistoricalOption<T> {
+    _inner: Option<T>,
+    _old: Option<T>,
+}
+
+impl<T> HistoricalOption<T> {
+    /// Creates a new HistoricalOption with Some value
+    pub fn new(value: T) -> Self {
+        Self {
+            _inner: Some(value),
+            _old: None,
+        }
+    }
+
+    /// Creates a new HistoricalOption with None
+    pub fn none() -> Self {
+        Self {
+            _inner: None,
+            _old: None,
+        }
+    }
+
+    /// Replaces the current value, storing the old one in history
+    pub fn replace(&mut self, new_value: Option<T>) {
+        self._old = self._inner.take();
+        self._inner = new_value;
+    }
+
+    /// Swaps the old value with the new one. 
+    pub fn swap(&mut self) {
+        let old = self._old.take();
+        let inner = self._inner.take();
+        self._inner = old;
+        self._old = inner;
+    }
+
+    /// Sets to None, storing the old value in history
+    pub fn clear(&mut self) {
+        self.replace(None);
+    }
+
+    /// Gets a reference to the current value
+    pub fn get(&self) -> Option<&T> {
+        self._inner.as_ref()
+    }
+
+    /// Gets a mutable reference to the current value
+    pub fn get_mut(&mut self) -> Option<&mut T> {
+        self._inner.as_mut()
+    }
+
+    /// Gets a reference to the old/previous value
+    pub fn old(&self) -> Option<&T> {
+        self._old.as_ref()
+    }
+
+    /// Takes the old value, leaving None in its place
+    pub fn take_old(&mut self) -> Option<T> {
+        self._old.take()
+    }
+
+    /// Returns true if the current value is Some
+    pub fn is_some(&self) -> bool {
+        self._inner.is_some()
+    }
+
+    /// Returns true if the current value is None
+    pub fn is_none(&self) -> bool {
+        self._inner.is_none()
+    }
+
+    /// Converts to the inner Option
+    pub fn into_inner(self) -> Option<T> {
+        self._inner
+    }
+
+    /// Turn the option ON. 
+    /// If there is a saved history value, restore it.
+    /// Otherwise, use the provided default.
+    pub fn enable_or(&mut self, default_val: T) {
+        if self._inner.is_none() {
+            self._inner = Some(self._old.take().unwrap_or(default_val));
+        }
+    }
+
+    /// Turn the option OFF.
+    /// Save the current value into history so it can be restored later.
+    pub fn disable(&mut self) {
+        if self._inner.is_some() {
+            self._old = self._inner.take();
+        }
+    }
+}
+
+impl<T> From<Option<T>> for HistoricalOption<T> {
+    fn from(opt: Option<T>) -> Self {
+        Self {
+            _inner: opt,
+            _old: None,
+        }
+    }
+}
+
+impl<T> From<T> for HistoricalOption<T> {
+    fn from(value: T) -> Self {
+        Self::new(value)
+    }
+}
+
+impl<T> Default for HistoricalOption<T> {
+    fn default() -> Self {
+        Self::none()
+    }
+}
\ No newline at end of file
diff --git a/eucalyptus-editor/src/editor/scene.rs b/eucalyptus-editor/src/editor/scene.rs
index 968ce6f..bbee890 100644
--- a/eucalyptus-editor/src/editor/scene.rs
+++ b/eucalyptus-editor/src/editor/scene.rs
@@ -170,6 +170,13 @@ impl Scene for Editor {
         }
 
         {
+            if let Some(fps) = EDITOR_SETTINGS.read().target_fps.get() {
+                log::debug!("setting new fps for editor: {}", fps);
+                self.scene_command = SceneCommand::SetFPS(*fps);
+            }
+        }
+
+        {
             // basic futurequeue spawn queue management.
             let mut completed = Vec::new();
             for (i, handle) in self.light_spawn_queue.iter().enumerate() {
diff --git a/eucalyptus-editor/src/editor/settings/editor.rs b/eucalyptus-editor/src/editor/settings/editor.rs
index 78adc72..b618605 100644
--- a/eucalyptus-editor/src/editor/settings/editor.rs
+++ b/eucalyptus-editor/src/editor/settings/editor.rs
@@ -1,8 +1,10 @@
 //! The scene for a window that opens up settings related to the eucalyptus-editor.
 
 use app_dirs2::AppDataType;
-use egui::{CentralPanel};
+use egui::{CentralPanel, Id, Slider, SliderClamping};
 use egui_dock::DockState;
+use egui_ltreeview::{Action, NodeBuilder};
+use eucalyptus_core::utils::option::HistoricalOption;
 use gilrs::{Button, GamepadId};
 use hecs::spin::Lazy;
 use parking_lot::RwLock;
@@ -31,6 +33,9 @@ pub struct EditorSettings {
     #[serde(default)]
     pub dock_layout: Option<DockState<EditorTab>>,
 
+    #[serde(default)]
+    pub target_fps: HistoricalOption<u32>,
+
     /// Is the debug menu shown?
     ///
     /// Primarily used internally for testing out features of the editor, however
@@ -47,6 +52,7 @@ impl EditorSettings {
         Self {
             dock_layout: None,
             is_debug_menu_shown: false,
+            target_fps: HistoricalOption::none(),
         }
     }
 
@@ -95,6 +101,16 @@ pub struct EditorSettingsWindow {
     scene_command: SceneCommand,
     input_state: InputState,
     window: Option<WindowId>,
+
+    current_leaf: EditorSettingsCurrentLeaf,
+}
+
+#[derive(Default)]
+enum EditorSettingsCurrentLeaf {
+    #[default]
+    None,
+
+    Performance,
 }
 
 impl EditorSettingsWindow {
@@ -103,6 +119,7 @@ impl EditorSettingsWindow {
             scene_command: SceneCommand::None,
             input_state: Default::default(),
             window: None,
+            current_leaf: Default::default(),
         }
     }
 }
@@ -116,8 +133,81 @@ impl Scene for EditorSettingsWindow {
 
     fn update(&mut self, _dt: f32, graphics: &mut RenderContext) {
         CentralPanel::default().show(&graphics.shared.get_egui_context(), |ui| {
-            ui.centered_and_justified(|ui| {
-                ui.label("Hello editor settings window! not implemented yet (˘・_・˘)")
+            let mut editor = EDITOR_SETTINGS.write();
+
+            egui::SidePanel::left("editor_settings_tree_panel")
+                .resizable(true)
+                .default_width(200.0)
+                .width_range(150.0..=400.0)
+                .show_inside(ui, |ui| {
+                    egui::ScrollArea::vertical()
+                        .auto_shrink([false; 2])
+                        .show(ui, |ui| {
+                            let (_resp, action) = egui_ltreeview::TreeView::new(Id::from("editor_settings"))
+                                .show(ui, |builder| {
+                                    builder.node(
+                                        NodeBuilder::leaf("Performance")
+                                            .label("Performance")
+                                    );
+                                });
+
+                            for a in action {
+                                match a {
+                                    Action::SetSelected(selected) => {
+                                        let selected = selected.first().cloned();
+                                        if let Some(s) = selected {
+                                            match s {
+                                                "Performance" => {
+                                                    self.current_leaf = EditorSettingsCurrentLeaf::Performance;
+                                                }
+                                                _ => {
+                                                    self.current_leaf = EditorSettingsCurrentLeaf::None;
+                                                },
+                                            }
+                                        }
+                                    }
+                                    Action::Move(_) => {}
+                                    Action::Drag(_) => {}
+                                    Action::Activate(_) => {}
+                                    Action::DragExternal(_) => {}
+                                    Action::MoveExternal(_) => {}
+                                }
+                            }
+                        });
+                });
+
+            egui::CentralPanel::default().show_inside(ui, |ui| {
+                egui::ScrollArea::vertical()
+                    .auto_shrink([false; 2])
+                    .show(ui, |ui| {
+                        match self.current_leaf {
+                            EditorSettingsCurrentLeaf::Performance => {
+                                ui.heading("Performance Settings");
+                                ui.separator();
+
+                                ui.label("Target FPS:");
+                                ui.horizontal(|ui| {
+                                    let mut local_set_max_fps = editor.target_fps.is_some();
+
+                                    if ui.checkbox(&mut local_set_max_fps, "Set max frames-per-second (FPS)").changed() {
+                                        if local_set_max_fps {
+                                            editor.target_fps.enable_or(120); 
+                                        } else {
+                                            editor.target_fps.disable(); 
+                                        }
+                                    }
+
+                                    if let Some(v) = editor.target_fps.get_mut() {
+                                        ui.add(
+                                            Slider::new(v, 1..=1000)
+                                            .clamping(SliderClamping::Never)
+                                        );
+                                    }
+                                });
+                            }
+                            _ => {}
+                        }
+                    });
             });
         });
 
diff --git a/eucalyptus-editor/src/editor/settings/project.rs b/eucalyptus-editor/src/editor/settings/project.rs
index 1aa171c..55d040b 100644
--- a/eucalyptus-editor/src/editor/settings/project.rs
+++ b/eucalyptus-editor/src/editor/settings/project.rs
@@ -1,6 +1,6 @@
 //! The scene for a window that opens up settings related to the project, "Play Mode" runtime and redback-runtime.  
 
-use egui::{CentralPanel, Color32, Id, RichText};
+use egui::{CentralPanel, Color32, Id, RichText, Slider, SliderClamping};
 use egui_ltreeview::{Action, NodeBuilder};
 use gilrs::{Button, GamepadId};
 use semver::Version;
@@ -55,7 +55,7 @@ impl Scene for ProjectSettingsWindow {
         CentralPanel::default().show(&graphics.shared.get_egui_context(), |ui| {
             let mut project = PROJECT.write();
 
-            egui::SidePanel::left("settings_tree_panel")
+            egui::SidePanel::left("project_settings_tree_panel")
                 .resizable(true)
                 .default_width(200.0)
                 .width_range(150.0..=400.0)
@@ -157,16 +157,22 @@ impl Scene for ProjectSettingsWindow {
 
                                 ui.label("Target FPS:");
                                 ui.horizontal(|ui| {
-                                    let mut local_max_fps = false;
-                                    ui.checkbox(&mut local_max_fps, "Set max fps");
+                                    let mut local_set_max_fps = project.runtime_settings.target_fps.is_some();
 
-                                    if local_max_fps {
-                                        project.runtime_settings.target_fps = u32::MAX;
+                                    if ui.checkbox(&mut local_set_max_fps, "Set max frames-per-second (FPS)").changed() {
+                                        if local_set_max_fps {
+                                            project.runtime_settings.target_fps.enable_or(120); 
+                                        } else {
+                                            project.runtime_settings.target_fps.disable(); 
+                                        }
                                     }
 
-                                    ui.add_enabled_ui(project.runtime_settings.target_fps == u32::MAX, |ui| {
-                                        ui.add(egui::Slider::new(&mut project.runtime_settings.target_fps, 30..=1000).clamping(egui::SliderClamping::Never));
-                                    });
+                                    if let Some(v) = project.runtime_settings.target_fps.get_mut() {
+                                        ui.add(
+                                            Slider::new(v, 1..=1000)
+                                            .clamping(SliderClamping::Never)
+                                        );
+                                    }
                                 });
                             }
                             _ => {}
diff --git a/eucalyptus-editor/src/runtime/scene.rs b/eucalyptus-editor/src/runtime/scene.rs
index 687b1a9..c1a46ca 100644
--- a/eucalyptus-editor/src/runtime/scene.rs
+++ b/eucalyptus-editor/src/runtime/scene.rs
@@ -228,6 +228,13 @@ impl Scene for PlayMode {
         graphics.shared.future_queue.poll();
         self.poll(graphics);
 
+        {
+            if let Some(fps) = PROJECT.read().runtime_settings.target_fps.get() {
+                log::debug!("setting new fps for play mode session: {}", fps);
+                self.scene_command = SceneCommand::SetFPS(*fps);
+            }
+        }
+
         if let Some(ref progress) = self.scene_progress {
             if !progress.scene_handle_requested && self.world_receiver.is_none() && self.scene_loading_handle.is_none() {
                 log::debug!("Starting async load for scene: {}", progress.requested_scene);