1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 05:10:03 -04:00
Commit Graph

1045 Commits

Author SHA1 Message Date
Emil Ernerfeldt
bea47a2ce7 Window: move only by dragging title bar (#8183)
* Part of https://github.com/emilk/egui/issues/8180

So far, you've been able to move any `egui::Window` by dragging anywhere
on it. This makes sense on touch screens with thick fingers, but less so
on non-touch-screens.

With this PR, you can now control it with a new enum `WindowDrag`
2026-05-22 12:39:43 +02:00
Emil Ernerfeldt
27373b06d0 Style: forbid .zip and .chain (#8188)
The `zip(a, b)` variant produces clearer code imho.

Downside: added dependency on `itertools`
2026-05-22 12:25:34 +02:00
Emil Ernerfeldt
e64b7683a2 Drag-to-scroll: now only on touch screens (#8181)
* 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.
2026-05-20 10:41:51 +02:00
Emil Ernerfeldt
9650ef85d6 Smoother CollapsingHeader animation (#8177)
Animate the spacing between the header and the body. It's subtle, but
looks slightly nicer when closing a panel.
2026-05-19 17:09:33 +02:00
Emil Ernerfeldt
f91b3ac10b Slow down animation time from 0.1s to 0.2s (#8176)
We now have easings, and we have nice animations of panels. I think
100ms feels a bit rushed now. 200ms feels nicer.
2026-05-19 16:20:05 +02:00
Emil Ernerfeldt
f9f589f460 Slide panels when animating them (#8175)
This looks A LOT nicer


https://github.com/user-attachments/assets/6f208e6c-6b6d-46d2-a40d-832be1256ca7
2026-05-19 15:53:05 +02:00
Emil Ernerfeldt
85ad9cac7e Rework Window margins and set clip_rect_margin to zero (#7725)
* Follows https://github.com/emilk/egui/pull/7722
* Part of https://github.com/emilk/egui/issues/5605
* Closes https://github.com/emilk/egui/issues/3385

## What
Sets `clip_rect_margin` to zero, and moves the margin of `Window`s with
`ScrollAreas`, so that the scroll bars are now on the very edge of the
windows they are in.

Windows with a bulit-in scroll area now lets the content go all the way
to the edges (left image).
However, if you just manually add a `ScrollArea` to a `Window`, you
won't get this effect (right image).
<img width="763" height="345" alt="Screenshot 2026-05-18 at 22 04 01"
src="https://github.com/user-attachments/assets/e41cdfcb-b0a6-4e5e-9691-d132a602d6a7"
/>

## Required
* #7803
* #7804
* #7805
* #7806
* https://github.com/emilk/egui/pull/7807
* https://github.com/emilk/egui/pull/7808
2026-05-19 15:43:42 +02:00
Emil Ernerfeldt
bcfb5bf493 Refactor Panels (#8174)
In preparation for nicer panel animation.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-19 14:41:16 +02:00
Lucas Meurer
82aaef3530 Fix resizing of Grid (#8170)
- fixes https://github.com/emilk/egui/issues/8168
- broke in https://github.com/emilk/egui/pull/8152


Now we get the min size by running a sizing pass once, when the resize
drag starts
2026-05-18 18:25:26 +02:00
Lucas Meurer
e204717b1d Atom support for egui::Window Titlebar (#8154)
* part of #7264 
* based on https://github.com/emilk/egui/pull/8152

The resize fix allows use to really simplify how the Window Titlebar is
rendered. Previously it was using some complex flow to calculate and
allocate the height first and then render it later once we knew the
windows final width.

Since now windows can't shrink past their minimum content widths, I can
just show the titlebar inline with the regular content, just outside of
the `Resize` container so that it is always visible.

This does change what the size of a window means. Before, size was just
the size of the contents, while now size (e.g. via min_height) will
include the Frames margin and outline, title bar and the contents.
Also, the window label now truncates as you shrink the window (meaning
windows can now be smaller than their label allows).

---------

Co-authored-by: lucasmerlin <8009393+lucasmerlin@users.noreply.github.com>
2026-05-13 15:11:15 +02:00
Lucas Meurer
571d366056 Allow Atoms in Ui::small_button (#8159)
* part of #7264
2026-05-13 12:10:38 +02:00
Lucas Meurer
1280495301 Don't allow resizing Window past minimum content size (#8152)
## Related

- part of #7264 (required for Atoms in window titlebar)
- part of https://github.com/emilk/egui/issues/2921

## What

This implements a fix for this weird edge case when resizing windows
past it's contents minimum allocated width:

Before:



https://github.com/user-attachments/assets/33c6c7b2-3621-4eba-8122-99a3930ff67b


After:


https://github.com/user-attachments/assets/5dd47d8f-32bb-4463-aa01-3a5c8f39b10e


There is a very slight flicker on the very first frame where we detect
the minimum size. We could cover this with a request_discard, but in
practise it should be barely noticable.

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2026-05-12 13:57:21 +02:00
kay-lambdadelta
2925b465c2 Remove 64 bit atomics in main crate (#8037)
This allows the base egui crate to run on platforms without 64 bit
atomics

* Addresses <https://github.com/emilk/egui/issues/7692>, but does not
address the `egui_extras` crate

* [x] I have followed the instructions in the PR template
2026-05-12 13:46:22 +02:00
YouStones
4a8618498e Add Classes to UiBuilder and some Widgets (#7843)
* Closes part of <https://github.com/emilk/egui/issues/3284>

Add a class system toward a CSS-like styling. 

Widget and Ui can implement the trait `HasClasses` which can be used
later by theme engines to compute a style based on the set of classes
the component has.

---------

Co-authored-by: adrien <221212@umons.ac.be>
Co-authored-by: Adrien Zianne <adrien@iq002.ipa.iqrypto.com>
Co-authored-by: lucasmerlin <hi@lucasmerlin.me>
2026-05-12 13:43:49 +02:00
Lucas Meurer
4610b7c673 Don't hide whitespaces in centered and right aligned text edits (#8102)
When enabled, trailing whitespace is included in the row width used for
horizontal alignment. This is useful for text editors where hiding
trailing spaces feels wrong when typing.

By default this is `false`, preserving existing behavior where trailing
whitespace is stripped for alignment (so "Hello " centers the same as
"Hello").


* follow up to https://github.com/emilk/egui/pull/8082
2026-04-17 08:55:58 +02:00
Hallvard Ystad
4016dee2bd Add regression test for O(n²) word boundary scan (#8077)
## Summary

`next_word_boundary_char_index` calls `char_index_from_byte_index` on
every word-boundary segment. Since `char_index_from_byte_index` scans
from the start of the string each time, this makes the function O(n·m)
where m is the number of segments — effectively O(n²) for large texts.

The fix replaces those repeated scans with a running char counter
maintained as we iterate segments. Same results, O(n) instead of O(n²).

## The problem

Any pointer interaction (click, double-click, drag) inside a `TextEdit`
with a large text buffer triggers `pointer_interaction` →
`select_word_at` → `ccursor_previous_word` →
`next_word_boundary_char_index`. On a 500 KB buffer this takes ~30
seconds, freezing the application.

## Benchmark results

Measured with cursor near end of text (worst case):

| Text size | Before | After | Speedup |
|-----------|--------|-------|---------|
| 1 KB | 147 µs | 38 µs | 3.9x |
| 100 KB | 1.2 s | 3.8 ms | 313x |
| 500 KB | 30 s | 18 ms | 1,636x |

Benchmark source:
[`bench/word-boundary-perf`](https://github.com/hallyhaa/egui/tree/bench/word-boundary-perf)
(separate branch, not part of this PR)

## Changes

- `next_word_boundary_char_index`: replace `char_index_from_byte_index`
calls with a `running_ci` counter (the fix — 6 changed lines)
- New tests for `ccursor_previous_word`, `ccursor_next_word`,
`select_word_at`, and a large-text performance test

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 20:59:15 +02:00
Emil Ernerfeldt
1cd89b5edc Remove everything that was marked #[deprecated] (#8105)
Simplify. Streamline. Spring cleaning.
2026-04-14 20:19:36 +02:00
rustbasic
fe5533e450 Optimize text selection performance for large documents (#7917)
**Perf: Optimize text selection and navigation performance for large
documents**

#### **Summary**
This PR significantly improves the performance of text selection
(double-clicking) and cursor navigation within `TextEdit` and `Label`
widgets, particularly when handling large documents (e.g., 1MB+ or
logs). It eliminates several $O(N^2)$ bottlenecks and unnecessary memory
allocations in `text_cursor_state.rs`.

#### **Problems Identified**
1. **$O(N^2)$ Word Boundary Scanning:** In
`next_word_boundary_char_index`, `char_index_from_byte_index` was called
repeatedly inside a loop. This caused the entire document to be scanned
from the beginning for every word found, leading to quadratic time
complexity.
2. **Heavy String Allocations:** `ccursor_previous_word` used
`collect::<String>()` and `rev()` to search backwards, causing a full
copy and memory allocation of the text (or line) every time the user
moved the cursor or double-clicked.
3. **Inefficient Line Start Finding:** `find_line_start` performed
global character counts (`text.chars().count()`) and global skips, which
is very slow for large files.
4. **Global Search Scope:** `select_word_at` was performing word
boundary searches across the entire document even for simple
double-click actions.

#### **Key Changes & Optimizations**
1. **Line-Scoped Selection:** Updated `select_word_at` to first identify
the current line and then perform word boundary searches within that
local scope. This reduces the search space from millions of characters
to hundreds.
2. **Linear Time ($O(N)$) Boundary Search:** Refactored
`next_word_boundary_char_index` to use a running cumulative character
counter. This ensures the text is scanned only once.
3. **Zero-Allocation Backwards Search:** Optimized
`ccursor_previous_word` to use `next_back()` on the
`DoubleEndedIterator` provided by `unicode-segmentation`. This removes
all temporary `String` allocations.
4. **Byte-Based Line Search:** Optimized `find_line_start` to use
byte-based reverse scanning (`rfind('\n')`), which is significantly
faster than counting characters from the start of the document.

#### **Performance Impact**
In my tests with large text files (over 10,000 lines / 1MB+):
- **Before:** Double-clicking a word caused a UI freeze for 2–5 seconds.
- **After:** Word selection and navigation are near-instantaneous
(0–1ms), providing a smooth "native-like" experience even in WASM
environments.
2026-04-14 13:49:54 +02:00
Lucas Meurer
902906f989 Fix centered & right aligned TextEdit (#8082)
A couple improvements to centered and right-aligned text edits:
- Fix text selection in centered and right aligned text edits
(ironically, this broke in #8076)
- Fix cursor movement in centered and right aligned text edits
(horizontal cursor position will be retained on vertical movement)
- Multiline text edit exceeding available width if there are atoms
- Added atoms & alignment options to text edit demo
- Improve how vertical_align and horizontal_align are applied
- Textedit atom is grow now, removing the need for the extra seperate
grow atom
- This allows us to apply the `align` on the text edit atom instead of
the whole AtomLayout
  - Fixes https://github.com/emilk/egui/pull/8022
  - Fixes https://github.com/emilk/egui/issues/7999
2026-04-14 13:13:59 +02:00
Umaĵo
86a7f47738 Delegate handling of IME interruptions to integrations to fix virtual keyboard flickering on web (#8078)
* Closes N/A
* Partially replaces #7983
* Related: #8045
* [x] I have followed the instructions in the PR template

## Details

In #7983, I modified `Memory::request_focus` to interrupt any ongoing
IME composition. This fixed a bug where clicking inside an already
focused `TextEdit` failed to cancel the active composition, resulting in
duplicated text:

https://github.com/emilk/egui/pull/8045#issuecomment-4193310616

To avoid introducing API changes in that PR, I ensured the IME state was
reset by forcing `PlatformOutput::ime` to `None` for at least one frame.
While this works well on desktop platforms, it causes virtual keyboard
flickering on the web:

https://github.com/emilk/egui/pull/8045#issuecomment-4193035008

In this PR, I delegate the responsibility for handling IME composition
interruptions to integrations, allowing each integration to decide how
to interrupt compositions in a flexible manner.

### The new field `should_interrupt_composition` on `IMEOutput`.

Instead of introducing a new `OutputCommand` variant, this PR adds a new
field `should_interrupt_composition` to `IMEOutput`.

Interrupting an active composition is only meaningful when IME remains
allowed. If IME should be disabled altogether, `PlatformOutput::ime` can
simply be set to `None`.
Given this, IMO, it is more appropriate to attach the interrupt signal
to `IMEOutput` (i.e., the type of `PlatformOutput::ime`).
2026-04-08 10:04:15 +02:00
Emil Ernerfeldt
b117a1ac19 Fix Context::is_pointer_over_egui and Context::egui_wants_pointer_input (#8081)
* Closes https://github.com/emilk/egui/issues/8041

These functions were broken when using the new `run_ui`.
2026-04-08 09:52:28 +02:00
Emil Ernerfeldt
74b9970a9f Fix wrong color of last glyph of selected text (#8075)
* Closes https://github.com/emilk/egui/issues/8059
2026-04-07 12:59:46 +02:00
Emil Ernerfeldt
3abba21f2d Add subpixel_binning to TextOptions and FontTweak (#8072)
This lets you turn off subpixel horizontal binning of glyphs. The option
is a trade-off between even kerning and sharp text.

* Closes https://github.com/emilk/egui/issues/8034
2026-04-07 10:38:37 +02:00
Emil Ernerfeldt
ab4bca65ea Make the width of the thin space configurable (#8070)
Adds `FontTweak::thin_space_width` and `FontTweak::tab_size`
2026-04-06 18:09:54 +02:00
Umaĵo
33e89e33be Improve IME handling, add public method owns_ime_events on Memory (#7983)
<!--
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!
-->

* Depends on #7967
* Closes #7485
* Should fix #7906 (This issue doesn't seem to have been resolved, but
the author closed it; I personally don't have the environment to verify
whether it is fixed.)
* Replaces #4137, #4896, and partially #7810
* [x] I have followed the instructions in the PR template

This PR started as a fix for #7485, but has since evolved into a broader
rewrite of IME-related logic.

## Overview

This PR primarily introduces a new public method, `owns_ime_events`, on
[`Memory`], and refactors parts of [`TextEdit`] to integrate with it.

Previously, each [`TextEdit`] widget independently determined whether to
handle IME events and stored its own IME-related state. This approach
made ownership-handling fragmented and was therefore error-prone.

With this PR:
- IME event ownership is centralized, ensuring that at most a single
widget owns IME events per frame.
- [`PlatformOutput`]'s `ime` field can be set to `None` for at least one
frame when IME composition is interrupted, allowing the IME to be
properly dismissed.

## Details

Two new public methods are introduced on [`Memory`]:

- `fn owns_ime_events(&self, id: Id) -> bool`: check IME event ownership
for the current frame for the widget with the given `id`.
- `fn interrupt_ime(&mut self)`: interrupt the current IME composition,
if any.

Since the newly added methods on [`Memory`] are public, other widgets
can also participate in IME handling without risking ownership conflicts
of IME events.

I also added an internal (`pub(crate)`) field on [`TextEditState`],
called `cursor_purpose`, to distinguish the role of the [`TextEdit`]
cursor.

Additionally, `egui::ImeEvent::Enabled` and `egui::ImeEvent::Disabled`
have been removed, as they are no longer used anywhere.

## Demonstrations

### Windows: The Korean IME text duplication bug fixed in #4137 does not
reappear.

<table>
<thead>
<tr>
<th></th>
<th>With this PR</th>
<th>Without this PR</th>
</tr>
</thead>
<tbody>
<tr>
<th>Behavior</th>
<td>Correct (no regression)</td>
<td>Correct</td>
</tr>
<tr>
<th>Screencast</th>
<td>


![win-kor-after](https://github.com/user-attachments/assets/1b080c8f-2031-406f-8781-aacafd5c879a)
</td>
<td>


![win-kor-before](https://github.com/user-attachments/assets/20258841-72fe-4652-b9a9-9b40e338ccf2)
</td>
</tr>
</tbody>
</table>

### Windows: Chinese and Japanese IMEs now behave more consistently with
the Korean IME in similar scenarios.

This change does not matter much, as composition is rarely interrupted
mid-process with these IMEs in typical usage.

<table>
<thead>
<tr>
<th></th>
<th>With this PR</th>
<th>Without this PR</th>
</tr>
</thead>
<tbody>
<tr>
<th>Behavior</th>
<td>Composition can be interrupted by clicking (like Korean IMEs)</td>
<td>Composition can not interrupted by clicking</td>
</tr>
<tr>
<th>Screencast (Builtin Chinese IME)</th>
<td>


![win-cmn-after](https://github.com/user-attachments/assets/2c76b0a9-da6d-48e1-84e0-47d9631f1196)
</td>
<td>


![win-cmn-before](https://github.com/user-attachments/assets/ea125fb8-c325-48d5-abaf-17d495b8f075)
</td>
</tr>
<tr>
<th>Screencast (Builtin Japanese IME)</th>
<td>


![win-jpn-after](https://github.com/user-attachments/assets/c69e5f48-65b1-4c0f-af4a-522d2f47b75d)
</td>
<td>


![win-jpn-before](https://github.com/user-attachments/assets/a0f1fdad-4f6c-40c2-af57-029f42acf6d5)
</td>
</tr>
</tbody>
</table>

### macOS: was buggy, still buggy

Likely due to this upstream bug in `winit`:
https://github.com/rust-windowing/winit/issues/4432
Once `winit` is updated to a version that includes the fix, the behavior
should become correct with this PR.

<table>
<thead>
<tr>
<th></th>
<th>With this PR</th>
<th>Without this PR</th>
</tr>
</thead>
<tbody>
<tr>
<th>Behavior</th>
<td>Buggy as before</td>
<td>Buggy: Characters are duplicated</td>
</tr>
<tr>
<th>Screencast</th>
<td>


![mac-kor-after](https://github.com/user-attachments/assets/c2bd90e8-e473-49c8-9537-c970c92889bf)
</td>
<td>


![mac-kor-before](https://github.com/user-attachments/assets/63b6cd8a-8903-4743-98bf-ee15296354ba)
</td>
</tr>
</tbody>
</table>

### Wayland + iBus: Korean IME duplication bug fixed

<table>
<thead>
<tr>
<th></th>
<th>With this PR</th>
<th>Without this PR</th>
</tr>
</thead>
<tbody>
<tr>
<th>Behavior</th>
<td>Correct</td>
<td>Buggy: Characters are duplicated</td>
</tr>
<tr>
<th>Screencast</th>
<td>


![wayland-kor-after-2](https://github.com/user-attachments/assets/b154add5-a1ce-4e3a-b243-e72480820c1b)
</td>
<td>


![wayland-kor-before-2](https://github.com/user-attachments/assets/43b28374-f273-4b6f-9845-3efd96ec9a37)
</td>
</tr>
</tbody>
</table>

### Wayland + iBus: #7485 is fixed

<table>
<thead>
<tr>
<th></th>
<th>With this PR</th>
<th>Without this PR</th>
</tr>
</thead>
<tbody>
<tr>
<th>Behavior</th>
<td>Correct</td>
<td>Buggy: Only a single ASCII character can be typed after
<code>TextEdit</code> is focused</td>
</tr>
<tr>
<th>Screencast</th>
<td>


![wayland-7485-after](https://github.com/user-attachments/assets/ec33a54d-1d4e-40f9-8c82-202104bd2d85)
</td>
<td>


![wayland-7485-before](https://github.com/user-attachments/assets/20d2d395-03fd-4966-a376-87249a41aab3)
</td>
</tr>
</tbody>
</table>

### Wayland + iBus: selection is also not broken

This PR does not reintroduce the selection bug fixed in #7973.

<table>
<thead>
<tr>
<th></th>
<th>With this PR</th>
</tr>
</thead>
<tbody>
<tr>
<th>Behavior</th>
<td>Correct</td>
</tr>
<tr>
<th>Screencast</th>
<td>


![wayland-focus-after](https://github.com/user-attachments/assets/daa29197-f7f7-4a7b-b454-c28ee9afa9c1)
</td>
</tr>
</tbody>
</table>

### X11 + Fcitx5: IME composition can be interrupted

But due to #7975, the experience is still subpar. (Uncommitted text is
lost after interruption.)

<table>
<thead>
<tr>
<th></th>
<th>With this PR</th>
<th>Without this PR</th>
</tr>
</thead>
<tbody>
<tr>
<th>Screencast</th>
<td>


![x11-after](https://github.com/user-attachments/assets/e626d9ed-89a2-4825-9cde-3a67723bcb82)
</td>
<td>


![x11-before](https://github.com/user-attachments/assets/da93b351-9488-4da9-aa56-b64190e84ec3)
</td>
</tr>
</tbody>
</table>


[`Memory`]: https://docs.rs/egui/latest/egui/struct.Memory.html
[`TextEdit`]:
https://docs.rs/egui/latest/egui/widgets/text_edit/struct.TextEdit.html
[`PlatformOutput`]:
https://docs.rs/egui/latest/egui/struct.PlatformOutput.html
[`TextEditState`]:
https://docs.rs/egui/latest/egui/widgets/text_edit/struct.TextEditState.html
2026-04-06 10:24:50 +02:00
Emil Ernerfeldt
4a09782fce Enable a few more clippy lints (#8064)
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
2026-04-04 16:20:29 +02:00
Emil Ernerfeldt
c2b482ff7e Flip if-else:s with a negation (#8063) 2026-04-04 12:03:41 +02:00
Alexander Schütz
1c9f74b8bd Add raw key methods to TypeIdMap (#8007)
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>
2026-03-26 08:03:39 +01:00
Emil Ernerfeldt
048f8ccd2a Tweak ScrollArea fade effect (#8023)
* Follow-up to https://github.com/emilk/egui/pull/8018
2026-03-25 15:44:41 +01:00
eason
02ff040b74 Fix: Visuals::interact_cursor support in Button (#7986)
Closes #7947

## Problem

`Visuals::interact_cursor` stopped working for buttons after the
`AtomLayout` refactor in commit 6eb7bb6e. Setting `interact_cursor` to
e.g. `CursorIcon::PointingHand` no longer changes the cursor when
hovering over a `Button`.

## Root Cause

When `Button` was rewritten to use `AtomLayout` in #5830, the
cursor-override block at the end of the old `Button::ui` was not carried
over to the new `Button::atom_ui` method.

The old code had:
```rust
if let Some(cursor) = ui.visuals().interact_cursor {
    if response.hovered() {
        ui.ctx().set_cursor_icon(cursor);
    }
}
```

This was the only place `interact_cursor` was checked, so the setting
became entirely non-functional.

## Fix

Re-add the same `interact_cursor` check in `Button::atom_ui`, right
after painting and before `widget_info`, matching the original behavior.

---------

Co-authored-by: easonysliu <easonysliu@tencent.com>
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2026-03-25 14:48:22 +01:00
Lucas Meurer
d232be740f Fix bug in ui stack color blending (#8021)
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2026-03-25 13:40:54 +01:00
Emil Ernerfeldt
0b0c561a81 Fade out the edges of ScrollAreas (#8018)
## Before:
<img width="381" height="307" alt="image"
src="https://github.com/user-attachments/assets/0528ae2a-44bf-4d9e-89a4-c3f4ab438eb2"
/>

It is very hard here to realize this is a scrollable area

## After
<img width="383" height="310" alt="image"
src="https://github.com/user-attachments/assets/9e0ee6de-8b96-4e5c-a505-f57977010990"
/>

The fade at the bottom tells the user they should try scrolling.

You can turn if off with `style.spacing.scroll.fade.enabled`
2026-03-25 12:53:00 +01:00
Lucas Meurer
845b8c2f09 Make egui::IdSet public (#8019)
It wasn't public before
2026-03-25 12:46:49 +01:00
Emil Ernerfeldt
e8f04292a9 Add UiStack::bg_color (#8020)
This lets you ask for the background color of a ui with
`ui.stack().bg_color()`
2026-03-25 12:41:35 +01:00
Emil Ernerfeldt
bfbf23b4fb Add Ui::is_tooltip (#8016) 2026-03-25 10:11:30 +01:00
Emil Ernerfeldt
12b5045633 Add Context::time (#8017) 2026-03-25 10:11:21 +01:00
Emil Ernerfeldt
2ccc8e8bab Add Context::text_edit_focused (#8014) 2026-03-25 09:21:39 +01:00
Lucas Meurer
4feac890aa Respect WidgetVisuals::expansion in TextEdit (#8013)
This broke in #7587

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2026-03-24 17:33:20 +01:00
Lucas Meurer
0d065f9e78 Add Response::parent_id and improve warn_if_rect_changes_id (#8010)
Reduces the amount of false positives

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2026-03-24 16:22:44 +01:00
Emil Ernerfeldt
a1af9abe70 Shrink the byte-size of Response slightly (#8011)
Small optimization! 96 -> 88 bytes, so no huge win.
2026-03-24 15:46:56 +01:00
rustbasic
5ed92c3011 Add Button::left_text (#7955)
feat: Add left_text() to egui::Button

This PR introduces the `left_text()` method to `egui::Button`. It
enables placing additional text content on the left side of the button's
primary label, which is useful for displaying auxiliary information,
labels, and for facilitating left-aligned text within the button.

```rust
                            let is_selected = true;
                            let selectable_label_widget = egui::Button::selectable(
                                is_selected,
                                "",
                            ).left_text("Left");

                            let desired_width = ui.available_width();
                            let desired_height = ui.spacing().interact_size.y;

                            interaction_response = ui.add_sized(
                                egui::vec2(desired_width, desired_height),
                                selectable_label_widget,
                            );
```
2026-03-24 14:03:20 +01:00
Frank
405eb81578 Fix menu keyboard toggle for open submenus (#7957)
Summary
This PR fixes submenu keyboard parity: pressing Enter/Space on an
already-open submenu button now collapses that submenu (matching
top-level menu button behavior).

What changed
Updated submenu interaction logic to distinguish pointer primary clicks
from keyboard/accessibility-triggered clicks.
Kept pointer/touch behavior unchanged (submenu button clicks still don’t
auto-close submenu).
Added regression tests for:
keyboard open of nested submenu,
keyboard close (second Enter) of nested submenu,
pointer clicks on submenu button keeping submenu open.


Validation
cargo test -p egui_kittest --test regression_tests

Breaking changes
None. Behavior change is limited to keyboard/accessibility activation of
already-open submenu buttons.

* Closes <https://github.com/emilk/egui/issues/7926> 
* [ X ] I have followed the instructions in the PR template

---------

Co-authored-by: Lucas Meurer <hi@lucasmerlin.me>
2026-03-24 14:03:00 +01:00
Umaĵo
b4f9cd7140 Much improved IME (#7967)
<!--
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!
-->

* Closes #7809
* Closes #7876
* Closes #7908
* Supersedes #7877
* Supersedes #7898
* The author of the PR above replaced it with #7914, which additionally
fixes another IME issue. I believe that fix deserves a separate PR.
* Reverts #4794  
* [x] I have followed the instructions in the PR template

This approach is better than #7898 (#7914) because it correctly handles
all three major IME types (Chinese, Japanese, and Korean) without
requiring a predefined “IME mode”.

## Environments I haved tested this PR in

<details><summary>macOS 15.7.3 (AArch64, Host of other virtual
machines)</summary>

Run command: `cargo run -p egui_demo_app --release`

Tested IMEs:

- builtin Chinese IME (Shuangpin - Simplified)
- builtin Japanese IME (Romaji)
- builtin Korean IME (2-Set)
</details>

<details><summary>Windows 11 25H2 (AArch64, Virtual Machine)</summary>

Build command: `cargo build --release -p egui_demo_app
--target=x86_64-pc-windows-gnu --features=glow --no-default-features`

(I cannot use `wgpu` due to [this
bug](https://github.com/emilk/egui/issues/4381), which prevents
debugging inside the VM. Anyways, the rendering backend should be
irrelevant here.)

Tested IMEs:

- builtin Chinese IME (Shuangpin)
- Sogou IME (Chinese Shuangpin)
- WeType IME (Chinese Shuangpin)
- builtin Japanese IME (Hiragana)
- builtin Korean IME (2 Beolsik)
</details>

<details><summary>Linux [Wayland + IBus] (AArch64, Virtual
Machine)</summary>

Fedora KDE Plasma Desktop 43 [Wayland + IBus 1.5.33-rc2]

(Not working at the moment because of [another
issue](https://github.com/emilk/egui/issues/7485) that will be fixed by
#7983. It is [a complicated
story](https://github.com/emilk/egui/pull/7973#issuecomment-4074627603).
)

> [!NOTE]
>
> IBus is partially broken in this system. The Input Method Selector
refuses to select IBus. As a workaround, I have to open System Settings
-> Virtual Keyboard and select “IBus Wayland” to start an IBus instance
that works in egui.
>
> The funny thing is: the Chinese Intelligent Pinyin IME is broken in
native Apps like System Settings and KWrite, but works correctly in
egui!
>
> <details><summary>Screencast: What</summary>
>
> ![2026-03-13 3 10
11 AM](https://github.com/user-attachments/assets/4001cf12-8089-46f5-9cf4-e41d8f77ee24)
> </details>

Build command: `cross build --release -p egui_demo_app
--target=aarch64-unknown-linux-gnu --features=wayland,wgpu
--no-default-features`

(The Linux toolchain on my mac is somehow broken, so I used `cross`
instead.)

Tested IMEs:

- Chinese Intelligent Pinyin IME (Shuangpin)
- Japanese Anthy IME (Hiragana)
- Korean Hangul IME
</details>

<details><summary>Linux [X11 + Fcitx5] (AArch64, Virtual
Machine)</summary>

Debian 13 [Cinnamon 6.4.10 + X11 + Fcitx5 5.1.2]

Build command: `cross build --release -p egui_demo_app
--target=aarch64-unknown-linux-gnu --features=x11,wgpu
--no-default-features`

Tested IMEs:

- Chinese Shuangpin IME
- Chinese Rime IME with `luna-pinyin`
- Japanese Mozc IME (Hiragana)
- Korean Hangul IME

Unlike macOS and Linux + Wayland, key-release events for keys processed
by the IME are still forwarded to `egui`. These appear to be harmless in
practice.
Unlike on Windows, however, they cannot be filtered reliably because
there are no corresponding key-press events marked as “processed by
IME”.

</details>

---

There are too many possible combinations to test (Operating Systems ×
[Desktop
Environment](https://en.wikipedia.org/wiki/Desktop_environment)s ×
[Windowing System](https://en.wikipedia.org/wiki/Windowing_system)s ×
[IMF](https://wiki.archlinux.org/title/Input_method#Input_method_framework)s
× [IME](https://en.wikipedia.org/wiki/Input_method)s × …), and I only
have access to a limited subset. For example, Google Japanese Input
refused to install on my Windows VM, and some paid Japanese IMEs are not
accessible to me. Therefore, I would appreciate feedback from people
other than me using all kinds of environments.

## Details

There are two possible approaches to removing keyboard events that have
already been processed by an IME:

* Approach 1: Filter out events inside `egui` that appear to have been
received during IME composition.
* Approach 2: Filter out such events in the platform backend
(terminology [borrowed from
imgui](https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md#using-standard-backends),
e.g. the `egui-winit` crate or the code under `web/` in the `eframe`
crate.).

Both approaches already exist in `egui`:

* #4794 uses the first approach, filtering these events in the
`TextEdit`-related code.
* `eframe` uses the second approach in its web integration. See:
<14afefa252/crates/eframe/src/web/events.rs (L173-L176)>

Compared to the first approach, the second has a clear advantage: when
events are passed from the platform backends into `egui`, they are
simplified and lose information. In contrast, events in the platform
backends are the original events, which allows them to be handled more
flexibly. This is also why #7898 (#7914), which attempts to address the
issue from within the `egui` crate, struggles to make all IMEs work
correctly at the same time and requires manually selecting an “IME
mode”: the events received by `egui` have already been reduced and
therefore lack necessary information.

A more appropriate solution is to consistently follow the second
approach, explicitly requiring platform backends not to forward events
that have already been processed by the IME to `egui`. This is the
method used in this PR. Specifically, this PR works within the
`egui-winit` crate, where the original `KeyboardInput` events can be
accessed. At least for key press events, these can be used directly to
determine whether the event has already been processed by the IME on
Windows (by checking whether `logical_key` equals
`winit:⌨️:NamedKey::Process`). This makes it straightforward to
ensure that all IMEs work correctly at the same time.

This PR also reverts #4794, which took the first approach. It filters
out some events that merely look like they were received during IME
composition but actually are not. It also messes up the order of those
events along the way.
As a result, it caused several IME-related issues. One of the sections
in the Demonstrations below will illustrate these problems.

## Demonstrations

<details><summary>Changes not included in this PR for displaying Unicode
characters in demonstrations</summary>

Download `unifont-17.0.03.otf` from
<https://unifoundry.com/pub/unifont/unifont-17.0.03/font-builds/>, and
place it at `crates/egui_demo_app/src/unifont-17.0.03.otf`.

In `crates/egui_demo_app/src/wrap_app.rs`, add these lines at the
beginning of `impl WrapApp`'s `pub fn new`:
```rust
        {
            const MAIN_FONT: &'static [u8] = include_bytes!("./unifont-17.0.03.otf");

            let mut fonts = egui::FontDefinitions::default();

            fonts.font_data.insert(
                "main-font".to_owned(),
                std::sync::Arc::new(egui::FontData::from_static(MAIN_FONT)),
            );

            let proportional = fonts
                .families
                .entry(egui::FontFamily::Proportional)
                .or_default();
            proportional.insert(0, "main-font".to_owned());

            cc.egui_ctx.set_fonts(fonts);
        }
```

(I took this from somewhere, but I forgot where it is. Sorry…)
</details>

[GNU Unifont](https://unifoundry.com/unifont/index.html) is licensed
under [OFL-1.1](https://unifoundry.com/OFL-1.1.txt).

### This PR Fixes: Focus on a single-line `TextEdit` is lost after
completing candidate selection with Japanese IME on Windows (#7809)

<details><summary>Screencast:  Japanese IME now behaves correctly while
Korean IME behaves as before</summary>


![7809](https://github.com/user-attachments/assets/6e92f6e6-fed4-46f4-96e1-e0e12dadc360)
</details>

### This PR Fixes: Committing Japanese IME text with <kbd>Enter</kbd>
inserts an unintended newline in multiline `TextEdit` on Windows (#7876)

<details><summary>Screencast:  Japanese IME now behaves correctly while
Korean IME behaves as before</summary>


![7876](https://github.com/user-attachments/assets/03d2cb22-fd0c-45fe-9132-e59fa39bfcf3)
</details>

### This PR Fixes: Backspacing deletes characters during composition in
certain Chinese IMEs (e.g., Sogou) on Windows (#7908)

<details><summary>Screencast:  Sogou IME now behaves
correctly</summary>


![7908](https://github.com/user-attachments/assets/2c63de28-26f0-4387-9c50-dceabfdbe99d)
</details>

### This PR Obsoletes #4794, because `egui` receives only IME events
during composition from now on

On Windows, “incompatible” events are filtered in `egui-winit`, aligning
the behavior with other systems.

<details><summary>Screencasts</summary>

Some Chinese IMEs on Windows:
![2026-03-13 12 25
37 AM](https://github.com/user-attachments/assets/064fd1c7-244b-4053-bd24-c65d768cd943)

The default Japanese IMEs on Windows:
![2026-03-13 12 28
33 AM](https://github.com/user-attachments/assets/f799b0b5-350b-4b05-a769-bcef16255bdb)
</details>

The 2-set Korean IMEs handle arrow keys differently. It will be
discussed in the next section.

### This PR Reverts #4794, because it introduced several bugs

Some of its bugs have already been worked around in the past, but those
workarounds might also be problematic. For example, #4912 is a
workaround for a bug (#4908) introduced by #4794, and that workaround is
in fact the root cause of the macOS backspacing bug I have worked around
with #7810. (The reversion of #4912 is out of the scope of this PR, I
will do that in #7983.)

#### It Caused: Arrow keys are incorrectly blocked during typical Korean
IME composition

When composing Korean text using 2-Set IMEs, users should still be able
to move the cursor with arrow keys regardless if the composition is
committed.

##### Correct behavior

<details><summary>Screencasts</summary>

macOS TextEdit:
![2026-03-12 8 04
15 PM](https://github.com/user-attachments/assets/24383568-f51c-4a74-9251-adfd942cad8f)

Windows Notepad:
![2026-03-12 8 05
08 PM](https://github.com/user-attachments/assets/5a29a5b5-69b8-407b-b1a4-84fdb8f8847d)

With #4794 reverted, `egui` also behaves correctly (tested on Linux +
Wayland, macOS, and Windows):
![2026-03-12 8 03
51 PM](https://github.com/user-attachments/assets/fcb7f25c-1329-4eb1-82f2-1cea33dcca73)
</details>

##### Incorrect behavior caused by #4794

`remove_ime_incompatible_events` removed arrow-key events in such cases.
As a result, the first arrow key press only commits the composition, and
users need to press the arrow key again to move the cursor:

<details><summary>Screencast</summary>

![2026-03-12 8 06
40 PM](https://github.com/user-attachments/assets/6760c6bd-b6ce-44ea-b192-6bd165191c01)
</details>

This is essentially the same issue described here:
https://github.com/emilk/egui/pull/7877#issuecomment-3852719948

#### It Caused: Backspacing leaves the last character in Korean IME
pre-edit text not removed on macOS

<details><summary>Screencasts</summary>

Before this PR:
![2026-03-17 10 48
12 PM](https://github.com/user-attachments/assets/88021e7e-caf6-4aa9-8f73-ecffc63cda06)

After this PR:
![2026-03-17 10 47
23 PM](https://github.com/user-attachments/assets/379cd0db-24e0-4c0e-a5b4-edb37d3e1df7)
</details>

### Korean IMEs also use <kbd>Enter</kbd> to confirm Hanja selections,
and will not work properly in the Korean “IME mode” proposed by #7898
(#7914)

<details><summary>Screencast: Korean IME using <kbd>Enter</kbd> and
<kbd>Space</kbd> for confirmation (IBus Korean Hangul IME)</summary>

The screencast below demonstrates that some Korean IMEs handle Hanja
selection in a way similar to Japanese IMEs: the
<kbd>Up</kbd>/<kbd>Down</kbd> arrow keys are used to navigate
candidates, and <kbd>Enter</kbd> confirms the selected candidate.

![2026-03-13 6 39
17 AM](https://github.com/user-attachments/assets/0b054cc6-2251-4689-95a4-d69a9be36371)
</details>

<details><summary>Screencasts: Another example</summary>

Using the built-in Korean IME on Windows, I type two lines: the first
line in Hangul, and the second line as the same word converted to Hanja.

Correct behavior in Notepad (reference):

![7914-ref](https://github.com/user-attachments/assets/1e9f9315-eb71-497a-b6e2-2b11eb6bbf7f)

Behavior after applying this PR, which matches the Notepad behavior:

![7914-7967](https://github.com/user-attachments/assets/26f12b9f-9354-45b8-b2a8-ede28c34c5b1)

Behavior after applying #7914 with the “IME mode” set to Korean (which
is also the behavior before this PR being applied):

![7914-7914](https://github.com/user-attachments/assets/0f82a019-c491-4b64-a92a-d88d62dfbd84)
On the second line, each time a Hanja character is confirmed, an
unintended newline is inserted. This mirrors the Japanese IME issues
that are supposed to be fixed by setting the “IME mode” to Japanese.
(These Japanese IME issues are fixed in this PR as mentioned before.)

</details>
2026-03-24 11:58:58 +01:00
ilya sheprut
cc7cfd27ca Fix horizontal_wrapping row height after using text_edit_multiline (#8000)
* [x] I have followed the instructions in the PR template

This PR have two commits:
* **First commit** - introduction of tests and their canonization image.
Expected behaviour is that `horizontal_wrapped_multiline_row_height`
would match `horizontal_wrapped_multiline_row_height_reference`, but it
doesn't. There is a bug in `horizontal_wrapped` that breaks line height
after using `text_edit_multiline`.
* **Second commit** - fix. You can see that
`horizontal_wrapped_multiline_row_height` now looks like
`horizontal_wrapped_multiline_row_height_reference` (although it's not a
perfect match, upd: found, this is because of this issue:
https://github.com/emilk/egui/issues/4921).

I have used LLM to help me with this PR (codex + claude code).

BTW, I'm using horizontal_wrapped with end_row instead of vertical +
horizontal alternation, because I automatically generate my UI through
some complex interactions between elements in my code, and it's can be
that my `horizontal` starts in one function, and ends in another.
Something like `begin_horizontal`/`end_horizontal`/`get_current_layout`
would be very handy, related to
https://github.com/emilk/egui/issues/1004.

Also, I would like indent to be supported in `horizontal_wrapped`, or
also, to have `indent_start`/`indent_end`. This is why I used
`monospace("| ")` in my example, it simulates my use-case.
2026-03-24 11:37:37 +01:00
RndUsr123
2a03ae1348 Enables every combination of TextEdit and LayoutJob alignments (#7831)
<!--
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!
-->

This is a fix/improvement that makes all kinds of alignments work in
`TextEdit` when a custom `LayoutJob` with halign is used.


I used the simplest approach possible to avoid unwanted bugs as I wasn't
sure what's safe to change, but there's potentially better ways to
achieve this. In particular, I'm not sure I fully understand the
rationale behind aligning rows in a `Galley` based on the latter's
leftmost border, considering the size is what's ultimately used in
widgets (in `TextEdit` at least).

Regardless, here's a demo of this PR:


https://github.com/user-attachments/assets/5d9801d7-73af-4576-80c5-47f169700462


* [x] I have followed the instructions in the PR template

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2026-03-24 11:35:37 +01:00
Emil Ernerfeldt
7fbd1315ec Include LICENSE files in published crates (#8004)
* Closes https://github.com/emilk/egui/issues/7977
2026-03-24 11:28:49 +01:00
Lucas Meurer
c0ea6117e0 Use AtomLayoutResponse in TextEditOutput (#8003)
This is necessary to add e.g. buttons as prefix/suffix within the
textedit (so you can read the rect and place the button).

I'm not sure if deref on the AtomLayoutResponse is the right call but it
should make the migration easier and is generally convenient (but might
lead to some confusion).
2026-03-24 11:04:36 +01:00
Emil Ernerfeldt
b077cf9102 Add some example docs to atoms (#7997) 2026-03-23 15:50:57 +01:00
Emil Ernerfeldt
4714aa7d31 Fix instable IDs following animated panels (#7994)
* Found thanks to https://github.com/emilk/egui/pull/7984

If you put an animated `Panel` inside a `Ui`, then the automatic ids for
following widgets would differ when the panel was collapsed or open.
2026-03-21 21:52:52 +01:00
Lucas Meurer
8b2315375b TextEdit Atom prefix/suffix (#7587)
* part of https://github.com/emilk/egui/issues/7264
* part of https://github.com/emilk/egui/issues/7445

This PR changes the layout within the TextEdit to be done with
AtomLayout. It also adds a Prefix and Postfix that allows adding
permanent icons / icon buttons within the textedit.

Breaking changes:
- Removed `TextEdit::hint_text_font`. Hint text is an atom now, so the
font can be set that way

<img width="264" height="130" alt="Screenshot 2025-10-06 at 12 25 21"
src="https://github.com/user-attachments/assets/03b6b56b-ca82-4ac3-b5c0-585cca336834"
/>

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
Co-authored-by: lucasmerlin <8009393+lucasmerlin@users.noreply.github.com>
2026-03-20 11:29:32 +01:00