This is a bit like my previous pr
(<https://github.com/emilk/egui/pull/8037>), but addresses the final bit
of 64 bit atomic dependent code, the svg loader in the `egui_extras`
crate.
As with the previous pr, this improves egui's compatibility on platforms
without 64 bit atomics.
* Closes <https://github.com/emilk/egui/issues/7692>
* [x] I have followed the instructions in the PR template
Less risk of confusing the two.
Found and fix a couple real bugs in the process!
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
### Problem
`Column::remainder().clip(true)` grows correctly when the panel is made
wider, but does not shrink when the panel is made narrower.
### Root cause
There are two places in `table.rs` where the `max_used_widths` floor is
applied to column widths. One correctly checks `column.clip`, the other
doesn't.
**Post-render** path (already correct, line ~827):
```rust
if !column.clip {
*column_width = column_width.at_least(max_used_widths[i]);
}
```
`TableState::load` **pre-render path** (the bug, line 647):
```rust
.at_least(column.width_range.min.max(*max_used)) // clip flag ignored
```
In `TableState::load`, `.at_least(max_used)` is applied to every column
regardless of `clip`. For a `Column::remainder()`, `max_used`
accumulates the historically widest rendered content. When the panel
shrinks, this floor prevents the column from computing a smaller width,
creating a cycle where it can never shrink.
### Fix
Apply the same `clip` guard that already exists in the post-render path:
```rust
.at_least(if column.clip {
column.width_range.min
} else {
column.width_range.min.max(*max_used)
})
```
When `clip = true`, the floor is just `width_range.min` (0.0 by
default), allowing the remainder column to shrink freely to the actual
remaining space.
### Minimal reproduction
<details>
<summary>Show code</summary>
```rust
use eframe::egui;
use egui_extras::{Column, TableBuilder};
fn main() -> eframe::Result {
eframe::run_ui_native(
"Column::remainder().clip(true) shrink test",
Default::default(),
|ui, _frame| {
egui::Frame::central_panel(ui.style()).show(ui, |ui| {
let mut text = String::from("some content");
egui::ScrollArea::horizontal().show(ui, |ui| {
TableBuilder::new(ui)
.column(Column::auto())
.column(Column::remainder().clip(true))
.header(20.0, |mut header| {
header.col(|ui| { ui.strong("Auto"); });
header.col(|ui| { ui.strong("Remainder + clip"); });
})
.body(|mut body| {
body.row(18.0, |mut row| {
row.col(|ui| { ui.label("label"); });
row.col(|ui| {
ui.add(
egui::TextEdit::singleline(&mut text)
.desired_width(f32::INFINITY),
);
});
});
});
});
});
},
)
}
```
</details>
**Without the fix:** make the window wider (column grows ✓), then
narrower — column does not shrink and a horizontal scrollbar appears ✗
https://github.com/user-attachments/assets/2b586588-9f72-4a15-80f4-afddadb69441
**With the fix:** the column shrinks correctly and no scrollbar appears
✓
https://github.com/user-attachments/assets/f1655641-e135-489c-9f59-2af3faa887ab
* Closes <https://github.com/emilk/egui/issues/4491>
* [x] I have followed the instructions in the PR template
Lets you create an `EhttpLoader` with arbitrary headers like so:
```rust
cc.egui_ctx.add_bytes_loader(std::sync::Arc::new(
egui_extras::loaders::http_loader::EhttpLoader::default().with_headers(&[
("User-Agent", "foo"),
])
));
```
I'm not sure if there are any problems with installing a second
`EhttpLoader` (in addition to the one installed by default when using
`egui_extras::install_image_loaders(&cc.egui_ctx)`. But I wasn't
sure how else to pass in configuration options to
`install_image_loaders`.
Current behavior fails when translating file uris that contain windows
UNC paths. This commit attempts to fix that behavior by looking at the
hostname attribute of the uri and changing behavior if the hostname is
present.
* Closes <https://github.com/emilk/egui/issues/8161>
* [x] I have followed the instructions in the PR template
## Related
* https://github.com/emilk/egui/pull/5851
* https://github.com/emilk/egui/pull/7988
## What
We want to make it easier to understand the lineage of a `Id` (which is
the parent `Id`, and the parent of that, etc?)
As a first step of that, we want to clarify the different between a
globally unique `Id`, and an `IdSalt`
I also introduced the `AsId` and `AsIdSalt` traits, which are
implemented of anything that implements `Hash` and `Debug`. The `Debug`
half of that is unused here, but will later be used in `Debug` builds to
produce a proper tree.
* Part of https://github.com/emilk/egui/issues/8180
Drag-to-scroll is a must-have on touch-screens, since there is no other
way to scroll.
However, when you are not on a touch screens, it is more surprising than
useful.
Enable these new clippy lints and fix all warnings:
* `format_push_string` — use `write!` instead of `s += &format!(…)` to
avoid extra allocations
* `ignored_unit_patterns` — use `()` instead of `_` when matching unit
* `missing_fields_in_debug` — ensure manual `Debug` impls account for
all fields
* `needless_raw_string_hashes` — remove unnecessary `r#` on string
literals
* `ref_option` — prefer `Option<&T>` over `&Option<T>` in function
signatures
This PR fundamentally solves the same problem as
https://github.com/emilk/egui/pull/5827 just implemented with a lot less
ambition and api surface on my end. It contains the bare minimum amount
of changes that I need in order to be able to solve my problem.
My Problem:
I am still suffering from the problem that the TypeIdMap blows up over a
very long time when using my application. (The user generally never
turns off the application, it is intended to be just kept running
forever, some users also never restart their computers) My application
generates a lot of content dynamically so it may for some time display
widgets with a certain set of TypeId's + Id's later hiding them. Some of
the elements that were hidden may turn visible again once an external
event occurs, some may forever be discarded.
I do know myself when which sections of the UI have to be purged because
they will never become visible again, so this PR contains the minimum
amount of necessary functions that allow me to implement this
housekeeping logic on my end.
The existing facilities are insufficient to handle this as the type T
which the TypeId and the hash is derived from are sometimes pub(crate)
privates of widget subcrates or even pub(crate) of egui internals
itself, so its impossible to manually remove those from the TypeIdMap
the only build-in method to remove them is to call "clear" on the
TypeIdMap, however that gets rid of everything, even the elements that
are still shown and should still be in the TypeIdMap.
If the changes in this PR are agreeable to you and you want me to write
unit test for the 4 functions that I have added then tell me and I will
write the tests for you.
If you need anything else changed please tell me.
I ran cargo clippy and cargo fmt, but your check.sh does not work on my
computer.
---------
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
## What
Two new builder methods on `DatePickerButton`:
- **`reverse_years(bool)`** — lists years in descending order (newest
first). Useful when users are more likely to pick recent years.
- **`year_scroll_to(i32)`** — scrolls the year dropdown to a specific
year when it first opens, centred in the list. Defaults to the currently
selected year, so the picker no longer opens at the top of a 110-item
list.
## Why
The year `ComboBox` currently always renders in ascending order and
opens scrolled to the top. For a range spanning e.g. 1925–2035, the
current year is buried near the bottom. Users have to scroll past ~100
entries every time they open the picker.
## Notes
- Both options are purely additive builder methods — no breaking
changes.
- `year_scroll_needed` is a persisted state flag that is set on popup
open and cleared after the first scroll, so the user can freely scroll
the list after that.
- Existing behaviour is unchanged when neither method is called.
Co-authored-by: Simon Deurell <simon@deurell.se>
This is a breaking change.
- Enables using `FrameCache` in cases where the cached value cannot be
cloned.
- Improves use cases where only a reference to the cached value is
needed.
- If the user needs an owned value, they can clone it themselves.
Adding a `get_ref` method instead of changing `get` would avoid the
breaking change, but I didn't want to do so because it is kind of
expected for `get` to return `&V` when querying a collection.
Following this MR https://github.com/emilk/egui/pull/7375
Without Syntect, the urrent theme selector is
`global_theme_preference_buttons`. It should be the dark_theme variable
of the local CodeTheme - same as syntect
Tested with
```sh
cargo run
# and
cargo run --features syntect
```
* [x] I have followed the instructions in the PR template
thanks for this amazing library!
<!--
Please read the "Making a PR" section of
[`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/main/CONTRIBUTING.md)
before opening a Pull Request!
* Keep your PR:s small and focused.
* The PR title is what ends up in the changelog, so make it descriptive!
* If applicable, add a screenshot or gif.
* If it is a non-trivial addition, consider adding a demo for it to
`egui_demo_lib`, or a new example.
* Do NOT open PR:s from your `master` branch, as that makes it hard for
maintainers to test and add commits to your PR.
* Remember to run `cargo fmt` and `cargo clippy`.
* Open the PR as a draft until you have self-reviewed it and run
`./scripts/check.sh`.
* When you have addressed a PR comment, mark it as resolved.
Please be patient! I will review your PR, but my time is limited!
-->
I am having an issue loading this image
"https://stacks.stanford.edu/image/iiif/wy534zh7137/SULAIR_rosette/full/400,/0/default.jpg".
It has a mime type of "image/jpeg; charset=utf-8", which is not common.
The code doesn't parse the full string that includes the optional
parameter "charset=utf-8" to create the jpeg image format.
A line is added to take only the mime type in the media type before the
";" and ignore the optional parameters.
* Closes <https://github.com/emilk/egui/issues/7738>
* [x] I have followed the instructions in the PR template
Co-authored-by: leungkkf <leungkkf@gmail.com>
### What
From the [lint
description](https://rust-lang.github.io/rust-clippy/master/index.html?search=or_fu#or_fun_call):
> The function will always be called. This is only bad if it allocates
or does some non-trivial amount of work.
But also:
> If the function has side-effects, not calling it will change the
semantic of the program, but you shouldn’t rely on that.
>
> The lint also cannot figure out whether the function you call is
actually expensive to call or not.
Still worth it to keep our happy paths clean, imo.
## Short bluesky announcement:
We just released egui 0.33.0!
Highlights:
- `egui::Plugin` a improved way to create and access egui plugins
- [kitdiff](https://github.com/rerun-io/kitdiff), a viewer for
egui_kittest image snapshots (and a general image diff tool)
- better kerning (check the diff on
[kitdiff](https://rerun-io.github.io/kitdiff/?url=https://github.com/emilk/egui/pull/7431))
https://github.com/user-attachments/assets/971f0493-6dae-42e5-8019-58b74cf5d203
## Relaese Changelog:
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](https://www.rerun.io/), a
startup building an SDK for visualizing streams of multimodal data.
# egui 0.33.0 changelog
Highlights from this release:
- `egui::Plugin` a improved way to create and access egui plugins
- [kitdiff](https://github.com/rerun-io/kitdiff), a viewer for
egui_kittest image snapshots (and a general image diff tool)
- better kerning
### Improved kerning
As a step towards using [parley](https://github.com/linebender/parley)
for font rendering, @valadaptive has refactored the font loading and
rendering code. A result of this (next to the font rendering code being
much nicer now) is improved kerning.
Notice how the c moved away from the k:

### `egui::Plugin` trait
We've added a new trait-based plugin api, meant to replace
`Context::on_begin_pass` and `Context::on_end_pass`.
This makes it a lot easier to handle state in your plugins. Instead of
having to write to egui memory it can live right on your plugin struct.
The trait based api also makes easier to add new hooks that plugins can
use. In addition to `on_begin_pass` and `on_end_pass`, the `Plugin`
trait now has a `input_hook` and `output_hook` which you can use to
inspect / modify the `RawInput` / `FullOutput`.
### kitdiff, a image diff viewer
At rerun we have a ton of snapshots. Some PRs will change most of them
(e.g. [the](https://github.com/rerun-io/rerun/pull/11253/files)
[one](https://rerun-io.github.io/kitdiff/?url=https://github.com/rerun-io/rerun/pull/11253/files)
that updated egui and introduced the kerning improvements, ~500
snapshots changed!).
If you really want to look at every changed snapshot it better be as
efficient as possible, and the experience on github, fiddeling with the
sliders, is kind of frustrating.
In order to fix this, we've made
[kitdiff](https://rerun-io.github.io/kitdiff/).
You can use it locally via
- `kitdiff files .` will search for .new.png and .diff.png files
- `kitdiff git` will compare the current files to the default branch
(main/master)
Or in the browser via
- going to https://rerun-io.github.io/kitdiff/ and pasting a PR or
github artifact url
- linking to kitdiff via e.g. a github workflow
`https://rerun-io.github.io/kitdiff/?url=<link_to_pr_or_artefact>`
To install kitdiff run `cargo install --git
https://github.com/rerun-io/kitdiff`
Here is a video showing the kerning changes in kitdiff ([try it
yourself](https://rerun-io.github.io/kitdiff/?url=https://github.com/rerun-io/rerun/pull/11253/files)):
https://github.com/user-attachments/assets/74640af1-09ba-435a-9d0c-2cbeee140c8f
### Migration guide
- `egui::Mutex` now has a timeout as a simple deadlock detection
- If you use a `egui::Mutex` in some place where it's held for longer
than a single frame, you should switch to the std mutex or parking_lot
instead (egui mutexes are wrappers around parking lot)
- `screen_rect` is deprecated
- In order to support safe areas, egui now has `viewport_rect` and
`content_rect`.
- Update all usages of `screen_rect` to `content_rect`, unless you are
sure that you want to draw outside the `safe area` (which would mean
your Ui may be covered by notches, system ui, etc.)
This was initially a PR to add kitdiff, but this now lives in it's own
crate: https://github.com/rerun-io/kitdiff
I needed to make the image loaders public, this way it's possible to
compose image loaders together (which allowed me to create a image diff
loader that uses two other image loaders). But you can't use the
`ctx.try_load_image` since that would deadlock, so you have to store a
reference to the other loader in the wrapping loader.
Currently, DatePickerButton will close without saving whenever a user
clicks a dropdown from year/month/date. The issue is caused because the
system mistakenly interprets the user as clicking off of the calendar.
This is unexpected and creates an unpleasant experience for the user.
This change now allows the user to use the dropdowns as expected; it
will close on save or cancel. The calendar still closes when user clicks
off of it, as before. The changes here are made in:
crates/egui_extras/src/datepicker/button.rs
I will admit that I am not an experienced Rust developer. The changes
were made with the help of ChatGPT 4.0.
I have tested the changes locally, as I am using the date picker in my
project.
* Closes <https://github.com/emilk/egui/issues/THE_RELEVANT_ISSUE>
* [x] I have followed the instructions in the PR template
---------
Co-authored-by: Lucas Meurer <hi@lucasmerlin.me>
* Recently CI runs started to hang randomly:
https://github.com/emilk/egui/actions/runs/17427449210/job/49477714447?pr=7359
This fixes the deadlock and adds the basic deadlock detection we also
added to Mutexes in #7468.
Also, interestingly, the more sophisticated deadlock detection (behind
the deadlock_detection feature) didn't catch this for some reason. I
wonder why it exists in the first place, when parking_lot also has built
in deadlock detection? It also seems to make tests slower, widget_tests
usually needs ~30s, with the deadlock detection removed its only ~12s.