1
0
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:
Jan Procházka
2023-09-13 16:27:08 +02:00
committed by GitHub
parent fc3bddd0cf
commit 67a3fcae38
25 changed files with 536 additions and 503 deletions

View File

@@ -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 }

View File

@@ -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 {

View File

@@ -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),
}

View File

@@ -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)]