diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a397e02d..407633736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ### Added +- Functionality: + - In the desktop version, changes to graphics options are now persisted across launches. + - `all-is-cubes-ui` library: - `apps::Settings` manages user-editable settings that eventually will be more than just the graphics options. - `apps::SessionBuilder::settings()` links a possibly-shared `Settings` to the created session. diff --git a/all-is-cubes-desktop/src/bin/all-is-cubes/main.rs b/all-is-cubes-desktop/src/bin/all-is-cubes/main.rs index 549527b15..c8a469614 100644 --- a/all-is-cubes-desktop/src/bin/all-is-cubes/main.rs +++ b/all-is-cubes-desktop/src/bin/all-is-cubes/main.rs @@ -13,6 +13,7 @@ use clap::{CommandFactory as _, Parser as _}; use all_is_cubes::euclid::Size2D; use all_is_cubes::listen; use all_is_cubes_render::camera::{GraphicsOptions, Viewport}; +use all_is_cubes_ui::apps::Settings; #[cfg(feature = "record")] use all_is_cubes_desktop::record; @@ -81,8 +82,8 @@ fn main() -> Result<(), anyhow::Error> { } })?; - let graphics_options = if no_config_files { - GraphicsOptions::default() + let settings = if no_config_files { + Settings::new(Arc::new(GraphicsOptions::default())) } else { load_config().context("Error loading configuration files")? }; @@ -104,14 +105,13 @@ fn main() -> Result<(), anyhow::Error> { let mut session = runtime.block_on( Session::builder() .ui(viewport_cell.as_source()) + .settings_from(settings) .quit(Arc::new(|| { // TODO: command the event loop to exit instead std::process::exit(0) })) .build(), ); - // TODO: this code should live in the lib - session.settings().set_graphics_options(graphics_options); universe_task.attach_to_session(&mut session); let session_done_time = Instant::now(); log::debug!( diff --git a/all-is-cubes-desktop/src/config_files.rs b/all-is-cubes-desktop/src/config_files.rs index beb0c3a9a..66f3b2b59 100644 --- a/all-is-cubes-desktop/src/config_files.rs +++ b/all-is-cubes-desktop/src/config_files.rs @@ -1,15 +1,17 @@ -use std::fs::create_dir_all; +use std::fs; use std::fs::File; use std::io::BufReader; use std::path::Path; +use std::sync::Arc; +use all_is_cubes_ui::apps::Settings; use directories_next::ProjectDirs; use serde::{de::DeserializeOwned, Serialize}; use all_is_cubes_render::camera::GraphicsOptions; /// Load preferences/settings/config files from a platform-appropriate read/write location. -pub fn load_config() -> Result { +pub fn load_config() -> Result { // TODO: make testable // TODO: allow users of this library function to pick their own config dir @@ -23,15 +25,23 @@ pub fn load_config() -> Result { let project_dirs = ProjectDirs::from("org.switchb", "", "all-is-cubes") .ok_or_else(|| anyhow::anyhow!("could not find configuration directory"))?; - create_dir_all(project_dirs.config_dir())?; + fs::create_dir_all(project_dirs.config_dir())?; + + let settings_path = project_dirs.config_dir().join("graphics.json"); let graphics_options = read_or_create_default_json_file( "graphics options", - &project_dirs.config_dir().join("graphics.json"), + &settings_path, GraphicsOptions::default, ); - Ok(graphics_options) + Ok(Settings::with_persistence( + Arc::new(graphics_options), + // TODO: ideally, writes would be performed asynchronously and with rate-limiting + Arc::new(move |data| { + write_json_file("graphics options", &settings_path, &data); + }), + )) } fn read_or_create_default_json_file( @@ -63,7 +73,7 @@ fn read_or_create_default_json_file( ); let value = default(); let json_text = serde_json::to_string_pretty(&value).unwrap(); - std::fs::write(path, json_text.as_bytes()).expect("Error writing default file"); + fs::write(path, json_text.as_bytes()).expect("Error writing default file"); value } Err(e) => { @@ -77,3 +87,27 @@ fn read_or_create_default_json_file( } } } + +fn write_json_file(description: &str, path: &Path, value: &V) { + match fs::OpenOptions::new().write(true).truncate(true).open(path) { + Ok(file) => match serde_json::to_writer_pretty(file, &value) { + Ok(()) => log::trace!("Wrote {} to {}", description, path.to_string_lossy()), + Err(e) => { + log::error!( + "Error while writing {} file {}: {}", + description, + path.to_string_lossy(), + e + ); + } + }, + Err(e) => { + log::error!( + "Error while opening {} file {}: {}", + description, + path.to_string_lossy(), + e + ); + } + } +} diff --git a/all-is-cubes-desktop/src/record/rmain.rs b/all-is-cubes-desktop/src/record/rmain.rs index 25990753f..3b6c61835 100644 --- a/all-is-cubes-desktop/src/record/rmain.rs +++ b/all-is-cubes-desktop/src/record/rmain.rs @@ -34,13 +34,14 @@ where // Modify graphics options to suit recording // TODO: Find a better place to put this policy, and in particular allow the user to // override it if they do want to record the UI. - dsession - .session - .settings() - .mutate_graphics_options(|graphics_options| { - graphics_options.show_ui = false; - graphics_options.debug_info_text = false; - }); + let settings = dsession.session.settings(); + // TODO: instead of blanket disinheriting, there should be a way to set explicitly ephemeral + // settings. + settings.disinherit(); + settings.mutate_graphics_options(|graphics_options| { + graphics_options.show_ui = false; + graphics_options.debug_info_text = false; + }); Ok(()) }