Download Latest Version 0.32.0 - Atoms, popups, and better SVG support source code.tar.gz (2.5 MB)
Email in envelope

Get an email when there's a new version of egui

Home / 0.32.0
Name Modified Size InfoDownloads / Week
Parent folder
0.32.0 - Atoms, popups, and better SVG support source code.tar.gz 2025-07-10 2.5 MB
0.32.0 - Atoms, popups, and better SVG support source code.zip 2025-07-10 2.8 MB
README.md 2025-07-10 24.5 kB
Totals: 3 Items   5.4 MB 2

egui is an easy-to-use immediate mode GUI for Rust that runs on both web and native.

Try it now: https://www.egui.rs/

egui development is sponsored by Rerun, a startup building an SDK for visualizing streams of multimodal data.

egui 0.32.0 changelog

This is a big egui release, with several exciting new features!

  • Atoms are new layout primitives in egui, for text and images
  • Popups, tooltips and menus have undergone a complete rewrite
  • Much improved SVG support
  • Crisper graphics (especially text!)

Let's dive in!

⚛️ Atoms

egui::Atom is the new, indivisible building block of egui (hence the name). It lets you mix images and text in many places where you would previously only be able to add text.

Atoms is the first step towards a more powerful layout engine in egui - more to come!

Right now an Atom is an enum that can be either WidgetText, Image, or Custom.

The new AtomLayout can be used within widgets to do basic layout. The initial implementation is as minimal as possible, doing just enough to implement what Button could do before. There is a new IntoAtoms trait that works with tuples of Atoms. Each atom can be customized with the AtomExt trait which works on everything that implements Into<Atom>, so e.g. RichText or Image. So to create a Button with text and image you can now do:

:::rs
let image = include_image!("my_icon.png").atom_size(Vec2::splat(12.0));
ui.button((image, "Click me!"));

Anywhere you see impl IntoAtoms you can add any number of images and text, in any order.

As of 0.32, we have ported the Button, Checkbox, RadioButton to use atoms (meaning they support adding Atoms and are built on top of AtomLayout). The Button implementation is not only more powerful now, but also much simpler, removing ~130 lines of layout math.

In combination with ui.read_response, custom widgets are really simple now, here is a minimal button implementation:

:::rs
pub struct ALButton<'a> {
    al: AtomLayout<'a>,
}

impl<'a> ALButton<'a> {
    pub fn new(content: impl IntoAtoms<'a>) -> Self {
        Self {
            al: AtomLayout::new(content.into_atoms()).sense(Sense::click()),
        }
    }
}

impl<'a> Widget for ALButton<'a> {
    fn ui(mut self, ui: &mut Ui) -> Response {
        let Self { al } = self;
        let response = ui.ctx().read_response(ui.next_auto_id());

        let visuals = response.map_or(&ui.style().visuals.widgets.inactive, |response| {
            ui.style().interact(&response)
        });

        let al = al.frame(
            Frame::new()
                .inner_margin(ui.style().spacing.button_padding)
                .fill(visuals.bg_fill)
                .stroke(visuals.bg_stroke)
                .corner_radius(visuals.corner_radius),
        );

        al.show(ui).response
    }
}

You can even use Atom::custom to add custom content to Widgets. Here is a button in a button:

https://github.com/user-attachments/assets/8c649784-dcc5-4979-85f8-e735b9cdd090

:::rs
let custom_button_id = Id::new("custom_button");
let response = Button::new((
    Atom::custom(custom_button_id, Vec2::splat(18.0)),
    "Look at my mini button!",
))
.atom_ui(ui);
if let Some(rect) = response.rect(custom_button_id) {
    ui.put(rect, Button::new("🔎").frame_when_inactive(false));
}

Currently, you need to use atom_ui to get a AtomResponse which will have the Rect to use, but in the future this could be streamlined, e.g. by adding a AtomKind::Callback or by passing the Rects back with egui::Response.

Basing our widgets on AtomLayout also allowed us to improve Response::intrinsic_size, which will now report the correct size even if widgets are truncated. intrinsic_size is the size that a non-wrapped, non-truncated, non-justified version of the widget would have, and can be useful in advanced layout calculations like egui_flex.

Details

❕ Improved popups, tooltips, and menus

Introduces a new egui::Popup api. Checkout the new demo on https://egui.rs:

https://github.com/user-attachments/assets/74e45243-7d05-4fc3-b446-2387e1412c05

We introduced a new RectAlign helper to align a rect relative to an other rect. The Popup will by default try to find the best RectAlign based on the source widgets position (previously submenus would annoyingly overlap if at the edge of the window):

https://github.com/user-attachments/assets/0c5adb6b-8310-4e0a-b936-646bb4ec02f7

Tooltip and menu have been rewritten based on the new Popup api. They are now compatible with each other, meaning you can just show a ui.menu_button() in any Popup to get a sub menu. There are now customizable MenuButton and SubMenuButton structs, to help with customizing your menu buttons. This means menus now also support PopupCloseBehavior so you can remove your close_menu calls from your click handlers!

The old tooltip and popup apis have been ported to the new api so there should be very little breaking changes. The old menu is still around but deprecated. ui.menu_button etc now open the new menu, if you can't update to the new one immediately you can use the old buttons from the deprecated egui::menu menu.

We also introduced ui.close() which closes the nearest container. So you can now conveniently close Windows, Collapsibles, Modals and Popups from within. To use this for your own containers, call UiBuilder::closable and then check for closing within that ui via ui.should_close().

Details

▲ Improved SVG support

You can render SVG in egui with

:::rs
ui.add(egui::Image::new(egui::include_image!("icon.svg"));

(Requires the use of egui_extras, with the svg feature enabled and a call to install_image_loaders).

Previously this would sometimes result in a blurry SVG, epecially if the Image was set to be dynamically scale based on the size of the Ui that contained it. Now SVG:s are always pixel-perfect, for truly scalable graphics.

svg-scaling

Details

✨ Crisper graphics

Non-SVG icons are also rendered better, and text sharpness has been improved, especially in light mode.

image

Details
  • Improve text sharpness #5838 by @emilk
  • Improve text rendering in light mode #7290 by @emilk
  • Improve texture filtering by doing it in gamma space #7311 by @emilk
  • Make text underline and strikethrough pixel perfect crisp #5857 by @emilk

Migration guide

We have some silently breaking changes (code compiles fine but behavior changed) that require special care:

wgpu backend features

  • wgpu 25 made the gles and vulkan backends optional
  • We missed this, so for now you need to manually opt in to those backends. Add the following to you Cargo.toml wgpu = "25" # enables the wgpu default features so we get the default backends
  • Previously menus would only close on click outside
  • Either
    • Remove the ui.close_menu() calls from button click handlers since they are obsolete
    • If the menu should stay open on clicks, change the PopupCloseBehavior: rs // Change this ui.menu_button("Text", |ui| { /* Menu Content */ }); // To this: MenuButton::new("Text").config( MenuConfig::default().close_behavior(PopupCloseBehavior::CloseOnClickOutside), ).ui(ui, |ui| { /* Menu Content */ }); You can also change the behavior only for a single SubMenu by using SubMenuButton, but by default it should be passed to any submenus when using MenuButton.

Memory::is_popup_open api now requires calls to Memory::keep_popup_open

  • The popup will immediately close if keep_popup_open is not called.
  • It's recommended to use the new Popup api which handles this for you.
  • If you can't switch to the new api for some reason, update the code to call keep_popup_open: rs if ui.memory(|mem| mem.is_popup_open(popup_id)) { ui.memory_mut(|mem| mem.keep_popup_open(popup_id)); // <- add this line let area_response = Area::new(popup_id).show(...) }

⭐ Other improvements

🔧 Changed

🔥 Removed

🐛 Fixed

🚀 Performance

eframe 0.32.0 changelog

⭐ Added

  • Add pointer events and focus handling for apps run in a Shadow DOM #5627 by @xxvvii
  • MacOS: Add movable_by_window_background option to viewport #5412 by @jim-ec
  • Add macOS-specific has_shadow and with_has_shadow to ViewportBuilder #6850 by @gaelanmcmillan
  • Add external eventloop support #6750 by @wpbrown

🔧 Changed

🐛 Fixed

  • Mark all keys as up if the app loses focus #5743 by @emilk
  • Fix text input on Android #5759 by @StratusFearMe21
  • Fix text distortion on mobile devices/browsers with glow backend #6893 by @wareya
  • Workaround libpng crash on macOS by not creating NSImage from png data #7252 by @Wumpf
  • Fix incorrect window sizes for non-resizable windows on Wayland #7103 by @GoldsteinE
  • Web: only consume copy/cut events if the canvas has focus #7270 by @emilk
Source: README.md, updated 2025-07-10