mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 13:20:05 -04:00
## 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>
75 lines
2.7 KiB
Rust
75 lines
2.7 KiB
Rust
use egui::Frame;
|
|
|
|
type AppKindUiState<'a, State> = Box<dyn FnMut(&mut egui::Ui, &mut State) + 'a>;
|
|
type AppKindUi<'a> = Box<dyn FnMut(&mut egui::Ui) + 'a>;
|
|
|
|
/// In order to access the [`eframe::App`] trait from the generic `State`, we store a function pointer
|
|
/// here that will return the dyn trait from the struct.
|
|
/// In the builder we have the correct `where`-clause to be able to create this.
|
|
/// Later we can use it anywhere to get the [`eframe::App`] from the `State`.
|
|
#[cfg(feature = "eframe")]
|
|
pub(crate) struct AppKindEframe<State> {
|
|
pub get_app: fn(&mut State) -> &mut dyn eframe::App,
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
pub take_app: fn(State) -> Box<dyn eframe::App>,
|
|
pub frame: eframe::Frame,
|
|
}
|
|
|
|
pub(crate) enum AppKind<'a, State> {
|
|
Ui(AppKindUi<'a>),
|
|
UiState(AppKindUiState<'a, State>),
|
|
#[cfg(feature = "eframe")]
|
|
Eframe(AppKindEframe<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(
|
|
&mut self,
|
|
ui: &mut egui::Ui,
|
|
state: &mut State,
|
|
sizing_pass: bool,
|
|
) -> Option<egui::Response> {
|
|
match self {
|
|
#[cfg(feature = "eframe")]
|
|
AppKind::Eframe(AppKindEframe { get_app, frame, .. }) => {
|
|
let app = get_app(state);
|
|
app.logic(ui, frame);
|
|
app.ui(ui, frame);
|
|
None
|
|
}
|
|
kind_ui => Some(kind_ui.run_ui(ui, state, sizing_pass)),
|
|
}
|
|
}
|
|
|
|
fn run_ui(
|
|
&mut self,
|
|
ui: &mut egui::Ui,
|
|
state: &mut State,
|
|
sizing_pass: bool,
|
|
) -> egui::Response {
|
|
let mut builder = egui::UiBuilder::new();
|
|
if sizing_pass {
|
|
builder.sizing_pass = true;
|
|
}
|
|
ui.scope_builder(builder, |ui| {
|
|
Frame::central_panel(ui.style())
|
|
// Only set outer margin, so we show no frame for tests with only free-floating windows/popups:
|
|
.outer_margin(8.0)
|
|
.inner_margin(0.0)
|
|
.show(ui, |ui| match self {
|
|
AppKind::Ui(f) => f(ui),
|
|
AppKind::UiState(f) => f(ui, state),
|
|
#[cfg(feature = "eframe")]
|
|
AppKind::Eframe(_) => unreachable!(
|
|
"run_ui should only be called with AppKind::Ui or AppKind::UiState"
|
|
),
|
|
});
|
|
})
|
|
.response
|
|
}
|
|
}
|