Merge pull request #10685 from RickiNano/fix/save-for-web-zoom-creep-10675

Save for Web: keep its size and zoom while the pointer moves (#10675)
This commit is contained in:
aiongg authored and GitHub committed 2026-11-01 08:30:53 -05:00
commit c79efbe05b
2 files changed
+55 -4

No files matched your search

@@ -543,6 +543,52 @@ fn web_preview_grows_with_resize_and_accepts_zoom_and_pan() {
assert_eq!(h.state().ui.dialogs.len(), 1);
}
/// #10675: moving the pointer over Save for Web repaints it, and each repaint must lay it out the
/// same way. At a fractional display scale the previews came out a fraction of a pixel wider than
/// the room they were given, so the dialog grew a little on every repaint and the fitted zoom
/// with it.
#[test]
fn web_preview_keeps_its_size_and_zoom_while_the_pointer_moves() {
let mut drifted = Vec::new();
for ppp in [1.0, 1.25, 1.5, 1.75, 2.25] {
for screen in [egui::vec2(1600.0, 1100.0), egui::vec2(1097.0, 617.0), egui::vec2(1280.0, 720.0)] {
for view in ["2up", "4up", "original"] {
let mut app = PhotocraftApp::new(photocraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({"width": 1980, "height": 1200})).unwrap();
let id = open_web(&mut app);
app.ui.dialog_mut(id).unwrap().fields.insert("__view".into(), json!(view));
let mut h = egui_kittest::Harness::builder().with_size(screen).with_pixels_per_point(ppp).build_ui_state(
|ui, app: &mut PhotocraftApp| {
if ui.ctx().fonts(|f| f.families().contains(&egui::FontFamily::Name("semibold".into()))) {
crate::dialogs::show(app, ui.ctx());
}
},
app,
);
PhotocraftApp::setup_context(&h.ctx, Default::default());
h.run_steps(8);
let layout = |h: &egui_kittest::Harness<'_, PhotocraftApp>| {
let f = &h.state().ui.dialogs[0].fields;
let rects: Vec<egui::Rect> = h.ctx.data(|m| m.get_temp(egui::Id::unique("pc-dialog-rects"))).unwrap();
(rects[0], f["__previewRects"].clone(), f["__effectiveZoom"].clone())
};
let before = layout(&h);
let pane = &before.1[0];
let at = egui::pos2(pane[0].as_f64().unwrap() as f32 + 20.0, pane[1].as_f64().unwrap() as f32 + 20.0);
for i in 0..30 {
h.event(egui::Event::PointerMoved(at + egui::vec2(i as f32 * 3.0, i as f32)));
h.step();
}
let after = layout(&h);
if after != before {
drifted.push(format!("{view} at {ppp}x on {screen:?}: {before:?} -> {after:?}"));
}
}
}
}
assert!(drifted.is_empty(), "the dialog or its zoom changed while the pointer moved: {drifted:#?}");
}
#[test]
fn palette_draft_survives_navigation_and_resets_when_colors_change() {
let (app, _) = app();
+9 -4
View File
@@ -265,7 +265,12 @@ pub(super) fn web_body(app: &mut PhotocraftApp, ui: &mut egui::Ui, f: &mut Map<S
}
let p = params(f);
f.insert("__previewRects".into(), json!([]));
let area = egui::vec2((ui.available_width() - 270.0 - ui.spacing().item_spacing.x).max(260.0), (ui.available_height() - 48.0).max(180.0));
// Whole physical pixels, rounded down: the dialog grows to fit its contents, so a preview a
// fraction of a pixel too wide at a fractional scale widened it on every repaint (#10675).
let ppp = ui.ctx().pixels_per_point();
let floor = |v: f32| (v * ppp).floor() / ppp;
let area = egui::vec2(floor((ui.available_width() - 270.0 - ui.spacing().item_spacing.x).max(260.0)), floor((ui.available_height() - 48.0).max(180.0)));
let (half_w, half_h) = (floor(area.x / 2.0 - 4.0), floor(area.y / 2.0 - 4.0));
ui.horizontal_top(|ui| {
// Previews grow with the dialog; settings retain their compact width.
ui.vertical(|ui| {
@@ -275,13 +280,13 @@ pub(super) fn web_body(app: &mut PhotocraftApp, ui: &mut egui::Ui, f: &mut Map<S
"optimized" => web_pane(app, ui, area, Some(&p), f, 1, frame),
"2up" => {
ui.horizontal(|ui| {
web_pane(app, ui, egui::vec2(area.x / 2.0 - 4.0, area.y), None, f, 0, frame);
web_pane(app, ui, egui::vec2(area.x / 2.0 - 4.0, area.y), Some(&p), f, 1, frame);
web_pane(app, ui, egui::vec2(half_w, area.y), None, f, 0, frame);
web_pane(app, ui, egui::vec2(half_w, area.y), Some(&p), f, 1, frame);
});
}
_ => {
let v = variants(&p);
let cell = egui::vec2(area.x / 2.0 - 4.0, area.y / 2.0 - 4.0);
let cell = egui::vec2(half_w, half_h);
ui.horizontal(|ui| {
web_pane(app, ui, cell, None, f, 0, frame);
web_pane(app, ui, cell, Some(&v[0]), f, 1, frame);