mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-30 04:37:29 -06:00
The modal dialogs were anchored to the window's centre, so none of them could be moved. They now share one modal frame (dialogs/modal.rs): the band from the window's top edge down to its heading moves it, as a title bar would, with a grab cursor; it stays on screen and keeps where it was moved (per dialog, for the session). The shared dialog frame, New Document (and More Settings), Preferences, Keyboard Shortcuts, Workspaces and Find Font all use it; About moves by its title bar. The form dialogs drew every string field as a text box, so Offset Path's Joins had to be typed. Their choice fields are now dropdowns (Joins: Miter, Round, Bevel; Average's axis; Shear's and Reflect's axis), the fields follow the dialog's order (Offset, Joins, Miter Limit; Rows, Columns, Gutter) instead of alphabetical, and Offset Path has a Preview that Cancel rolls back. The axis labels are translated in a new "axis" catalog context: the plain Horizontal and Vertical rows mean writing direction in Japanese. ja, es, zh-hans and zh-hant get the axis rows (Horizontal, Vertical, Both) and Joins, Miter, Bevel, Offset and Axis. Headless tests drag the heading of four dialog windows (moves, a drag below the heading doesn't, kept on screen, reopens where it was left), open the Joins dropdown and choose Round (same result as the command with round joins), and check the axis dropdowns and the preview. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
43 lines
2.1 KiB
Rust
43 lines
2.1 KiB
Rust
//! The shared dialog frame at every UI scale (Preferences → User Interface → UI Scaling zooms the
|
||
//! whole interface, so a window holds fewer points).
|
||
|
||
use serde_json::json;
|
||
use vectorcraft_engine::Session;
|
||
|
||
use super::*;
|
||
use crate::theme;
|
||
|
||
/// The save prompt's rect after a few frames in a window of `physical` pixels at UI scale `zoom`.
|
||
fn prompt(app: &mut VectorcraftApp, ctx: &egui::Context, physical: egui::Vec2, zoom: f32) -> (egui::Rect, egui::Rect) {
|
||
ctx.set_zoom_factor(zoom);
|
||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, physical / zoom);
|
||
for _ in 0..10 {
|
||
let input = egui::RawInput { screen_rect: Some(screen), ..Default::default() };
|
||
ctx.run_ui(input, |ui| show(app, ui.ctx())).textures_delta.clear();
|
||
}
|
||
let rect = ctx.memory(|m| m.area_rect(egui::Id::new(("dialog", crate::unsaved::KIND)))).unwrap();
|
||
(rect, screen)
|
||
}
|
||
|
||
#[test]
|
||
fn the_save_prompt_fits_its_content_and_the_window_at_every_ui_scale() {
|
||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||
app.run("file.new", json!({"width": 200, "height": 200})).unwrap();
|
||
let ctx = egui::Context::default();
|
||
theme::install_fonts(&ctx);
|
||
theme::apply(&ctx, Default::default());
|
||
for name in ["Untitled-1", "Quarterly campaign poster, final revision with the printer's notes"] {
|
||
app.ui.dialog = Some(Dialog::new(crate::unsaved::KIND, json!({"index": 0, "name": name, "then": "close"})));
|
||
// Scaled up and down while the prompt is open, in a full-size and in the smallest window.
|
||
for (w, h, zoom) in
|
||
[(1440.0, 900.0, 1.0), (1440.0, 900.0, 2.0), (1440.0, 900.0, 1.5), (800.0, 500.0, 2.0), (800.0, 500.0, 1.0), (1440.0, 900.0, 0.75)]
|
||
{
|
||
let (rect, screen) = prompt(&mut app, &ctx, egui::vec2(w, h), zoom);
|
||
let at = format!("{name}: {w} × {h} px at {zoom}×: {rect:?} in {screen:?}");
|
||
assert!(screen.contains_rect(rect), "inside the window, {at}");
|
||
// As tall as its text and buttons (no empty band around the buttons).
|
||
assert!(rect.height() < 200.0, "{at}");
|
||
}
|
||
}
|
||
}
|