1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 22:30:03 -04:00

Merge remote-tracking branch 'egui/master' into dynamic-grid

This commit is contained in:
René Rössler
2022-02-27 13:30:29 +01:00
107 changed files with 4529 additions and 3858 deletions

9
egui_extras/CHANGELOG.md Normal file
View File

@@ -0,0 +1,9 @@
# Changelog for egui_extras
All notable changes to the `egui_extras` integration will be noted in this file.
## Unreleased
## 0.17.0 - 2022-02-22
* `RetainedImage`: conventience for loading svg, png, jpeg etc and showing them in egui.

View File

@@ -1,29 +1,60 @@
[package]
name = "egui_extras"
version = "0.16.0"
version = "0.17.0"
authors = [
"Dominik Rössler <dominik@freshx.de>",
"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>",
"René Rössler <rene@freshx.de>",
]
description = "Extra functionality and widgets for the egui GUI library"
edition = "2021"
rust-version = "1.56"
description = "Extra widgets for egui"
authors = [
"René Rössler <rene@freshx.de>",
"Dominik Rössler <dominik@freshx.de>",
]
homepage = "https://github.com/emilk/egui"
license = "MIT OR Apache-2.0"
homepage = "https://github.com/emilk/egui/tree/master/egui_extras"
readme = "README.md"
repository = "https://github.com/emilk/egui/tree/master/egui_extras"
categories = ["gui", "graphics"]
keywords = ["egui", "gui", "gamedev"]
readme = "../README.md"
repository = "https://github.com/emilk/egui"
categories = ["gui", "game-development"]
keywords = ["gui", "imgui", "immediate", "portable", "gamedev"]
include = ["../LICENSE-APACHE", "../LICENSE-MIT", "**/*.rs", "Cargo.toml"]
[dependencies]
egui = { version = "0.16.0", path = "../egui", default-features = false }
[package.metadata.docs.rs]
all-features = true
[lib]
[features]
default = []
# Support loading svg images
svg = ["resvg", "tiny-skia", "usvg"]
# Datepicker widget
datepicker = ["chrono"]
# Persistence
persistence = ["serde"]
[dependencies]
egui = { version = "0.17.0", path = "../egui", default-features = false, features = [
"single_threaded",
] }
parking_lot = "0.12"
# Optional dependencies:
# Date operations needed for datepicker widget
chrono = { version = "0.4", optional = true }
# Add support for loading images with the `image` crate.
# You also need to ALSO opt-in to the image formats you want to support, like so:
# image = { version = "0.24", features = ["jpeg", "png"] }
image = { version = "0.24", optional = true, default-features = false, features = [] }
# svg feature
resvg = { version = "0.22", optional = true }
tiny-skia = { version = "0.6", optional = true }
usvg = { version = "0.22", optional = true }
# feature "persistence":
serde = { version = "1", features = ["derive"], optional = true }
[features]
default = ["chrono"]
persistence = ["serde"]

8
egui_extras/README.md Normal file
View File

@@ -0,0 +1,8 @@
# egui_extras
[![Latest version](https://img.shields.io/crates/v/egui_extras.svg)](https://crates.io/crates/egui_extras)
[![Documentation](https://docs.rs/egui_extras/badge.svg)](https://docs.rs/egui_extras)
![MIT](https://img.shields.io/badge/license-MIT-blue.svg)
![Apache](https://img.shields.io/badge/license-Apache-blue.svg)
This is a crate that adds some features on top top of [`egui`](https://github.com/emilk/egui). This crate are for experimental features, and features that require big dependencies that does not belong in `egui`.

177
egui_extras/src/image.rs Normal file
View File

@@ -0,0 +1,177 @@
use parking_lot::Mutex;
/// An image to be shown in egui.
///
/// Load once, and save somewhere in your app state.
///
/// Use the `svg` and `image` features to enable more constructors.
pub struct RetainedImage {
debug_name: String,
size: [usize; 2],
/// Cleared once [`Self::texture`] has been loaded.
image: Mutex<egui::ColorImage>,
/// Lazily loaded when we have an egui context.
texture: Mutex<Option<egui::TextureHandle>>,
}
impl RetainedImage {
pub fn from_color_image(debug_name: impl Into<String>, image: ColorImage) -> Self {
Self {
debug_name: debug_name.into(),
size: image.size,
image: Mutex::new(image),
texture: Default::default(),
}
}
/// Load a (non-svg) image.
///
/// Requires the "image" feature. You must also opt-in to the image formats you need
/// with e.g. `image = { version = "0.24", features = ["jpeg", "png"] }`.
///
/// # Errors
/// On invalid image or unsupported image format.
#[cfg(feature = "image")]
pub fn from_image_bytes(
debug_name: impl Into<String>,
image_bytes: &[u8],
) -> Result<Self, String> {
Ok(Self::from_color_image(
debug_name,
load_image_bytes(image_bytes)?,
))
}
/// Pass in the bytes of an SVG that you've loaded.
///
/// # Errors
/// On invalid image
#[cfg(feature = "svg")]
pub fn from_svg_bytes(debug_name: impl Into<String>, svg_bytes: &[u8]) -> Result<Self, String> {
Ok(Self::from_color_image(
debug_name,
load_svg_bytes(svg_bytes)?,
))
}
/// Pass in the str of an SVG that you've loaded.
///
/// # Errors
/// On invalid image
#[cfg(feature = "svg")]
pub fn from_svg_str(debug_name: impl Into<String>, svg_str: &str) -> Result<Self, String> {
Self::from_svg_bytes(debug_name, svg_str.as_bytes())
}
/// The size of the image data (number of pixels wide/high).
pub fn size(&self) -> [usize; 2] {
self.size
}
/// The size of the image data (number of pixels wide/high).
pub fn size_vec2(&self) -> egui::Vec2 {
let [w, h] = self.size();
egui::vec2(w as f32, h as f32)
}
/// The debug name of the image, e.g. the file name.
pub fn debug_name(&self) -> &str {
&self.debug_name
}
/// The texture if for this image.
pub fn texture_id(&self, ctx: &egui::Context) -> egui::TextureId {
self.texture
.lock()
.get_or_insert_with(|| {
let image: &mut ColorImage = &mut self.image.lock();
let image = std::mem::take(image);
ctx.load_texture(&self.debug_name, image)
})
.id()
}
/// Show the image with the given maximum size.
pub fn show_max_size(&self, ui: &mut egui::Ui, max_size: egui::Vec2) -> egui::Response {
let mut desired_size = self.size_vec2();
desired_size *= (max_size.x / desired_size.x).min(1.0);
desired_size *= (max_size.y / desired_size.y).min(1.0);
self.show_size(ui, desired_size)
}
/// Show the image with the original size (one image pixel = one gui point).
pub fn show(&self, ui: &mut egui::Ui) -> egui::Response {
self.show_size(ui, self.size_vec2())
}
/// Show the image with the given scale factor (1.0 = original size).
pub fn show_scaled(&self, ui: &mut egui::Ui, scale: f32) -> egui::Response {
self.show_size(ui, self.size_vec2() * scale)
}
/// Show the image with the given size.
pub fn show_size(&self, ui: &mut egui::Ui, desired_size: egui::Vec2) -> egui::Response {
// We need to convert the SVG to a texture to display it:
// Future improvement: tell backend to do mip-mapping of the image to
// make it look smoother when downsized.
ui.image(self.texture_id(ui.ctx()), desired_size)
}
}
// ----------------------------------------------------------------------------
use egui::ColorImage;
/// Load a (non-svg) image.
///
/// Requires the "image" feature. You must also opt-in to the image formats you need
/// with e.g. `image = { version = "0.24", features = ["jpeg", "png"] }`.
///
/// # Errors
/// On invalid image or unsupported image format.
#[cfg(feature = "image")]
pub fn load_image_bytes(image_bytes: &[u8]) -> Result<egui::ColorImage, String> {
let image = image::load_from_memory(image_bytes).map_err(|err| err.to_string())?;
let size = [image.width() as _, image.height() as _];
let image_buffer = image.to_rgba8();
let pixels = image_buffer.as_flat_samples();
Ok(egui::ColorImage::from_rgba_unmultiplied(
size,
pixels.as_slice(),
))
}
/// Load an SVG and rasterize it into an egui image.
///
/// Requires the "svg" feature.
///
/// # Errors
/// On invalid image
#[cfg(feature = "svg")]
pub fn load_svg_bytes(svg_bytes: &[u8]) -> Result<egui::ColorImage, String> {
let mut opt = usvg::Options::default();
opt.fontdb.load_system_fonts();
let rtree = usvg::Tree::from_data(svg_bytes, &opt.to_ref()).map_err(|err| err.to_string())?;
let pixmap_size = rtree.svg_node().size.to_screen_size();
let [w, h] = [pixmap_size.width(), pixmap_size.height()];
let mut pixmap = tiny_skia::Pixmap::new(w, h)
.ok_or_else(|| format!("Failed to create SVG Pixmap of size {}x{}", w, h))?;
resvg::render(
&rtree,
usvg::FitTo::Original,
tiny_skia::Transform::default(),
pixmap.as_mut(),
)
.ok_or_else(|| "Failed to render SVG".to_owned())?;
let image = egui::ColorImage::from_rgba_unmultiplied(
[pixmap.width() as _, pixmap.height() as _],
pixmap.data(),
);
Ok(image)
}

View File

@@ -1,5 +1,7 @@
//! `egui_extras`: Widgets for egui which are not in the main egui crate
//! This is a crate that adds some features on top top of [`egui`](https://github.com/emilk/egui). This crate are for experimental features, and features that require big dependencies that does not belong in `egui`.
// Forbid warnings in release builds:
#![cfg_attr(not(debug_assertions), deny(warnings))]
#![forbid(unsafe_code)]
#![warn(
clippy::all,
@@ -84,14 +86,16 @@
mod datepicker;
mod grid;
pub mod image;
mod layout;
mod sizing;
mod table;
#[cfg(feature = "chrono")]
pub use datepicker::DatePickerButton;
pub use crate::datepicker::DatePickerButton;
pub use grid::*;
pub(crate) use layout::Layout;
pub use sizing::Size;
pub use table::*;
pub use crate::grid::*;
pub use crate::image::RetainedImage;
pub(crate) use crate::layout::Layout;
pub use crate::sizing::Size;
pub use crate::table::*;