mirror of
https://github.com/aphrody-labs/wordcraft.git
synced 2026-10-14 03:29:04 +00:00
Every dialog was anchored CENTER_CENTER, which egui re-applies each frame, so a tab of a different height split the change between top and bottom and the tab strip jumped. Dialogs are now centred on the frame they open, then anchored CENTER_TOP at that top edge until they close; only the bottom edge moves. egui still keeps the window on screen. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
2 files changed
+72
-11
No files matched your search
@@ -897,8 +897,37 @@ pub fn table_grid_picker(ui: &mut Ui, app: &mut WordApp) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Where the open dialog's top edge sits, as (window id, offset below the top of the screen).
|
||||
fn dialog_top_key() -> egui::Id {
|
||||
egui::Id::new("dialog_top")
|
||||
}
|
||||
|
||||
/// Anchors a dialog: centred on the frame it opens, then with its top edge held where that put
|
||||
/// it, so a taller or shorter tab (Paragraph, Font, Borders and Shading…) only moves the bottom
|
||||
/// edge and the tab strip stays under the mouse (#507). egui keeps it on screen if it grows past
|
||||
/// the bottom.
|
||||
fn place_dialog<'a>(ctx: &egui::Context, id: egui::Id, w: egui::Window<'a>) -> egui::Window<'a> {
|
||||
match ctx.data(|d| d.get_temp::<(egui::Id, f32)>(dialog_top_key())) {
|
||||
Some((open, top)) if open == id => w.anchor(egui::Align2::CENTER_TOP, vec2(0.0, top)),
|
||||
_ => w.anchor(egui::Align2::CENTER_CENTER, vec2(0.0, -40.0)),
|
||||
}
|
||||
}
|
||||
|
||||
/// After a dialog's first frame: holds its top edge where centring its measured size put it.
|
||||
fn remember_dialog_top(ctx: &egui::Context, id: egui::Id, rect: egui::Rect) {
|
||||
if ctx.data(|d| d.get_temp::<(egui::Id, f32)>(dialog_top_key())).is_some_and(|(open, _)| open == id) {
|
||||
return;
|
||||
}
|
||||
let screen = ctx.content_rect();
|
||||
let top = (screen.center().y - 40.0 - rect.height() / 2.0 - screen.top()).max(0.0);
|
||||
ctx.data_mut(|d| d.insert_temp(dialog_top_key(), (id, top)));
|
||||
}
|
||||
|
||||
pub fn show(app: &mut WordApp, ctx: &egui::Context) {
|
||||
let Some(mut d) = app.dialog.take() else { return };
|
||||
let Some(mut d) = app.dialog.take() else {
|
||||
ctx.data_mut(|d| d.remove::<(egui::Id, f32)>(dialog_top_key()));
|
||||
return;
|
||||
};
|
||||
let mut open = true;
|
||||
let mut close = false;
|
||||
let title = match &d {
|
||||
@@ -939,16 +968,14 @@ pub fn show(app: &mut WordApp, ctx: &egui::Context) {
|
||||
Dialog::DefineList { .. } => "Define New Multilevel List",
|
||||
Dialog::TrackOptions { .. } => "Track Changes Options",
|
||||
};
|
||||
egui::Window::new(tl!(title))
|
||||
.id(egui::Id::new(("dialog", title)))
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(egui::Align2::CENTER_CENTER, vec2(0.0, -40.0))
|
||||
.open(&mut open)
|
||||
.show(ctx, |ui| {
|
||||
ui.set_min_width(340.0);
|
||||
close = body(app, ui, &mut d);
|
||||
});
|
||||
let id = egui::Id::new(("dialog", title));
|
||||
let shown = place_dialog(ctx, id, egui::Window::new(tl!(title)).id(id).collapsible(false).resizable(false)).open(&mut open).show(ctx, |ui| {
|
||||
ui.set_min_width(340.0);
|
||||
close = body(app, ui, &mut d);
|
||||
});
|
||||
if let Some(r) = shown {
|
||||
remember_dialog_top(ctx, id, r.response.rect);
|
||||
}
|
||||
if open && !close {
|
||||
app.dialog = Some(d);
|
||||
} else {
|
||||
@@ -2294,4 +2321,37 @@ mod tests {
|
||||
assert_eq!(p.align, Some(Align::Left), "right on the page is the start edge");
|
||||
assert_eq!(p.indent_left, Some(72.0), "the right indent is the start indent");
|
||||
}
|
||||
|
||||
/// Switching the Paragraph dialog's tabs re-centred it, so the tab strip jumped up and down
|
||||
/// by half the height difference (#507): the top edge now stays put and only the bottom moves.
|
||||
#[test]
|
||||
fn switching_paragraph_tabs_keeps_the_top_edge() {
|
||||
use egui_kittest::kittest::Queryable;
|
||||
let mut app = app_with("Some text");
|
||||
app.dialog = Dialog::open("paragraph", &mut app);
|
||||
let mut h = egui_kittest::Harness::builder().with_size(egui::vec2(1440.0, 900.0)).build_ui_state(
|
||||
|ui, app: &mut WordApp| {
|
||||
let ctx = ui.ctx().clone();
|
||||
app.logic(&ctx);
|
||||
app.ui(ui);
|
||||
},
|
||||
app,
|
||||
);
|
||||
let mut tops = vec![];
|
||||
let mut oks = vec![];
|
||||
for page in [0u8, 1, 2, 0] {
|
||||
if let Some(Dialog::Paragraph { tab, .. }) = &mut h.state_mut().dialog {
|
||||
*tab = page;
|
||||
}
|
||||
for _ in 0..4 {
|
||||
h.step();
|
||||
}
|
||||
tops.push(h.get_by_label("Indents and Spacing").rect().top());
|
||||
oks.push(h.get_by_role_and_label(egui::accesskit::Role::Button, "OK").rect().top());
|
||||
}
|
||||
assert!((oks[0] - oks[1]).abs() > 10.0, "the tabs differ in height: {oks:?}");
|
||||
for t in &tops {
|
||||
assert!((t - tops[0]).abs() <= 0.5, "the tab strip stays put: {tops:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ own (`AGENTS.md`: no Microsoft iconography, ever).
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-11 | trivial | Dialogs keep their top edge in place when switching tabs (Paragraph, Font, Borders and Shading…): only the bottom edge moves (#507) |
|
||||
| 2026-10-11 | trivial | Interface size setting in File › Options › General (80–200 %), kept between runs and applied on top of the system's display scale; `ui.interfaceSize` for agents (#475) |
|
||||
| 2026-10-11 | trivial | Font box (ribbon and mini toolbar): typing lists the matching fonts (prefix first, then any part of the name); Up/Down, Enter or a click picks one, Escape cancels (#359) |
|
||||
| 2026-10-10 | trivial | Tabs dialog (Paragraph › Tabs…, double-click a ruler tab) and Borders and Shading with Borders, Page Border and Shading tabs (#320) |
|
||||
|
||||
Reference in new issue
Block a user