tirbofish/dropbear · commit
4667c90d3e54534095bb4f6cfb654aecd9886cfc
fix: fixed some editor and project settings stuff, which means HistoricalOption was implemented as a new type. Unverified
@@ -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 @@ -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); + } _ => {} } } @@ -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]; } } @@ -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(), } } } @@ -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}; @@ -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() + } +} @@ -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() { @@ -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) + ); + } + }); + } + _ => {} + } + }); }); }); @@ -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) + ); + } }); } _ => {} @@ -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);