eframe: Add `NativeOptions::persistence_path` (#4423)

This allows customizing the persistence path in NativeOptions.
Previously, persistence wouldn't work with android because
directories-next doesn't support android so eframe would just fail to
find a place where it could store its config.

* Closes #4098 (android users can now specify a path that works with
android, by e.g. using app_dirs2, which supports android)
This commit is contained in:
lucasmerlin 2024-05-27 18:57:39 +02:00 committed by GitHub
parent 192a111272
commit 8553e738e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 44 additions and 15 deletions

View File

@ -150,6 +150,7 @@ pub trait App {
/// On web the state is stored to "Local Storage". /// On web the state is stored to "Local Storage".
/// ///
/// On native the path is picked using [`crate::storage_dir`]. /// On native the path is picked using [`crate::storage_dir`].
/// The path can be customized via [`NativeOptions::persistence_path`].
fn save(&mut self, _storage: &mut dyn Storage) {} fn save(&mut self, _storage: &mut dyn Storage) {}
/// Called once on shutdown, after [`Self::save`]. /// Called once on shutdown, after [`Self::save`].
@ -362,6 +363,10 @@ pub struct NativeOptions {
/// Controls whether or not the native window position and size will be /// Controls whether or not the native window position and size will be
/// persisted (only if the "persistence" feature is enabled). /// persisted (only if the "persistence" feature is enabled).
pub persist_window: bool, pub persist_window: bool,
/// The folder where `eframe` will store the app state. If not set, eframe will get the paths
/// from [directories_next].
pub persistence_path: Option<std::path::PathBuf>,
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -379,6 +384,8 @@ impl Clone for NativeOptions {
#[cfg(feature = "wgpu")] #[cfg(feature = "wgpu")]
wgpu_options: self.wgpu_options.clone(), wgpu_options: self.wgpu_options.clone(),
persistence_path: self.persistence_path.clone(),
..*self ..*self
} }
} }
@ -418,6 +425,8 @@ impl Default for NativeOptions {
wgpu_options: egui_wgpu::WgpuConfiguration::default(), wgpu_options: egui_wgpu::WgpuConfiguration::default(),
persist_window: true, persist_window: true,
persistence_path: None,
} }
} }
} }

View File

@ -1,6 +1,8 @@
//! Common tools used by [`super::glow_integration`] and [`super::wgpu_integration`]. //! Common tools used by [`super::glow_integration`] and [`super::wgpu_integration`].
use web_time::Instant; use web_time::Instant;
use std::path::PathBuf;
use winit::event_loop::EventLoopWindowTarget; use winit::event_loop::EventLoopWindowTarget;
use raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _}; use raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _};
@ -132,6 +134,16 @@ pub fn create_storage(_app_name: &str) -> Option<Box<dyn epi::Storage>> {
None None
} }
#[allow(clippy::unnecessary_wraps)]
pub fn create_storage_with_file(_file: impl Into<PathBuf>) -> Option<Box<dyn epi::Storage>> {
#[cfg(feature = "persistence")]
return Some(Box::new(
super::file_storage::FileStorage::from_ron_filepath(_file),
));
#[cfg(not(feature = "persistence"))]
None
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/// Everything needed to make a winit-based integration for [`epi`]. /// Everything needed to make a winit-based integration for [`epi`].

View File

@ -41,7 +41,7 @@ impl Drop for FileStorage {
impl FileStorage { impl FileStorage {
/// Store the state in this .ron file. /// Store the state in this .ron file.
fn from_ron_filepath(ron_filepath: impl Into<PathBuf>) -> Self { pub(crate) fn from_ron_filepath(ron_filepath: impl Into<PathBuf>) -> Self {
crate::profile_function!(); crate::profile_function!();
let ron_filepath: PathBuf = ron_filepath.into(); let ron_filepath: PathBuf = ron_filepath.into();
log::debug!("Loading app state from {:?}…", ron_filepath); log::debug!("Loading app state from {:?}…", ron_filepath);

View File

@ -195,13 +195,17 @@ impl GlowWinitApp {
) -> Result<&mut GlowWinitRunning> { ) -> Result<&mut GlowWinitRunning> {
crate::profile_function!(); crate::profile_function!();
let storage = epi_integration::create_storage( let storage = if let Some(file) = &self.native_options.persistence_path {
self.native_options epi_integration::create_storage_with_file(file)
.viewport } else {
.app_id epi_integration::create_storage(
.as_ref() self.native_options
.unwrap_or(&self.app_name), .viewport
); .app_id
.as_ref()
.unwrap_or(&self.app_name),
)
};
let egui_ctx = create_egui_context(storage.as_deref()); let egui_ctx = create_egui_context(storage.as_deref());

View File

@ -406,13 +406,17 @@ impl WinitApp for WgpuWinitApp {
self.recreate_window(event_loop, running); self.recreate_window(event_loop, running);
running running
} else { } else {
let storage = epi_integration::create_storage( let storage = if let Some(file) = &self.native_options.persistence_path {
self.native_options epi_integration::create_storage_with_file(file)
.viewport } else {
.app_id epi_integration::create_storage(
.as_ref() self.native_options
.unwrap_or(&self.app_name), .viewport
); .app_id
.as_ref()
.unwrap_or(&self.app_name),
)
};
let egui_ctx = winit_integration::create_egui_context(storage.as_deref()); let egui_ctx = winit_integration::create_egui_context(storage.as_deref());
let (window, builder) = create_window( let (window, builder) = create_window(
&egui_ctx, &egui_ctx,