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

75 Commits

Author SHA1 Message Date
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
Emil Ernerfeldt
1fdc5c0775 Fix text selection of centered and right-aligned text (#8076)
* Fixes https://github.com/emilk/egui/issues/8049
* Bug introduced in https://github.com/emilk/egui/pull/7831
2026-04-07 13:29:57 +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
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
Gautier Cailly
16cad760a5 Integrate harfrust for text shaping (#8031)
* Related to #56 (Improve text — tracking issue)

## Summary

This PR integrates [harfrust](https://crates.io/crates/harfrust) (a
pure-Rust port of HarfBuzz) into epaint's text layout pipeline,
replacing the character-by-character glyph positioning with proper
OpenType text shaping.

### What this enables

- **GPOS kerning**: most modern fonts only ship kerning in GPOS tables
(not the legacy `kern` table). Pairs like "AV", "VA", "AT" are now
properly tightened.
- **GSUB substitutions**: ligatures (fi, fl), contextual alternates, and
other OpenType features.
- **Combining marks**: diacritics (e.g. ɔ̃) are positioned via anchor
tables instead of being rendered as standalone replacement glyphs.

### Before/After

#### Kerning, etc.

<img width="838" height="726" alt="before_main"
src="https://github.com/user-attachments/assets/f0f26d5f-b117-43a6-b39c-ea40d2e73836"
/>

<img width="838" height="726" alt="after_harfrust"
src="https://github.com/user-attachments/assets/d983e5da-486c-4f39-bd4f-5782a90c6b39"
/>

 #### Ligatures

<img width="1117" height="698" alt="before_closeup"
src="https://github.com/user-attachments/assets/7a3b08b4-cf6f-45b7-98ba-07c473cd3b02"
/>

<img width="1117" height="698" alt="after_closeup"
src="https://github.com/user-attachments/assets/6cfc5f21-d32f-4f09-be0c-59c8c553d44f"
/>

### Architecture

The shaping integrates into the existing pipeline without changing the
public API:

1. **`Font::segment_into_runs`** — segments text into contiguous runs by
font face (grapheme-cluster aware, never splits combining sequences)
2. **`FontFace::shape_text`** — calls harfrust to shape each run,
returning glyph IDs + positioned advances/offsets
3. **`layout_shaped_run`** — emits `Glyph` structs from the shaping
output, with NOTDEF fallback to other font faces for missing glyphs
4. **Buffer recycling** — `FontsImpl` pools a `harfrust::UnicodeBuffer`
to avoid per-layout allocations

### Disclaimer

I'm far from being a good Rust programmer. Claude Code did most of the
heavy lifting here. I did my best and used my limited knowledge to avoid
making too many mistakes. If this PR isn't up to quality standards,
please don't hesitate to close it.

## Test plan

- [x] `cargo test -p epaint` — all 18 text tests pass, including 6 new
ones
- [x] `cargo clippy -p epaint --all-features` — clean
- [x] `cargo fmt` — clean
- [ ] Snapshot tests need regeneration (expected: shaping changes glyph
positions)
- New tests added:
- `test_gpos_kerning` — verifies GPOS kerning tightens "AV", "VA", "AT"
pairs
- `test_combining_diacritics` — combining tilde doesn't add extra width
  - `test_shaping_basic_latin` — sanity check for Latin text
  - `test_shaping_empty_string` — empty input doesn't panic
  - `test_shaping_multiple_newlines` — newline splitting works correctly
  - `test_shaping_mixed_font_fallback` — Latin + emoji in same string

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2026-04-06 14:25:04 +02:00
Emil Ernerfeldt
eb35f7d12f Improve test of text rendering 2026-03-29 10:00:36 +02: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
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
Emil Ernerfeldt
49fad9a7b2 Roll out new egui icon and logo (#7995)
For the first time _ever_, egui has a logo!

<img width="3925" height="1406" alt="egui-logo"
src="https://github.com/user-attachments/assets/cfaf1d43-9338-490f-ae82-99b420baa1b0"
/>

Made by [Studio Gruhl](https://www.studiogruhl.com/) and paid for by
[Rerun.io](https://rerun.io/).
2026-03-21 22:47:15 +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
Lucas Meurer
14afefa252 Fix galley width calculation being off due to subpixel binning (#7972)
- fix for https://github.com/rerun-io/reality/pull/1075

The galleys row size was calculated by looking at the last glyphs pos_x,
which got changed to be rounded to integers when we added subpixel
binning. This introduced a subtle bug which caused the width of galleys
to be slightly off.
This PR fixes this by looking at the actual cursor position instead,
which is not rounded.

Also added a test to ensure this is correct. Previously, for the second
and last line, the `x` was too close to the `0`.

<img width="48" height="67" alt="image"
src="https://github.com/user-attachments/assets/a69a4cc3-b3f3-4553-ab92-73cb2e7a358c"
/>

---------

Co-authored-by: lucasmerlin <8009393+lucasmerlin@users.noreply.github.com>
2026-03-12 13:49:16 +01:00
Emil Ernerfeldt
b733679760 Fix text color when selecting newline character (#7951)
* Closes https://github.com/emilk/egui/issues/7865
2026-03-03 13:27:56 +01:00
Lucas Meurer
a354c02e76 Add Atom prefix/suffix support to DragValue (#7949) 2026-03-03 11:35:29 +01:00
Roman Popov
f1cde5aab4 Fix CentralPanel::show_inside_dyn to round panel_rect (#7868)
While using CentralPanel::show_inside_dyn inside egui_tiles I've noticed
that sometimes it renders "Unaligned" message with certain tile
positions. Match SidePanel behavior by calling round_ui() on the panel
rect.
<img width="364" height="173" alt="image"
src="https://github.com/user-attachments/assets/08a9cef3-67f8-49af-9ea7-75aff711186a"
/>

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2026-02-04 17:52:39 +01:00
Emil Ernerfeldt
ca1e76f38b Constrain demo windows to the space left after the panels (#7785) 2025-12-17 21:03:52 +01:00
Emil Ernerfeldt
51493be066 Better framing of demo snapshots (#7787) 2025-12-17 18:06:26 +01:00
Emil Ernerfeldt
9487dc35ec Viewports: give the caller a Ui instead of Context (#7779)
* Part of https://github.com/emilk/egui/issues/3524

This is a breaking change, as it changes the how embedded viewports
work.
Before it was up to the user to display a `egui::Window` if they wanted.
Now egui creates an `egui::Window` for you, so you only need to add the
contents.

To signal this change in behavior, `ViewportClass::Embedded` is gone and
is now called `ViewportClass::EmbeddedWindow`.
2025-12-15 18:51:57 +01:00
valadaptive
609dd2d28e Replace ab_glyph with Skrifa + vello_cpu; enable font hinting (#7694)
<!--
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 N/A
* [x] I have followed the instructions in the PR template

I'll probably come back to this and clean it up a bit. This PR
reimplements ab_glyph's functionality on top of Skrifa, a somewhat
lower-level font API that's being used in Chrome now.

Skrifa doesn't perform rasterization itself, so I'm using
[vello_cpu](https://github.com/linebender/vello) from the Linebender
project for rasterization. It's still in its early days, but I believe
it's already quite fast. It also supports color and gradient fills, so
color emoji support will be easier.

Skrifa also supports font hinting, which should make text look a bit
nicer / less blurry.

Here's the current ab_glyph rendering:

<img width="1592" height="1068" alt="image"
src="https://github.com/user-attachments/assets/2385b66e-23f8-4c6e-b8c2-ea90e0eea4e4"
/>

Here's Skrifa *without* hinting--it looks almost identical, but there
are some subpixel differences, probably due to rasterizer behavior:

<img width="1592" height="1068" alt="image"
src="https://github.com/user-attachments/assets/a815f3e9-65ac-4940-bc00-571177bef53d"
/>

Here's Skrifa  *with* hinting:

<img width="1592" height="1068" alt="image"
src="https://github.com/user-attachments/assets/d6cc0669-3537-4377-bba9-ed5ef09664db"
/>

Hinting does make the horizontal strokes look a bit bolder, which makes
me wonder once again about increasing the font weight from "light" to
"regular".

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2025-12-06 16:11:33 +01:00
Lucas Meurer
a19629ef4a Add kittest.toml config file (#7643)
* part of https://github.com/rerun-io/rerun/issues/10991

---------

Co-authored-by: lucasmerlin <8009393+lucasmerlin@users.noreply.github.com>
2025-11-25 14:51:18 +01:00
Emil Ernerfeldt
f74b7c7e79 Paint mouse cursor in kittest snapshot images (#7721)
Very simple triangle shape, but helps understand why a widget has a
hovered effect
2025-11-18 06:24:03 +01:00
Emil Ernerfeldt
9a073d9399 Prevent widgets sometimes appearing to move relative to each other (#7710)
Sometimes when moving a window, having a tooltip attached to the mouse
pointer, or scrolling a `ScrollArea`, you would see this disturbing
effect:


![drift-bug](https://github.com/user-attachments/assets/013a5f49-ee02-417c-8441-1e1a0369e8bd)

This is caused by us rounding many visual elements (lines, rectangles,
text, …) to physical pixels in order to keep them sharp. If the
window/tooltip itself is not rounded to a physical pixel, then you can
get this behavior.

So from now on the position of all
areas/windows/tooltips/popups/ScrollArea gets rounded to the closes
pixel.

* Unlocked by https://github.com/emilk/egui/pull/7709
2025-11-13 10:52:46 +01:00
Emil Ernerfeldt
fa4cfec777 Change text color of selected text (#7691)
Selected text now gets the color of `visuals.selection.stroke.color`.
This means you can have inverted colors for selected text, like in the
new test:
<img width="154" height="46" alt="image"
src="https://github.com/user-attachments/assets/2666361d-d7e2-4d50-8e4d-2fcc128f1a81"
/>


It also means the color of selected text in labels matches that of the
text color of selected buttons.
2025-11-07 15:34:36 +01:00
Emil Ernerfeldt
92de16a88c Fix kerning snapshot test directory 2025-10-09 15:50:36 +02:00
Emil Ernerfeldt
47a437403f Use software texture filtering in kittest (#7602) 2025-10-08 16:24:02 +02:00
Emil Ernerfeldt
683214bfe8 Add kittest snapshot test of italics (#7603) 2025-10-07 17:22:31 +02:00
Emil Ernerfeldt
603dba29e6 Add snapshot test for text spacing/kerning (#7545) 2025-09-16 13:30:28 +02:00
Alan Everett
b0c568a78e Add rotation gesture support for trackpad sources (#7453)
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
Co-authored-by: Lucas Meurer <hi@lucasmerlin.me>
2025-09-11 12:44:17 +02:00
Emil Ernerfeldt
72b9b9d750 Increase default text size from 12.5 to 13.0 (#7521) 2025-09-09 15:47:24 +02:00
Emil Ernerfeldt
c2912369ca Improve text demo: more fine control of letter spacing (#7520) 2025-09-09 09:35:25 +02:00
valadaptive
d5b0a6f446 More even text kerning (#7431)
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2025-09-08 17:29:41 +02:00
Emil Ernerfeldt
aedd43c88f kittest: Add UPDATE_SNAPSHOTS=force (#7508)
This adds a new mode, `UPDATE_SNAPSHOTS=force`, which will lower the
threshold to zero, overwriting every image that is not _exactly_ the
same.

Most comparisons has a threshold because different GPUs render slightly
differently. However, setting that threshold accurately can be hard.

Sometimes a test will pass locally, but fail on CI. In those cases you
want to force an update of the failing test. You can use
`UPDATE_SNAPSHOTS=force` for that.

And sometimes a small change _should_ update all images, but the change
is so tiny that it falls under the threshold. Still, you want to make a
point of showing that these images have changes. You can use
`UPDATE_SNAPSHOTS=force` for that.
2025-09-05 16:45:36 +02:00
Emil Ernerfeldt
f46926aaf1 Improve texture filtering by doing it in gamma space (#7311)
* Closes https://github.com/emilk/egui/pull/5839

This makes some transparent images look a lot nicer when blended:


![image](https://github.com/user-attachments/assets/7f370aaf-886a-423c-8391-c378849b63ca)

Cursive text will also look nicer.

This unfortunately changes the contract of what
`register_native_texture` expects

---------

Co-authored-by: Adrian Blumer <blumer.adrian@gmail.com>
2025-07-07 17:46:27 +02:00
Emil Ernerfeldt
dd1052108e Add snapshot test for image blending (#7309)
This adds a test that can be used to see the improvements made by this
PR (if any):
* https://github.com/emilk/egui/pull/5839
2025-07-07 13:58:22 +02:00
Emil Ernerfeldt
dc79998044 Improve text rendering in light mode (#7290)
This changes how we convert glyph coverage to alpha (and ultimately a
color), but only in light mode.

This is a bit of a hack, because it doesn't fix dark-on-light text in
_dark mode_ (if you have any), but for the common case this PR is a huge
improvement.

You can also tweak this yourself now using
`Visuals::text_alpha_from_coverage` or from the UI (bottom of the
image):


![image](https://github.com/user-attachments/assets/350210d4-c0bb-44b6-84cc-47c2e9d4b9f0)



## Before / After

![widget_gallery_light_x1](https://github.com/user-attachments/assets/21f5a2a0-6b4e-4985-b17f-cd1c7cc01b46)
![widget_gallery_light_x1](https://github.com/user-attachments/assets/5dfec04a-c81c-43ef-8d86-fc48ef7958f1)


## Black text Before/after
If you think the text above looks too weak, it's only because of the
default text color. Here's how it looks like with perfectly `#000000`
black text:


![image](https://github.com/user-attachments/assets/56a4a4f3-c431-4991-b941-a566a4ae94ed)
![Screenshot 2025-07-02 at 13 59
30](https://github.com/user-attachments/assets/df5a91ad-0bb8-4a0f-81a2-50852e7556c1)
2025-07-02 14:58:37 +02:00
Emil Ernerfeldt
8bedaf6e5b Add light-mode Widget Gallery screenshot test (#7288)
Part of some work to improve text rendering in light mode (again!)
2025-07-02 12:00:36 +02:00
Emil Ernerfeldt
0857527f1d Add Visuals::weak_text_alpha and weak_text_color (#7285)
* Closes https://github.com/emilk/egui/issues/7262

This also makes the default weak color slightly less weak in most cases.
2025-07-01 20:42:54 +02:00
Emil Ernerfeldt
9e021f78da Change ui.disable() to modify opacity (#7282)
* Closes https://github.com/emilk/egui/pull/6765


Branched off of https://github.com/emilk/egui/pull/6765 by @tye-exe.

I needed to branch off to update snapshot images

---------

Co-authored-by: tye-exe <tye@mailbox.org>
Co-authored-by: Tye <131195812+tye-exe@users.noreply.github.com>
2025-07-01 14:05:53 +02:00
Patrick Marks
df2c16ef0a Add anchored text rotation method, and clarify related docs (#7130)
Add a helper method to perform rotation about a specified anchor.

* Closes #7051
2025-06-16 01:42:01 +02:00
Lucas Meurer
6eb7bb6e08 Add AtomLayout, abstracing layouting within widgets (#5830)
Today each widget does its own custom layout, which has some drawbacks:
- not very flexible
- you can add an `Image` to `Button` but it will always be shown on the
left side
  - you can't add a `Image` to a e.g. a `SelectableLabel`
- a lot of duplicated code

This PR introduces `Atoms` and `AtomLayout` which abstracts over "widget
content" and layout within widgets, so it'd be possible to add images /
text / custom rendering (for e.g. the checkbox) to any widget.

A simple custom button implementation is now as easy as this:
```rs
pub struct ALButton<'a> {
    al: AtomicLayout<'a>,
}

impl<'a> ALButton<'a> {
    pub fn new(content: impl IntoAtomics) -> Self {
        Self { al: content.into_atomics() }
    }
}

impl<'a> Widget for ALButton<'a> {
    fn ui(mut self, ui: &mut Ui) -> Response {
        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)
        });

        self.al.frame = self
            .al
            .frame
            .inner_margin(ui.style().spacing.button_padding)
            .fill(visuals.bg_fill)
            .stroke(visuals.bg_stroke)
            .corner_radius(visuals.corner_radius);

        self.al.show(ui)
    }
}

```

The initial implementation only does very basic layout, just enough to
be able to implement most current egui widgets, so:
- only horizontal layout
- everything is centered
- a single item may grow/shrink based on the available space
- everything can be contained in a Frame


There is a trait `IntoAtoms` that conveniently allows you to construct
`Atoms` from a tuple
```
   ui.button((Image::new("image.png"), "Click me!"))
```
to get a button with image and text.


This PR reimplements three egui widgets based on the new AtomLayout:
 - Button
   - matches the old button pixel-by-pixel
- Button with image is now [properly
aligned](https://github.com/emilk/egui/pull/5830/files#diff-962ce2c68ab50724b01c6b64c683c4067edd9b79fcdcb39a6071021e33ebe772)
in justified layouts
   - selected button style now matches SelecatbleLabel look
- For some reason the DragValue text seems shifted by a pixel almost
everywhere, but I think it's more centered now, yay?
 - Checkbox
- basically pixel-perfect but apparently the check mesh is very slightly
different so I had to update the snapshot
   - somehow needs a bit more space in some snapshot tests?
 - RadioButton
   - pixel-perfect
   - somehow needs a bit more space in some snapshot tests?

I plan on updating TextEdit based on AtomLayout in a separate PR (so
you could use it to add a icon within the textedit frame).
2025-06-13 09:39:52 +02:00
Emil Ernerfeldt
2cf6a3a9a6 Track original SVG size (#7098)
This fixes bugs related to how an `Image` follows the size of an SVG.

We track the "source size" of each image, i.e. the original width/height
of the SVG, which can be different from whatever it was rasterized as.
2025-05-28 08:33:01 +02:00
Emil Ernerfeldt
b8334f365b Fix sometimes blurry SVGs (#7071)
* Closes https://github.com/emilk/egui/issues/3501

The problem occurs when you want to render the same SVG at different
scales, either at the same time in different parts of your UI, or at two
different times (e.g. the DPI changes).

The solution is to use the `SizeHint` as part of the key.

However, when you have an SVG in a resizable container, that can lead to
hundreds of versions of the same SVG. So new eviction code is added to
handle this case.
2025-05-21 20:01:40 +02:00
Emil Ernerfeldt
f57cb27386 Include test windows in egui_demo_lib snapshot tests (#7070) 2025-05-21 17:44:27 +02:00
Emil Ernerfeldt
f23618701f Update emoji-icon-font with fix for fullwidth latin characters (#7067)
* This PR is based on https://github.com/emilk/egui/pull/5877 by
@danielhjacobs

## Original PR description
Replace the current `emoji-icon-font.ttf` with the updated font from
https://github.com/jslegers/emoji-icon-font/pull/19 to address
https://github.com/emilk/egui/issues/1284. The second commit then
removes the now unnecessary hack.

* Closes https://github.com/emilk/egui/issues/1284
* Closes https://github.com/emilk/egui/pull/5877

---------

Co-authored-by: Daniel Jacobs <danielhunterjacobs@gmail.com>
2025-05-21 13:22:23 +02:00
Lucas Meurer
7055141c18 Update failing snapshot tests (#5894)
For some reason the pipeline in https://github.com/emilk/egui/pull/5698
succeeded even though the snapshots should have been updated. This
updates the snapshots.
2025-04-09 12:11:46 +02:00
Aarni Koskela
36e007bd8c Add overline option for Table rows (#5637)
* Closes no issue, I just needed this for an app and figured it could be
useful.
* [x] I have followed the instructions in the PR template

This PR adds an `overline` option for `egui_extras::TableRow`, which is
useful for visually grouping rows. The overline consumes no layout
space.

A screenshot of the demo app, showing every 7th row getting an overline.

<img width="704" alt="Screenshot 2025-01-25 at 14 40 08"
src="https://github.com/user-attachments/assets/9ccbee3d-296d-4afd-9290-c669e4ede1c0"
/>

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2025-04-08 11:36:43 +02:00
Emil Ernerfeldt
e3acd71090 Make text background rects pixel-sharp (#5864)
Small visual teak: make sure the background text color is pixel-aligned.
2025-03-30 16:21:00 +02:00
Grayden
ddf9d267fc Fix in Scene: make scene_rect full size on reset (#5801)
<!--
Please read the "Making a PR" section of
[`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/master/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!
-->

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

# Overview

This is a small change that supports draggable elements inside a
`Scene`.

When a Scene is initialized with a `Rect::Zero`, following the [example
in the
demo](https://github.com/emilk/egui/blob/master/crates/egui_demo_lib/src/demo/scene.rs#L15),
it will [automatically be reset to the `inner_rect` of the
UI](https://github.com/emilk/egui/blob/master/crates/egui/src/containers/scene.rs#L120-L123).

This centers the scene on the inner-rect contents, however the resulting
`scene_rect` doesn't fill the entire `outer_rect`. This probably isn't
an issue for most users of `Scene`.

However, I want to support draggable elements on a `Scene`, and to do
that I need to map the pointer-position in the window to the scene_rect
position.

As is, the example of draggable elements on Scene works after the user
has modified the scene rect in some way (zoom or pan), when `scene_rect`
is set to `to_global.inverse() * outer_rect`
([here](https://github.com/emilk/egui/blob/master/crates/egui/src/containers/scene.rs#L114-L118)).
Before a user modifies the scene rect, the pointer-position cannot be
reliably mapped to the scene_rect, since the scene_rect doesn't span the
entire window.

This PR just forces that translation to always run after the scene_rect
is reset to `inner_rect`. The practical result is that the scene_rect
will now always span the full outer_rect.

# Example

Here's a small app that demonstrates the functionality I'm trying to
support. I'm new to Egui so there may be better patterns for what I'm
trying to do, but if you run this against `main` and this branch you'll
notice the difference.

```rs
use eframe::egui::*;

/// Map coordinates from the src rect to the target rect
fn map_to_rect(position: Pos2, src_rect: Rect, dest_rect: Rect) -> Pos2 {
    let x = (position.x - src_rect.min.x) / (src_rect.max.x - src_rect.min.x)
        * (dest_rect.max.x - dest_rect.min.x)
        + dest_rect.min.x;
    let y = (position.y - src_rect.min.y) / (src_rect.max.y - src_rect.min.y)
        * (dest_rect.max.y - dest_rect.min.y)
        + dest_rect.min.y;
    Pos2::new(x, y)
}

pub fn draggable_scene_element(
    ui: &mut Ui,
    id: Id,
    position: &mut Rect,
    scene_rect: Rect,
    container_rect: Rect,
) -> Response {
    let is_being_dragged = ui.ctx().is_being_dragged(id);
    if is_being_dragged {
        let r = ui.put(*position, |ui: &mut Ui| ui.label("Draggable"));

        if let Some(pointer_pos) = ui.ctx().pointer_interact_pos() {
            let pointer_pos = map_to_rect(pointer_pos, container_rect, scene_rect);
            let delta = pointer_pos.to_vec2() - position.center().to_vec2();
            *position = position.translate(delta);
        };
        r
    } else {
        let r = ui.put(*position, |ui: &mut Ui| ui.label("Draggable"));
        ui
            .interact(position.clone(), id, Sense::drag())
            .on_hover_cursor(CursorIcon::Grab);
        r
    }
}

struct MyApp {
    scene_rect: Rect,
    position: Rect,
}
impl MyApp {
    fn new() -> Self {
        Self {
            scene_rect: Rect::ZERO,
            position: Rect::from_min_size(Pos2::new(-50., -50.), Vec2::new(100., 100.)),
        }
    }
}

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {
        CentralPanel::default().show(ctx, |ui| {
            let scene_rect = self.scene_rect.clone();
            let container_rect = ui.min_rect();
            Scene::default().show(ui, &mut self.scene_rect, |ui| {
                ui.put(
                    Rect::from_min_size(Pos2::new(100., 200.), Vec2::new(100., 100.)),
                    |ui: &mut Ui| ui.label("static element"),
                );
                ui.put(self.position, |ui: &mut Ui| {
                    draggable_scene_element(
                        ui,
                        Id::from("demo"),
                        &mut self.position,
                        scene_rect,
                        container_rect,
                    )
                });
            });
        });
    }
}
```

# Summary

I need a way to map pointer coordinates to scene coordinates, in order
to support draggable elements in a scene. This patch makes that easier
by ensuring the scene_rect will always be the full size of the
outer_rect.

If you have a better way to accomplish what I'm after, I'm happy to
close this. Thanks!
2025-03-25 10:26:07 +01:00
Emil Ernerfeldt
2058dcb881 Improve text sharpness (#5838)
This improves the sharpness of text slightly, thanks to
<https://hikogui.org/2022/10/24/the-trouble-with-anti-aliasing.html> by
@hikogui (🙏 ).

The difference is very small, but in dark mode (bright text on dark
background) text is sometimes significantly sharper, and also slightly
brighter.

The difference in light mode (dark text on light background) is much
smaller (barely perceptable).

To compare the before/after I suggest you open both in new tabs, then
quickly change between them.

### Low-DPI
#### Before
![Code Editor
old](https://github.com/user-attachments/assets/e10a3cad-932f-48cd-b7d6-5bfe70954c5e)

#### After
![Code
Editor](https://github.com/user-attachments/assets/2e7383fe-8023-4425-91c8-93df3c22c0fe)

#### Before

![old-white](https://github.com/user-attachments/assets/51c41c59-e897-4831-857a-f3ffe17ce7d4)

#### After

![new-white](https://github.com/user-attachments/assets/4ac6f951-8c57-4bcc-a5d5-788cf52ea7d8)

### High-DPI
The difference is starkest on low-DPI screens (like the above
screenshots). On high-DPI screens, the difference is much smaller

#### Before
![widget_gallery
old](https://github.com/user-attachments/assets/f2149a30-aef8-4383-b48c-73d33a03ca02)

#### After

![widget_gallery](https://github.com/user-attachments/assets/c9ceb8be-8a32-490c-9364-2c6562b741f6)
2025-03-21 12:56:47 +01:00
Emil Ernerfeldt
3f731ec794 Fix semi-transparent colors appearing too bright (#5824)
The bug was in `Color32::from_rgba_unmultiplied` and by extension
affects:

* `Color32::from_rgba_unmultiplied`
* `hex_color!`
* `HexColor`
* `ColorImage::from_rgba_unmultiplied`
* All images with transparency (png, webp, …)
* `Color32::from_white_alpha`

The bug caused translucent colors to appear too bright.

## More
Color is hard.

When I started out egui I thought "linear space is objectively better,
for everything!" and then I've been slowly walking that back for various
reasons:

* sRGB textures not available everywhere
* gamma-space is more _perceptually_ even, so it makes sense to use for
anti-aliasing
* other applications do everything in gamma space, so that's what people
expect (this PR)

Similarly, pre-multiplied alpha _makes sense_ for blending colors. It
also enables additive colors, which is nice. But it does complicate
things. Especially when mixed with sRGB/gamma (As @karhu [points
out](https://github.com/emilk/egui/pull/5824#issuecomment-2738099254)).

## Related
* Closes https://github.com/emilk/egui/issues/5751
* Closes https://github.com/emilk/egui/issues/5771 ? (probably; hard to
tell without a repro)
* But not https://github.com/emilk/egui/issues/5810

## TODO
* [x] I broke the RGBA u8 color picker. Fix it

---------

Co-authored-by: Andreas Reich <andreas@rerun.io>
2025-03-21 10:45:25 +01:00
lucasmerlin
cd22517280 ⚠️ Improved menu based on egui::Popup (#5716)
Continuation of #5713

**Silently breaking changes:**
- Menus now close on click by default, this is configurable via
`PopupCloseBehavior`

**Additional additions:**
- `Button::right_text`
- `StyleModifier`

This is a rewrite of the egui menus, with the following goals:
- submenu buttons should work everywhere, in a popup, context menu,
area, in some random Ui
- remove the menu state from Ui
- should work just like the previous menu
- ~proper support for keyboard navigation~
  - It's better now but requires further work to be perfect
- support `PopupCloseBehavior`

* Closes #4607 
* [x] I have followed the instructions in the PR template
2025-03-03 14:56:30 +01:00