From 63d6d3170e02d039e7a26d2ca93fcb41db1b5d9d Mon Sep 17 00:00:00 2001 From: jprochazk <1665677+jprochazk@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:31:17 +0200 Subject: [PATCH] remove last remanant of `RawImage` --- crates/egui/src/load.rs | 101 +---------------- crates/egui/src/load/bytes_loader.rs | 57 ++++++++++ crates/egui/src/load/texture_loader.rs | 60 ++++++++++ crates/egui/src/widgets/button.rs | 53 +++++---- crates/egui/src/widgets/image.rs | 111 ------------------- crates/egui/src/widgets/mod.rs | 2 +- crates/egui_demo_app/Cargo.toml | 1 + crates/egui_glium/examples/native_texture.rs | 3 +- 8 files changed, 158 insertions(+), 230 deletions(-) create mode 100644 crates/egui/src/load/bytes_loader.rs create mode 100644 crates/egui/src/load/texture_loader.rs diff --git a/crates/egui/src/load.rs b/crates/egui/src/load.rs index 2e1cc1a471a..50fde707a23 100644 --- a/crates/egui/src/load.rs +++ b/crates/egui/src/load.rs @@ -52,6 +52,11 @@ //! For example, a loader may determine that it doesn't support loading a specific URI //! if the protocol does not match what it expects. +mod bytes_loader; +mod texture_loader; + +use self::bytes_loader::DefaultBytesLoader; +use self::texture_loader::DefaultTextureLoader; use crate::Context; use ahash::HashMap; use epaint::mutex::Mutex; @@ -458,102 +463,6 @@ pub trait TextureLoader { fn byte_size(&self) -> usize; } -#[derive(Default)] -pub(crate) struct DefaultBytesLoader { - cache: Mutex, Bytes>>, -} - -impl DefaultBytesLoader { - pub(crate) fn insert(&self, uri: impl Into>, bytes: impl Into) { - self.cache - .lock() - .entry(uri.into()) - .or_insert_with(|| bytes.into()); - } -} - -impl BytesLoader for DefaultBytesLoader { - fn id(&self) -> &str { - generate_loader_id!(DefaultBytesLoader) - } - - fn load(&self, _: &Context, uri: &str) -> BytesLoadResult { - match self.cache.lock().get(uri).cloned() { - Some(bytes) => Ok(BytesPoll::Ready { - size: None, - bytes, - mime: None, - }), - None => Err(LoadError::NotSupported), - } - } - - fn forget(&self, uri: &str) { - let _ = self.cache.lock().remove(uri); - } - - fn forget_all(&self) { - self.cache.lock().clear(); - } - - fn byte_size(&self) -> usize { - self.cache.lock().values().map(|bytes| bytes.len()).sum() - } -} - -#[derive(Default)] -struct DefaultTextureLoader { - cache: Mutex>, -} - -impl TextureLoader for DefaultTextureLoader { - fn id(&self) -> &str { - generate_loader_id!(DefaultTextureLoader) - } - - fn load( - &self, - ctx: &Context, - uri: &str, - texture_options: TextureOptions, - size_hint: SizeHint, - ) -> TextureLoadResult { - let mut cache = self.cache.lock(); - if let Some(handle) = cache.get(&(uri.into(), texture_options)) { - let texture = SizedTexture::from_handle(handle); - Ok(TexturePoll::Ready { texture }) - } else { - match ctx.try_load_image(uri, size_hint)? { - ImagePoll::Pending { size } => Ok(TexturePoll::Pending { size }), - ImagePoll::Ready { image } => { - let handle = ctx.load_texture(uri, image, texture_options); - let texture = SizedTexture::from_handle(&handle); - cache.insert((uri.into(), texture_options), handle); - Ok(TexturePoll::Ready { texture }) - } - } - } - } - - fn forget(&self, uri: &str) { - self.cache.lock().retain(|(u, _), _| u != uri); - } - - fn forget_all(&self) { - self.cache.lock().clear(); - } - - fn end_frame(&self, _: usize) {} - - fn byte_size(&self) -> usize { - self.cache - .lock() - .values() - .map(|texture| texture.byte_size()) - .sum() - } -} - type BytesLoaderImpl = Arc; type ImageLoaderImpl = Arc; type TextureLoaderImpl = Arc; diff --git a/crates/egui/src/load/bytes_loader.rs b/crates/egui/src/load/bytes_loader.rs new file mode 100644 index 00000000000..451ce150e28 --- /dev/null +++ b/crates/egui/src/load/bytes_loader.rs @@ -0,0 +1,57 @@ +use super::*; + +#[derive(Default)] +pub struct DefaultBytesLoader { + cache: Mutex, Bytes>>, +} + +impl DefaultBytesLoader { + pub fn insert(&self, uri: impl Into>, bytes: impl Into) { + self.cache + .lock() + .entry(uri.into()) + .or_insert_with_key(|uri| { + let bytes: Bytes = bytes.into(); + + #[cfg(feature = "log")] + log::trace!("loaded {} bytes for uri {uri:?}", bytes.len()); + + bytes + }); + } +} + +impl BytesLoader for DefaultBytesLoader { + fn id(&self) -> &str { + generate_loader_id!(DefaultBytesLoader) + } + + fn load(&self, _: &Context, uri: &str) -> BytesLoadResult { + match self.cache.lock().get(uri).cloned() { + Some(bytes) => Ok(BytesPoll::Ready { + size: None, + bytes, + mime: None, + }), + None => Err(LoadError::NotSupported), + } + } + + fn forget(&self, uri: &str) { + #[cfg(feature = "log")] + log::trace!("forget {uri:?}"); + + let _ = self.cache.lock().remove(uri); + } + + fn forget_all(&self) { + #[cfg(feature = "log")] + log::trace!("forget all"); + + self.cache.lock().clear(); + } + + fn byte_size(&self) -> usize { + self.cache.lock().values().map(|bytes| bytes.len()).sum() + } +} diff --git a/crates/egui/src/load/texture_loader.rs b/crates/egui/src/load/texture_loader.rs new file mode 100644 index 00000000000..89d616e4760 --- /dev/null +++ b/crates/egui/src/load/texture_loader.rs @@ -0,0 +1,60 @@ +use super::*; + +#[derive(Default)] +pub struct DefaultTextureLoader { + cache: Mutex>, +} + +impl TextureLoader for DefaultTextureLoader { + fn id(&self) -> &str { + crate::generate_loader_id!(DefaultTextureLoader) + } + + fn load( + &self, + ctx: &Context, + uri: &str, + texture_options: TextureOptions, + size_hint: SizeHint, + ) -> TextureLoadResult { + let mut cache = self.cache.lock(); + if let Some(handle) = cache.get(&(uri.into(), texture_options)) { + let texture = SizedTexture::from_handle(handle); + Ok(TexturePoll::Ready { texture }) + } else { + match ctx.try_load_image(uri, size_hint)? { + ImagePoll::Pending { size } => Ok(TexturePoll::Pending { size }), + ImagePoll::Ready { image } => { + let handle = ctx.load_texture(uri, image, texture_options); + let texture = SizedTexture::from_handle(&handle); + cache.insert((uri.into(), texture_options), handle); + Ok(TexturePoll::Ready { texture }) + } + } + } + } + + fn forget(&self, uri: &str) { + #[cfg(feature = "log")] + log::trace!("forget {uri:?}"); + + self.cache.lock().retain(|(u, _), _| u != uri); + } + + fn forget_all(&self) { + #[cfg(feature = "log")] + log::trace!("forget all"); + + self.cache.lock().clear(); + } + + fn end_frame(&self, _: usize) {} + + fn byte_size(&self) -> usize { + self.cache + .lock() + .values() + .map(|texture| texture.byte_size()) + .sum() + } +} diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index 3334c9c430d..9f973a556b2 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -21,7 +21,7 @@ use crate::*; /// # }); /// ``` #[must_use = "You should put this widget in an ui with `ui.add(widget);`"] -pub struct Button { +pub struct Button<'a> { text: WidgetText, shortcut_text: WidgetText, wrap: Option, @@ -34,10 +34,10 @@ pub struct Button { frame: Option, min_size: Vec2, rounding: Option, - image: Option, + image: Option>, } -impl Button { +impl<'a> Button<'a> { pub fn new(text: impl Into) -> Self { Self { text: text.into(), @@ -57,15 +57,11 @@ impl Button { /// Creates a button with an image to the left of the text. The size of the image as displayed is defined by the provided size. #[allow(clippy::needless_pass_by_value)] pub fn image_and_text( - texture_id: TextureId, - image_size: impl Into, + image_source: impl Into>, text: impl Into, ) -> Self { Self { - image: Some(widgets::RawImage::new(SizedTexture { - id: texture_id, - size: image_size.into(), - })), + image: Some(Image::new(image_source)), ..Self::new(text) } } @@ -142,7 +138,7 @@ impl Button { } } -impl Widget for Button { +impl Widget for Button<'_> { fn ui(self, ui: &mut Ui) -> Response { let Button { text, @@ -158,6 +154,11 @@ impl Widget for Button { image, } = self; + let image_size = image + .as_ref() + .and_then(|image| image.size()) + .unwrap_or(Vec2::splat(0.0)); + let frame = frame.unwrap_or_else(|| ui.visuals().button_frame); let mut button_padding = ui.spacing().button_padding; @@ -166,8 +167,8 @@ impl Widget for Button { } let mut text_wrap_width = ui.available_width() - 2.0 * button_padding.x; - if let Some(image) = &image { - text_wrap_width -= image.size().x + ui.spacing().icon_spacing; + if image.is_some() { + text_wrap_width -= image_size.x + ui.spacing().icon_spacing; } if !shortcut_text.is_empty() { text_wrap_width -= 60.0; // Some space for the shortcut text (which we never wrap). @@ -178,9 +179,9 @@ impl Widget for Button { .then(|| shortcut_text.into_galley(ui, Some(false), f32::INFINITY, TextStyle::Button)); let mut desired_size = text.size(); - if let Some(image) = &image { - desired_size.x += image.size().x + ui.spacing().icon_spacing; - desired_size.y = desired_size.y.max(image.size().y); + if image.is_some() { + desired_size.x += image_size.x + ui.spacing().icon_spacing; + desired_size.y = desired_size.y.max(image_size.y); } if let Some(shortcut_text) = &shortcut_text { desired_size.x += ui.spacing().item_spacing.x + shortcut_text.size().x; @@ -206,10 +207,10 @@ impl Widget for Button { .rect(rect.expand(visuals.expansion), rounding, fill, stroke); } - let text_pos = if let Some(image) = &image { + let text_pos = if image.is_some() { let icon_spacing = ui.spacing().icon_spacing; pos2( - rect.min.x + button_padding.x + image.size().x + icon_spacing, + rect.min.x + button_padding.x + image_size.x + icon_spacing, rect.center().y - 0.5 * text.size().y, ) } else { @@ -235,11 +236,23 @@ impl Widget for Button { let image_rect = Rect::from_min_size( pos2( rect.min.x + button_padding.x, - rect.center().y - 0.5 - (image.size().y / 2.0), + rect.center().y - 0.5 - (image_size.y / 2.0), ), - image.size(), + image_size, ); - image.paint_at(ui, image_rect); + match image.load(ui) { + Ok(TexturePoll::Ready { texture }) => { + image.paint_at(ui, image_rect, &texture); + } + Ok(TexturePoll::Pending { .. }) => { + ui.add(Spinner::new().size(image_rect.size().min_elem())) + .on_hover_text(format!("Loading {:?}…", image.uri())); + } + Err(err) => { + ui.colored_label(ui.visuals().error_fg_color, "⚠") + .on_hover_text(err.to_string()); + } + }; } } diff --git a/crates/egui/src/widgets/image.rs b/crates/egui/src/widgets/image.rs index 687a1e208bb..4b516e3c0bb 100644 --- a/crates/egui/src/widgets/image.rs +++ b/crates/egui/src/widgets/image.rs @@ -17,8 +17,6 @@ use epaint::{util::FloatOrd, RectShape}; /// - [`ImageSource::Bytes`] will also load the image using the [asynchronous loading process][`load`], but with lower latency. /// - [`ImageSource::Texture`] will use the provided texture. /// -/// To use a texture you already have with a simpler API, consider using [`RawImage`]. -/// /// See [`load`] for more information. #[must_use = "You should put this widget in an ui with `ui.add(widget);`"] #[derive(Debug, Clone)] @@ -477,8 +475,6 @@ pub enum ImageSource<'a> { /// /// The user is responsible for loading the texture, determining its size, /// and allocating a [`TextureId`] for it. - /// - /// Note that a simpler API for this exists in [`RawImage`]. Texture(SizedTexture), /// Load the image from some raw bytes. @@ -556,113 +552,6 @@ impl> From for ImageSource<'static> { } } -/// A widget which displays a sized texture. -#[must_use = "You should put this widget in an ui with `ui.add(widget);`"] -#[derive(Debug, Clone)] -pub struct RawImage { - texture: SizedTexture, - texture_options: TextureOptions, - image_options: ImageOptions, - sense: Sense, -} - -impl RawImage { - /// Load the image from some source. - pub fn new(texture: impl Into) -> Self { - Self { - texture: texture.into(), - texture_options: Default::default(), - image_options: Default::default(), - sense: Sense::hover(), - } - } - - /// Texture options used when creating the texture. - #[inline] - pub fn texture_options(mut self, texture_options: TextureOptions) -> Self { - self.texture_options = texture_options; - self - } - - /// Make the image respond to clicks and/or drags. - #[inline] - pub fn sense(mut self, sense: Sense) -> Self { - self.sense = sense; - self - } - - /// Select UV range. Default is (0,0) in top-left, (1,1) bottom right. - pub fn uv(mut self, uv: impl Into) -> Self { - self.image_options.uv = uv.into(); - self - } - - /// A solid color to put behind the image. Useful for transparent images. - pub fn bg_fill(mut self, bg_fill: impl Into) -> Self { - self.image_options.bg_fill = bg_fill.into(); - self - } - - /// Multiply image color with this. Default is WHITE (no tint). - pub fn tint(mut self, tint: impl Into) -> Self { - self.image_options.tint = tint.into(); - self - } - - /// Rotate the image about an origin by some angle - /// - /// Positive angle is clockwise. - /// Origin is a vector in normalized UV space ((0,0) in top-left, (1,1) bottom right). - /// - /// To rotate about the center you can pass `Vec2::splat(0.5)` as the origin. - /// - /// Due to limitations in the current implementation, - /// this will turn off rounding of the image. - pub fn rotate(mut self, angle: f32, origin: Vec2) -> Self { - self.image_options.rotation = Some((Rot2::from_angle(angle), origin)); - self.image_options.rounding = Rounding::ZERO; // incompatible with rotation - self - } - - /// Round the corners of the image. - /// - /// The default is no rounding ([`Rounding::ZERO`]). - /// - /// Due to limitations in the current implementation, - /// this will turn off any rotation of the image. - pub fn rounding(mut self, rounding: impl Into) -> Self { - self.image_options.rounding = rounding.into(); - if self.image_options.rounding != Rounding::ZERO { - self.image_options.rotation = None; // incompatible with rounding - } - self - } -} - -impl RawImage { - /// Returns the [`TextureId`] of the texture from which this image was created. - pub fn texture_id(&self) -> TextureId { - self.texture.id - } - - /// Returns the size of the texture from which this image was created. - pub fn size(&self) -> Vec2 { - self.texture.size - } - - pub fn paint_at(&self, ui: &mut Ui, rect: Rect) { - paint_image_at(ui, rect, &self.image_options, &self.texture); - } -} - -impl Widget for RawImage { - fn ui(self, ui: &mut Ui) -> Response { - let (rect, response) = ui.allocate_exact_size(self.size(), self.sense); - self.paint_at(ui, rect); - response - } -} - #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct ImageOptions { diff --git a/crates/egui/src/widgets/mod.rs b/crates/egui/src/widgets/mod.rs index 708fcd84304..875a8599c43 100644 --- a/crates/egui/src/widgets/mod.rs +++ b/crates/egui/src/widgets/mod.rs @@ -22,7 +22,7 @@ pub mod text_edit; pub use button::*; pub use drag_value::DragValue; pub use hyperlink::*; -pub use image::{Image, ImageFit, ImageOptions, ImageSize, ImageSource, RawImage}; +pub use image::{Image, ImageFit, ImageOptions, ImageSize, ImageSource}; pub use label::*; pub use progress_bar::ProgressBar; pub use selected_label::SelectableLabel; diff --git a/crates/egui_demo_app/Cargo.toml b/crates/egui_demo_app/Cargo.toml index 989539619a6..c3fec7453e3 100644 --- a/crates/egui_demo_app/Cargo.toml +++ b/crates/egui_demo_app/Cargo.toml @@ -36,6 +36,7 @@ chrono = { version = "0.4", default-features = false, features = [ eframe = { version = "0.22.0", path = "../eframe", default-features = false } egui = { version = "0.22.0", path = "../egui", features = [ "extra_debug_asserts", + "log", ] } egui_demo_lib = { version = "0.22.0", path = "../egui_demo_lib", features = [ "chrono", diff --git a/crates/egui_glium/examples/native_texture.rs b/crates/egui_glium/examples/native_texture.rs index 94977cd6176..eb5a956f464 100644 --- a/crates/egui_glium/examples/native_texture.rs +++ b/crates/egui_glium/examples/native_texture.rs @@ -30,8 +30,7 @@ fn main() { egui::SidePanel::left("my_side_panel").show(egui_ctx, |ui| { if ui .add(egui::Button::image_and_text( - texture_id, - button_image_size, + (texture_id, button_image_size), "Quit", )) .clicked()