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

15 Commits

Author SHA1 Message Date
Lucas Meurer
72699b7032 Remove fallback_gap 2026-08-27 13:24:58 +02:00
Lucas Meurer
487c8f1dda Let the theme set a button's atom gap
`Button` hard-coded `spacing.icon_spacing` as the gap between its icon and
its text. A theme that gives a button its own metrics wants to set that gap
too, so it becomes a `ButtonStyle` field, overridden by `Button::gap`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 09:49:37 +02:00
Emil Ernerfeldt
6aea7eff94 Enable the clippy::std_instead_of_core lint (#8394)
Prefer `core::` over `std::` where either work

* Part of https://github.com/emilk/egui/issues/5735
2026-08-06 13:19:13 +02:00
Lucas Meurer
080ce81c8b Add AtomLayout::direction (#8221)
* To be merged after #8219 

Adds support for vertical atom layout. Together with #8219 this
basically creates a minimal flex layout engine
2026-06-24 13:56:54 +02:00
Lucas Meurer
f209b9aea0 Add AtomKind::Layout, for nesting AtomLayout (#8219)
This allows you to put an `AtomLayout` inside another `AtomLayout`.
Right now this has limited use, but once we add wrapping, vertical
`AtomLayout` and `AtomUi`, this will be a really powerful new layouting
primitive for egui.

Added a test for this in https://github.com/emilk/egui/pull/8221
2026-06-22 19:09:43 +02:00
冷凍アカギ
172fb54f7f Add AtomLayout::selectable for opt-in text selection (#8224)
* Closes https://github.com/emilk/egui/issues/8217
* [x] I have followed the instructions in the PR template

## What

`AllocatedAtomLayout::paint` painted text via
`ui.painter().galley(...)`, so text in atom-based widgets could never be
selected. As discussed in #8217, routing it through
`LabelSelectionState::label_text_selection` unconditionally would break
`Button` / `Checkbox` / `RadioButton` / `TextEdit` (whose labels should
not be selectable, and whose click/drag handling would fight the
selection drag).

So this adds an **opt-in** `AtomLayout::selectable(bool)` (default
`false`). When enabled, the layout also senses clicks and drags
(mirroring `Label::layout_in_ui`) and paints its text through the
label-selection machinery. The underline is `Stroke::NONE`, so when
nothing is selected the painted output is identical to the existing path
— the default (`selectable == false`) behaviour, and existing snapshots,
are unchanged.

## Tests

Two tests in `tests/egui_tests/tests/test_atoms.rs`:

- `test_atom_selectable_senses_click_and_drag` — a `selectable(true)`
layout senses click+drag; the default stays inert.
- `test_atom_selectable_text_can_be_copied` — selecting (drag) the text
of a selectable layout and copying yields the text via
`OutputCommand::CopyText`, while a non-selectable layout yields nothing.

## Notes

- Verified locally: `cargo fmt --all -- --check`, `cargo clippy -p egui`
(clean), and the two new tests pass. I haven't run the full
`./scripts/check.sh` (wasm/typos) locally, hence opening as a draft.
- This only adds the opt-in API; no existing widget is made selectable.
Happy to wire it up on a specific widget and/or add an `egui_demo_lib`
demo if you'd like — just let me know.
2026-06-13 20:25:00 +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
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
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
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
8b90dc60c6 ⚠️ Atom improvements: Atom::id, align, closure, max_size (#7958)
Migration guide:
- `AtomKind::Custom` has been removed. You can now set an id to any kind
via `Atom::custom` or `AtomExt::atom_id`.
2026-03-10 12:03:10 +01:00
Jochen Görtler
2dbfe3a083 Enable or_fun_call lint to avoid unnecessary allocations (#7754)
### What

From the [lint
description](https://rust-lang.github.io/rust-clippy/master/index.html?search=or_fu#or_fun_call):

> The function will always be called. This is only bad if it allocates
or does some non-trivial amount of work.

But also:

> If the function has side-effects, not calling it will change the
semantic of the program, but you shouldn’t rely on that.
> 
> The lint also cannot figure out whether the function you call is
actually expensive to call or not.

Still worth it to keep our happy paths clean, imo.
2025-12-05 10:46:34 +01:00
Lucas Meurer
c2de29a8de Fix WidgetText::Text ignoring fallback font and overrides (#7361)
* closes https://github.com/emilk/egui/issues/7356
2025-08-13 14:56:26 +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
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