diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 1f59647c1..9137442f0 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -152,8 +152,8 @@ pub(crate) use export_for_screens::{ saved_options as screen_saved_options, saved_rows as screen_saved_rows, }; pub(crate) use form::{humanize, number_list_text, plugin_color_button, read_number_list, unit_suffix}; -pub(crate) use new_document::preset_name; pub use new_document::{create_without_dialog as new_document_without_dialog, open as open_new_document, preset_card}; +pub(crate) use new_document::{popup_open as new_document_popup_open, preset_name}; pub use png_options::open as open_raster_options; pub use save_pdf::{open as open_save_pdf, open_preset as open_pdf_preset}; pub use tools::open_tool_dialog; @@ -560,6 +560,10 @@ pub(crate) fn field_set(app: &mut VectorcraftApp, field: &str, old: Option<&Valu /// opened over (`ui.dialog.cancel`). pub fn cancel(app: &mut VectorcraftApp) { missing_fonts::requeue_replaced(app); + // More Settings' pop-up and Save Preset end on their own; New Document stays. + if app.ui.dialog.as_mut().is_some_and(new_document::close_popup) { + return; + } if let Some(d) = app.ui.dialog.take() { if d.kind == swatch_conflict::KIND { crate::placing::cancel_swatch_paste(app); diff --git a/crates/ui-egui/src/dialogs/flatten.rs b/crates/ui-egui/src/dialogs/flatten.rs index 3cd0a5111..485934893 100644 --- a/crates/ui-egui/src/dialogs/flatten.rs +++ b/crates/ui-egui/src/dialogs/flatten.rs @@ -316,6 +316,21 @@ mod tests { assert!(!transparent_left(&app)); } + /// Cancel with the Save Preset row showing closes the dialog and rolls the preview back. + #[test] + fn cancel_while_saving_a_preset_closes_the_dialog() { + let mut app = scene(); + let before = app.session.doc().unwrap().doc.clone(); + app.run("ui.flattenTransparencyDialog", json!({})).unwrap(); + app.ui.dialog.as_mut().unwrap().fields.insert("preview".into(), json!(true)); + frame(&mut app); + app.ui.dialog.as_mut().unwrap().fields.insert(SAVING.into(), json!(true)); + frame(&mut app); + super::super::cancel(&mut app); + assert!(app.ui.dialog.is_none() && !app.session.in_interaction()); + assert_eq!(app.session.doc().unwrap().doc, before, "Cancel rolls back"); + } + #[test] fn ok_says_in_the_status_bar_when_an_image_could_not_be_decoded() { const WARNING: &str = "an image couldn't be decoded and is missing from the picture"; diff --git a/crates/ui-egui/src/dialogs/new_document.rs b/crates/ui-egui/src/dialogs/new_document.rs index 982dd7c6f..8e0200933 100644 --- a/crates/ui-egui/src/dialogs/new_document.rs +++ b/crates/ui-egui/src/dialogs/new_document.rs @@ -1,14 +1,16 @@ //! File → New: category tabs (Recent, Saved and the built-in categories of `file.newPresets`) //! over a grid of preset cards, and a Preset Details column (name and Save Preset, size and //! units, orientation, artboards, bleed, background contents, Advanced Options, More Settings); -//! Close and Create. More Settings is the classic form of the same fields (artboard layout, -//! spacing and columns, profile and size lists), so both share one set of fields and widgets. +//! Close and Create. More Settings opens a pop-up over the dialog with the artboard layout, spacing +//! and columns, editing the same fields. Save Preset turns the top of Preset Details into the +//! preset's name, Cancel and Save, and hides Close and Create until the preset is saved or canceled; +//! the rows under it stay editable and are what the preset stores. //! //! Fields: `file.new`'s params (`preset`, `name`, `width`, `height` (pt), `units`, `artboards`, //! `artboardLayout`, `bleed`, `backgroundContents`, `colorMode`, `rasterEffectsPpi`, -//! `previewMode`), plus `category` (the tab, also More Settings' Profile), `advanced` (Advanced -//! Options open), `bleedLinked` and `presetName` (Save Preset's name). Setting `preset` fills the -//! fields from that preset; changing any of them afterwards makes the settings custom. +//! `previewMode`), plus `category` (the tab), `advanced` (Advanced Options open), `bleedLinked` and +//! `presetName` (Save Preset's name). Setting `preset` fills the fields from that preset; changing +//! any of them afterwards makes the settings custom. use serde_json::{Map, Value, json}; use vectorcraft_doc::Unit; @@ -20,11 +22,11 @@ use crate::theme::{self, Tokens}; use crate::{VectorcraftApp, widgets}; pub(super) const KIND: &str = "newDocument"; -/// More Settings. +/// New Document with its More Settings pop-up open. pub(super) const MORE: &str = "newDocumentMore"; pub(super) const SPEC: DialogSpec = DialogSpec { inputs: |_| &["presetName"], ..DialogSpec::window(show, confirm) }; -pub(super) const MORE_SPEC: DialogSpec = DialogSpec { inputs: |_| &["presetName"], ..DialogSpec::window(show_more, confirm) }; +pub(super) const MORE_SPEC: DialogSpec = SPEC; /// The fields `file.new` takes. const PARAMS: [&str; 12] = [ @@ -44,8 +46,11 @@ const PARAMS: [&str; 12] = [ /// The preset the fields were last filled from, as `apply` set them; a Recent or Saved card keeps /// its own units here, which the units chosen in Preset Details don't change. const APPLIED: &str = "__applied"; -/// Save Preset's name field is showing. +/// Preset Details is saving a preset (Save Preset's name, Cancel and Save are showing). const SAVING: &str = "__saving"; +/// Preset Details has drawn the saving top: set from the mode's second frame, so the first one fills +/// in the default name and focuses it. +const SAVING_SHOWN: &str = "__savingShown"; /// The units New Document offers (all ten). const UNITS: [Unit; 10] = [ Unit::Pixels, @@ -65,6 +70,10 @@ const COLOR_MODES: [(&str, &str); 2] = [("rgb", "RGB Color"), ("cmyk", "CMYK Col const DETAILS: f32 = 292.0; /// Height of the dialog's content. const HEIGHT: f32 = 600.0; +/// Height of the top of Preset Details in either state: the rows under it don't move. +const HEADER: f32 = 100.0; +/// Width of the More Settings pop-up's rows. +const MORE_WIDTH: f32 = 310.0; /// Open New Document on the most recent settings (or Letter), named after the next untitled /// document. @@ -116,8 +125,17 @@ fn sync(app: &VectorcraftApp, d: &mut Dialog) { } } -/// Create the document; the dialog stays open when that fails (bad size, …). +/// Create the document; the dialog stays open when that fails (bad size, …). While a preset is being +/// saved this is its Save, and no document is created. fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result { + if d.bool(SAVING) { + let mut d = d.clone(); + let r = save_preset(app, &mut d); + if r.is_ok() { + app.ui.dialog = Some(d); + } + return r; + } let p = params(app, d); let r = app.run("file.new", Value::Object(p)); if r.is_ok() { @@ -175,17 +193,45 @@ fn window(ctx: &egui::Context, id: &str, margin: i8, add: impl FnOnce(&mut egui: super::modal::show(ctx, id, egui::Id::unique(("dialog", id)), -20.0, margin, add); } +/// Whether More Settings' pop-up is open over New Document. +pub(crate) fn popup_open(d: &Dialog) -> bool { + d.kind == MORE +} + +/// Close New Document's More Settings pop-up, or else leave its Save Preset, keeping the fields. +/// False when neither is showing, or `d` is another dialog. +pub(super) fn close_popup(d: &mut Dialog) -> bool { + if d.kind != KIND && d.kind != MORE { + return false; + } + if popup_open(d) { + d.kind = KIND.into(); + return true; + } + let saving = d.bool(SAVING); + if saving { + d.fields.insert(SAVING.into(), json!(false)); + d.fields.remove(SAVING_SHOWN); + } + saving +} + /// What the buttons of a window asked for. #[derive(Default)] struct Buttons { create: bool, close: bool, + /// More Settings' pop-up was open as the frame began: Enter is the pop-up's. + popup: bool, + /// A preset was being saved as the frame began: nothing creates the document this frame. + saving: bool, } impl Buttons { /// Act on the buttons (Enter also creates) once the window is drawn. fn finish(self, app: &mut VectorcraftApp, ctx: &egui::Context, d: Dialog) { - let create = self.create || ctx.input(|i| i.key_pressed(egui::Key::Enter)) && !ctx.memory(|m| m.focused().is_some()); + let enter = !self.popup && crate::shortcuts::pressed_anew(ctx, egui::Key::Enter) && !ctx.memory(|m| m.focused().is_some()); + let create = !self.saving && !d.bool(SAVING) && (self.create || enter); app.ui.dialog = Some(d); if self.close { app.ui.dialog = None; @@ -199,7 +245,7 @@ fn show(app: &mut VectorcraftApp, ctx: &egui::Context) { let Some(mut d) = app.ui.dialog.clone() else { return }; sync(app, &mut d); let t = Tokens::get(ctx); - let mut b = Buttons::default(); + let mut b = Buttons { popup: popup_open(&d), saving: d.bool(SAVING), ..Default::default() }; window(ctx, KIND, 0, |ui| { // No heading: the top margin (14) and the empty part of the category tabs' row (30) move the // window. @@ -220,6 +266,10 @@ fn show(app: &mut VectorcraftApp, ctx: &egui::Context) { }); }); }); + // While More Settings' pop-up is open, a click outside it only closes it. + if b.popup { + ui.interact(ui.min_rect(), ui.scope_id().with("newdoc-popup-shield"), egui::Sense::CLICK | egui::Sense::DRAG); + } }); b.finish(app, ctx, d); } @@ -356,17 +406,24 @@ fn details(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, b: &mut let t = Tokens::get(ui.ctx()); ui.spacing_mut().item_spacing = egui::vec2(6.0, 4.0); let top = ui.cursor().top(); - ui.label(egui::RichText::new(tl!("PRESET DETAILS")).font(theme::semibold(11.0)).color(t.text_dim)); - ui.add_space(8.0); - let caption = form::caption(ui, tl!("Document Name")); - ui.horizontal(|ui| { - ui.label_inputs_by(caption.id, |ui| form::text(ui, d, "name", DETAILS - 50.0)); - save_button(app, ui, d); - }); - if d.bool(SAVING) { - save_row(app, ui, d); + let saving = d.bool(SAVING); + if saving { + let opening = !d.bool(SAVING_SHOWN); + d.fields.insert(SAVING_SHOWN.into(), json!(true)); + save_header(app, ui, d, opening); + } else { + d.fields.remove(SAVING_SHOWN); + ui.label(egui::RichText::new(tl!("PRESET DETAILS")).font(theme::semibold(11.0)).color(t.text_dim)); + ui.add_space(8.0); + let caption = form::caption(ui, tl!("Document Name")); + ui.horizontal(|ui| { + ui.label_inputs_by(caption.id, |ui| form::text(ui, d, "name", DETAILS - 50.0)); + if widgets::icon_button(ui, "save", tl!("Save Preset"), false, 26.0).clicked() { + d.fields.insert(SAVING.into(), json!(true)); + } + }); } - ui.add_space(8.0); + ui.add_space((HEADER - (ui.cursor().top() - top)).max(0.0)); let unit = unit(d); let caption = form::caption(ui, tl!("Width")); ui.horizontal(|ui| { @@ -387,7 +444,7 @@ fn details(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, b: &mut ui.add_space(6.0); ui.vertical(|ui| { let caption = form::caption(ui, tl!("Artboards")); - ui.label_inputs_by(caption.id, |ui| artboards(ui, d, 76.0)); + ui.label_inputs_by(caption.id, |ui| artboards(ui, d, "newdoc-artboards", 76.0)); }); }); ui.add_space(8.0); @@ -399,8 +456,9 @@ fn details(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, b: &mut ui.add_space(8.0); advanced(ui, d, 110.0, DETAILS - 116.0); ui.add_space(12.0); - if widgets::flat_button(ui, tl!("More Settings"), DETAILS).clicked() { - d.kind = MORE.into(); + more_settings(ui, d); + if saving { + return; } // Close and Create at the bottom right. ui.add_space((HEIGHT - (ui.cursor().top() - top) - 28.0).max(12.0)); @@ -411,44 +469,159 @@ fn details(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, b: &mut }); } -/// The Save Preset button beside the name: it shows or hides [`save_row`]. -fn save_button(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) { - if widgets::icon_button(ui, "save", tl!("Save Preset"), d.bool(SAVING), 26.0).clicked() { - let saving = !d.bool(SAVING); - d.fields.insert(SAVING.into(), json!(saving)); - if saving && d.str("presetName").is_empty() { - let template = "Preset {number}"; - let name = crate::i18n::fmt(tl!(template), &[("number", &(app.session.prefs.new_doc_presets.len() + 1).to_string())]); - d.fields.insert("presetName".into(), json!(name)); - } +/// The key under which a layer remembers that its first field should take the keyboard focus. +fn pending_focus(ui: &egui::Ui) -> egui::Id { + ui.layer_id().id.with("newdoc-focus") +} + +/// Draw `add`; its first enabled field takes the keyboard focus, with its text selected, when +/// `opening` says so or a failed Save asked for it. +fn focus_first(ui: &mut egui::Ui, opening: bool, add: impl FnOnce(&mut egui::Ui) -> R) -> R { + let pending = pending_focus(ui); + if opening { + ui.data_mut(|m| m.insert_temp(pending, true)); + } + // The first frame of a new window only measures it: no field can take the focus until the next. + let focus = !ui.is_sizing_pass() && ui.data(|m| m.get_temp::(pending)).unwrap_or(false); + if focus { + ui.data_mut(|m| m.insert_temp(widgets::dialog_focus_flag(), true)); + } + let r = add(ui); + if focus && ui.data_mut(|m| m.remove_temp::(widgets::dialog_focus_flag())).is_none() { + ui.data_mut(|m| m.remove::(pending)); + } + r +} + +/// The top of Preset Details while a preset is being saved: the heading, the preset's name, Cancel +/// and Save. Enter in the name is Save. `opening` on the mode's first frame. +fn save_header(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, opening: bool) { + let t = Tokens::get(ui.ctx()); + if opening && d.str("presetName").is_empty() { + d.fields.insert("presetName".into(), json!(new_preset_name(app))); + } + ui.label(egui::RichText::new(tl!("Save Document Preset")).font(theme::semibold(13.0)).color(t.text)); + ui.add_space(5.0); + let name = focus_first(ui, opening, |ui| form::text_edit_named(ui, d, "presetName", tl!("Preset Name"), DETAILS - 14.0)); + let enter = name.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)); + ui.add_space(4.0); + let (mut save, mut cancel) = (enter, false); + ui.allocate_ui_with_layout(egui::vec2(ui.available_width(), 28.0), egui::Layout::right_to_left(egui::Align::Center), |ui| { + save |= widgets::primary_button(ui, tl!("Save")).clicked(); + ui.add_space(8.0); + cancel = widgets::secondary_button(ui, tl!("Cancel")).clicked(); + }); + if cancel { + d.fields.insert(SAVING.into(), json!(false)); + d.fields.remove(SAVING_SHOWN); + } else if save && let Err(e) = save_preset(app, d) { + app.status(e); + let pending = pending_focus(ui); + ui.data_mut(|m| m.insert_temp(pending, true)); } } -/// Save Preset: the preset's name, Save and Cancel. -fn save_row(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) { - ui.add_space(4.0); - let caption = form::caption(ui, tl!("Preset Name")); - ui.horizontal(|ui| { - let name = ui.label_inputs_by(caption.id, |ui| form::text_edit(ui, d, "presetName", DETAILS - 140.0)); - let enter = name.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)); - if widgets::flat_button(ui, tl!("Save"), 54.0).clicked() || enter { - let mut p: Map = PARAMS.iter().filter_map(|k| Some((k.to_string(), d.fields.get(*k)?.clone()))).collect(); - p.insert("name".into(), json!(d.str("presetName"))); - p.remove("preset"); - match app.run("file.newPresets.save", Value::Object(p)) { - Ok(r) => { - d.fields.insert(SAVING.into(), json!(false)); - d.fields.insert("category".into(), json!("Saved")); - d.fields.insert("preset".into(), r["name"].clone()); - d.fields.remove(APPLIED); +/// Save Preset's default name: the first "Preset N" no saved preset has (`file.newPresets.save` +/// replaces a preset of the same name). +fn new_preset_name(app: &VectorcraftApp) -> String { + let saved = &app.session.prefs.new_doc_presets; + let template = "Preset {number}"; + (1..=saved.len() + 1) + .map(|n| crate::i18n::fmt(tl!(template), &[("number", &n.to_string())])) + .find(|name| !saved.iter().any(|p| p.name.trim().eq_ignore_ascii_case(name.trim()))) + .unwrap_or_default() +} + +/// Save the fields as the preset named `presetName` and leave saving, on the saved preset. +fn save_preset(app: &mut VectorcraftApp, d: &mut Dialog) -> Result { + let mut p: Map = PARAMS.iter().filter_map(|k| Some((k.to_string(), d.fields.get(*k)?.clone()))).collect(); + p.insert("name".into(), json!(d.str("presetName"))); + p.remove("preset"); + let r = app.run("file.newPresets.save", Value::Object(p))?; + d.fields.insert(SAVING.into(), json!(false)); + d.fields.remove(SAVING_SHOWN); + // The next Save Preset offers a new default name instead of this preset's. + d.fields.remove("presetName"); + d.fields.insert("category".into(), json!("Saved")); + d.fields.insert("preset".into(), r["name"].clone()); + d.fields.remove(APPLIED); + Ok(r) +} + +/// The More Settings button and its pop-up, which holds what Preset Details doesn't: the artboard +/// layout, spacing and columns. Done, a click outside it and Escape close it; the fields it edited +/// stay as they are. +fn more_settings(ui: &mut egui::Ui, d: &mut Dialog) { + let button = widgets::flat_button(ui, tl!("More Settings"), DETAILS); + let mut open = (d.kind == MORE) != button.clicked(); + let mut done = false; + let opening = button.ctx.read_response(egui::Popup::default_response_id(&button)).is_none(); + egui::Popup::from_response(&button) + .open_bool(&mut open) + .close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside) + .align(egui::RectAlign::TOP_END) + .accessible_name(tl!("More Settings")) + .show(|ui| done = more_settings_popup(ui, opening, d)); + d.kind = if open && !done { MORE } else { KIND }.into(); +} + +/// More Settings' pop-up. True when Done (or Enter) finished with it. +fn more_settings_popup(ui: &mut egui::Ui, opening: bool, d: &mut Dialog) -> bool { + const L: f32 = 150.0; + let t = Tokens::get(ui.ctx()); + let (lay, cols, spacing, rtl) = layout(d); + let n = d.f64("artboards", 1.0); + let several = n > 1.0; + let room = ui.ctx().content_rect().size() - egui::Vec2::splat(4.0 * super::EDGE_GAP) - egui::vec2(0.0, 44.0); + focus_first(ui, opening, |ui| { + egui::ScrollArea::both().id_salt("newdoc-popup-rows").max_width(room.x.max(0.0)).max_height(room.y.max(0.0)).show(ui, |ui| { + ui.set_width(MORE_WIDTH); + ui.label(egui::RichText::new(tl!("More Settings")).font(theme::semibold(13.0)).color(t.text)); + ui.add_space(6.0); + widgets::label_row(ui, tl!("Number of Artboards:"), L, |ui| artboards(ui, d, "newdoc-popup-artboards", 76.0)); + widgets::label_row(ui, "", L, |ui| { + for (l, icon) in [ + (ArtboardLayout::GridByRow, "dc-grid-view"), + (ArtboardLayout::GridByColumn, "grid-3x3"), + (ArtboardLayout::Row, "arrow-left-right"), + (ArtboardLayout::Column, "arrow-up-down"), + ] { + if widgets::icon_button_enabled(ui, icon, l.label(), lay == l, several, 26.0).clicked() { + set_layout(d, "layout", json!(l.id())); + } } - Err(e) => app.status(e), - } - } - if widgets::flat_button(ui, tl!("Cancel"), 60.0).clicked() { - d.fields.insert(SAVING.into(), json!(false)); - } + ui.add_space(6.0); + if widgets::icon_button_enabled(ui, "chevrons-left", tl!("Change to Right-to-Left Layout"), rtl, several, 26.0).clicked() { + set_layout(d, "rightToLeft", json!(!rtl)); + } + }); + ui.add_enabled_ui(several, |ui| { + widgets::label_row(ui, tl!("Spacing:"), L, |ui| { + if let Some(v) = widgets::num_field(ui, "newdoc-spacing", Some(spacing), unit(d), 100.0) { + set_layout(d, "spacing", json!(v.max(0.0))); + } + }); + let grid = matches!(lay, ArtboardLayout::GridByRow | ArtboardLayout::GridByColumn); + ui.add_enabled_ui(grid, |ui| { + widgets::label_row(ui, tl!("Columns:"), L, |ui| { + if let Some(v) = widgets::spin_plain(ui, "newdoc-columns", cols as f64, "", 0, 76.0, 1.0, 1.0, &[]) { + set_layout(d, "columns", json!(v.round().clamp(1.0, n) as u64)); + } + }); + }); + }); + }); }); + ui.add_space(8.0); + // A bad value in a field keeps the pop-up open, as it keeps a dialog open. + let rejected = ui.data_mut(|m| m.remove_temp::(widgets::dialog_reject_commit_flag())).unwrap_or(false); + let enter = !rejected && ui.input(|i| i.key_pressed(egui::Key::Enter)) && ui.memory(|m| m.focused().is_none()); + let row = egui::vec2(ui.min_rect().width(), ui.spacing().interact_size.y); + let mut done = false; + ui.allocate_ui_with_layout(row, egui::Layout::right_to_left(egui::Align::Center), |ui| { + done = widgets::primary_button(ui, tl!("Done")).clicked() || enter; + }); + done } fn units_dropdown(ui: &mut egui::Ui, d: &mut Dialog, width: f32) { @@ -481,10 +654,10 @@ fn orientation(ui: &mut egui::Ui, d: &mut Dialog) { }); } -fn artboards(ui: &mut egui::Ui, d: &mut Dialog, width: f32) { +fn artboards(ui: &mut egui::Ui, d: &mut Dialog, id: &str, width: f32) { let n = d.f64("artboards", 1.0); ui.horizontal(|ui| { - if let Some(v) = widgets::spin_plain(ui, "newdoc-artboards", n, "", 0, width, 1.0, 1.0, &[]) { + if let Some(v) = widgets::spin_plain(ui, id, n, "", 0, width, 1.0, 1.0, &[]) { d.fields.insert("artboards".into(), json!(v.round().clamp(1.0, MAX_ARTBOARDS as f64) as u64)); } }); @@ -543,131 +716,10 @@ fn advanced(ui: &mut egui::Ui, d: &mut Dialog, label: f32, field: f32) { } } -/// More Settings: the classic form of the same fields. -fn show_more(app: &mut VectorcraftApp, ctx: &egui::Context) { - let Some(mut d) = app.ui.dialog.clone() else { return }; - sync(app, &mut d); - let t = Tokens::get(ctx); - let mut b = Buttons::default(); - let mut templates = false; - const L: f32 = 150.0; - window(ctx, MORE, 22, |ui| { - ui.set_width(560.0); - super::modal::heading(ui, tl!("More Settings")); - ui.add_space(12.0); - widgets::label_row(ui, tl!("Name:"), L, |ui| { - form::text(ui, &mut d, "name", 300.0); - save_button(app, ui, &mut d); - }); - if d.bool(SAVING) { - // Under the name, in the fields' column. - widgets::label_row(ui, "", L, |ui| { - ui.vertical(|ui| save_row(app, ui, &mut d)); - }); - } - // Profile: the preset categories (the tabs); Size: the profile's presets. - let profiles: Vec<&str> = newdoc::CATEGORIES.iter().map(|c| c.0).collect(); - let profile = profiles.iter().find(|p| p.eq_ignore_ascii_case(&d.str("category"))).copied().unwrap_or("[Custom]"); - widgets::label_row(ui, tl!("Profile:"), L, |ui| { - if let Some(p) = widgets::dropdown(ui, "newdoc-profile", profile, &profiles, 220.0).and_then(|i| profiles.get(i)) { - d.fields.insert("category".into(), json!(p)); - if let Some(first) = newdoc::category(&app.session, p).and_then(|v| v.into_iter().next()) { - apply(&mut d, &first); - } - } - }); - ui.add_space(6.0); - let (lay, cols, spacing, rtl) = layout(&d); - let n = d.f64("artboards", 1.0); - let several = n > 1.0; - widgets::label_row(ui, tl!("Number of Artboards:"), L, |ui| { - artboards(ui, &mut d, 76.0); - ui.add_space(10.0); - for (l, icon) in [ - (ArtboardLayout::GridByRow, "dc-grid-view"), - (ArtboardLayout::GridByColumn, "grid-3x3"), - (ArtboardLayout::Row, "arrow-left-right"), - (ArtboardLayout::Column, "arrow-up-down"), - ] { - if widgets::icon_button_enabled(ui, icon, l.label(), lay == l, several, 26.0).clicked() { - set_layout(&mut d, "layout", json!(l.id())); - } - } - ui.add_space(6.0); - if widgets::icon_button_enabled(ui, "chevrons-left", tl!("Change to Right-to-Left Layout"), rtl, several, 26.0).clicked() { - set_layout(&mut d, "rightToLeft", json!(!rtl)); - } - }); - ui.add_enabled_ui(several, |ui| { - widgets::label_row(ui, tl!("Spacing:"), L, |ui| { - if let Some(v) = widgets::num_field(ui, "newdoc-spacing", Some(spacing), unit(&d), 100.0) { - set_layout(&mut d, "spacing", json!(v.max(0.0))); - } - ui.add_space(20.0); - widgets::field_label(ui, egui::RichText::new(tl!("Columns:")).color(t.text)); - let grid = matches!(lay, ArtboardLayout::GridByRow | ArtboardLayout::GridByColumn); - ui.add_enabled_ui(grid, |ui| { - if let Some(v) = widgets::spin_plain(ui, "newdoc-columns", cols as f64, "", 0, 76.0, 1.0, 1.0, &[]) { - set_layout(&mut d, "columns", json!(v.round().clamp(1.0, n) as u64)); - } - }); - }); - }); - ui.add_space(6.0); - let sizes = newdoc::category(&app.session, profile).unwrap_or_default(); - let size_names: Vec<&str> = sizes.iter().map(|s| s.name.as_str()).collect(); - let preset = d.str("preset"); - widgets::label_row(ui, tl!("Size:"), L, |ui| { - // The profile's sizes are built in; the preset may be one the user saved. - let shown = if preset.is_empty() { tl!("Custom") } else { preset_name(&preset) }; - if let Some(s) = super::mixed_dropdown(ui, "newdoc-size", shown, &size_names, 220.0, |_| true).and_then(|i| sizes.get(i)) { - apply(&mut d, s); - } - }); - let unit = unit(&d); - widgets::label_row(ui, tl!("Width:"), L, |ui| { - form::length(ui, &mut d, "width", unit, 120.0); - ui.add_space(20.0); - ui.label(egui::RichText::new(tl!("Units:")).color(t.text)); - units_dropdown(ui, &mut d, 120.0); - }); - widgets::label_row(ui, tl!("Height:"), L, |ui| { - form::length(ui, &mut d, "height", unit, 120.0); - ui.add_space(20.0); - ui.label(egui::RichText::new(tl!("Orientation:")).color(t.text)); - orientation(ui, &mut d); - }); - ui.add_space(4.0); - widgets::label_row(ui, tl!("Bleed:"), L, |ui| form::bleed(ui, &mut d, unit, 64.0)); - ui.add_space(4.0); - widgets::label_row(ui, tl!("Background Contents:"), L, |ui| background(ui, &mut d, 220.0)); - ui.add_space(8.0); - advanced(ui, &mut d, L, 220.0); - ui.add_space(16.0); - ui.horizontal(|ui| { - templates = widgets::secondary_button(ui, tl!("Templates…")).on_hover_text(tl!("New from Template")).clicked(); - ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - b.create |= widgets::primary_button(ui, tl!("Create Document")).clicked(); - ui.add_space(8.0); - // Cancel goes back to New Document. - if widgets::secondary_button(ui, tl!("Cancel")).clicked() { - d.kind = KIND.into(); - } - }); - }); - }); - if templates { - app.ui.dialog = None; - if let Err(e) = app.run("file.newFromTemplate", json!({})) { - app.status(e); - } - return; - } - b.finish(app, ctx, d); -} - #[cfg(test)] mod tests { + use egui::accesskit::Role; + use egui_kittest::kittest::{NodeT, Queryable}; use serde_json::json; use vectorcraft_engine::Session; @@ -792,6 +844,26 @@ mod tests { run(&mut app, vec![press(false)]); assert!(app.ui.dialog.is_none(), "the double-click created it: {:?}", app.ui.status); assert_eq!(app.session.documents().len(), 1); + + // While a preset is being saved, a click fills the fields and a double-click creates nothing. + app.run("file.newDialog", json!({})).unwrap(); + set(&mut app, "category", json!("Mobile")); + set(&mut app, "preset", json!("Letter")); + set(&mut app, super::SAVING, json!(true)); + // Far enough from the clicks above that the next two count as a click and a double-click. + for _ in 0..12 { + run(&mut app, vec![]); + } + let at = card(&mut app, &mut run); + let press = |pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() }; + run(&mut app, vec![egui::Event::PointerMoved(at), press(true)]); + run(&mut app, vec![press(false)]); + assert!(field(&app, "preset").as_str().is_some_and(|p| p == "Phone 393×852"), "a click fills the fields: {:?}", field(&app, "preset")); + run(&mut app, vec![press(true)]); + run(&mut app, vec![press(false)]); + run(&mut app, vec![]); + assert_eq!(app.session.documents().len(), 1, "nothing was created while saving"); + assert_eq!(field(&app, super::SAVING), json!(true)); } /// A category tab opens on its first preset, so Orientation matches its (landscape) cards, and @@ -878,14 +950,14 @@ mod tests { assert!(selected(&out), "the turned preset stays chosen: {:?}", field(&app, "preset")); } - /// The document's name field has a caption of its own under the Preset Details heading, and - /// the column stays inside the dialog's height with Save Preset and Advanced Options open. + /// The document's name field has a caption of its own under the Preset Details heading, the + /// column stays inside the dialog's height with Advanced Options open in both states, and saving + /// a preset replaces only the top of the column: the rows under it do not move. #[test] fn the_name_field_is_captioned_and_the_column_fits() { let mut app = VectorcraftApp::new(Session::new(), Default::default()); app.run("file.newDialog", json!({})).unwrap(); set(&mut app, "advanced", json!(true)); - set(&mut app, super::SAVING, json!(true)); let ctx = egui::Context::default(); crate::theme::install_fonts(&ctx); let mut time = 0.0; @@ -897,8 +969,6 @@ mod tests { out.textures_delta.clear(); out }; - run(&mut app); - let out = run(&mut app); fn walk(s: &egui::Shape, out: &mut Vec<(String, egui::Rect)>) { match s { egui::Shape::Text(t) => out.push((t.galley.text().to_string(), t.visual_bounding_rect())), @@ -906,13 +976,33 @@ mod tests { _ => {} } } - let mut texts = vec![]; - out.shapes.iter().for_each(|c| walk(&c.shape, &mut texts)); - let at = |name: &str| texts.iter().find(|(t, _)| t == name).map(|(_, r)| *r).unwrap_or_else(|| panic!("{name} is missing: {texts:?}")); - let (heading, caption, value) = (at("PRESET DETAILS"), at("Document Name"), at("Untitled-1")); + let mut texts_of = |app: &mut VectorcraftApp| { + run(app); + let out = run(app); + let mut texts = vec![]; + out.shapes.iter().for_each(|c| walk(&c.shape, &mut texts)); + texts + }; + let texts = texts_of(&mut app); + let at = |texts: &[(String, egui::Rect)], name: &str| { + texts.iter().find(|(t, _)| t == name).map(|(_, r)| *r).unwrap_or_else(|| panic!("{name} is missing: {texts:?}")) + }; + let (heading, caption, value) = (at(&texts, "PRESET DETAILS"), at(&texts, "Document Name"), at(&texts, "Untitled-1")); assert!(heading.bottom() <= caption.top() && caption.bottom() <= value.top(), "{heading:?} {caption:?} {value:?}"); - assert!(value.bottom() <= at("Preset Name").top(), "the preset's own name field follows"); - let used = at("Create").bottom() - heading.top(); + let used = at(&texts, "Create").bottom() - heading.top(); + assert!(used <= super::HEIGHT, "the column used {used}"); + let width = at(&texts, "Width").top(); + + set(&mut app, super::SAVING, json!(true)); + let saving = texts_of(&mut app); + for gone in ["PRESET DETAILS", "Document Name", "Close", "Create"] { + assert!(saving.iter().all(|(t, _)| t != gone), "{gone} shows while saving"); + } + let (heading, cancel) = (at(&saving, "Save Document Preset"), at(&saving, "Cancel")); + assert!(heading.bottom() <= cancel.top()); + assert!(cancel.bottom() <= at(&saving, "Width").top(), "Cancel and Save are above the rows"); + assert_eq!(at(&saving, "Width").top(), width, "the rows stay where they were"); + let used = at(&saving, "More Settings").bottom() - heading.top(); assert!(used <= super::HEIGHT, "the column used {used}"); } @@ -957,7 +1047,7 @@ mod tests { let out = frame(&mut app); assert!(!out.shapes.is_empty()); } - // More Settings shares the fields; Cancel goes back. + // More Settings edits the same fields. app.ui.dialog.as_mut().unwrap().kind = super::MORE.into(); set(&mut app, "artboards", json!(3)); frame(&mut app); @@ -977,48 +1067,380 @@ mod tests { assert_eq!(app.session.prefs.recent_new_docs[0].name, "Zine"); } - /// Save Preset works in More Settings too (#4893), saving its settings: the artboard layout - /// only More Settings shows among them. - #[test] - fn more_settings_saves_a_preset_of_its_settings() { + type Harness = egui_kittest::Harness<'static, VectorcraftApp>; + + /// New Document open in a harness of `size`, with the app's key handling and the dialogs drawn. + fn harness(size: egui::Vec2) -> Harness { let mut app = VectorcraftApp::new(Session::new(), Default::default()); app.run("file.newDialog", json!({})).unwrap(); - app.ui.dialog.as_mut().unwrap().kind = super::MORE.into(); - set(&mut app, "artboards", json!(4)); - set(&mut app, "artboardLayout", json!({"layout": "column", "spacing": 30})); - set(&mut app, super::SAVING, json!(true)); - set(&mut app, "presetName", json!("Strip")); - let ctx = egui::Context::default(); - crate::theme::install_fonts(&ctx); - let mut time = 0.0; - let mut run = |app: &mut VectorcraftApp, events| { - time += 0.5; - let screen_rect = Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1400.0, 1000.0))); - let raw = egui::RawInput { screen_rect, events, time: Some(time), ..Default::default() }; - let mut out = ctx.run_ui(raw, |ui| crate::dialogs::show(app, ui.ctx())); - out.textures_delta.clear(); - out - }; - run(&mut app, vec![]); - let out = run(&mut app, vec![]); - fn walk(s: &egui::Shape, out: &mut Vec<(String, egui::Pos2)>) { - match s { - egui::Shape::Text(t) => out.push((t.galley.text().to_string(), t.visual_bounding_rect().center())), - egui::Shape::Vec(v) => v.iter().for_each(|s| walk(s, out)), - _ => {} + let mut fonts = false; + let mut h = egui_kittest::Harness::builder().with_size(size).build_ui_state( + move |ui, app: &mut VectorcraftApp| { + if !std::mem::replace(&mut fonts, true) { + crate::theme::install_fonts(ui.ctx()); + return; + } + crate::shortcuts::handle(app, ui.ctx()); + crate::dialogs::show(app, ui.ctx()); + }, + app, + ); + h.run_steps(5); + h + } + + fn big() -> Harness { + harness(egui::vec2(1400.0, 1000.0)) + } + + /// Open a pop-up as a control client would, with `artboards` set, and let it draw. + fn open_more(h: &mut Harness, artboards: u64) { + set(h.state_mut(), "artboards", json!(artboards)); + h.state_mut().ui.dialog.as_mut().unwrap().kind = super::MORE.into(); + h.run_steps(4); + } + + fn kind(h: &Harness) -> String { + h.state().ui.dialog.as_ref().unwrap().kind.clone() + } + + /// A primary click at `at`, as a pointer makes it. + fn click_at(h: &mut Harness, at: egui::Pos2) { + h.event(egui::Event::PointerMoved(at)); + h.run_steps(1); + h.event(egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed: true, modifiers: Default::default() }); + h.run_steps(1); + h.event(egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed: false, modifiers: Default::default() }); + h.run_steps(3); + } + + /// Press Save Preset, leaving the preset's name focused. + fn start_saving(h: &mut Harness) { + h.get_by_label("Save Preset").click(); + h.run_steps(4); + } + + /// Replace the text of field `label` with `text` and press Enter. + fn enter_in(h: &mut Harness, label: &str, text: &str) { + let all = egui::Modifiers { ctrl: true, command: true, ..Default::default() }; + h.get_by_role_and_label(Role::TextInput, label).focus(); + h.run_steps(2); + h.key_press_modifiers(all, egui::Key::A); + h.get_by_role_and_label(Role::TextInput, label).type_text(text); + h.run_steps(2); + h.key_press(egui::Key::Enter); + h.run_steps(3); + } + + /// More Settings opens over New Document with only what Preset Details lacks, its first field + /// takes the keyboard focus, and Done leaves New Document open with what the pop-up set. + #[test] + fn more_settings_is_a_popup_holding_only_the_artboard_layout() { + let mut h = big(); + h.get_by_label("More Settings").click(); + h.run_steps(4); + assert_eq!(kind(&h), super::MORE); + assert!(h.get_by_role_and_label(Role::TextInput, "Number of Artboards:").is_focused(), "the pop-up's first field has the focus"); + for shown in ["Number of Artboards:", "Spacing:", "Columns:", "Create", "Close"] { + let _ = h.get_by_label(shown); + } + for gone in [ + "Name:", + "Profile:", + "Size:", + "Width:", + "Height:", + "Units:", + "Orientation:", + "Bleed:", + "Background Contents:", + "Create Document", + "Templates…", + ] { + assert!(h.query_by_label(gone).is_none(), "{gone} is in the pop-up"); + } + h.get_by_label("Done").click(); + h.run_steps(3); + assert_eq!(kind(&h), super::KIND, "Done goes back to New Document"); + assert!(h.query_by_label("Spacing:").is_none()); + assert!(h.state().session.documents().is_empty()); + } + + /// Escape, a click outside and `ui.dialog.cancel` each close only the pop-up, and the fields it + /// edited stay set. Escape on New Document itself then closes the dialog. + #[test] + fn a_popup_closes_on_its_own_and_keeps_the_values() { + let mut h = big(); + open_more(&mut h, 4); + h.get_by_label(super::ArtboardLayout::Column.label()).click(); + h.run_steps(3); + assert_eq!(kind(&h), super::MORE, "a click inside keeps it open"); + h.key_press(egui::Key::Escape); + h.run_steps(3); + assert_eq!(kind(&h), super::KIND, "Escape closes only the pop-up"); + assert_eq!(field(h.state(), "artboardLayout")["layout"], json!("column")); + h.key_press(egui::Key::Escape); + h.run_steps(3); + assert!(h.state().ui.dialog.is_none(), "the next Escape closes New Document"); + + let mut h = big(); + open_more(&mut h, 4); + let outside = h.get_by_label("PRESET DETAILS").rect().center(); + click_at(&mut h, outside); + assert_eq!(kind(&h), super::KIND, "a click outside closes the pop-up"); + assert_eq!(field(h.state(), "artboards"), json!(4)); + + open_more(&mut h, 4); + crate::dialogs::cancel(h.state_mut()); + assert_eq!(kind(&h), super::KIND, "ui.dialog.cancel closes only the pop-up"); + set(h.state_mut(), super::SAVING, json!(true)); + crate::dialogs::cancel(h.state_mut()); + assert_eq!((kind(&h), field(h.state(), super::SAVING)), (super::KIND.to_string(), json!(false)), "and Save Preset's"); + crate::dialogs::cancel(h.state_mut()); + assert!(h.state().ui.dialog.is_none(), "with no pop-up it cancels New Document"); + } + + /// A click that lands on Create, Close, a preset card or a category tab while the pop-up is open + /// only closes the pop-up: New Document stays open with the values the pop-up set. + #[test] + fn a_click_outside_the_popup_does_not_reach_the_dialog() { + let mut reached = vec![]; + for target in ["Create", "Close", "card", "Web"] { + let mut h = big(); + open_more(&mut h, 4); + let (category, preset) = (field(h.state(), "category"), field(h.state(), "preset")); + let at = if target == "card" { + // The Print tab's second card: the first is the chosen one. + let tab = h.get_by_label("Print").rect(); + egui::pos2(tab.left() + 146.0 + 12.0 + 73.0, tab.bottom() + 120.0) + } else { + h.get_by_label(target).rect().center() + }; + click_at(&mut h, at); + let kept = h.state().ui.dialog.is_some() + && kind(&h) == super::KIND + && h.state().session.documents().is_empty() + && field(h.state(), "artboards") == json!(4) + && (field(h.state(), "category"), field(h.state(), "preset")) == (category, preset); + if !kept { + reached.push(target); } } - let mut texts = vec![]; - out.shapes.iter().for_each(|c| walk(&c.shape, &mut texts)); - assert!(texts.iter().any(|(t, _)| t == "Preset Name"), "the preset's name field shows"); - let at = texts.iter().find(|(t, _)| t == "Save").map(|(_, p)| *p).expect("a Save button"); - let press = |pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() }; - run(&mut app, vec![egui::Event::PointerMoved(at), press(true)]); - run(&mut app, vec![press(false)]); - let saved = app.session.prefs.new_doc_presets.iter().find(|p| p.name == "Strip").expect("saved"); + assert!(reached.is_empty(), "the click reached {reached:?}"); + } + + /// Enter applies the field and closes the pop-up. It does not create the document. + #[test] + fn enter_in_a_popup_does_not_create_the_document() { + let mut h = big(); + open_more(&mut h, 3); + enter_in(&mut h, "Spacing:", "25"); + assert_eq!(kind(&h), super::KIND, "Enter closes the pop-up"); + assert_eq!(field(h.state(), "artboardLayout")["spacing"], json!(25.0)); + assert!(h.state().session.documents().is_empty(), "no document was made"); + } + + /// One artboard disables the layout, Spacing and Columns; Columns stays within the artboards + /// and Spacing is not negative. + #[test] + fn the_popup_keeps_the_layout_rules() { + let mut h = big(); + open_more(&mut h, 1); + for name in [super::ArtboardLayout::Row.label(), "Change to Right-to-Left Layout"] { + assert!(h.get_by_label(name).accesskit_node().is_disabled(), "{name}"); + } + for name in ["Spacing:", "Columns:"] { + assert!(h.get_by_role_and_label(Role::TextInput, name).accesskit_node().is_disabled(), "{name}"); + } + open_more(&mut h, 3); + assert!(!h.get_by_role_and_label(Role::TextInput, "Spacing:").accesskit_node().is_disabled()); + enter_in(&mut h, "Columns:", "99"); + assert_eq!(field(h.state(), "artboardLayout")["columns"], json!(3)); + open_more(&mut h, 3); + enter_in(&mut h, "Spacing:", "-5"); + assert_eq!(field(h.state(), "artboardLayout")["spacing"], json!(0.0)); + } + + /// Saving swaps the top of Preset Details for the heading, the preset's name, Cancel and Save, and + /// hides Close and Create; the rows under it stay editable, and Enter in one of them neither saves + /// nor creates. + #[test] + fn save_preset_is_a_mode_of_the_panel() { + let mut h = big(); + start_saving(&mut h); + let _ = (h.get_by_label("Save Document Preset"), h.get_by_label("Cancel"), h.get_by_label("Save")); + for gone in ["PRESET DETAILS", "Create", "Close"] { + assert!(h.query_by_label(gone).is_none(), "{gone} shows while saving"); + } + assert!(h.query_by_role_and_label(Role::TextInput, "Document Name").is_none(), "the document's name is hidden"); + let _ = (h.get_by_label("Advanced Options"), h.get_by_label("More Settings")); + enter_in(&mut h, "Artboards", "2"); + assert_eq!(field(h.state(), "artboards"), json!(2), "the rows stay editable"); + assert_eq!(field(h.state(), super::SAVING), json!(true), "Enter in a row does not save"); + assert!(h.state().session.documents().is_empty() && h.state().session.prefs.new_doc_presets.is_empty()); + h.get_by_label("More Settings").click(); + h.run_steps(4); + assert_eq!(kind(&h), super::MORE, "More Settings opens while saving"); + } + + /// Save Preset focuses the default name with its text selected, Enter in it is Save, and the + /// saved preset keeps the fields. + #[test] + fn enter_in_the_preset_name_saves() { + let mut h = big(); + set(h.state_mut(), "artboards", json!(3)); + h.run_steps(2); + start_saving(&mut h); + assert_eq!(field(h.state(), "presetName"), json!("Preset 1")); + assert!(h.get_by_role_and_label(Role::TextInput, "Preset Name").is_focused()); + h.event(egui::Event::Text("Trio".into())); + h.run_steps(2); + assert_eq!(field(h.state(), "presetName"), json!("Trio"), "typing replaced the selected text"); + h.key_press(egui::Key::Enter); + h.run_steps(3); + let saved = h.state().session.prefs.new_doc_presets.iter().find(|p| p.name == "Trio").expect("Enter saved it"); + assert_eq!(saved.artboards, 3); + assert_eq!((field(h.state(), super::SAVING), field(h.state(), "artboards")), (json!(false), json!(3)), "the fields stay"); + assert_eq!(field(h.state(), "name"), json!("Untitled-1")); + assert!(h.state().session.documents().is_empty() && h.state().ui.dialog.is_some()); + } + + /// Save Preset's default name is the first "Preset N" no saved preset has, after a save as well. + #[test] + fn the_next_save_offers_a_new_name() { + let mut h = big(); + start_saving(&mut h); + assert_eq!(field(h.state(), "presetName"), json!("Preset 1")); + h.get_by_label("Save").click(); + h.run_steps(3); + start_saving(&mut h); + assert_eq!(field(h.state(), "presetName"), json!("Preset 2"), "not the saved preset's name"); + set(h.state_mut(), "presetName", json!("preset 3")); + h.run_steps(2); + h.get_by_label("Save").click(); + h.run_steps(3); + start_saving(&mut h); + assert_eq!(field(h.state(), "presetName"), json!("Preset 2"), "not a name a saved preset has"); + } + + /// A New Document that opens already saving (as a control client can set it) fills in and focuses + /// the default name, also after the last one was closed while saving. + #[test] + fn a_dialog_that_opens_saving_fills_in_the_name() { + let mut h = big(); + start_saving(&mut h); + h.state_mut().ui.dialog = None; + h.run_steps(2); + h.state_mut().run("file.newDialog", json!({})).unwrap(); + set(h.state_mut(), super::SAVING, json!(true)); + h.run_steps(4); + assert_eq!(field(h.state(), "presetName"), json!("Preset 1")); + assert!(h.get_by_role_and_label(Role::TextInput, "Preset Name").is_focused()); + } + + /// Enter that is still held after Enter saved a preset does not create the document. + #[test] + fn a_held_enter_after_a_save_does_not_create() { + let mut h = big(); + start_saving(&mut h); + let enter = |repeat| egui::Event::Key { key: egui::Key::Enter, physical_key: None, pressed: true, repeat, modifiers: Default::default() }; + // Pressed and not released: the next presses are the key's repeats. + h.event(enter(false)); + h.run_steps(1); + assert_eq!(field(h.state(), super::SAVING), json!(false), "Enter saved"); + for _ in 0..3 { + h.event(enter(true)); + h.run_steps(1); + } + assert!(h.state().session.documents().is_empty() && h.state().ui.dialog.is_some()); + } + + /// Escape in the mode is Cancel: it saves nothing, keeps edits made to the fields, and New Document + /// stays open. The Cancel button does the same. + #[test] + fn escape_and_cancel_leave_saving_and_keep_the_edits() { + let mut h = big(); + start_saving(&mut h); + set(h.state_mut(), "artboards", json!(5)); + h.run_steps(2); + h.key_press(egui::Key::Escape); + h.run_steps(3); + assert_eq!((field(h.state(), super::SAVING), field(h.state(), "artboards")), (json!(false), json!(5)), "Escape keeps the edits"); + assert!(h.state().session.prefs.new_doc_presets.is_empty(), "and saves nothing"); + assert!(h.state().ui.dialog.is_some(), "and New Document stays open"); + + start_saving(&mut h); + h.get_by_label("Cancel").click(); + h.run_steps(3); + assert_eq!(field(h.state(), super::SAVING), json!(false)); + let _ = h.get_by_label("Create"); + } + + /// `ui.dialog.confirm` while saving is Save: it stores the preset and creates no document. + #[test] + fn confirm_while_saving_does_not_create_a_document() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.newDialog", json!({})).unwrap(); + frame(&mut app); + set(&mut app, super::SAVING, json!(true)); + set(&mut app, "presetName", json!("Zed")); + crate::dialogs::confirm(&mut app).unwrap(); + assert!(app.session.documents().is_empty() && app.session.prefs.new_doc_presets.iter().any(|p| p.name == "Zed")); + assert_eq!((field(&app, super::SAVING), field(&app, "category")), (json!(false), json!("Saved"))); + set(&mut app, super::SAVING, json!(true)); + set(&mut app, "presetName", json!("")); + assert!(crate::dialogs::confirm(&mut app).is_err(), "an empty name is refused"); + assert_eq!(field(&app, super::SAVING), json!(true)); + assert!(app.session.documents().is_empty()); + } + + /// The preset keeps the artboard layout, spacing and columns set in More Settings' pop-up + /// (#4893), and New Document stays open on it. An empty name stays in the mode. + #[test] + fn save_preset_saves_what_more_settings_set() { + let mut h = big(); + open_more(&mut h, 4); + enter_in(&mut h, "Spacing:", "30"); + open_more(&mut h, 4); + h.get_by_label(super::ArtboardLayout::Column.label()).click(); + h.run_steps(3); + h.get_by_label("Change to Right-to-Left Layout").click(); + h.run_steps(3); + h.get_by_label("Done").click(); + h.run_steps(3); + h.get_by_label("Save Preset").click(); + h.run_steps(4); + assert!(field(h.state(), super::SAVING) == json!(true) && h.get_by_role_and_label(Role::TextInput, "Preset Name").is_focused()); + let _ = (h.get_by_label("Save"), h.get_by_label("Cancel")); + set(h.state_mut(), "presetName", json!(" ")); + h.run_steps(2); + h.get_by_label("Save").click(); + h.run_steps(3); + assert_eq!(field(h.state(), super::SAVING), json!(true), "an empty name stays in the mode"); + set(h.state_mut(), "presetName", json!("Strip")); + h.run_steps(2); + h.get_by_label("Save").click(); + h.run_steps(3); + let saved = h.state().session.prefs.new_doc_presets.iter().find(|p| p.name == "Strip").expect("saved"); assert_eq!(saved.artboards, 4); + assert!(saved.right_to_left); assert_eq!((saved.layout, saved.spacing), (super::ArtboardLayout::Column, 30.0)); - assert_eq!((field(&app, "category"), field(&app, "preset"), field(&app, super::SAVING)), (json!("Saved"), json!("Strip"), json!(false))); - assert_eq!(app.ui.dialog.as_ref().unwrap().kind, super::MORE, "More Settings stays open"); + assert_eq!( + (field(h.state(), "category"), field(h.state(), "preset"), field(h.state(), super::SAVING)), + (json!("Saved"), json!("Strip"), json!(false)) + ); + assert_eq!(kind(&h), super::KIND, "New Document stays open"); + assert!(h.state().session.documents().is_empty()); + } + + /// In a small window the pop-up stays on screen, and its button stays in view under rows that scroll. + #[test] + fn the_popup_stays_inside_a_small_window() { + let mut h = harness(egui::vec2(480.0, 260.0)); + open_more(&mut h, 3); + let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(480.0, 260.0)); + for label in ["Number of Artboards:", "Done"] { + assert!(screen.contains_rect(h.get_by_label(label).rect()), "{label} is off screen"); + } } } diff --git a/crates/ui-egui/src/dialogs/tests_accessibility.rs b/crates/ui-egui/src/dialogs/tests_accessibility.rs index 8525fd8bb..d3ef1ce8c 100644 --- a/crates/ui-egui/src/dialogs/tests_accessibility.rs +++ b/crates/ui-egui/src/dialogs/tests_accessibility.rs @@ -86,9 +86,8 @@ fn document_setup_names_each_bleed_side() { fn new_document_names_its_fields_by_their_captions() { let mut app = app(); app.run("file.newDialog", json!({})).unwrap(); - app.ui.dialog.as_mut().expect("New Document is open").fields.insert("__saving".into(), json!(true)); let h = strict(app); - for name in ["Document Name", "Preset Name", "Width", "Height", "Artboards"] { + for name in ["Document Name", "Width", "Height", "Artboards"] { let _ = h.get_by_role_and_label(Role::TextInput, name); } for name in ["Units", "Background Contents"] { @@ -96,7 +95,7 @@ fn new_document_names_its_fields_by_their_captions() { } } -/// New Document's More Settings, with Save Preset open, leaves no field unnamed. +/// New Document's More Settings pop-up and Save Preset mode leave no field unnamed. #[test] fn new_document_more_settings_names_its_fields() { let mut app = app(); @@ -104,8 +103,11 @@ fn new_document_more_settings_names_its_fields() { let d = app.ui.dialog.as_mut().expect("New Document is open"); d.kind = super::new_document::MORE.into(); d.fields.insert("__saving".into(), json!(true)); + d.fields.insert("artboards".into(), json!(3)); let h = strict(app); - let _ = h.get_by_role_and_label(Role::TextInput, "Preset Name"); + for name in ["Preset Name", "Number of Artboards:", "Spacing:", "Columns:"] { + let _ = h.get_by_role_and_label(Role::TextInput, name); + } } /// Split Into Grid keeps translated visible labels while naming row and column gutter and total diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 97f8b7006..50868dca6 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -284,7 +284,6 @@ font-technology Variable Променливи Update While Dragging Обновяване при плъзгане Hide Video Rulers Скриване на видеолинийките Background Contents Съдържание на фона - Background Contents: Съдържание на фона: Background: Фон: Backspace Backspace Bar Graph Tool Инструмент „Стълбчеста диаграма“ @@ -654,7 +653,6 @@ font-technology Variable Променливи Couldn't {} {name}: {e} Неуспешно {name} на {}: {e} Create Създаване Create Clipping Mask Създаване на маска за изрязване - Create Document Създаване на документ Create from Guides Създаване от водачи Create from Selection Създаване от селекцията Create From: Създаване от: @@ -2074,7 +2072,6 @@ font-technology Variable Променливи New Color Group… Нова цветова група… New Document Presets Предварителни настройки за нов документ New file Нов файл - New from Template Нов от шаблон New from Template… Нов от шаблон… New Graphic Style Нов графичен стил New Graphic Style (Alt-click to name it) Нов графичен стил (кликнете с Alt, за да го наименувате) @@ -2858,6 +2855,7 @@ digits Malayalam Малаялам Save PDF Preset Запази PDF предварителна настройка Save Perspective Grid Preset Запази предварителна настройка на перспективната мрежа Save Preset Запази предварителна настройка + Save Document Preset Запази предварителна настройка на документа Save Preset… Запази предварителна настройка… Save Print Preset Запази предварителна настройка за печат Save Recovery Data Запази данни за възстановяване @@ -3422,7 +3420,6 @@ digits Malayalam Малаялам Template Шаблон Templates Шаблони Templates Folder Папка с шаблони - Templates… Шаблони… Text Displayed in Cell: Текст, показван в клетката: Text Export Options Опции за експортиране на текст Text Fill & Stroke Color Цвят на запълване и щрих на текста diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index 00859a655..55af09bc9 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -2531,15 +2531,12 @@ digits Malayalam Malajalam Selected Swatches Odabrani uzorci boja Advanced Options Napredne opcije Background Contents Sadržaj pozadine - Background Contents: Sadržaj pozadine: BLANK DOCUMENT PRESETS PREDLOŠCI PRAZNIH DOKUMENATA Bleed Napust Change to Right-to-Left Layout Prebaci na raspored s desna nalijevo Columns: Kolone: - Create Document Kreiraj dokument Documents you create appear here. Dokumenti koje kreirate pojavljuju se ovdje. More Settings Više postavki - New from Template Novo iz predloška Number of Artboards: Broj radnih ploča: Orientation: Orijentacija: PRESET DETAILS DETALJI PREDLOŠKA @@ -2550,7 +2547,6 @@ digits Malayalam Malajalam RECENT NEDAVNO RECENT (1) NEDAVNO (1) SAVED SAČUVANO - Templates… Predlošci… Transparent (Default) Prozirno (zadano) Save for Office Documents Sačuvaj za Office dokumente Transparent Background Prozirna pozadina @@ -3486,6 +3482,7 @@ theme Appearance Izgled Arrange by Row Rasporedi po redu Arrange by Column Rasporedi po koloni Save Preset Sačuvaj predložak + Save Document Preset Sačuvaj predložak dokumenta Screen (72 ppi) Ekran (72 ppi) Social Square Post 1080×1080 Kvadratna objava za društvene mreže 1080×1080 Distribute Rasporedi ravnomjerno diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 53ebd517f..a37133f14 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -423,7 +423,6 @@ antiAlias Sharp Nítid Back Enrere Background Color: Color de fons: Background Contents Contingut del fons - Background Contents: Contingut del fons: Background Level Nivell de fons Background: Fons: Backspace Retrocés @@ -917,7 +916,6 @@ antiAlias Sharp Nítid Create Clipping Mask Crea una màscara de retall Create Compound Path From Ellipses Crea un traçat compost a partir d’el·lipses Create Dimension Crea una dimensió - Create Document Crea un document Create from Guides Crea a partir de guies Create from Selection Crea a partir de la selecció Create From: Crea a partir de: @@ -2597,7 +2595,6 @@ digits Malayalam Malaiàlam New Design Disseny nou New Document Presets Preajustos de document nous New file Fitxer nou - New from Template Nou a partir d’una plantilla New from Template… Nou a partir d’una plantilla… New Graphic Style Estil gràfic nou New Graphic Style (Alt-click to name it) Estil gràfic nou (Alt+clic per posar-hi nom) @@ -3643,6 +3640,7 @@ digits Malayalam Malaiàlam Save PDF Preset Desa el preajust de PDF Save Perspective Grid Preset Desa el preajust de quadrícula en perspectiva Save Preset Desa el preajust + Save Document Preset Desa el preajust de document Save Preset… Desa el preajust… Save Print Preset Desa el preajust d’impressió Save Recovery Data Desa les dades de recuperació @@ -4369,7 +4367,6 @@ digits Malayalam Malaiàlam Template literal not closed El literal de plantilla no està tancat Templates Plantilles Templates Folder Carpeta de plantilles - Templates… Plantilles… Tetrad Tètrada Tetrad 2 Tètrada 2 Tetrad 3 Tètrada 3 diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 3c9090be2..ece95b1cb 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -2562,15 +2562,12 @@ digits Malayalam Malajálamské Selected Swatches Vybrané položky vzorníku Advanced Options Rozšířené možnosti Background Contents Obsah pozadí - Background Contents: Obsah pozadí: BLANK DOCUMENT PRESETS PŘEDNASTAVENÍ PRÁZDNÝCH DOKUMENTŮ Bleed Spadávka Change to Right-to-Left Layout Změnit na rozvržení zprava doleva Columns: Sloupce: - Create Document Vytvořit dokument Documents you create appear here. Zde se zobrazí dokumenty, které vytvoříte. More Settings Další nastavení - New from Template Nový ze šablony Number of Artboards: Počet pracovních ploch: Orientation: Orientace: PRESET DETAILS PODROBNOSTI PŘEDNASTAVENÍ @@ -2581,7 +2578,6 @@ digits Malayalam Malajálamské RECENT NEDÁVNÉ RECENT (1) NEDÁVNÉ (1) SAVED ULOŽENÉ - Templates… Šablony… Transparent (Default) Průhledné (výchozí) Save for Office Documents Uložit pro kancelářské dokumenty Transparent Background Průhledné pozadí @@ -3536,6 +3532,7 @@ theme Appearance Motiv Arrange by Row Uspořádat po řádcích Arrange by Column Uspořádat po sloupcích Save Preset Uložit přednastavení + Save Document Preset Uložit přednastavení dokumentu Screen (72 ppi) Obrazovka (72 ppi) Social Square Post 1080×1080 Čtvercový příspěvek pro sociální sítě 1080×1080 Distribute Rozmístit diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index df445718b..7bc024666 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -2350,15 +2350,12 @@ digits Malayalam Malayalam Selected Swatches Markerede farveprøver Advanced Options Avancerede indstillinger Background Contents Baggrundens indhold - Background Contents: Baggrundens indhold: BLANK DOCUMENT PRESETS TOMME DOKUMENTFORUDINDSTILLINGER Bleed Beskæring Change to Right-to-Left Layout Skift til højre-til-venstre-layout Columns: Kolonner: - Create Document Opret dokument Documents you create appear here. Dokumenter, du opretter, vises her. More Settings Flere indstillinger - New from Template Nyt fra skabelon Number of Artboards: Antal tegnebrætter: Orientation: Orientering: PRESET DETAILS FORUDINDSTILLINGSDETALJER @@ -2369,7 +2366,6 @@ digits Malayalam Malayalam RECENT SENESTE RECENT (1) SENESTE (1) SAVED GEMT - Templates… Skabeloner… Transparent (Default) Gennemsigtig (standard) Save for Office Documents Gem til Office-dokumenter Transparent Background Gennemsigtig baggrund @@ -3273,6 +3269,7 @@ theme Appearance Udseende Arrange by Row Arranger efter række Arrange by Column Arranger efter kolonne Save Preset Gem forudindstilling + Save Document Preset Gem dokumentforudindstilling Screen (72 ppi) Skærm (72 ppi) Social Square Post 1080×1080 Socialt firkantet opslag 1080×1080 Distribute Fordel diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 6c6c48238..60ac922fa 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -272,7 +272,6 @@ font-technology Variable Variabel Update While Dragging Beim Ziehen aktualisieren Hide Video Rulers Videolineale ausblenden Background Contents Hintergrundinhalt - Background Contents: Hintergrundinhalt: Background: Hintergrund: Backspace Rücktaste Bar Graph Tool Balkendiagramm-Werkzeug @@ -647,7 +646,6 @@ font-technology Variable Variabel Couldn't {} {name}: {e} Fehler bei „{}“ für {name}: {e} Create Erstellen Create Clipping Mask Schnittmaske erstellen - Create Document Dokument erstellen Create from Guides Aus Hilfslinien erstellen Create from Selection Aus Auswahl erstellen Create From: Erstellen aus: @@ -2099,7 +2097,6 @@ file-filter Templates Vorlagen New Color Group… Neue Farbgruppe… New Document Presets Vorgaben für neue Dokumente New file Neue Datei - New from Template Neu aus Vorlage New from Template… Neu aus Vorlage… New Graphic Style Neuer Grafikstil New Graphic Style (Alt-click to name it) Neuer Grafikstil (Alt-Klick, um ihn zu benennen) @@ -2891,6 +2888,7 @@ digits Malayalam Malayalam Save PDF Preset PDF-Vorgabe speichern Save Perspective Grid Preset Vorgabe für Perspektivenraster speichern Save Preset Vorgabe speichern + Save Document Preset Dokumentvorgabe speichern Preset {number} Vorgabe {number} Save Preset… Vorgabe speichern… Save Print Preset Druckvorgabe speichern @@ -3454,7 +3452,6 @@ effect Points Punkte Template Vorlage Templates Vorlagen Templates Folder Vorlagenordner - Templates… Vorlagen… Text Displayed in Cell: In der Zelle angezeigter Text: Text Export Options Textexportoptionen Text Fill & Stroke Color Flächen- und Konturfarbe des Texts diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index b2d4f2a6e..2abc58dee 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -275,7 +275,6 @@ font-technology Variable Μεταβλητές Update While Dragging Ενημέρωση κατά τη μεταφορά Hide Video Rulers Απόκρυψη χαράκων βίντεο Background Contents Περιεχόμενα φόντου - Background Contents: Περιεχόμενο φόντου: Background: Φόντο: Backspace Backspace Bar Graph Tool Ραβδόγραμμα (Bar Graph Tool) @@ -628,7 +627,6 @@ font-technology Variable Μεταβλητές Couldn't {} {name}: {e} Δεν ήταν δυνατή η {} του {name}: {e} Create Δημιουργία Create Clipping Mask Δημιουργία μάσκας αποκοπής - Create Document Δημιουργία εγγράφου Create from Guides Δημιουργία από οδηγούς Create from Selection Δημιουργία από επιλογή Create From: Δημιουργία από: @@ -2074,7 +2072,6 @@ font-technology Variable Μεταβλητές Temperature Θερμοκρασία Template Πρότυπο Templates Πρότυπα - Templates… Πρότυπα… Threshold Κατώφλι Tile Παράθεση Tint Χρωματική απόχρωση @@ -2902,7 +2899,6 @@ theme Appearance Εμφάνιση New Color Group… Νέα ομάδα χρωμάτων… New Document Presets Νέες προεπιλογές εγγράφου New file Νέο αρχείο - New from Template Νέο από πρότυπο New from Template… Νέο από πρότυπο… New Graphic Style Νέο στυλ γραφικών New Graphic Style (Alt-click to name it) Νέο στυλ γραφικών (Alt-κλικ για ονομασία) @@ -3466,6 +3462,7 @@ digits Malayalam Μαλαγιάλαμ Save PDF Preset Αποθήκευση προεπιλογής PDF Save Perspective Grid Preset Αποθήκευση προεπιλογής πλέγματος προοπτικής Save Preset Αποθήκευση προεπιλογής + Save Document Preset Αποθήκευση προεπιλογής εγγράφου Save Print Preset Αποθήκευση προεπιλογής εκτύπωσης Save Recovery Data Αποθήκευση δεδομένων ανάκτησης Save Save for Web Preset Αποθήκευση προεπιλογής για ιστό diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 9d5059411..f8ce0608a 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -297,7 +297,6 @@ font-technology Variable Variables Update While Dragging Actualizar al arrastrar Hide Video Rulers Ocultar reglas de video Background Contents Contenido de fondo - Background Contents: Contenido de fondo: Background: Fondo: Backspace Retroceso Bar Graph Tool Herramienta Gráfico de barras @@ -669,7 +668,6 @@ font-technology Variable Variables Couldn't {} {name}: {e} No se pudo {} {name}: {e} Create Crear Create Clipping Mask Crear máscara de recorte - Create Document Crear documento Create from Guides Crear a partir de guías Create from Selection Crear a partir de la selección Create From: Crear a partir de: @@ -2146,7 +2144,6 @@ font-technology Variable Variables New Color Group… Nuevo grupo de colores… New Document Presets Preajustes de documento nuevo New file Archivo nuevo - New from Template Nuevo a partir de plantilla New from Template… Nuevo a partir de plantilla… New Graphic Style Nuevo estilo gráfico New Graphic Style (Alt-click to name it) Nuevo estilo gráfico (Alt+clic para darle nombre) @@ -2933,6 +2930,7 @@ digits Malayalam Malayálam Save PDF Preset Guardar preajuste de PDF Save Perspective Grid Preset Guardar preajuste de cuadrícula de perspectiva Save Preset Guardar preajuste + Save Document Preset Guardar preajuste de documento Save Preset… Guardar preajuste… Save Print Preset Guardar preajuste de impresión Save Recovery Data Guardar datos de recuperación @@ -3510,7 +3508,6 @@ digits Malayalam Malayálam Template Plantilla Templates Plantillas Templates Folder Carpeta de plantillas - Templates… Plantillas… Text Displayed in Cell: Texto mostrado en la celda: Text Export Options Opciones de exportación de texto Text Fill & Stroke Color Color de relleno y trazo del texto diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index dbdf5d111..e8e3b33fd 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -2506,15 +2506,12 @@ digits Malayalam Malajalam Selected Swatches Valitut värimallit Advanced Options Lisäasetukset Background Contents Taustan sisältö - Background Contents: Taustan sisältö: BLANK DOCUMENT PRESETS TYHJÄN ASIAKIRJAN ESIASETUKSET Bleed Leikkuuvara Change to Right-to-Left Layout Vaihda oikealta vasemmalle -asetteluun Columns: Sarakkeet: - Create Document Luo asiakirja Documents you create appear here. Luomasi asiakirjat näkyvät tässä. More Settings Lisää asetuksia - New from Template Uusi mallista Number of Artboards: Piirtoalustojen määrä: Orientation: Suunta: PRESET DETAILS ESIASETUKSEN TIEDOT @@ -2525,7 +2522,6 @@ digits Malayalam Malajalam RECENT VIIMEISIMMÄT RECENT (1) VIIMEISIMMÄT (1) SAVED TALLENNETUT - Templates… Mallit… Transparent (Default) Läpinäkyvä (oletus) Save for Office Documents Tallenna Office-asiakirjoja varten Transparent Background Läpinäkyvä tausta @@ -3474,6 +3470,7 @@ theme Appearance Ulkoasu Arrange by Row Järjestä riveittäin Arrange by Column Järjestä sarakkeittain Save Preset Tallenna esiasetus + Save Document Preset Tallenna asiakirjan esiasetus Screen (72 ppi) Näyttö (72 ppi) Social Square Post 1080×1080 Neliömäinen somejulkaisu 1080×1080 Distribute Jaa tasaisesti diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 7c6d1fb0e..4cafac1f6 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -298,7 +298,6 @@ font-technology Variable Variables Update While Dragging Mettre à jour pendant le déplacement Hide Video Rulers Masquer les règles vidéo Background Contents Contenu de l’arrière-plan - Background Contents: Contenu de l’arrière-plan : Background: Arrière-plan : Backspace Retour arrière Bar Graph Tool Outil Graphe à barres @@ -670,7 +669,6 @@ font-technology Variable Variables Couldn't {} {name}: {e} Impossible de {} {name} : {e} Create Créer Create Clipping Mask Créer un masque d’écrêtage - Create Document Créer un document Create from Guides Créer à partir des repères Create from Selection Créer à partir de la sélection Create From: Créer à partir de : @@ -2138,7 +2136,6 @@ font-technology Variable Variables New Color Group… Nouveau groupe de couleurs… New Document Presets Paramètres prédéfinis de nouveau document New file Nouveau fichier - New from Template Nouveau d’après un modèle New from Template… Nouveau d’après un modèle… New Graphic Style Nouveau style graphique New Graphic Style (Alt-click to name it) Nouveau style graphique (Alt+clic pour le nommer) @@ -2925,6 +2922,7 @@ digits Malayalam Malayalam Save PDF Preset Enregistrer le paramètre prédéfini PDF Save Perspective Grid Preset Enregistrer le paramètre prédéfini de grille de perspective Save Preset Enregistrer le paramètre prédéfini + Save Document Preset Enregistrer le paramètre prédéfini du document Save Preset… Enregistrer le paramètre prédéfini… Save Print Preset Enregistrer le paramètre prédéfini d’impression Save Recovery Data Enregistrer les données de récupération @@ -3502,7 +3500,6 @@ digits Malayalam Malayalam Template Modèle Templates Modèles Templates Folder Dossier des modèles - Templates… Modèles… Text Displayed in Cell: Texte affiché dans la cellule : Text Export Options Options d’exportation du texte Text Fill & Stroke Color Couleur de fond et de contour du texte diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index 1dac03dec..9cbd1121a 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -491,7 +491,6 @@ antiAlias Sharp शार्प Back वापस Background Color: बैकग्राउंड रंग: Background Contents बैकग्राउंड सामग्री - Background Contents: बैकग्राउंड सामग्री: Background Level बैकग्राउंड स्तर Background: बैकग्राउंड: Backspace Backspace @@ -1029,7 +1028,6 @@ antiAlias Sharp शार्प Create Clipping Mask क्लिपिंग मास्क बनाएँ Create Compound Path From Ellipses एलिप्स से कंपाउंड पाथ बनाएँ Create Dimension आयाम बनाएँ - Create Document डॉक्यूमेंट बनाएं Create from Guides गाइड से बनाएँ Create from Selection सिलेक्शन से बनाएँ Create From: से बनाएँ: @@ -2836,7 +2834,6 @@ antiAlias Sharp शार्प New Design नई डिजाइन New Document Presets नया डॉक्यूमेंट प्रीसेट New file नई फ़ाइल - New from Template टेम्पलेट से नया New from Template… टेम्पलेट से नया… New Graphic Style नई ग्राफिक स्टाइल New Graphic Style (Alt-click to name it) नया ग्राफ़िक स्टाइल (नाम देने के लिए Alt-क्लिक करें) @@ -3957,6 +3954,7 @@ antiAlias Sharp शार्प Save PDF Preset PDF प्रीसेट सेव करें Save Perspective Grid Preset सेव पर्सपेक्टिव ग्रिड प्रीसेट Save Preset प्रीसेट सेव करें + Save Document Preset डॉक्यूमेंट प्रीसेट सेव करें Save Preset… प्रीसेट सेव करें… Save Print Preset सेव करें प्रिंट प्रीसेट Save Recovery Data रिकवरी डेटा सेव करें @@ -4736,7 +4734,6 @@ antiAlias Sharp शार्प Template literal not closed टेम्पलेट साक्षर नहीं बंद Templates टेम्पलेट्स Templates Folder टेम्पलेट्स फ़ोल्डर - Templates… टेम्पलेट्स… Tetrad टेट्राड Tetrad 2 टेट्राड 2 Tetrad 3 टेट्राड 3 diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 87842df37..601bfac2e 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -393,7 +393,6 @@ font-technology Variable Változó Update While Dragging Frissítés húzás közben Hide Video Rulers Videovonalzók elrejtése Background Contents Háttértartalom - Background Contents: Háttértartalom: Background: Háttér: Backspace Backspace Balance Egyensúly @@ -864,7 +863,6 @@ font-technology Variable Változó Craquelure… Repedezettség… Create Létrehozás Create Clipping Mask Vágómaszk létrehozása - Create Document Dokumentum létrehozása Create from Guides Létrehozás segédvonalakból Create from Selection Létrehozás a kijelölésből Create From: Létrehozás ebből: @@ -2598,7 +2596,6 @@ file-filter Templates Sablonok New Data Set… Új adatkészlet… New Document Presets Új dokumentum előbeállításai New file Új fájl - New from Template Új sablonból New from Template… Új sablonból… New Graphic Style Új grafikai stílus New Graphic Style (Alt-click to name it) Új grafikai stílus (Alt+kattintás: elnevezéssel) @@ -3516,6 +3513,7 @@ mark-style Japanese Japán Save PDF Preset PDF-előbeállítás mentése Save Perspective Grid Preset Perspektivikus rács előbeállításának mentése Save Preset Előbeállítás mentése + Save Document Preset Dokumentum-előbeállítás mentése Preset {number} Előbeállítás {number} Save Preset… Előbeállítás mentése… Save Print Preset Nyomtatási előbeállítás mentése @@ -4164,7 +4162,6 @@ mark-style Japanese Japán Template Sablon Templates Sablonok Templates Folder Sablonok mappája - Templates… Sablonok… Tetrad Tetrád Tetrad 2 Tetrád 2 Tetrad 3 Tetrád 3 diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 476a6988f..9ed84d0cb 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -2350,15 +2350,12 @@ digits Malayalam Malayalam Selected Swatches Swatch terpilih Advanced Options Opsi lanjutan Background Contents Konten latar belakang - Background Contents: Konten latar belakang: BLANK DOCUMENT PRESETS PRESET DOKUMEN KOSONG Bleed Bleed Change to Right-to-Left Layout Ubah ke tata letak kanan ke kiri Columns: Kolom: - Create Document Buat dokumen Documents you create appear here. Dokumen yang Anda buat muncul di sini. More Settings Pengaturan lainnya - New from Template Baru dari templat Number of Artboards: Jumlah artboard: Orientation: Orientasi: PRESET DETAILS DETAIL PRESET @@ -2369,7 +2366,6 @@ digits Malayalam Malayalam RECENT TERKINI RECENT (1) TERKINI (1) SAVED TERSIMPAN - Templates… Templat… Transparent (Default) Transparan (default) Save for Office Documents Simpan untuk dokumen Office Transparent Background Latar belakang transparan @@ -3273,6 +3269,7 @@ theme Appearance Tampilan Arrange by Row Susun per baris Arrange by Column Susun per kolom Save Preset Simpan preset + Save Document Preset Simpan preset dokumen Screen (72 ppi) Layar (72 ppi) Social Square Post 1080×1080 Postingan persegi media sosial 1080×1080 Distribute Distribusikan diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index f10e1f272..c3815cea4 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -297,7 +297,6 @@ font-technology Variable Variabili Update While Dragging Aggiorna durante il trascinamento Hide Video Rulers Nascondi righelli video Background Contents Contenuto sfondo - Background Contents: Contenuto sfondo: Background: Sfondo: Backspace Backspace Bar Graph Tool Strumento Grafico a barre @@ -669,7 +668,6 @@ font-technology Variable Variabili Couldn't {} {name}: {e} Impossibile {} {name}: {e} Create Crea Create Clipping Mask Crea maschera di ritaglio - Create Document Crea documento Create from Guides Crea da guide Create from Selection Crea dalla selezione Create From: Crea da: @@ -2175,7 +2173,6 @@ file-filter Templates Modelli New Color Group… Nuovo gruppo colori… New Document Presets Preset nuovo documento New file Nuovo file - New from Template Nuovo da modello New from Template… Nuovo da modello… New Graphic Style Nuovo stile grafico New Graphic Style (Alt-click to name it) Nuovo stile grafico (Alt+clic per assegnare un nome) @@ -2967,6 +2964,7 @@ digits Malayalam Malayalam Save PDF Preset Salva preset PDF Save Perspective Grid Preset Salva preset griglia prospettica Save Preset Salva preset + Save Document Preset Salva preset documento Preset {number} Preset {number} Save Preset… Salva preset… Save Print Preset Salva preset di stampa @@ -3546,7 +3544,6 @@ digits Malayalam Malayalam Template Modello Templates Modelli Templates Folder Cartella modelli - Templates… Modelli… Text Displayed in Cell: Testo visualizzato nella cella: Text Export Options Opzioni di esportazione testo Text Fill & Stroke Color Colore riempimento e traccia testo diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index b2c2b37a0..165372bcb 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -1076,7 +1076,6 @@ antiAlias Sharp シャープ Update While Dragging ドラッグ中に更新 Hide Video Rulers ビデオ定規を隠す Background Contents 背景のカラー - Background Contents: 背景のカラー: Background: 背景: Bar Graph Tool 棒グラフツール Baseline Shift ベースラインシフト @@ -1302,7 +1301,6 @@ antiAlias Sharp シャープ Corners… コーナー… Chamfer 面取り Create Clipping Mask クリッピングマスクを作成 - Create Document ドキュメントを作成 Create From: 作成元: Create Graph グラフを作成 Create New Layer 新規レイヤーを作成 @@ -2362,7 +2360,6 @@ antiAlias Sharp シャープ New Symbol… 新規シンボル… New Workspace 新規ワークスペース New file 新規ファイル - New from Template テンプレートから新規作成 New theme from the current color 現在のカラーから新規テーマ New {kind} Style… 新規{kind}スタイル… Next 次へ @@ -2905,6 +2902,7 @@ mark-style Japanese 日本式 Save PDF Preset PDF プリセットを保存 Save Perspective Grid Preset 遠近グリッドプリセットを保存 Save Preset プリセットを保存 + Save Document Preset ドキュメントプリセットを保存 Preset {number} プリセット {number} Save Preset… 保存… Save Print Preset プリントプリセットを保存 @@ -3253,7 +3251,6 @@ mark-style Japanese 日本式 Target Contents 内容をターゲット Target: ターゲット: Templates Folder テンプレートフォルダー - Templates… テンプレート… Text Displayed in Cell: セルに表示するテキスト: Text Export Options テキスト書き出しオプション Text Import Options テキスト読み込みオプション diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index a625c7bf5..4ba0bef7c 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -2252,15 +2252,12 @@ digits Malayalam მალაიალამური Selected Swatches მონიშნული ფერის ნიმუშები Advanced Options დამატებითი პარამეტრები Background Contents ფონის შიგთავსი - Background Contents: ფონის შიგთავსი: BLANK DOCUMENT PRESETS ცარიელი დოკუმენტის პრესეტები Bleed ჭრისპირა არე Change to Right-to-Left Layout მარჯვნიდან მარცხნივ განლაგებაზე გადასვლა Columns: სვეტები: - Create Document დოკუმენტის შექმნა Documents you create appear here. თქვენ მიერ შექმნილი დოკუმენტები აქ გამოჩნდება. More Settings მეტი პარამეტრი - New from Template შაბლონიდან შექმნა Number of Artboards: სამუშაო დაფების რაოდენობა: Orientation: ორიენტაცია: PRESET DETAILS პრესეტის დეტალები @@ -2270,7 +2267,6 @@ digits Malayalam მალაიალამური Preview Mode გადახედვის რეჟიმი RECENT ბოლოდროინდელი SAVED შენახული - Templates… შაბლონები… Transparent (Default) გამჭვირვალე (ნაგულისხმევი) Save for Office Documents შენახვა Office-ის დოკუმენტებისთვის Transparent Background გამჭვირვალე ფონი @@ -3095,6 +3091,7 @@ theme Appearance იერსახე Arrange by Row სტრიქონების მიხედვით დალაგება Arrange by Column სვეტების მიხედვით დალაგება Save Preset პრესეტის შენახვა + Save Document Preset დოკუმენტის პრესეტის შენახვა Screen (72 ppi) ეკრანი (72 ppi) Social Square Post 1080×1080 კვადრატული პოსტი 1080×1080 Distribute განაწილება diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index e281e8316..3303149a8 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -1066,7 +1066,6 @@ antiAlias Sharp 날카롭게 Update While Dragging 드래그하는 동안 업데이트 Hide Video Rulers 비디오 눈금자 숨기기 Background Contents 배경 내용 - Background Contents: 배경 내용: Background: 배경: Bar Graph Tool 막대 그래프 도구 Baseline Shift 기준선 이동 @@ -1292,7 +1291,6 @@ antiAlias Sharp 날카롭게 Corners… 모퉁이… Chamfer 모따기 Create Clipping Mask 클리핑 마스크 만들기 - Create Document 문서 만들기 Create From: 만들기 기준: Create Graph 그래프 만들기 Create New Layer 새 레이어 만들기 @@ -2352,7 +2350,6 @@ antiAlias Sharp 날카롭게 New Symbol… 새 심볼… New Workspace 새 작업 영역 New file 새 파일 - New from Template 템플릿에서 새로 만들기 New theme from the current color 현재 색상으로 새 테마 만들기 New {kind} Style… 새 {kind} 스타일… Next 다음 @@ -2894,6 +2891,7 @@ mark-style Japanese 일본식 Save PDF Preset PDF 사전 설정 저장 Save Perspective Grid Preset 원근 격자 사전 설정 저장 Save Preset 사전 설정 저장 + Save Document Preset 문서 사전 설정 저장 Preset {number} 사전 설정 {number} Save Preset… 사전 설정 저장… Save Print Preset 인쇄 사전 설정 저장 @@ -3242,7 +3240,6 @@ mark-style Japanese 일본식 Target Contents 대상 내용 Target: 대상: Templates Folder 템플릿 폴더 - Templates… 템플릿… Text Displayed in Cell: 셀에 표시할 텍스트: Text Export Options 텍스트 내보내기 옵션 Text Import Options 텍스트 가져오기 옵션 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 073203ca3..ec86634e2 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -2364,15 +2364,12 @@ digits Malayalam Malayalam Selected Swatches Geselecteerde stalen Advanced Options Geavanceerde opties Background Contents Achtergrondinhoud - Background Contents: Achtergrondinhoud: BLANK DOCUMENT PRESETS LEGE DOCUMENTVOORINSTELLINGEN Bleed Afrulling Change to Right-to-Left Layout Omschakelen naar lay-out van rechts naar links Columns: Kolommen: - Create Document Document maken Documents you create appear here. Documenten die je maakt, verschijnen hier. More Settings Meer instellingen - New from Template Nieuw uit sjabloon Number of Artboards: Aantal tekenborden: Orientation: Oriëntatie: PRESET DETAILS VOORINSTELLINGSDETAILS @@ -2383,7 +2380,6 @@ digits Malayalam Malayalam RECENT RECENT RECENT (1) RECENT (1) SAVED OPGESLAGEN - Templates… Sjablonen… Transparent (Default) Transparant (standaard) Save for Office Documents Opslaan voor Office-documenten Transparent Background Transparante achtergrond @@ -3287,6 +3283,7 @@ theme Appearance Vormgeving Arrange by Row Ordenen op rij Arrange by Column Ordenen op kolom Save Preset Voorinstelling opslaan + Save Document Preset Documentvoorinstelling opslaan Screen (72 ppi) Scherm (72 ppi) Social Square Post 1080×1080 Vierkante socialmediapost 1080×1080 Distribute Verdelen diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 8d1be1679..7b1195fe7 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -2476,15 +2476,12 @@ digits Malayalam Malajalam Selected Swatches Zaznaczone próbki Advanced Options Opcje zaawansowane Background Contents Zawartość tła - Background Contents: Zawartość tła: BLANK DOCUMENT PRESETS USTAWIENIA PUSTEGO DOKUMENTU Bleed Spad Change to Right-to-Left Layout Zmień na układ od prawej do lewej Columns: Kolumny: - Create Document Utwórz dokument Documents you create appear here. Tu pojawią się utworzone dokumenty. More Settings Więcej ustawień - New from Template Nowy z szablonu Number of Artboards: Liczba obszarów kompozycji: Orientation: Orientacja: PRESET DETAILS SZCZEGÓŁY USTAWIENIA @@ -2494,7 +2491,6 @@ digits Malayalam Malajalam Preview Mode Tryb podglądu RECENT OSTATNIE SAVED ZAPISANE - Templates… Szablony… Transparent (Default) Przezroczyste (domyślnie) Save for Office Documents Zapisz dla dokumentów biurowych Transparent Background Przezroczyste tło @@ -3415,6 +3411,7 @@ theme Appearance Wygląd Arrange by Row Ułóż według wierszy Arrange by Column Ułóż według kolumn Save Preset Zapisz ustawienie + Save Document Preset Zapisz ustawienie dokumentu Screen (72 ppi) Ekran (72 ppi) Social Square Post 1080×1080 Kwadratowy post społecznościowy 1080×1080 Distribute Rozmieść diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index ebe44a68c..b8989eee9 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -283,7 +283,6 @@ antiAlias Sharp Nítido Update While Dragging Atualizar ao Arrastar Hide Video Rulers Ocultar Réguas de Vídeo Background Contents Conteúdo do Plano de Fundo - Background Contents: Conteúdo do Plano de Fundo: Background: Plano de Fundo: Bar Graph Tool Ferramenta Gráfico de Barras Baseline Shift Deslocamento da Linha de Base @@ -596,7 +595,6 @@ antiAlias Sharp Nítido Corners… Cantos… Create Criar Create Clipping Mask Criar Máscara de Recorte - Create Document Criar Documento Create From: Criar De: Create Gradient Mesh… Criar Malha de Gradiente… Create Graph Criar Gráfico @@ -1792,7 +1790,6 @@ antiAlias Sharp Nítido Duplicate Toolbar Duplicar Barra de Ferramentas Delete Toolbar Excluir Barra de Ferramentas New file Novo arquivo - New from Template Novo a partir de Modelo New from Template… Novo a partir de Modelo… New theme from the current color Novo tema a partir da cor atual New {kind} Style… Novo Estilo de {kind}… @@ -2457,6 +2454,7 @@ digits Malayalam Malaiala Save PDF Preset Salvar Predefinido de PDF Save Perspective Grid Preset Salvar Predefinido de Grade de Perspectiva Save Preset Salvar Predefinido + Save Document Preset Salvar Predefinido de Documento Save Preset… Salvar Predefinido… Save Print Preset Salvar Predefinido de Impressão Save Recovery Data Salvar Dados de Recuperação @@ -2880,7 +2878,6 @@ digits Malayalam Malaiala Target: Alvo: Temperature/Tint… Temperatura/Tom… Templates Folder Pasta de Modelos - Templates… Modelos… Text Displayed in Cell: Texto Exibido na Célula: Text Export Options Opções de Exportação de Texto Text Fill & Stroke Color Cor de Preenchimento e Traçado do Texto diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index dc6402e83..453594a19 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -295,7 +295,6 @@ font-technology Variable Вариативные Update While Dragging Обновлять при перетаскивании Hide Video Rulers Скрыть видео-линейки Background Contents Содержимое фона - Background Contents: Содержимое фона: Background: Фон: Backspace Backspace Bar Graph Tool Инструмент «Гистограмма» @@ -667,7 +666,6 @@ font-technology Variable Вариативные Couldn't {} {name}: {e} Не удалось {} {name}: {e} Create Создать Create Clipping Mask Создать маску обрезки - Create Document Создать документ Create from Guides Создать из направляющих Create from Selection Создать из выделения Create From: Создать из: @@ -2170,7 +2168,6 @@ file-filter Templates Шаблоны New Color Group… Новая цветовая группа… New Document Presets Наборы новых документов New file Новый файл - New from Template Создать из шаблона New from Template… Создать из шаблона… New Graphic Style Новый графический стиль New Graphic Style (Alt-click to name it) Новый графический стиль (Alt+щелчок, чтобы задать имя) @@ -2962,6 +2959,7 @@ digits Malayalam Малаялам Save PDF Preset Сохранить набор PDF Save Perspective Grid Preset Сохранить набор сетки перспективы Save Preset Сохранить набор + Save Document Preset Сохранить набор документа Preset {number} Набор {number} Save Preset… Сохранить набор… Save Print Preset Сохранить набор печати @@ -3541,7 +3539,6 @@ digits Malayalam Малаялам Template Шаблон Templates Шаблоны Templates Folder Папка шаблонов - Templates… Шаблоны… Text Displayed in Cell: Текст, отображаемый в ячейке: Text Export Options Параметры экспорта текста Text Fill & Stroke Color Цвет заливки и обводки текста diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index a8ce1904d..b29bb9389 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -286,7 +286,6 @@ font-technology Variable Variabla Update While Dragging Uppdatera vid dragning Hide Video Rulers Dölj videolinjaler Background Contents Bakgrundsinnehåll - Background Contents: Bakgrundsinnehåll: Background: Bakgrund: Backspace Backspace Bar Graph Tool Stapeldiagramverktyg @@ -656,7 +655,6 @@ font-technology Variable Variabla Couldn't {} {name}: {e} Det gick inte att {} {name}: {e} Create Skapa Create Clipping Mask Skapa klippmask - Create Document Skapa dokument Create from Guides Skapa från stödlinjer Create from Selection Skapa från markering Create From: Skapa från: @@ -2085,7 +2083,6 @@ font-technology Variable Variabla New Color Group… Ny färggrupp… New Document Presets Förinställningar för nytt dokument New file Ny fil - New from Template Ny från mall New from Template… Ny från mall… New Graphic Style Ny grafikstil New Graphic Style (Alt-click to name it) Ny grafikstil (Alt-klicka för att namnge den) @@ -2869,6 +2866,7 @@ digits Malayalam Malayalam Save PDF Preset Spara PDF-förinställning Save Perspective Grid Preset Spara förinställning för perspektivrutnät Save Preset Spara förinställning + Save Document Preset Spara dokumentförinställning Save Preset… Spara förinställning… Save Print Preset Spara utskriftsförinställning Save Recovery Data Spara återställningsdata @@ -3432,7 +3430,6 @@ digits Malayalam Malayalam Template Mall Templates Mallar Templates Folder Mallmapp - Templates… Mallar… Text Displayed in Cell: Text som visas i cellen: Text Export Options Alternativ för textexport Text Fill & Stroke Color Textens fyllnads- och konturfärg diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index c2804006c..4e7619b7e 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -282,7 +282,6 @@ font-technology Variable Değişken Update While Dragging Sürüklerken Güncelle Hide Video Rulers Video Cetvellerini Gizle Background Contents Arka Plan İçeriği - Background Contents: Arkaplan İçeriği: Background: Arka plan: Backspace Geri tuşu Bar Graph Tool Çubuk Grafik Aracı @@ -648,7 +647,6 @@ font-technology Variable Değişken Couldn't {} {name}: {e} {} yapılamadı {name}: {e} Create Oluştur Create Clipping Mask Kırpma Maskesi Oluştur - Create Document Belge Oluştur Create from Guides Rehberlerden Oluştur Create from Selection Seçimden Oluştur Create From: Şuradan Oluştur: @@ -1958,7 +1956,6 @@ font-technology Variable Değişken New Color Group… Yeni Renk Grubu… New Document Presets Yeni Belge Ön Ayarları New file Yeni dosya - New from Template Şablondan yeni New from Template… Şablondan yeni… New Graphic Style Yeni Grafik Stili New Graphic Style (Alt-click to name it) Yeni Grafik Stili (Adını vermek için Alt tuşuna basarak tıklayın) @@ -2734,6 +2731,7 @@ digits Malayalam Malayalam Save PDF Preset PDF Hazır Ayarını Kaydet Save Perspective Grid Preset Perspektif Izgarası Ön Ayarını Kaydet Save Preset Ön Ayarı Kaydet + Save Document Preset Belge Ön Ayarını Kaydet Save Preset… Ön Ayarı Kaydet… Save Print Preset Yazdırma Ön Ayarını Kaydet Save Recovery Data Kurtarma Verilerini Kaydet @@ -3323,7 +3321,6 @@ digits Malayalam Malayalam Template Şablon Templates Şablonlar Templates Folder Şablonlar Klasörü - Templates… Şablonlar… Text Displayed in Cell: Hücrede Görüntülenen Metin: Text Export Options Metin Dışa Aktarma Seçenekleri Text Fill & Stroke Color Metin Dolgu ve Kontur Rengi diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 0108c96a5..5729680e6 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -291,7 +291,6 @@ font-technology Variable Варіативні Update While Dragging Оновлювати під час перетягування Hide Video Rulers Приховати відео-лінійки Background Contents Вміст фону - Background Contents: Вміст фону: Background: Фон: Backspace Backspace Bar Graph Tool Інструмент «Гістограма» @@ -663,7 +662,6 @@ font-technology Variable Варіативні Couldn't {} {name}: {e} Не вдалося {} {name}: {e} Create Створити Create Clipping Mask Створити відсічну маску - Create Document Створити документ Create from Guides Створити з напрямних Create from Selection Створити із виділення Create From: Створити з: @@ -2167,7 +2165,6 @@ file-filter Templates Шаблони New Color Group… Нова колірна група… New Document Presets Набір нових документів New file Новий файл - New from Template Створити із шаблону New from Template… Створити із шаблону… New Graphic Style Новий графічний стиль New Graphic Style (Alt-click to name it) Новий графічний стиль (Alt+клацання, щоб задати ім’я) @@ -2959,6 +2956,7 @@ digits Malayalam Малаялам Save PDF Preset Зберегти набір PDF Save Perspective Grid Preset Зберегти набір сітки перспективи Save Preset Зберегти набір + Save Document Preset Зберегти набір документа Preset {number} Набір {number} Save Preset… Зберегти набір… Save Print Preset Зберегти набір друку @@ -3539,7 +3537,6 @@ effect Points Точки Template Шаблон Templates Шаблони Templates Folder Папка шаблонів - Templates… Шаблони… Text Displayed in Cell: Текст, що відображається в комірці: Text Export Options Параметри експорту тексту Text Fill & Stroke Color Колір заливки та обведення тексту diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 5b979b003..ea1d2ed9e 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -320,7 +320,6 @@ font-technology Variable Biến thiên Update While Dragging Cập nhật khi kéo Hide Video Rulers Ẩn thước video Background Contents Nội dung nền - Background Contents: Nội dung nền: Background: Nền: Backspace Backspace Balance Cân bằng @@ -765,7 +764,6 @@ font-technology Variable Biến thiên Create Tạo Create Clipping Mask Tạo mặt nạ cắt Create Compound Path From Ellipses Tạo đường dẫn phức từ hình elip - Create Document Tạo tài liệu Create from Guides Tạo từ đường căn Create from Selection Tạo từ vùng chọn Create From: Tạo từ: @@ -2408,7 +2406,6 @@ digits Hindi Hindi New Design Thiết kế mới New Document Presets Thiết lập sẵn tài liệu mới New file Tệp mới - New from Template Mới từ mẫu New from Template… Mới từ mẫu… New Graphic Style Kiểu đồ họa mới New Graphic Style (Alt-click to name it) Kiểu đồ họa mới (Alt-bấm để đặt tên) @@ -3272,6 +3269,7 @@ mark-style Japanese Kiểu Nhật Save PDF Preset Lưu thiết lập sẵn PDF Save Perspective Grid Preset Lưu thiết lập sẵn lưới phối cảnh Save Preset Lưu thiết lập sẵn + Save Document Preset Lưu thiết lập sẵn tài liệu Preset {number} Thiết lập sẵn {number} Save Preset… Lưu thiết lập sẵn… Save Print Preset Lưu thiết lập sẵn in @@ -3873,7 +3871,6 @@ mark-style Japanese Kiểu Nhật Template Mẫu Templates Mẫu Templates Folder Thư mục mẫu - Templates… Mẫu… Text Văn bản Text Displayed in Cell: Văn bản hiển thị trong ô: Text Export Options Tùy chọn xuất văn bản diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index d7b40b95d..7ce4da2fd 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -243,7 +243,6 @@ antiAlias Sharp 锐利 Update While Dragging 拖动时更新 Hide Video Rulers 隐藏视频标尺 Background Contents 背景内容 - Background Contents: 背景内容: Background: 背景: Backspace Backspace Bar Graph Tool 条形图工具 @@ -614,7 +613,6 @@ antiAlias Sharp 锐利 Couldn't {} {name}: {e} 无法{} {name}:{e} Create 创建 Create Clipping Mask 创建剪切蒙版 - Create Document 创建文档 Create from Guides 从参考线创建 Create from Selection 从选区创建 Create From: 创建自: @@ -2044,7 +2042,6 @@ file-filter Templates 模板 New Color Group… 新建颜色组… New Document Presets 新建文档预设 New file 新建文件 - New from Template 从模板新建 New from Template… 从模板新建… New Graphic Style 新建图形样式 New Graphic Style (Alt-click to name it) 新建图形样式 (Alt+点击可命名) @@ -2828,6 +2825,7 @@ digits Malayalam 马拉雅拉姆数字 Save PDF Preset 保存 PDF 预设 Save Perspective Grid Preset 存储透视网格预设 Save Preset 保存预设 + Save Document Preset 保存文档预设 Preset {number} 预设 {number} Save Preset… 存储预设… Save Print Preset 保存打印预设 @@ -3374,7 +3372,6 @@ digits Malayalam 马拉雅拉姆数字 Template 模板 Templates 模板 Templates Folder 模板文件夹 - Templates… 模板… Text Displayed in Cell: 单元格中显示的文字: Text Export Options 文本导出选项 Text Fill & Stroke Color 文字填色和描边颜色 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 9e27d9c7b..961118d55 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -256,7 +256,6 @@ font-technology Variable 可變 Update While Dragging 拖曳時更新 Hide Video Rulers 隱藏視訊尺標 Background Contents 背景內容 - Background Contents: 背景內容: Background: 背景: Backspace Backspace Bar Graph Tool 橫條圖工具 @@ -631,7 +630,6 @@ font-technology Variable 可變 Couldn't {} {name}: {e} 無法{} {name}:{e} Create 建立 Create Clipping Mask 建立剪裁遮色片 - Create Document 建立文件 Create from Guides 從參考線建立 Create from Selection 從選取範圍建立 Create From: 建立來源: @@ -3064,7 +3062,6 @@ file-filter Templates 範本 New Color Group… 新增顏色群組… New Document Presets 新增文件預設集 New file 新增檔案 - New from Template 從範本新增 New from Template… 從範本新增… New Graphic Style 新增繪圖樣式 New Graphic Style (Alt-click to name it) 新增繪圖樣式 (Alt+點擊可命名) @@ -3861,6 +3858,7 @@ digits Malayalam 馬來亞拉姆數字 Save PDF Preset 儲存 PDF 預設集 Save Perspective Grid Preset 儲存透視格點預設集 Save Preset 儲存預設集 + Save Document Preset 儲存文件預設集 Preset {number} 預設集 {number} Save Preset… 儲存預設集… Save Print Preset 儲存列印預設集 @@ -4438,7 +4436,6 @@ digits Malayalam 馬來亞拉姆數字 Template 範本 Templates 範本 Templates Folder 範本資料夾 - Templates… 範本… Text Displayed in Cell: 儲存格中顯示的文字: Text Export Options 文字匯出選項 Text Fill & Stroke Color 文字填色與筆畫顏色 diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index 6d7ac9474..63e311aea 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -226,7 +226,9 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context) { } // Escape closes a dropdown list or popover open in the dialog (each closes itself on it) and // leaves the dialog open. - let popup = app.ui.dialog.as_ref().is_some_and(|d| egui::Popup::is_any_open(ctx) || crate::dialogs::brush_options::tips_open(d)); + let popup = app.ui.dialog.as_ref().is_some_and(|d| { + egui::Popup::is_any_open(ctx) || crate::dialogs::brush_options::tips_open(d) || crate::dialogs::new_document_popup_open(d) + }); // Escape in Preferences' search field with text clears the field first. if !popup && pressed_anew(ctx, Key::Escape) && !crate::prefs_dialog::clear_search_on_escape(app, ctx) { crate::dialogs::cancel(app); diff --git a/docs/control-protocol.md b/docs/control-protocol.md index 2cb542c6b..7a4aa6c6b 100644 --- a/docs/control-protocol.md +++ b/docs/control-protocol.md @@ -572,9 +572,15 @@ New Document: File → New… (`file.newDialog`, Cmd+N) opens the `newDocument` `colorMode`, `rasterEffectsPpi`, `previewMode`) plus `category` (the tab: Recent, Saved, Mobile, Web, Print, Film & Video, Art & Illustration, Branding, Social), `advanced` (Advanced Options open), `bleedLinked` and `presetName` (Save Preset's name). Setting `preset` to a name from `file.newPresets` fills the other fields from that preset; -changing a field afterwards makes the settings custom (`preset` becomes ""). More Settings is the `newDocumentMore` -dialog over the same fields (its Profile is `category`). `ui.dialog.confirm` runs `file.new` with the fields (with -`previewMode: "pixel"` the app turns Pixel Preview on); on an error the dialog stays open. +changing a field afterwards makes the settings custom (`preset` becomes ""). The `newDocumentMore` dialog is New +Document with the More Settings pop-up open over it, which edits `artboards` and `artboardLayout` (layout, right-to-left, +spacing and columns). While `__saving` is true, Preset Details shows Save Document Preset with the `presetName` field, +Cancel and Save in place of the document's name and hides Close and Create. `ui.dialog.cancel` closes the More Settings +pop-up first, then leaves saving, and keeps the fields each time. `ui.dialog.confirm` runs `file.new` with the fields +(with `previewMode: "pixel"` the app turns Pixel Preview on); on an error the dialog stays open. While `__saving` is +true, `ui.dialog.confirm` saves the preset and does not create a document; after a save `presetName` is empty, and the +next Save Preset fills it with the first `Preset N` that no saved preset has. `ui.click` on the More Settings button +opens the `newDocumentMore` pop-up. Swatch Conflict: through the app, when the pasted objects' global or spot swatches conflict with the document's (see `clipboard.conflicts`) and the params have no `swatchConflict`, every Paste command opens the `swatchConflict` diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index a93770b88..b4bcc9f8e 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -649,6 +649,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - **Window › New Window (G7, #4116):** New Window (`window.newWindow`) opens another view of the current document ([public source](https://helpx.adobe.com/illustrator/using/viewing-artwork.html) describes showing one document in several windows at once), with its own zoom, center, rotation, artboard and Outline or Preview, as a tab of its own; an edit in one view shows in every view. Tile, Cascade, Float in Window, Float All in Windows and Consolidate All Windows arrange each view as they arrange a document, and Next and Previous Document step through the views. Closing a view leaves the document open; closing its last view closes the document, asking to save it first. The view's number after the name in its tab ("Name:2", while the document has more than one view) is VectorCraft's own. Tests: `crates/ui-egui/src/doc_windows/tests.rs`. - **Document Setup modal handoff:** Document Setup → Edit Artboards applies valid settings, closes the dialog and activates the Artboard tool. Pointer-click tests cover successful handoff, invalid settings, OK, Cancel and Undo in `dialogs/document_setup.rs`. +- **New Document's More Settings and Save Preset:** More Settings opens a pop-up over New Document with only Number of Artboards, the artboard layout, Change to Right-to-Left Layout, Spacing and Columns; Done, Escape or a click outside closes it and keeps the values. Save Preset turns the top of Preset Details into a Save Document Preset heading with the preset's name, Cancel and Save, and hides the document's name, Close and Create until the preset is saved or canceled; nothing in that mode creates a document. Save Preset's default name is the first "Preset N" no saved preset has. VectorCraft's own layout. Harness tests in `dialogs/new_document.rs` cover both. - **Control bar with the Artboard tool:** while the Artboard tool is in use, the Control bar shows the active artboard's controls in place of the object controls: Preset, Portrait and Landscape, New Artboard, Delete Artboard, the Name field, Artboard Options, Rearrange All Artboards, the Transform link, Move and Scale Artwork with Artboard and, while the bar has room, the reference point with X and Y, and W and H with the Constrain Width and Height Proportions link. Each runs the command its Properties or Transform panel counterpart runs (`artboard.setProps`, `artboard.new`, `artboard.delete`, `tool.options {tool: "artboard"}`), and the reference point is the one the Transform panel uses. In an 800-point window the controls up to the Transform link fit, Move and Scale Artwork with Artboard show as their boxes alone with their names in tooltips, and the link opens the Transform panel with the artboard's position and size. Pointer and key tests in `panels/artboards.rs` cover the controls, W about the reference point, the orientation and the name. Public reference: [Using multiple artboards](https://web.archive.org/web/20210925094818/https://helpx.adobe.com/illustrator/using/using-multiple-artboards.html) (archived copy). - **Names of new and renamed artboards, brushes and patterns:** `artboard.new` and `artboard.setProps` trim the white space around the name they are given, as the Artboards panel's rename does, and a blank name gives the artboard its default name, Artboard N for the Nth artboard. `brush.new`, `brush.duplicate`, `brush.options`, `object.pattern.make`, `object.pattern.saveCopy` and `pattern.options` trim the name as the Brush Options dialog and the Pattern Options panel do, and compare the trimmed name with the names there are; a blank name gives the name each command gives when there is none, and Brush Options and Pattern Options keep the name. Tests in `tests/panelcmds.rs`, `tests/brushsym.rs` and `tests/pattern.rs` cover each command. Public references: [Using multiple artboards](https://web.archive.org/web/20210925094818/https://helpx.adobe.com/illustrator/using/using-multiple-artboards.html), [Brushes](https://web.archive.org/web/20251018101754/https://helpx.adobe.com/illustrator/using/brushes.html) and [Create and edit patterns](https://web.archive.org/web/20211031180358/https://helpx.adobe.com/illustrator/using/create-edit-patterns.html) (archived copies). - **Pattern names and swatch names:** `pattern.options` refuses a new name that another pattern, a swatch or a color group has. It takes a name that only the pattern's own swatches have. The reference page does not say what happens when the new name is taken; the refusal follows VectorCraft's rule that swatches and color groups share one namespace (`Document::swatch_name_taken`), which `swatch.new` and `swatch.edit` also follow. A rename gives the new name only to the pattern's swatches that had the old one; any other swatch with the old name keeps it, and art linked to that swatch stays linked. `object.pattern.make` and `object.pattern.saveCopy` number a color group's name as they number a swatch's. Tests in `tests/pattern.rs` cover each command. Public reference: [Create and edit patterns](https://web.archive.org/web/20211031180358/https://helpx.adobe.com/illustrator/using/create-edit-patterns.html) (archived copy).