From 1f414c059d9e873fec0010932bf6d79f5e5116a9 Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Fri, 8 Mar 2024 17:24:34 +0800 Subject: [PATCH] `egui_extras`: Fixed handling of `file://` protocol for images (#4107) * Remove the leading slash from the path if the target OS is Windows. This is because Windows paths are not supposed to start with a slash. For example, `file:///C:/path/to/file` is a valid URI, but `/C:/path/to/file` is not a valid path. * Use the input URI consistently as the cache key. Currently, the cache key is inconsistently set as either the path or the URI, while the forget key is always the URI. This inconsistency should be resolved. --------- Co-authored-by: Emil Ernerfeldt --- crates/egui_extras/src/loaders/file_loader.rs | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/egui_extras/src/loaders/file_loader.rs b/crates/egui_extras/src/loaders/file_loader.rs index 10bfec539762..afa7d0eefd6d 100644 --- a/crates/egui_extras/src/loaders/file_loader.rs +++ b/crates/egui_extras/src/loaders/file_loader.rs @@ -25,6 +25,19 @@ impl FileLoader { const PROTOCOL: &str = "file://"; +/// Remove the leading slash from the path if the target OS is Windows. +/// +/// This is because Windows paths are not supposed to start with a slash. +/// For example, `file:///C:/path/to/file` is a valid URI, but `/C:/path/to/file` is not a valid path. +#[inline] +fn trim_extra_slash(s: &str) -> &str { + if cfg!(target_os = "windows") { + s.trim_start_matches('/') + } else { + s + } +} + impl BytesLoader for FileLoader { fn id(&self) -> &str { Self::ID @@ -32,12 +45,12 @@ impl BytesLoader for FileLoader { fn load(&self, ctx: &egui::Context, uri: &str) -> BytesLoadResult { // File loader only supports the `file` protocol. - let Some(path) = uri.strip_prefix(PROTOCOL) else { + let Some(path) = uri.strip_prefix(PROTOCOL).map(trim_extra_slash) else { return Err(LoadError::NotSupported); }; let mut cache = self.cache.lock(); - if let Some(entry) = cache.get(path).cloned() { + if let Some(entry) = cache.get(uri).cloned() { // `path` has either begun loading, is loaded, or has failed to load. match entry { Poll::Ready(Ok(file)) => Ok(BytesPoll::Ready { @@ -54,7 +67,7 @@ impl BytesLoader for FileLoader { // Set the file to `pending` until we finish loading it. let path = path.to_owned(); - cache.insert(path.clone(), Poll::Pending); + cache.insert(uri.to_owned(), Poll::Pending); drop(cache); // Spawn a thread to read the file, so that we don't block the render for too long. @@ -63,7 +76,7 @@ impl BytesLoader for FileLoader { .spawn({ let ctx = ctx.clone(); let cache = self.cache.clone(); - let _uri = uri.to_owned(); + let uri = uri.to_owned(); move || { let result = match std::fs::read(&path) { Ok(bytes) => { @@ -82,10 +95,10 @@ impl BytesLoader for FileLoader { } Err(err) => Err(err.to_string()), }; - let prev = cache.lock().insert(path, Poll::Ready(result)); + let prev = cache.lock().insert(uri.clone(), Poll::Ready(result)); assert!(matches!(prev, Some(Poll::Pending))); ctx.request_repaint(); - log::trace!("finished loading {_uri:?}"); + log::trace!("finished loading {uri:?}"); } }) .expect("failed to spawn thread");