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:
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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:?}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user