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

58 Commits

Author SHA1 Message Date
Emil Ernerfeldt
b730815797 Update MSRV from 1.92 to 1.95 (#8348)
No particular reason

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 08:37:03 +00:00
Emil Ernerfeldt
428e027ec7 Export ByteRange and CharRange (#8247) 2026-06-22 03:45:48 +00:00
Emil Ernerfeldt
13d6b5afcf Use strongly typed CharIndex and ByteIndex + bug fixes (#8245)
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>
2026-06-21 02:24:00 +02:00
Emil Ernerfeldt
eac51da9ca Add LayoutJob::format_at_byte (#8244)
This PR adds two small additions to `LayoutJob`:

- `LayoutJob::format_at_byte` to query the `TextFormat` of the section
covering a given byte index.
- An optimization to `LayoutJob::append` that merges newly appended text
into the previous section when the format matches and there is no
leading space.

It also documents the `LayoutJob::sections` invariant (sections are
ordered and together cover the whole text with no gaps or overlaps) and
adds `LayoutJob::debug_sanity_check`, which verifies this in debug
builds. It is called from `format_at_byte` and from the text layouter.

## Why the `easymarkeditor` snapshot changed

The `append` optimization changes how many sections a `LayoutJob` ends
up with: consecutive runs of identically-formatted text now collapse
into a single section instead of one section per `append` call. The
easymark editor produces many such adjacent same-format sections, so it
is affected.

This matters because text is **shaped per section**: `layout_section`
runs the shaper once per section, so each section is an independent
shaping run. Merging two adjacent sections into one means the text
across the old boundary is now shaped together as a single run, which
enables cross-boundary kerning (and, in principle, ligatures) that
previously did not happen. Additionally, `extra_letter_spacing` is
skipped before the first glyph of a section, so merging removes a "first
glyph" boundary and lets the spacing apply there.

The net effect is sub-pixel glyph position shifts at the former section
boundaries, which is why `easymarkeditor.png` was regenerated. The new
output is the more correct one — the text is now shaped as the author
wrote it, rather than being artificially split at `append` boundaries.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 22:07:18 +02:00
Emil Ernerfeldt
e925a41419 Fix glyph caching on font variations (#8189)
* Closes https://github.com/emilk/egui/pull/8029

---------

Co-authored-by: lucasmerlin <hi@lucasmerlin.me>
2026-05-22 15:30:21 +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
Lucas Meurer
e9b8c0d918 Fix text layout bugs in wrapped texts (#8137)
Fixes some bugs that happen randomly when resizing horizontal_wrapped
texts:


https://github.com/user-attachments/assets/141392d2-0239-465a-ba7b-c864f7823319

Adds regression tests (I enjoy using claude to fix these bugs, first
have it create a minimal repro test case, then fix the bug by iterating
until it figures out a fix).
2026-05-04 13:45:50 +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
Gautier Cailly
fef269277b Fix grapheme cluster glyph count to restore cursor/selection invariant (#8088)
May close #8087, but cannot test macOS builtin Japanese IME.

## Summary

PR #8031 (harfrust text shaping) introduced a regression: when harfrust
shapes multi-codepoint clusters (flag emojis, ligatures, combining
marks) into fewer glyphs than input characters, the invariant
`glyphs.len() == char_count` breaks. This causes IME composition to
duplicate characters and text selection to behave incorrectly.

## Fix

In `layout_shaped_run()`, after emitting shaped glyphs for a cluster, we
now check if the cluster had more characters than glyphs. If so,
zero-width "continuation" glyphs are emitted for the extra characters,
restoring the 1:1 glyph-to-character mapping.

Continuation glyphs have `UvRect::default()` (`is_nothing() == true`),
so `tessellate_glyphs` skips them entirely. Background, underline, and
strikethrough rendering handle zero-width glyphs naturally.

Only `crates/epaint/src/text/text_layout.rs` is modified. No changes to
cursor logic, selection code, or public API.

## Test plan

- [x] `cargo fmt --all -- --check`
- [x] `cargo clippy -p epaint --tests`
- [x] `cargo test -p epaint -p egui` (all pass)
- [x] New test `test_grapheme_cluster_glyph_count`: verifies glyph count
== char count for flag emojis, combining marks, and plain ASCII
- [x] New test `test_grapheme_cluster_cursor_roundtrip`: verifies cursor
position stability through `pos_from_cursor` -> `cursor_from_pos`
round-trips on text containing flag emojis
- [x] Manual testing with demo app: selection and cursor navigation work
correctly on `A🇯🇵B`
- [ ] IME testing (macOS Japanese IME) needs to be validated by someone
on macOS

---

**This PR was developed with the assistance of Claude Code.**

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2026-04-15 11:27:48 +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
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
valadaptive
699fc7e887 Add font variations API (#7859)
<!--
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

This was mostly from last month, but I never got around to submitting
it.

This PR adds font variation coordinates to the `TextFormat` struct, and
uses them when rendering text. The coordinates are stored in a
`SmallVec`; I've chosen to store up to 2 inline, which makes it take up
24 bytes (the minimum possible for a `SmallVec`). The variation axis
tags are stored as the `font_types::Tag` type, which I've chosen to
re-export from `epaint::text`.

The variation coordinates are resolved to a `skrifa::Location` during
font rendering/scaling, and are cached in the same way as all the other
scaled metrics. I've renamed the `ScaledMetrics` struct to
`StyledMetrics`, since it now also contains the resolved variation
coordinates. I haven't benchmarked the performance of text layout with
variation coordinates, but the existing text layout performance is
unchanged.

I've replaced the API for manually overriding a font's weight
(https://github.com/emilk/egui/pull/7790) with an API for manually
overriding any variation coordinates via `FontTweak`. This should
support the same use case as #7790 while being substantially more
flexible.

I have *not* yet added any higher-level API for mapping style attributes
(weight, width, slant, etc) to variation coordinates or to different
font faces within a single family. That's a pretty huge can of worms,
and it'd involve rethinking the split between `FontId` and `TextFormat`
(and whether `FontId` is so big that we should provide a way to reuse
it). This API is intentionally pretty low-level for now.

Likewise, I've intentionally not used variation coordinates when
computing a font's row height. I can't think of any fonts that change
their vertical metrics depending on variation axes, so this should be
fine for now.

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2026-03-03 22:58:42 +01:00
RndUsr123
124bde4883 Fixes the overly aggressive overflow elision in truncate() and similar for os scaling other than 100% (#7867)
* Closes #7818
* [x] I have followed the instructions in the PR template

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2026-03-03 08:46:45 +01:00
Emil Ernerfeldt
7fe58bbfd4 Forbid uses of unwrap() in the code (#7795) 2025-12-19 20:34:18 +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
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
Hubert Głuchowski
e861c8ec79 Avoid cloning Rows during Galley::concat (#7649)
<!--
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!
-->

Moves `ends_with_newline` into `PlacedRow` to avoid clones during
layout.
I don't think there was a rationale stronger than "don't change too
much" for not doing this in https://github.com/emilk/egui/pull/5411, so
I should've just done this from the start.
This was a significant part of the profile for text layout (as it cloned
almost every `Row`, even though it only needed to change a single
boolean).

Before:
<img width="757" height="250" alt="image"
src="https://github.com/user-attachments/assets/d1c2afd1-f1ec-4cf5-9d05-f5a5a78052df"
/>

After:
<img width="615" height="249" alt="image"
src="https://github.com/user-attachments/assets/c70966da-c892-4e84-adba-494d0f37f263"
/>

(note that these profiles focus solely on the top-level
`Galley::layout_inline` subtree, also don't compare sample count as the
duration of these tests was completely arbitrary)

egui_demo_lib `*text_layout*` benches:
<img width="791" height="461" alt="image"
src="https://github.com/user-attachments/assets/4f97ce84-2768-4876-9488-d42f8f358ed1"
/>

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

(As usual, the tests fail for me even on master but the failures on
master and with these changes seem the same :))
2025-10-31 10:45:32 +01:00
Emil Ernerfeldt
bd45406fad Use a lot more let-else (#7582) 2025-10-02 19:47:00 +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
ef039aa566 Enable more clippy lints (#7418)
More is more!
2025-08-05 19:47:26 +02:00
Lucas Meurer
9fd0ad36e0 Implement BitOr and BitOrAssign for Rect (#7319) 2025-07-09 15:29:51 +02:00
Lucas Meurer
207e71c2ae Exclude \n when splitting Galleys (#7316)
* Follow up to #7146 

Previously when galleys were splitted, each exept the last had an extra
empty row that had to be removed when they were concated. This changes
it to remove the `\n` from the layout jobs when splitting.
2025-07-09 14:53:19 +02:00
Lucas Meurer
508c60b2e2 Add Galley::intrinsic_size and use it in AtomLayout (#7146)
- part of https://github.com/emilk/egui/issues/5762
- also allows me to simplify sizing logic in egui_flex
2025-07-09 08:19:04 +02:00
valadaptive
7ac137bfc1 Make the font atlas use a color image (#7298)
* [x] I have followed the instructions in the PR template

Splitting this out from the Parley work as requested. This removes
`FontImage` and makes the font atlas use a `ColorImage`. It converts
alpha to coverage at glyph-drawing time, not at delta-upload time.

This doesn't do much now, but will allow for color emoji rendering once
we start using Parley.

I've changed things around so that we pass in `text_alpha_to_coverage`
to the `Fonts` the same way we do with `pixels_per_point` and
`max_texture_side`, reusing the existing code to check if the setting
differs and recreating the font atlas if so. I'm not quite sure why this
wasn't done in the first place.

I've left `ImageData` as an enum for now, in case we want to add support
for more texture pixel formats in the future (which I personally think
would be worthwhile). If you'd like, I can just remove that enum
entirely.
2025-07-04 13:15:48 +02:00
Emil Ernerfeldt
b2995dcb83 Use Rust edition 2024 (#7280) 2025-06-30 14:01:57 +02:00
Emil Ernerfeldt
f9245954eb Enable more clippy lints (#6853)
* Follows https://github.com/emilk/egui/pull/6848
2025-04-24 17:32:50 +02:00
Hubert Głuchowski
557bd56e19 Optimize editing long text by caching each paragraph (#5411)
## What
(written by @emilk)
When editing long text (thousands of line), egui would previously
re-layout the entire text on each edit. This could be slow.

With this PR, we instead split the text into paragraphs (split on `\n`)
and then cache each such paragraph. When editing text then, only the
changed paragraph needs to be laid out again.

Still, there is overhead from splitting the text, hashing each
paragraph, and then joining the results, so the runtime complexity is
still O(N).

In our benchmark, editing a 2000 line string goes from ~8ms to ~300 ms,
a speedup of ~25x.

In the future, we could also consider laying out each paragraph in
parallel, to speed up the initial layout of the text.

## Details
This is an ~~almost complete~~ implementation of the approach described
by emilk [in this
comment](<https://github.com/emilk/egui/issues/3086#issuecomment-1724205777>),
excluding CoW semantics for `LayoutJob` (but including them for `Row`).
It supersedes the previous unsuccessful attempt here:
https://github.com/emilk/egui/pull/4000.

Draft because:
- [X] ~~Currently individual rows will have `ends_with_newline` always
set to false.
This breaks selection with Ctrl+A (and probably many other things)~~
- [X] ~~The whole block for doing the splitting and merging should
probably become a function (I'll do that later).~~
- [X] ~~I haven't run the check script, the tests, and haven't made sure
all of the examples build (although I assume they probably don't rely on
Galley internals).~~
- [x] ~~Layout is sometimes incorrect (missing empty lines, wrapping
sometimes makes text overlap).~~
- A lot of text-related code had to be changed so this needs to be
properly tested to ensure no layout issues were introduced, especially
relating to the now row-relative coordinate system of `Row`s. Also this
requires that we're fine making these very breaking changes.

It does significantly improve the performance of rendering large blocks
of text (if they have many newlines), this is the test program I used to
test it (adapted from <https://github.com/emilk/egui/issues/3086>):
<details>
<summary>code</summary>

```rust
use eframe::egui::{self, CentralPanel, TextEdit};
use std::fmt::Write;

fn main() -> Result<(), eframe::Error> {
    let options = eframe::NativeOptions {
        ..Default::default()
    };

    eframe::run_native(
        "editor big file test",
        options,
        Box::new(|_cc| Ok(Box::<MyApp>::new(MyApp::new()))),
    )
}

struct MyApp {
    text: String,
}

impl MyApp {
    fn new() -> Self {
        let mut string = String::new();
        for line_bytes in (0..50000).map(|_| (0u8..50)) {
            for byte in line_bytes {
                write!(string, " {byte:02x}").unwrap();
            }
            write!(string, "\n").unwrap();
        }
        println!("total bytes: {}", string.len());
        MyApp { text: string }
    }
}

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        CentralPanel::default().show(ctx, |ui| {
            let start = std::time::Instant::now();
            egui::ScrollArea::vertical().show(ui, |ui| {
                let code_editor = TextEdit::multiline(&mut self.text)
                    .code_editor()
                    .desired_width(f32::INFINITY)
                    .desired_rows(40);
                let response = code_editor.show(ui).response;
                if response.changed() {
                    println!("total bytes now: {}", self.text.len());
                }
            });
            let end = std::time::Instant::now();
            let time_to_update = end - start;
            if time_to_update.as_secs_f32() > 0.5 {
                println!("Long update took {:.3}s", time_to_update.as_secs_f32())
            }
        });
    }
}
```
</details>

I think the way to proceed would be to make a new type, something like
`PositionedRow`, that would wrap an `Arc<Row>` but have a separate `pos`
~~and `ends_with_newline`~~ (that would mean `Row` only holds a `size`
instead of a `rect`). This type would of course have getters that would
allow you to easily get a `Rect` from it and probably a `Deref` to the
underlying `Row`.
~~I haven't done this yet because I wanted to get some opinions whether
this would be an acceptable API first.~~ This is now implemented, but of
course I'm still open to discussion about this approach and whether it's
what we want to do.

Breaking changes (currently):
- The `Galley::rows` field has a different type.
- There is now a `PlacedRow` wrapper for `Row`.
- `Row` now uses a coordinate system relative to itself instead of the
`Galley`.

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

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2025-04-01 18:55:39 +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
Emil Ernerfeldt
83254718a3 Clean up strikethrough/underline code in epaint 2025-03-30 13:15:41 +02:00
Emil Ernerfeldt
7ea3f762b8 Make text underline and strikethrough pixel perfect crisp (#5857)
Small visual fix: pixel-align any text underline or strikethrough.
Before they could be often be blurry.
2025-03-28 20:37:38 +01:00
Nicolas
58b2ac88c0 Add assert messages and print bad argument values in asserts (#5216)
Enabled the `missing_assert_message` lint

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

---------

Co-authored-by: Lucas Meurer <lucasmeurer96@gmail.com>
2025-03-25 09:20:29 +01:00
GiGaGon
668abc2838 Add expand_bg to customize size of text background (#5365)
This removes the `expand(1.0)` on text background colors, since it makes
translucent background colors have bad looking bleeding.

There is probably a smarter solution than disabling the highlighting
entirely, but I don't see a way to do that while keeping the area
consumed consistent between translucent/solid colors, or adding a decent
step up in complexity.

Since this makes it impossible to tell if selected text is highlighted,
this also adds a blanket `0.5` gamma multiply to the text selection
background color. If that is undesirable because it's a bad arbitrary
number choice, or if it's too much of an unexpected change and just the
default values should be changed, please let me know.

These changes cause the tests that use screenshots with highlighted text
to fail, though I am not sure how to update those tests to match the
changes.

<details>
<summary>Comparison Images</summary>

Current:


![image](https://github.com/user-attachments/assets/6dc85492-4f8e-4e7a-84b4-3ee10a48b8b3)

After changes:


![image](https://github.com/user-attachments/assets/9b35bbd3-159d-42a9-b22f-80febb707cfa)
 

</details>

<details>
<summary>Code used to make comparison images</summary>

```rs
fn color_text_format(ui: &Ui, color: Color32) -> TextFormat {
    TextFormat { font_id: FontId::monospace(ui.text_style_height(&egui::TextStyle::Monospace)), background: color, ..Default::default() }
}

fn color_sequence_galley(ui: &Ui, text: &str, colors: [Color32; 3]) -> Arc<Galley> {
    let mut layout_job = LayoutJob::default();
    for color in colors {
        layout_job.append(text, 0.0, color_text_format(ui, color));
    }
    ui.fonts(|f| f.layout_job(layout_job))
}

fn color_sequence_row(ui: &mut Ui, label_text: &str, text: &str, colors: [Color32; 3]) {
    ui.label(label_text);
    ui.label(color_sequence_galley(ui, text, colors));
    ui.end_row();
}

egui::Grid::new("comparison display").show(ui, |ui| {
    ui.ctx().set_pixels_per_point(2.0);
    let transparent = Color32::TRANSPARENT;
    let solid = Color32::RED;
    let solid_2 = Color32::GREEN;
    let translucent_1 = Color32::GRAY.gamma_multiply(0.5);
    let translucent_2 = Color32::GREEN.gamma_multiply(0.5);
    color_sequence_row(ui, "Transparent to Solid:", " ", [transparent, solid, transparent]);
    color_sequence_row(ui, "Translucent to Transparent:", " ", [transparent, translucent_1, transparent]);
    color_sequence_row(ui, "Solid to Transparent:", " ", [solid, solid_2, solid]);
    color_sequence_row(ui, "Solid to Solid:", " ", [solid, transparent, solid]);
    color_sequence_row(ui, "Solid to Translucent:", " ", [solid, translucent_1, solid]);
    color_sequence_row(ui, "Translucent to Translucent:", " ", [translucent_1, translucent_2, translucent_1]);
    
    color_sequence_row(ui, "Transparent to Solid:", "a", [transparent, solid, transparent]);
    color_sequence_row(ui, "Translucent to Transparent:", "a", [transparent, translucent_1, transparent]);
    color_sequence_row(ui, "Solid to Transparent:", "a", [solid, solid_2, solid]);
    color_sequence_row(ui, "Solid to Solid:", "a", [solid, transparent, solid]);
    color_sequence_row(ui, "Solid to Translucent:", "a", [solid, translucent_1, solid]);
    color_sequence_row(ui, "Translucent to Translucent:", "a", [translucent_1, translucent_2, translucent_1]);
})
```
</details>

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

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2025-03-21 13:45:34 +01:00
Emil Ernerfeldt
164f56f554 Fix some clippy issues found by 1.84.0 (#5603) 2025-01-13 08:29:13 +01:00
Emil Ernerfeldt
dfcc679d5a Round widget coordinates to even multiple of 1/32 (#5517)
* Closes https://github.com/emilk/egui/pull/5197
* Closes https://github.com/emilk/egui/issues/5163

This should help prevent rounding errors in layout code.

@lucasmerlin you may wanna test this with `egui_flex`
2024-12-26 20:54:24 +01:00
Emil Ernerfeldt
92adfa57dc Improve comment in text layout code 2024-09-26 09:43:27 +02:00
Emil Ernerfeldt
2a40d16e5a Center-align all text vertically (#5117)
* Closes https://github.com/emilk/egui/issues/4929
* Builds on top of https://github.com/emilk/egui/pull/2724 by @lictex
(ptal!)
* Implement `Center` and `Max` vertical text alignment properly
* Change default vertical alignment of text to centering

The end result is that text centers better in buttons and other places,
especially when mixing in emojis.
Before, mixing text of different heights (e.g. emojis and latin text) in
a label or button would cause the text to jump vertically.

## Before
This is `master`, with custom `FontTweak` to move fonts up and down:
<img width="1714" alt="image"
src="https://github.com/user-attachments/assets/a10e2927-e824-4580-baea-124c0b38a527">
<img width="102" alt="image"
src="https://github.com/user-attachments/assets/cd41f415-197b-42cd-9558-d46d63c21dcb">


## After
This PR, with the default (zero) `FontTweak`

<img width="102" alt="image"
src="https://github.com/user-attachments/assets/15e7d896-66b1-4996-ab58-dd1850b19a63">

<img width="1714" alt="image"
src="https://github.com/user-attachments/assets/54ec708c-7698-4754-b1fc-fea0fd240ec9">
2024-09-19 11:44:29 +02:00
Emil Ernerfeldt
f4ed394a85 Add UI to modify FontTweak live (#5125)
This will make it easier to get nice sizing and vertical alignments of
fonts
2024-09-18 13:43:33 +02:00
Emil Ernerfeldt
6b7f431237 Fix text sometime line-breaking or truncating too early (#5077) 2024-09-06 13:24:11 +02:00
Emil Ernerfeldt
b2dcb7d8db Fix bug in size calculation of truncated text (#5076)
The width of the elision character (`…`) was never included in the size
calculation
2024-09-06 11:30:32 +02:00
Nicolas
343c3d16c3 Remove wildcard imports (#5018)
<!--
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!
-->

I removed (I hope so) all wildcard imports I found.

For me on my pc this improved the build time:
- for egui -5s
- for eframe -12s

* [x] I have followed the instructions in the PR template
2024-08-28 12:18:42 +02:00
Emil Ernerfeldt
a59f9ed279 Nicer looking text selection, especially in light mode (#5017)
* Closes https://github.com/emilk/egui/issues/4727

This changes the text selection painting from being painted on top of
the text, to being painted behind the text, but in front of any text
background. The result is much nicer looking text selection, especially
in light mode:

### The new selections
<img width="198" alt="Screenshot 2024-08-27 at 18 58 35"
src="https://github.com/user-attachments/assets/bd342946-299c-44ab-bc2d-2aa8ddbca8eb">
<img width="187" alt="Screenshot 2024-08-27 at 18 59 26"
src="https://github.com/user-attachments/assets/352bed32-5150-49b9-a9f9-c7679a0d30b2">


### What selections used to look like
<img width="143" alt="Screenshot 2024-08-27 at 19 03 08"
src="https://github.com/user-attachments/assets/f3cbd798-cfed-4ad4-aa3a-d7480efcfa3c">
<img width="143" alt="Screenshot 2024-08-27 at 19 03 23"
src="https://github.com/user-attachments/assets/9925d18d-da82-4a44-8a98-ea6857ecc14f">


### New selection of some text with a background
<img width="134" alt="Screenshot 2024-08-27 at 18 59 12"
src="https://github.com/user-attachments/assets/1d291d7f-efbd-4efd-b6d2-cd63c9fc4fa4">
2024-08-27 19:09:44 +02:00
Emil Ernerfeldt
cc3b3629b8 Round text galley sizes to nearest ui point size (#4578)
Previously, many labels had non-integer widths. This lead to rounding
errors.

This was most notable for the new `Area` sizing code:

We would run the initial sizing pass, to measure the size of e.g. a
tooltip.
Say the tooltip contains text that was 100.123 ui points wide. With a
16pt border, that becomes 116.123, which is stored in the `Area` state
as the width. The next frame, we use that stored size as the wrapping
width. With perfect precision, we would then tell the label to wrap to
100.123 pts, which the text would _just_ fit in. However, due to
rounding errors we might end up asking it to wrap to 100.12**2** pts,
meaning the last word would now wrap and end up on the next line.

By rounding label sizes to perfect integers, we avoid such rounding
errors, and most ui elements will now end up on perfect integer point
coordinates (and `f32` can precisely express and do arithmetic on all
integers < 2^24).

Visually this has very little impact. Some labels move by a pixel here
and there, mostly for the better.
2024-05-29 18:23:11 +02:00
Joe Sorensen
2ce82cce21 Added ability to define colors at UV coordinates along a path (#4353)
<!--
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 add commits to your PR.
* Remember to run `cargo fmt` and `cargo cranky`.
* 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 had to make a couple types not Copy because closures, but it should'nt
be a massive deal.

I tried my best to make the API change as non breaking as possible.
Anywhere a PathStroke is used, you can just use a normal Stroke instead.
As mentioned above, the bezier paths couldn't be copy anymore, but IMO
that's a minor caveat.

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2024-04-22 18:35:09 +02:00
Juan Campa
c630a8de89 Fix incorrect line breaks (#4377)
<!--
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 add commits to your PR.
* Remember to run `cargo fmt` and `cargo cranky`.
* 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!
-->

While breaking a paragraph, it was possible to lose line break
candidates that could've been used on the next line, causing egui to
unnecessarily overrun `wrap.max_width`.

This PR fixes it so that we don't forget about those candidates.


Before:
Note that the window can't resize to the requested width because the
text is not wrapping.


https://github.com/emilk/egui/assets/1410520/6430a334-2995-4b40-bc34-8f01923f9f95

After:


https://github.com/emilk/egui/assets/1410520/225fa4cd-cbbb-4a7e-9580-7f1814c05ee7

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2024-04-21 10:58:40 +02:00
Emil Ernerfeldt
f8d7d0ebaa Enforce writing username in TODO comments (#4235) 2024-03-26 11:48:24 +01:00
Emil Ernerfeldt
2bc2fb9c39 Fix some lints found by clippy 1.73 2023-10-06 09:01:31 +02:00
Emil Ernerfeldt
33a0f50f6a Improve text truncation: always include elision character (#3366)
* Add Row::text

* Rename elide_at_width -> truncate_at_width

* Move text layout tests to own module

* Add test to check that elision character is always included

* Include elision character in more circumstances

* Append overflow character if we can't replace

* Always append … when eliding

* Add a secondary text to the text layout demo
2023-09-21 10:41:49 +02:00
Emil Ernerfeldt
8073ca6fe0 Fix problems with tabs in text (#3355)
* Move ascent out of `GlyphInfo`

* Give a function a better name

* Make tab and thin space act more like a normal space
2023-09-18 14:09:15 +02:00
Emil Ernerfeldt
82704bebbf Update MSRV to Rust 1.70.0 (#3310)
* Update to Rust 1.70

* Fix renamed clippy lint

* Use let-else more

* Code cleanup

* Remove derelict Safety comments

* Enable more clippy lints
2023-09-06 07:59:24 +02:00