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

Fix TextEdit hint text not following horizontal_align/vertical_align (#8332)

## Summary

- Closes #8309
- [x] I have followed the instructions in the PR template

`TextEdit` hint text was always aligned to `Align2::LEFT_TOP`, ignoring
the alignment set via `TextEdit::horizontal_align` / `vertical_align`.
This caused the hint text, the cursor, and the typed text to disagree on
alignment: e.g. a centered `TextEdit` showed a left-aligned hint with a
centered cursor.

The hint text atoms now use the widget's `align`, so the hint matches
the input text alignment. The default `align` is still `LEFT_TOP`, so
multi line text edits (and the default styling) are unchanged.

### Root cause

In `crates/egui/src/widgets/text_edit/builder.rs`, the hint-text branch
hardcoded:

```rust
atoms.push_right(atom.atom_align(Align2::LEFT_TOP));
```

while the input-text branch used `.atom_align(self.align)`. The hint
path now uses `align` as well.

### Drive-by: silence `clippy::unnecessary_wraps` in
`egui_kittest::app_kind`

`AppKind::run` returns `Option<egui::Response>`. The `Option` wrap is
required when the `eframe` feature is enabled (the `Eframe` branch
returns `None`), but `clippy::unnecessary_wraps` fires when
`egui_kittest` is built standalone without the `eframe` feature (e.g.
`cargo clippy -p egui_kittest`). The workspace CI run doesn't hit it
because feature unification via `egui_demo_app` enables `eframe`, but
it's a real annoyance for anyone linting the crate on its own. Added a
scoped `#[cfg_attr(not(feature = "eframe"),
expect(clippy::unnecessary_wraps))]` with an explanatory comment.

## Test plan

- [x] Added `textedit_hint_text_should_follow_text_alignment` kittest
regression in `crates/egui_kittest/tests/regression_tests.rs`. It fails
before the fix (`hint_center_x=24.25` vs `edit_center_x=100`) and passes
after.
- [x] `cargo test -p egui`
- [x] `cargo test -p egui_kittest --all-features --test
regression_tests`
- [x] `cargo clippy -p egui_kittest --all-features --test
regression_tests -- -D warnings`
- [x] `RUSTFLAGS="-D warnings" cargo clippy -p egui_kittest --lib`
(pre-existing `unnecessary_wraps` now silenced)
- [x] `cargo clippy -p egui -- -D warnings`
- [x] `cargo fmt --check`

---------

Co-authored-by: Lucas Meurer <hi@lucasmerlin.me>
This commit is contained in:
Davy
2026-08-03 01:55:18 -07:00
committed by GitHub
parent c676d939ca
commit dcd0c72d53
3 changed files with 54 additions and 3 deletions

View File

@@ -622,9 +622,10 @@ impl TextEdit<'_> {
first = false; first = false;
} }
// The hint text should be shown left top instead of centered (important for // Align the hint text the same as the input text so the hint, the
// multi line text edits) // cursor, and the typed text all share one alignment. The default
atoms.push_right(atom.atom_align(Align2::LEFT_TOP)); // `align` is `LEFT_TOP`, which keeps multi line text edits unchanged.
atoms.push_right(atom.atom_align(align));
} }
// Calculate the empty galley, so it can be read later. The available width is // Calculate the empty galley, so it can be read later. The available width is

View File

@@ -23,6 +23,10 @@ pub(crate) enum AppKind<'a, State> {
} }
impl<State> AppKind<'_, State> { impl<State> AppKind<'_, State> {
// The `Option` is needed when the `eframe` feature is enabled, because the
// `Eframe` variant has no `egui::Response` to return. Without `eframe` the
// wrap is unnecessary, so we silence `clippy::unnecessary_wraps` for that case.
#[cfg_attr(not(feature = "eframe"), expect(clippy::unnecessary_wraps))]
pub fn run( pub fn run(
&mut self, &mut self,
ui: &mut egui::Ui, ui: &mut egui::Ui,

View File

@@ -713,3 +713,49 @@ fn collapsing_panel_must_not_grow_enclosing_window() {
); );
} }
} }
/// The hint text of a `TextEdit` should follow the same alignment as the input
/// text, instead of always being left-top aligned.
///
/// Regression test for <https://github.com/emilk/egui/issues/8309>.
#[test]
pub fn textedit_hint_text_should_follow_text_alignment() {
let mut input = String::new();
let mut harness = Harness::builder()
.with_size(Vec2::new(200.0, 40.0))
.build_ui(|ui| {
ui.add(
egui::TextEdit::singleline(&mut input)
.hint_text("Hint")
.desired_width(200.0)
.horizontal_align(egui::Align::Center),
);
});
harness.run();
let text_edit = harness.get_by_role(accesskit::Role::TextInput);
let edit_rect = text_edit.rect();
// Find the hint text shape (the only text shape while the input is empty).
let hint_shape = harness
.output()
.shapes
.iter()
.find_map(|clipped| {
let egui::epaint::Shape::Text(text_shape) = &clipped.shape else {
return None;
};
(text_shape.galley.text() == "Hint").then_some(text_shape)
})
.expect("hint text shape should be painted");
let hint_center_x = hint_shape.pos.x + hint_shape.galley.size().x / 2.0;
let edit_center_x = edit_rect.center().x;
assert!(
(hint_center_x - edit_center_x).abs() < 1.0,
"hint text should be centered in the TextEdit: hint_center_x={hint_center_x}, \
edit_center_x={edit_center_x}, edit_rect={edit_rect:?}",
);
}