mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -04:00
Final polish for new image loading (#3328)
* add egui logo to widget gallery * improve "no image loaders" error message * rework static URIs to accept `Cow<'static>` * remove `RetainedImage` from `http_app` in `egui_demo_app` * hide `RetainedImage` from docs * use `ui.image`/`Image` over `RawImage` * remove last remanant of `RawImage` * remove unused doc link * add style option to disable image spinners * use `Into<Image>` instead of `Into<ImageSource>` to allow configuring the underlying image * propagate `image_options` through `ImageButton` * calculate image size properly in `Button` * properly calculate size in `ImageButton` * Update crates/egui/src/widgets/image.rs Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * improve no image loaders error message * add `size()` helper to `TexturePoll` * try get size from poll in `Button` * add `paint_at` to `Spinner` * use `Spinner::paint_at` and hover on image button response * `show_spinner` -> `show_loading_spinner` * avoid `allocate_ui` in `Image` when painting spinner * make icon smaller + remove old texture * add `load_and_calculate_size` + expose `paint_image_at` * update `egui_plot` to paint image in the right place * Add helpers for painting an ImageSource directly * Use max_size=INF as default * Use new API in WidgetGallery * Make egui_demo_app work by default * Remove Option from scale * Refactor ImageSize * Fix docstring * Small refactor --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
@@ -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",
|
||||
@@ -45,8 +46,9 @@ log = { version = "0.4", features = ["std"] }
|
||||
# Optional dependencies:
|
||||
|
||||
bytemuck = { version = "1.7.1", optional = true }
|
||||
egui_extras = { version = "0.22.0", optional = true, path = "../egui_extras", features = [
|
||||
egui_extras = { version = "0.22.0", path = "../egui_extras", features = [
|
||||
"log",
|
||||
"image",
|
||||
] }
|
||||
rfd = { version = "0.11", optional = true }
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use egui_extras::RetainedImage;
|
||||
use egui::Image;
|
||||
use poll_promise::Promise;
|
||||
|
||||
struct Resource {
|
||||
@@ -10,7 +8,7 @@ struct Resource {
|
||||
text: Option<String>,
|
||||
|
||||
/// If set, the response was an image.
|
||||
image: Option<RetainedImage>,
|
||||
image: Option<Image<'static>>,
|
||||
|
||||
/// If set, the response was text with some supported syntax highlighting (e.g. ".rs" or ".md").
|
||||
colored_text: Option<ColoredText>,
|
||||
@@ -19,21 +17,27 @@ struct Resource {
|
||||
impl Resource {
|
||||
fn from_response(ctx: &egui::Context, response: ehttp::Response) -> Self {
|
||||
let content_type = response.content_type().unwrap_or_default();
|
||||
let image = if content_type.starts_with("image/") {
|
||||
RetainedImage::from_image_bytes(&response.url, &response.bytes).ok()
|
||||
if content_type.starts_with("image/") {
|
||||
ctx.include_bytes(response.url.clone(), response.bytes.clone());
|
||||
let image = Image::from_uri(response.url.clone());
|
||||
|
||||
Self {
|
||||
response,
|
||||
text: None,
|
||||
colored_text: None,
|
||||
image: Some(image),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let text = response.text();
|
||||
let colored_text = text.and_then(|text| syntax_highlighting(ctx, &response, text));
|
||||
let text = text.map(|text| text.to_owned());
|
||||
|
||||
let text = response.text();
|
||||
let colored_text = text.and_then(|text| syntax_highlighting(ctx, &response, text));
|
||||
let text = text.map(|text| text.to_owned());
|
||||
|
||||
Self {
|
||||
response,
|
||||
text,
|
||||
image,
|
||||
colored_text,
|
||||
Self {
|
||||
response,
|
||||
text,
|
||||
colored_text,
|
||||
image: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,6 +69,7 @@ impl eframe::App for HttpApp {
|
||||
});
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
let prev_url = self.url.clone();
|
||||
let trigger_fetch = ui_url(ui, frame, &mut self.url);
|
||||
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
@@ -79,6 +84,7 @@ impl eframe::App for HttpApp {
|
||||
let (sender, promise) = Promise::new();
|
||||
let request = ehttp::Request::get(&self.url);
|
||||
ehttp::fetch(request, move |response| {
|
||||
ctx.forget_image(&prev_url);
|
||||
ctx.request_repaint(); // wake up UI thread
|
||||
let resource = response.map(|response| Resource::from_response(&ctx, response));
|
||||
sender.send(resource);
|
||||
@@ -195,9 +201,7 @@ fn ui_resource(ui: &mut egui::Ui, resource: &Resource) {
|
||||
}
|
||||
|
||||
if let Some(image) = image {
|
||||
let mut size = image.size_vec2();
|
||||
size *= (ui.available_width() / size.x).min(1.0);
|
||||
image.show_size(ui, size);
|
||||
ui.add(image.clone());
|
||||
} else if let Some(colored_text) = colored_text {
|
||||
colored_text.ui(ui);
|
||||
} else if let Some(text) = &text {
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct ImageViewer {
|
||||
chosen_fit: ChosenFit,
|
||||
fit: ImageFit,
|
||||
maintain_aspect_ratio: bool,
|
||||
max_size: Option<Vec2>,
|
||||
max_size: Vec2,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
@@ -43,7 +43,7 @@ impl Default for ImageViewer {
|
||||
chosen_fit: ChosenFit::Fraction,
|
||||
fit: ImageFit::Fraction(Vec2::splat(1.0)),
|
||||
maintain_aspect_ratio: true,
|
||||
max_size: None,
|
||||
max_size: Vec2::splat(2048.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,10 +160,10 @@ impl eframe::App for ImageViewer {
|
||||
ui.add(Slider::new(&mut fract.y, 0.0..=1.0).text("height"));
|
||||
}
|
||||
ChosenFit::OriginalSize => {
|
||||
if !matches!(self.fit, ImageFit::Original(_)) {
|
||||
self.fit = ImageFit::Original(Some(1.0));
|
||||
if !matches!(self.fit, ImageFit::Original { .. }) {
|
||||
self.fit = ImageFit::Original { scale: 1.0 };
|
||||
}
|
||||
let ImageFit::Original(Some(scale)) = &mut self.fit else {
|
||||
let ImageFit::Original{scale} = &mut self.fit else {
|
||||
unreachable!()
|
||||
};
|
||||
ui.add(Slider::new(scale, 0.1..=4.0).text("scale"));
|
||||
@@ -173,21 +173,8 @@ impl eframe::App for ImageViewer {
|
||||
// max size
|
||||
ui.add_space(5.0);
|
||||
ui.label("The calculated size will not exceed the maximum size");
|
||||
let had_max_size = self.max_size.is_some();
|
||||
let mut has_max_size = had_max_size;
|
||||
ui.checkbox(&mut has_max_size, "Max size");
|
||||
match (had_max_size, has_max_size) {
|
||||
(true, false) => self.max_size = None,
|
||||
(false, true) => {
|
||||
self.max_size = Some(ui.available_size());
|
||||
}
|
||||
(true, true) | (false, false) => {}
|
||||
}
|
||||
|
||||
if let Some(max_size) = self.max_size.as_mut() {
|
||||
ui.add(Slider::new(&mut max_size.x, 0.0..=2048.0).text("width"));
|
||||
ui.add(Slider::new(&mut max_size.y, 0.0..=2048.0).text("height"));
|
||||
}
|
||||
ui.add(Slider::new(&mut self.max_size.x, 0.0..=2048.0).text("width"));
|
||||
ui.add(Slider::new(&mut self.max_size.y, 0.0..=2048.0).text("height"));
|
||||
|
||||
// aspect ratio
|
||||
ui.add_space(5.0);
|
||||
@@ -209,7 +196,7 @@ impl eframe::App for ImageViewer {
|
||||
});
|
||||
image = image.rotate(angle, origin);
|
||||
match self.fit {
|
||||
ImageFit::Original(scale) => image = image.fit_to_original_size(scale),
|
||||
ImageFit::Original { scale } => image = image.fit_to_original_size(scale),
|
||||
ImageFit::Fraction(fract) => image = image.fit_to_fraction(fract),
|
||||
ImageFit::Exact(size) => image = image.fit_to_exact_size(size),
|
||||
}
|
||||
|
||||
@@ -165,7 +165,6 @@ pub struct WrapApp {
|
||||
|
||||
impl WrapApp {
|
||||
pub fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
||||
#[cfg(feature = "image_viewer")]
|
||||
egui_extras::loaders::install(&_cc.egui_ctx);
|
||||
|
||||
#[allow(unused_mut)]
|
||||
|
||||
Reference in New Issue
Block a user