diff --git a/crates/ui-egui/src/credits.rs b/crates/ui-egui/src/credits.rs index 35d014dc3..440f94c6e 100644 --- a/crates/ui-egui/src/credits.rs +++ b/crates/ui-egui/src/credits.rs @@ -263,22 +263,25 @@ pub fn contributors_ui(app: &mut VectorcraftApp, ui: &mut egui::Ui) { let counts = crate::i18n::fmt(tl!("{people} contributors · {commits} commits"), &[("people", &people), ("commits", &commits)]); ui.label(RichText::new(counts).small().weak()); ui.separator(); - egui::ScrollArea::both().max_height(SCROLL_HEIGHT).auto_shrink([false, true]).show(ui, |ui| { - if list.is_empty() { - ui.label(tl!("No contributor data was built into this copy.")); - } else if v.table { - table(app, ui, &list, &mut v); - } else { - ui.horizontal_wrapped(|ui| { - for (i, c) in list.iter().enumerate() { - if i > 0 { - ui.label(RichText::new("·").weak()); + if v.table && !list.is_empty() { + // The table scrolls its rows under its header itself, and sideways as a whole. + egui::ScrollArea::horizontal().auto_shrink([false, true]).show(ui, |ui| table(app, ui, &list, &mut v)); + } else { + egui::ScrollArea::both().max_height(SCROLL_HEIGHT).auto_shrink([false, true]).show(ui, |ui| { + if list.is_empty() { + ui.label(tl!("No contributor data was built into this copy.")); + } else { + ui.horizontal_wrapped(|ui| { + for (i, c) in list.iter().enumerate() { + if i > 0 { + ui.label(RichText::new("·").weak()); + } + link(app, ui, c, v.names); } - link(app, ui, c, v.names); - } - }); - } - }); + }); + } + }); + } ui.data_mut(|d| d.insert_temp(id, v)); } @@ -291,34 +294,55 @@ fn link(app: &mut VectorcraftApp, ui: &mut egui::Ui, c: &Contributor, names: Nam r } +/// The contributors as a table whose header row, the sort buttons, stays above the rows as they +/// scroll (#4944). fn table(app: &mut VectorcraftApp, ui: &mut egui::Ui, list: &[&Contributor], v: &mut View) { - egui::Grid::new("credits_table").striped(true).num_columns(SortKey::ALL.len()).show(ui, |ui| { - for k in SortKey::ALL { - let arrow = if v.key == k { if v.ascending { " ▲" } else { " ▼" } } else { "" }; - if ui.button(RichText::new(format!("{}{arrow}", k.label().1)).strong()).on_hover_text(k.label().0).clicked() { - if v.key == k { - v.ascending = !v.ascending; - } else { - v.key = k; - v.ascending = k.default_ascending(); - } + let (row, names) = (ui.spacing().interact_size.y, v.names); + egui_extras::TableBuilder::new(ui) + .id_salt("credits_table") + .striped(true) + .columns(egui_extras::Column::auto(), SortKey::ALL.len()) + .max_scroll_height(SCROLL_HEIGHT - row) + .header(row, |mut header| { + for k in SortKey::ALL { + header.col(|ui| { + let arrow = if v.key == k { if v.ascending { " ▲" } else { " ▼" } } else { "" }; + if ui.button(RichText::new(format!("{}{arrow}", k.label().1)).strong()).on_hover_text(k.label().0).clicked() { + if v.key == k { + v.ascending = !v.ascending; + } else { + v.key = k; + v.ascending = k.default_ascending(); + } + } + }); } - } - ui.end_row(); - for c in list { - link(app, ui, c, v.names); - ui.label(group(c.prs)); - ui.label(group(c.commits)); - ui.label(group(c.lines_added)); - ui.label(group(c.lines_deleted)); - ui.label(signed(c.lines_delta())); - ui.label(group(c.binary_added)); - ui.label(group(c.binary_deleted)); - ui.label(day(c.first_commit)); - ui.label(day(c.last_commit)); - ui.end_row(); - } - }); + }) + .body(|mut body| { + for c in list { + body.row(row, |mut r| { + r.col(|ui| { + link(app, ui, c, names); + }); + let cells = [ + group(c.prs), + group(c.commits), + group(c.lines_added), + group(c.lines_deleted), + signed(c.lines_delta()), + group(c.binary_added), + group(c.binary_deleted), + day(c.first_commit).to_string(), + day(c.last_commit).to_string(), + ]; + for text in cells { + r.col(|ui| { + ui.label(text); + }); + } + }); + } + }); } /// About ▸ Models: AI models credited in Co-Authored-By trailers. @@ -423,4 +447,51 @@ mod tests { assert_eq!(day("2026-10-07T18:45:23Z"), "2026-10-07"); assert_eq!(day(""), "—"); } + + /// #4944: in the table, the header row (the sort buttons) stays put while the rows scroll + /// under it. + #[test] + fn the_table_header_stays_above_scrolling_rows() { + let first = sorted(CONTRIBUTORS, NameMode::Username, SortKey::FirstCommit, true).first().map(|c| c.name(NameMode::Username)).unwrap(); + assert!(CONTRIBUTORS.len() > 30, "enough rows to scroll"); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + ctx.data_mut(|d| d.insert_temp(egui::Id::unique("credits_view"), View { table: true, ..View::default() })); + let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default()); + let mut time = 0.0; + // Where the frame painted `text`, if it did. + let mut frame = |events: Vec, find: &[&str]| { + time += 0.1; + let input = egui::RawInput { events, time: Some(time), ..Default::default() }; + let mut out = ctx.run_ui(input, |ui| { + ui.set_width(660.0); + contributors_ui(&mut app, ui); + }); + out.textures_delta.clear(); + let at = |text: &str| { + out.shapes.iter().find_map(|c| match &c.shape { + egui::Shape::Text(t) if t.galley.text() == text => Some(t.pos), + _ => None, + }) + }; + find.iter().map(|t| at(t)).collect::>() + }; + frame(vec![], &[]); + let before = frame(vec![], &["PRs", &first]); + let (Some(header), Some(row)) = (before[0], before[1]) else { panic!("header and first row painted: {before:?}") }; + let wheel = egui::Event::MouseWheel { + unit: egui::MouseWheelUnit::Point, + delta: egui::vec2(0.0, -200.0), + phase: egui::TouchPhase::Move, + modifiers: Default::default(), + source: egui::MouseWheelSource::Unknown, + }; + frame(vec![egui::Event::PointerMoved(row + egui::vec2(4.0, 4.0)), wheel], &[]); + let mut after = vec![]; + for _ in 0..10 { + after = frame(vec![], &["PRs", &first]); + } + assert_eq!(after[0], Some(header), "the header stays where it was"); + assert!(after[1].is_none_or(|p| p.y < row.y), "the rows scrolled: {:?} from {row:?}", after[1]); + } } diff --git a/crates/ui-egui/src/dialogs/save_options.rs b/crates/ui-egui/src/dialogs/save_options.rs index cbf450a6a..8ee5de9c9 100644 --- a/crates/ui-egui/src/dialogs/save_options.rs +++ b/crates/ui-egui/src/dialogs/save_options.rs @@ -7,12 +7,13 @@ //! editable), `path`, `format`, and one field per option (agents set them with `ui.dialog.set`). //! Native and `.ai` files show their save options (VectorCraft Options after Save As): each //! artboard to a separate file, with `all` (true: every artboard) or `range`, Include Linked Files, -//! Embed ICC Profiles, Create PDF-Compatible File and Use Compression. A `.ai` file's Version comes -//! first: PDF-compatible, or legacy version 7 (`aiVersion: "7"`, a file per artboard), which takes -//! only Transparent Images (`transparentImages`), Hidden Objects (`hiddenObjects`: a hidden layer, -//! omitted or made visible), Text (`outlineText`: editable or outlines) and, with several -//! artboards, a picker of the artboards to save (thumbnails to check, field `boards`, saved as -//! `range`). +//! Embed ICC Profiles, Create PDF-Compatible File and Use Compression. A native file's Version is a +//! menu of the versions VectorCraft writes (`version`, named by the releases that read each). A +//! `.ai` file's Version comes first: PDF-compatible, or legacy version 7 (`aiVersion: "7"`, a file +//! per artboard), which takes only Transparent Images (`transparentImages`), Hidden Objects +//! (`hiddenObjects`: a hidden layer, omitted or made visible), Text (`outlineText`: editable or +//! outlines) and, with several artboards, a picker of the artboards to save (thumbnails to check, +//! field `boards`, saved as `range`). use std::sync::OnceLock; @@ -342,6 +343,10 @@ fn option_row(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, o: &Forma ui.end_row(); return; } + if o.name == "version" && o.ty == "integer" { + native_version(ui, d, o); + return; + } if o.name == "transparentImages" { ui.label(egui::RichText::new(tl!("Transparent Images:")).color(t.text_dim)).on_hover_text(o.description); let choices = [tl!("Flatten"), tl!("Omit")]; @@ -379,7 +384,9 @@ fn option_row(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, o: &Forma if o.ty == "boolean" { ui.label(""); let on = value.as_bool().unwrap_or(false); - if widgets::check_tip(ui, label.trim_end_matches(':'), on, true, o.description) { + // Versions older than compression read only plain files. + let enabled = o.name != "compress" || !uncompressed(d); + if widgets::check_tip(ui, label.trim_end_matches(':'), on && enabled, enabled, o.description) { d.fields.insert(o.name.into(), json!(!on)); } ui.end_row(); @@ -412,6 +419,43 @@ fn option_row(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, o: &Forma ui.end_row(); } +/// The native versions VectorCraft writes, newest first, each with the first release that reads +/// it (the format version each release tag writes: 2 in 0.1, 3 from 0.2, 4 from 0.8). +const NATIVE_VERSIONS: [(u32, &str); 4] = [(4, "0.8"), (3, "0.2"), (2, "0.1"), (1, "0.1")]; + +/// A native file's Version (#4931): a menu of the versions VectorCraft writes, named by the +/// releases that read them, so a version it can't write can't be chosen. A version without +/// compression turns Use Compression off. +fn native_version(ui: &mut egui::Ui, d: &mut Dialog, o: &FormatOption) { + let t = Tokens::get(ui.ctx()); + ui.label(egui::RichText::new(tl!("Version:")).color(t.text_dim)).on_hover_text(o.description); + let names: Vec = NATIVE_VERSIONS + .iter() + .map(|(n, release)| crate::i18n::fmt(tl!("VectorCraft {release} and later (version {n})"), &[("release", release), ("n", &n.to_string())])) + .collect(); + let names: Vec<&str> = names.iter().map(String::as_str).collect(); + let at = NATIVE_VERSIONS.iter().position(|(n, _)| *n == native_version_of(d)).unwrap_or(0); + if let Some(i) = widgets::dropdown_names(ui, ("save-option", o.name), names.get(at).copied().unwrap_or_default(), &names, 254.0) + && let Some((n, _)) = NATIVE_VERSIONS.get(i) + { + d.fields.insert(o.name.into(), json!(n)); + if *n < vectorcraft_format::COMPRESSED_SINCE { + d.fields.insert("compress".into(), json!(false)); + } + } + ui.end_row(); +} + +/// The native version the dialog will write (the current one unless chosen). +fn native_version_of(d: &Dialog) -> u32 { + d.fields.get("version").and_then(Value::as_u64).and_then(|v| u32::try_from(v).ok()).unwrap_or(vectorcraft_format::VERSION) +} + +/// Is a native file to be saved in a version that can't be compressed? +fn uncompressed(d: &Dialog) -> bool { + native_version_of(d) < vectorcraft_format::COMPRESSED_SINCE +} + /// Web Save As: another format brings its own options and file extension. fn switch_format(app: &mut VectorcraftApp, d: &mut Dialog, id: &str) { let (Some(old), Some(new)) = (fileio::format(&d.str("format")), fileio::format(id)) else { return }; @@ -444,6 +488,10 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> DialogResult { .iter() .filter_map(|o| d.fields.get(o.name).filter(|v| !v.is_null() && v.as_str() != Some("")).map(|v| (o.name.to_string(), v.clone()))) .collect(); + if uncompressed(d) { + // Use Compression shows off there, whatever it held before. + options.remove("compress"); + } if legacy_ai(d) { // The artboards checked in the picker (all of them: no range). options.remove("range"); diff --git a/crates/ui-egui/src/dialogs/save_options/tests.rs b/crates/ui-egui/src/dialogs/save_options/tests.rs index fe46331a5..e5483a935 100644 --- a/crates/ui-egui/src/dialogs/save_options/tests.rs +++ b/crates/ui-egui/src/dialogs/save_options/tests.rs @@ -347,3 +347,46 @@ fn eps_save_as_keeps_page_options_and_writes_eps_without_export_only_switches() assert!(vectorcraft_eps::native(&saved[0].1).is_some()); assert_eq!(app.session.active().unwrap().format, "eps"); } + +/// #4931: a native file's Version is a menu of the versions VectorCraft writes, the current one +/// first and chosen, each named by the releases that read it. An older version without +/// compression saves plain, whatever Use Compression held. +#[test] +fn native_version_is_a_menu_of_the_versions_vectorcraft_writes() { + let listed: Vec = NATIVE_VERSIONS.iter().map(|(n, _)| *n).collect(); + let writable: Vec = (vectorcraft_format::MIN_VERSION..=vectorcraft_format::VERSION).rev().collect(); + assert_eq!(listed, writable, "every version VectorCraft writes, newest first"); + let (mut app, written, _) = desktop("art.vectorcraft"); + assert_eq!(app.run("file.saveAs", json!({})).unwrap()["pending"], KIND); + let text = crate::tests_labels::painted_text(&mut app, |app, ui| super::super::show(app, ui.ctx())); + assert!(text.contains("VectorCraft 0.8 and later (version 4)"), "{text}"); + assert_eq!(native_version_of(dialog(&app)), vectorcraft_format::VERSION); + let d = app.ui.dialog.as_mut().unwrap(); + d.fields.insert("version".into(), json!(2)); + d.fields.insert("compress".into(), json!(true)); + assert!(uncompressed(d)); + frame(&mut app, true); + assert!(app.ui.dialog.is_none(), "saved: {}", app.ui.status); + let (_, bytes) = written.borrow()[0].clone(); + let head = String::from_utf8_lossy(&bytes[..bytes.len().min(200)]).to_string(); + assert!(head.contains("\"version\": 2") || head.contains("\"version\":2"), "{head}"); +} + +/// #4933: a save refused before anything is written (here a version agents can still set) says +/// so in the "Can't save" alert, in the reason's words without the command's, and writes nothing. +#[test] +fn a_refused_save_says_so_in_an_alert() { + let (mut app, written, _) = desktop("art.vectorcraft"); + assert_eq!(app.run("file.saveAs", json!({})).unwrap()["pending"], KIND); + app.ui.dialog.as_mut().unwrap().fields.insert("version".into(), json!(55)); + frame(&mut app, false); + frame(&mut app, true); + let d = dialog(&app); + assert_eq!( + (d.kind.as_str(), d.str("message").as_str(), d.str("detail").as_str()), + (crate::dialogs::confirm::MESSAGE, "Can't save “art.vectorcraft”.", "can't save version 55: VectorCraft writes versions 1 to 4") + ); + assert!(written.borrow().is_empty()); + let st = app.session.active().unwrap(); + assert!(st.is_dirty() && st.path.is_none(), "not saved"); +} diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 83697b7f3..e09a59232 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -906,7 +906,7 @@ font-technology Variable Променливи Edit Symbol Редактиране на символ Edit Text Редактиране на текст Edit the applied effect, or add another one? Да се редактира ли приведеният ефект или да се добави друг? - Edit the selected point's colour in the Color panel Редактиране на цвета на избраната точка в панела Цвят + Edit the point Редактиране на точката Edit the stop Редактиране на опорната точка Edit Theme Редактиране на тема Edit theme Редактиране на тема @@ -5926,3 +5926,4 @@ breakText Glyphs Глифи Set Size of Each Задаване на размер на всеки @msg select exactly two objects изберете точно два обекта @msg give `width` or `height` задайте `width` или `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} и по-нови (версия {n}) diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index 3a737414d..f213baaab 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -1516,7 +1516,7 @@ lighting Light Svjetlo Color: Boja: Delete Stop Izbriši tačku Edit Gradient Uredi gradijent - Edit the selected point's colour in the Color panel Uredite boju odabrane tačke u panelu Boja + Edit the point Uredi tačku Edit the stop Uredi tačku Eyedropper: click the art to sample the stop's colour Kapaljka: kliknite crtež da uzmete uzorak boje tačke Eyedropper: click the art to sample the key color Kapaljka: kliknite crtež da uzmete uzorak ključne boje @@ -5960,3 +5960,4 @@ shape Open Otvoren Set Size of Each Postavi veličinu svakog @msg select exactly two objects odaberite tačno dva objekta @msg give `width` or `height` zadajte `width` ili `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} i novije (verzija {n}) diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index c04ca4698..8d8054c25 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -1309,7 +1309,7 @@ Edit Symbol Edita el símbol Edit Text Edita el text Edit the applied effect, or add another one? Vols editar l’efecte aplicat o afegir-ne un altre? - Edit the selected point's colour in the Color panel Edita el color del punt seleccionat al panell Color + Edit the point Edita el punt Edit the stop Edita la parada Edit Theme Edita el tema Edit theme Edita el tema @@ -6033,3 +6033,4 @@ breakText Glyphs Glifs Set Size of Each Defineix la mida de cadascun @msg select exactly two objects selecciona exactament dos objectes @msg give `width` or `height` indica `width` o `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} i posteriors (versió {n}) diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 558384bb2..0caa797bd 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -1539,7 +1539,7 @@ lighting Light Světlo Color: Barva: Delete Stop Odstranit zarážku Edit Gradient Upravit přechod - Edit the selected point's colour in the Color panel Upravit barvu vybraného bodu v panelu Barva + Edit the point Upravit bod Edit the stop Upravit zarážku Eyedropper: click the art to sample the stop's colour Kapátko: kliknutím na kresbu naberete barvu zarážky Eyedropper: click the art to sample the key color Kapátko: kliknutím na kresbu naberete klíčovou barvu @@ -6042,3 +6042,4 @@ breakText Glyphs Glyfy Set Size of Each Nastavit velikost jednotlivě @msg select exactly two objects vyberte právě dva objekty @msg give `width` or `height` zadejte `width` nebo `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} a novější (verze {n}) diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 4914683cc..1507f30ef 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -1453,7 +1453,7 @@ lighting Light Lys Color: Farve: Delete Stop Slet stop Edit Gradient Redigér gradient - Edit the selected point's colour in the Color panel Redigér det markerede punkts farve i farvepanelet + Edit the point Redigér punktet Edit the stop Redigér stoppet Eyedropper: click the art to sample the stop's colour Pipette: klik på grafikken for at prøvetage stoppets farve Eyedropper: click the art to sample the key color Pipette: klik på grafikken for at prøvetage nøglefarven @@ -5940,3 +5940,4 @@ breakText Glyphs Glyffer Set Size of Each Angiv størrelse for hver @msg select exactly two objects markér præcis to objekter @msg give `width` or `height` angiv `width` eller `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} og nyere (version {n}) diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index aa374c33b..22d228097 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -900,7 +900,7 @@ font-technology Variable Variabel Edit Symbol Symbol bearbeiten Edit Text Text bearbeiten Edit the applied effect, or add another one? Den angewendeten Effekt bearbeiten oder einen weiteren hinzufügen? - Edit the selected point's colour in the Color panel Die Farbe des ausgewählten Punkts im Bedienfeld „Farbe“ bearbeiten + Edit the point Punkt bearbeiten Edit the stop Farbstopp bearbeiten Edit Theme Thema bearbeiten Edit theme Thema bearbeiten @@ -5628,3 +5628,4 @@ breakText Glyphs Glyphen Set Size of Each Größe einzeln festlegen @msg select exactly two objects wählen Sie genau zwei Objekte aus @msg give `width` or `height` geben Sie `width` oder `height` an + VectorCraft {release} and later (version {n}) VectorCraft {release} und neuer (Version {n}) diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index f1758e7a4..a993b9840 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -876,7 +876,7 @@ font-technology Variable Μεταβλητές Edit Symbol Επεξεργασία συμβόλου Edit Text Επεξεργασία κειμένου Edit the applied effect, or add another one? Επεξεργασία του εφαρμοσμένου εφέ ή προσθήκη άλλου; - Edit the selected point's colour in the Color panel Επεξεργασία του χρώματος επιλεγμένου σημείου στον πίνακα Χρώμα + Edit the point Επεξεργασία σημείου Edit the stop Επεξεργασία σημείου ντεγκραντέ Edit Theme Επεξεργασία θέματος Edit theme Επεξεργασία θέματος @@ -5995,3 +5995,4 @@ breakText Glyphs Γλύφοι Set Size of Each Ορισμός μεγέθους καθενός @msg select exactly two objects επιλέξτε ακριβώς δύο αντικείμενα @msg give `width` or `height` δώστε `width` ή `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} και νεότερες (έκδοση {n}) diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index bc7ddaf56..19e524cf2 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -921,7 +921,7 @@ font-technology Variable Variables Edit Symbol Editar símbolo Edit Text Editar texto Edit the applied effect, or add another one? ¿Editar el efecto aplicado o agregar otro? - Edit the selected point's colour in the Color panel Edita el color del punto seleccionado en el panel Color + Edit the point Editar el punto Edit the stop Editar la parada Edit Theme Editar tema Edit theme Editar tema @@ -5985,3 +5985,4 @@ breakText Glyphs Glifos Set Size of Each Definir el tamaño de cada uno @msg select exactly two objects selecciona exactamente dos objetos @msg give `width` or `height` indica `width` o `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} y posteriores (versión {n}) diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 1d6fd7c02..eb9c0e525 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -1513,7 +1513,7 @@ lighting Light Valo Color: Väri: Delete Stop Poista väripiste Edit Gradient Muokkaa liukuväriä - Edit the selected point's colour in the Color panel Muokkaa valitun pisteen väriä Väri-paneelissa + Edit the point Muokkaa pistettä Edit the stop Muokkaa väripistettä Eyedropper: click the art to sample the stop's colour Pipetti: ota väripisteen väri näytteeksi napsauttamalla kuvitusta Eyedropper: click the art to sample the key color Pipetti: ota avainväri näytteeksi napsauttamalla kuvitusta @@ -6073,3 +6073,4 @@ breakText Glyphs Merkit Set Size of Each Aseta kunkin koko @msg select exactly two objects valitse täsmälleen kaksi objektia @msg give `width` or `height` anna `width` tai `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} ja uudemmat (versio {n}) diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 3e2342133..d2ecba42d 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -924,7 +924,7 @@ font-technology Variable Variables Edit Symbol Modifier le symbole Edit Text Modifier le texte Edit the applied effect, or add another one? Modifier l’effet appliqué ou en ajouter un autre ? - Edit the selected point's colour in the Color panel Modifier la couleur du point sélectionné dans le panneau Couleur + Edit the point Modifier le point Edit the stop Modifier l’étape de dégradé Edit Theme Modifier le thème Edit theme Modifier le thème @@ -6040,3 +6040,4 @@ breakText Glyphs Glyphes Set Size of Each Définir la taille de chaque objet @msg select exactly two objects sélectionnez exactement deux objets @msg give `width` or `height` indiquez `width` ou `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} et ultérieures (version {n}) diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 10be9ac87..b29f8ef71 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -1189,7 +1189,7 @@ font-technology Variable Változó Edit Symbol Szimbólum szerkesztése Edit Text Szöveg szerkesztése Edit the applied effect, or add another one? Szerkeszted az alkalmazott effektust, vagy újat adsz hozzá? - Edit the selected point's colour in the Color panel A kijelölt pont színe a Szín panelen szerkeszthető + Edit the point Pont szerkesztése Edit the stop Színátmenet-pont szerkesztése Edit Theme Téma szerkesztése Edit theme Téma szerkesztése @@ -6029,3 +6029,4 @@ breakText Glyphs Glifák Set Size of Each Egyenkénti méret beállítása @msg select exactly two objects jelölj ki pontosan két objektumot @msg give `width` or `height` add meg a `width` vagy a `height` értéket + VectorCraft {release} and later (version {n}) VectorCraft {release} és újabb (verzió: {n}) diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 53927a020..705f57370 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -1453,7 +1453,7 @@ lighting Light Cahaya Color: Warna: Delete Stop Hapus stop Edit Gradient Edit gradien - Edit the selected point's colour in the Color panel Edit warna titik terpilih di panel Warna + Edit the point Edit titik Edit the stop Edit stop Eyedropper: click the art to sample the stop's colour Pipet: klik karya seni untuk mengambil sampel warna stop Eyedropper: click the art to sample the key color Pipet: klik karya seni untuk mengambil sampel warna kunci @@ -5940,3 +5940,4 @@ breakText Glyphs Glif Set Size of Each Atur ukuran masing-masing @msg select exactly two objects pilih tepat dua objek @msg give `width` or `height` berikan `width` atau `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} dan yang lebih baru (versi {n}) diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index d48cb24f5..9d6a31cca 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -921,7 +921,7 @@ font-technology Variable Variabili Edit Symbol Modifica simbolo Edit Text Modifica testo Edit the applied effect, or add another one? Modificare l'effetto applicato o aggiungerne un altro? - Edit the selected point's colour in the Color panel Modifica il colore del punto selezionato nel pannello Colore + Edit the point Modifica il punto Edit the stop Modifica il cursore colore Edit Theme Modifica tema Edit theme Modifica tema @@ -5662,3 +5662,4 @@ breakText Glyphs Glifi Set Size of Each Imposta dimensione di ciascuno @msg select exactly two objects seleziona esattamente due oggetti @msg give `width` or `height` indica `width` o `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} e successive (versione {n}) diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index cd1d86f4d..abe51ff0a 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -1420,7 +1420,7 @@ font-technology Variable バリアブル Edit or Apply Color Group… カラーグループを編集または適用… Edit or Apply Colors カラーを編集/適用 Edit the applied effect, or add another one? 適用済みの効果を編集しますか? それとも別の効果を追加しますか? - Edit the selected point's colour in the Color panel 選択したポイントのカラーをカラーパネルで編集 + Edit the point ポイントを編集 Edit the stop 分岐点を編集 Edit theme テーマを編集 Edit… 編集… @@ -5728,3 +5728,4 @@ breakText Glyphs 字形 Set Size of Each 個別にサイズを設定 @msg select exactly two objects オブジェクトをちょうど 2 つ選択してください @msg give `width` or `height` `width` または `height` を指定してください + VectorCraft {release} and later (version {n}) VectorCraft {release} 以降 (バージョン {n}) diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 798b57b0b..adcb85d67 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -1370,7 +1370,7 @@ lighting Light სინათლე Color: ფერი: Delete Stop გადასვლის წერტილის წაშლა Edit Gradient გრადიენტის რედაქტირება - Edit the selected point's colour in the Color panel მონიშნული წერტილის ფერის რედაქტირება ფერის პანელში + Edit the point წერტილის რედაქტირება Edit the stop გადასვლის წერტილის რედაქტირება Eyedropper: click the art to sample the stop's colour პიპეტი: დაწკაპეთ ნახატზე გადასვლის წერტილის ფერის ასაღებად Eyedropper: click the art to sample the key color პიპეტი: დაწკაპეთ ნახატზე საკვანძო ფერის ასაღებად @@ -6032,3 +6032,4 @@ breakText Glyphs გლიფები Set Size of Each თითოეულის ზომის დაყენება @msg select exactly two objects მონიშნეთ ზუსტად ორი ობიექტი @msg give `width` or `height` მიუთითეთ `width` ან `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} და უფრო ახალი (ვერსია {n}) diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 38eb0dad0..b3bf3cd62 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -1411,7 +1411,7 @@ font-technology Variable 가변 Edit or Apply Color Group… 색상 그룹 편집 또는 적용… Edit or Apply Colors 색상 편집 또는 적용 Edit the applied effect, or add another one? 적용된 효과를 편집하거나 다른 효과를 추가하시겠습니까? - Edit the selected point's colour in the Color panel 색상 패널에서 선택한 포인트의 색상 편집 + Edit the point 포인트 편집 Edit the stop 정지점 편집 Edit theme 테마 편집 Edit… 편집… @@ -5625,3 +5625,4 @@ breakText Glyphs 글리프 Set Size of Each 각각 크기 설정 @msg select exactly two objects 개체를 정확히 두 개 선택하세요 @msg give `width` or `height` `width` 또는 `height`를 지정하세요 + VectorCraft {release} and later (version {n}) VectorCraft {release} 이상 (버전 {n}) diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 2a56400a2..c3bf46c3e 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -1467,7 +1467,7 @@ lighting Light Licht Color: Kleur: Delete Stop Stoppunt verwijderen Edit Gradient Verloop bewerken - Edit the selected point's colour in the Color panel Bewerk de kleur van het geselecteerde punt in het Kleur-paneel + Edit the point Punt bewerken Edit the stop Stoppunt bewerken Eyedropper: click the art to sample the stop's colour Pipet: klik op de illustratie om de kleur van het stoppunt over te nemen Eyedropper: click the art to sample the key color Pipet: klik op de illustratie om de sleutelkleur over te nemen @@ -5984,3 +5984,4 @@ breakText Glyphs Glyphs Set Size of Each Formaat van elk instellen @msg select exactly two objects selecteer precies twee objecten @msg give `width` or `height` geef `width` of `height` op + VectorCraft {release} and later (version {n}) VectorCraft {release} en nieuwer (versie {n}) diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 1b8e322eb..519595ffc 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -1453,7 +1453,7 @@ lighting Light Światło Color: Kolor: Delete Stop Usuń punkt koloru Edit Gradient Edytuj gradient - Edit the selected point's colour in the Color panel Edytuj kolor zaznaczonego punktu w panelu Kolor + Edit the point Edytuj punkt Edit the stop Edytuj punkt koloru Eyedropper: click the art to sample the stop's colour Kroplomierz: kliknij kompozycję, aby pobrać kolor punktu Eyedropper: click the art to sample the key color Kroplomierz: kliknij kompozycję, aby pobrać kolor kluczowy @@ -5898,3 +5898,4 @@ breakText Glyphs Glify Set Size of Each Ustaw rozmiar każdego @msg select exactly two objects zaznacz dokładnie dwa obiekty @msg give `width` or `height` podaj `width` lub `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} i nowsze (wersja {n}) diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index f64b2436d..b07ec8f66 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -816,7 +816,7 @@ Edit or Apply Color Group… Editar ou Aplicar Grupo de Cores… Edit or Apply Colors Editar ou Aplicar Cores Edit the applied effect, or add another one? Editar o efeito aplicado, ou adicionar outro? - Edit the selected point's colour in the Color panel Edite a cor do ponto selecionado no painel Cor + Edit the point Editar o ponto Edit the stop Editar a parada Edit theme Editar tema Edit… Editar… @@ -5962,3 +5962,4 @@ breakText Glyphs Glifos Set Size of Each Definir Tamanho de Cada Um @msg select exactly two objects selecione exatamente dois objetos @msg give `width` or `height` informe `width` ou `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} e posteriores (versão {n}) diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 816096e51..685d6862b 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -919,7 +919,7 @@ font-technology Variable Вариативные Edit Symbol Редактировать символ Edit Text Редактировать текст Edit the applied effect, or add another one? Редактировать применённый эффект или добавить другой? - Edit the selected point's colour in the Color panel Редактируйте цвет выбранной точки на панели «Цвет» + Edit the point Редактировать точку Edit the stop Редактировать точку перехода Edit Theme Редактировать тему Edit theme Редактировать тему @@ -6020,3 +6020,4 @@ breakText Glyphs Глифы Set Size of Each Задать размер каждого @msg select exactly two objects выделите ровно два объекта @msg give `width` or `height` укажите `width` или `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} и новее (версия {n}) diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 46e4bd844..b5ff04674 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -908,7 +908,7 @@ font-technology Variable Variabla Edit Symbol Redigera symbol Edit Text Redigera text Edit the applied effect, or add another one? Vill du redigera den använda effekten eller lägga till en till? - Edit the selected point's colour in the Color panel Redigera den markerade punktens färg i panelen Färg + Edit the point Redigera punkten Edit the stop Redigera stoppet Edit Theme Redigera tema Edit theme Redigera tema @@ -6095,3 +6095,4 @@ breakText Glyphs Glyfer Set Size of Each Ange storlek för vart och ett @msg select exactly two objects markera exakt två objekt @msg give `width` or `height` ange `width` eller `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} och senare (version {n}) diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 1c2feb846..4961fcd15 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -900,7 +900,7 @@ font-technology Variable Değişken Edit Symbol Sembolü Düzenle Edit Text Metni Düzenle Edit the applied effect, or add another one? Uygulanan efekti düzenlemek veya başka bir efekt eklemek mi istiyorsunuz? - Edit the selected point's colour in the Color panel Seçilen noktanın rengini Renk panelinde düzenleyin + Edit the point Noktayı düzenle Edit the stop Durağı düzenle Edit Theme Temayı Düzenle Edit theme Temayı düzenle @@ -6000,3 +6000,4 @@ breakText Glyphs Glifler Set Size of Each Her Birinin Boyutunu Ayarla @msg select exactly two objects tam olarak iki nesne seç @msg give `width` or `height` `width` veya `height` belirt + VectorCraft {release} and later (version {n}) VectorCraft {release} ve sonrası (sürüm {n}) diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 05f4ffe48..886675579 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -915,7 +915,7 @@ font-technology Variable Варіативні Edit Symbol Редагувати символ Edit Text Редагувати текст Edit the applied effect, or add another one? Редагувати застосований ефект або додати інший? - Edit the selected point's colour in the Color panel Редагуйте колір вибраної точки на панелі «Колір» + Edit the point Редагувати точку Edit the stop Редагувати точку переходу Edit Theme Редагувати тему Edit theme Редагувати тему @@ -5642,3 +5642,4 @@ breakText Glyphs Гліфи Set Size of Each Задати розмір кожного @msg select exactly two objects виділіть рівно два об’єкти @msg give `width` or `height` укажіть `width` або `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} і новіші (версія {n}) diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index b2744f443..f0ed3842e 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -1071,7 +1071,7 @@ font-technology Variable Biến thiên Edit Symbol Chỉnh sửa ký hiệu Edit Text Chỉnh sửa văn bản Edit the applied effect, or add another one? Chỉnh sửa hiệu ứng đã áp dụng, hay thêm hiệu ứng mới? - Edit the selected point's colour in the Color panel Chỉnh sửa màu của điểm đã chọn trong bảng Màu + Edit the point Chỉnh sửa điểm Edit the stop Chỉnh sửa điểm dừng Edit Theme Chỉnh sửa bộ màu Edit theme Chỉnh sửa bộ màu @@ -5644,3 +5644,4 @@ breakText Glyphs Glyph Set Size of Each Đặt kích thước từng cái @msg select exactly two objects hãy chọn đúng hai đối tượng @msg give `width` or `height` hãy cho `width` hoặc `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} trở lên (phiên bản {n}) diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index cfea6e224..b18470640 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -861,7 +861,7 @@ Edit Symbol 编辑符号 Edit Text 编辑文字 Edit the applied effect, or add another one? 编辑已应用的效果,还是再添加一个? - Edit the selected point's colour in the Color panel 在颜色面板中编辑所选点的颜色 + Edit the point 编辑点 Edit the stop 编辑色标 Edit Theme 编辑主题 Edit theme 编辑主题 @@ -5884,3 +5884,4 @@ breakText Glyphs 字形 Set Size of Each 分别设置大小 @msg select exactly two objects 请恰好选择两个对象 @msg give `width` or `height` 请提供 `width` 或 `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} 及更高版本 (版本 {n}) diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 0605652e5..2b7e38a40 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -883,7 +883,7 @@ font-technology Variable 可變 Edit Symbol 編輯符號 Edit Text 編輯文字 Edit the applied effect, or add another one? 要編輯已套用的效果,還是新增另一個? - Edit the selected point's colour in the Color panel 在顏色面板中編輯所選點的顏色 + Edit the point 編輯點 Edit the stop 編輯色標 Edit Theme 編輯主題 Edit theme 編輯主題 @@ -5681,3 +5681,4 @@ breakText Glyphs 字符 Set Size of Each 個別設定大小 @msg select exactly two objects 請選取正好兩個物件 @msg give `width` or `height` 請提供 `width` 或 `height` + VectorCraft {release} and later (version {n}) VectorCraft {release} 及更新版本 (版本 {n}) diff --git a/crates/ui-egui/src/io/save.rs b/crates/ui-egui/src/io/save.rs index b9e2ddeed..72e45c0b0 100644 --- a/crates/ui-egui/src/io/save.rs +++ b/crates/ui-egui/src/io/save.rs @@ -398,7 +398,16 @@ fn write_plan(app: &mut VectorcraftApp, plan: SavePlan) -> Result if app.background.busy_with(uid) { background::wait_all(app); } - let job = fileio::save_job(&mut app.session, plan).map_err(|e| e.to_string())?; + let refused = plan.path.clone().unwrap_or_else(|| plan.name.clone()); + // Refused before writing (#4933): told as a failed write is, in the reason's own words. + let job = fileio::save_job(&mut app.session, plan).map_err(|e| { + let reason = match &e { + EngineError::BadParams { msg, .. } => msg.clone(), + e => e.to_string(), + }; + report_write_error(app, false, &refused, &reason); + e.to_string() + })?; let (retargets, target) = (job.plan().retargets(), job.plan().path.clone()); let (format, warnings) = (job.plan().format.id, job.known_warnings()); let label = match &target { diff --git a/crates/ui-egui/src/panels/gradient.rs b/crates/ui-egui/src/panels/gradient.rs index 0f6bcf395..2b71ea054 100644 --- a/crates/ui-egui/src/panels/gradient.rs +++ b/crates/ui-egui/src/panels/gradient.rs @@ -18,7 +18,7 @@ //! //! A freeform gradient shows its section instead of the angle, aspect and slider: the Draw //! toggle (Points or Lines: how the Gradient tool adds points) and the selected point's colour -//! (edited in the Color panel), opacity and spread, with Delete Point. +//! (click: its popover, as a stop's), opacity and spread, with Delete Point. //! //! The Presets menu lists ready-made gradients by theme (the built-in gradient libraries), then //! the gradients saved with the panel menu's Save Gradient Preset…; choosing one applies it to the @@ -484,13 +484,18 @@ fn freeform_section(app: &mut VectorcraftApp, ui: &mut Ui, g: &GradientPaint) { let point = sel.map(|i| f.points[i]); ui.horizontal(|ui| { widgets::dim_label(ui, tl!("Color:")); - let (r, resp) = ui.allocate_exact_size(vec2(18.0, 18.0), Sense::hover()); + let (r, resp) = ui.allocate_exact_size(vec2(18.0, 18.0), Sense::click()); let paint = point.map_or(Paint::None, |p| Paint::solid(p.color)); widgets::swatch_tile(ui, r, &paint, false, resp.hovered()); - resp.on_hover_text(tl!("Edit the selected point's colour in the Color panel")); if let Some(p) = point { widgets::dim_label(ui, &p.color.to_hex().to_uppercase()); } + // The selected point's popover, as a stop's swatch opens the stop's (#4953). + if let Some(i) = sel + && resp.on_hover_text(tl!("Edit the point")).clicked() + { + show_popover(app, i, r.left_bottom() + vec2(0.0, 4.0)); + } }); ui.horizontal(|ui| { ui.add_enabled_ui(sel.is_some(), |ui| { @@ -640,6 +645,11 @@ pub(crate) fn stop_eyedropper(app: &mut VectorcraftApp) { /// Open the stop popover for stop `i` at screen position `at`. fn open_popover(app: &mut VectorcraftApp, i: usize, at: Pos2) { select_stop(app, Some(i)); + show_popover(app, i, at); +} + +/// The popover for the selected stop or freeform point `i`, at screen position `at`. +fn show_popover(app: &mut VectorcraftApp, i: usize, at: Pos2) { app.ui.dialog = Some(Dialog::new("gradientStop", json!({ "index": i, "screen": [at.x, at.y], "tab": "color" }))); } diff --git a/crates/ui-egui/src/panels/gradient/tests.rs b/crates/ui-egui/src/panels/gradient/tests.rs index a3b7f5cfa..83a290bb1 100644 --- a/crates/ui-egui/src/panels/gradient/tests.rs +++ b/crates/ui-egui/src/panels/gradient/tests.rs @@ -543,6 +543,31 @@ fn double_clicking_a_stop_opens_the_shared_popover_beside_it() { assert_eq!(app.session.selected_stop(), Some(1)); } +/// #4953: with a freeform point selected, clicking the Color swatch opens the popover for that +/// point, which recolours it and leaves the other points as they were. +#[test] +fn the_freeform_color_swatch_opens_the_points_popover() { + let mut app = app(json!({"gradient": {"kind": "freeform"}})); + app.run("paint.freeform.selectPoint", json!({"index": 1})).unwrap(); + let mut p = Panel::new(); + let texts = p.frame(&mut app, vec![], Modifiers::NONE); + let label = text_at(&texts, "Color:").unwrap_or_else(|| panic!("the Color row: {texts:?}")); + let swatch = p.widget_after(label); + p.click(&mut app, swatch.center()); + let d = app.ui.dialog.clone().expect("the popover opened"); + assert_eq!( + (d.kind.as_str(), &d.fields["index"], &d.fields["screen"]), + ("gradientStop", &json!(1), &json!([swatch.left(), swatch.bottom() + 4.0])) + ); + let points = |app: &VectorcraftApp| shown_points(&fill(app)).points.iter().map(|p| p.color.to_hex()).collect::>(); + let before = points(&app); + app.ui.dialog.as_mut().unwrap().fields.insert("color".into(), json!("#ff0000")); + crate::dialogs::confirm(&mut app).unwrap(); + let mut want = before.clone(); + want[1] = "#ff0000".into(); + assert_eq!((fill(&app).gradient.kind, points(&app)), (GradientKind::Freeform, want)); +} + #[test] fn the_stop_eyedropper_arms_the_eyedropper_for_the_stop() { let mut app = app(json!({"gradient": {}})); diff --git a/docs/ui-parity.md b/docs/ui-parity.md index ecda87aae..fee3db42d 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -1,6 +1,6 @@ # UI and interaction parity -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (files opened from the file manager while VectorCraft runs open in its window on Windows and Linux, #4892) · **Target:** Adobe Illustrator 2026 (30.x) +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-01 · **Change:** minor (native Save As Version menu, refused-save alert, fixed Contributors table header, freeform point swatch opens its popover: #4931, #4933, #4944, #4953) · **Target:** Adobe Illustrator 2026 (30.x) ## Feature submission evidence @@ -381,6 +381,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development | Date | Change | Summary | |---|---|---| +| 2026-11-01 | minor | VectorCraft Options' Version is a menu of the versions VectorCraft writes, each named by the releases that read it, and an uncompressible version turns Use Compression off (#4931); a save refused before writing shows the Can't save alert with the reason (#4933); About › Contributors' table keeps its header row above the scrolling rows (#4944); the Gradient panel's freeform point swatch opens the point's popover (#4953); scores unchanged | | 2026-10-31 | minor | A file opened from the file manager while VectorCraft runs opens as a new tab in that window, which comes to the front, on Windows and Linux (#4892); `--new-instance` opts out; scores unchanged | | 2026-10-31 | minor | Object › Path › Smooth: Auto-Smooth takes a shaky line to one smooth curve, and direct-selected anchors smooth only their section; scores unchanged | | 2026-10-31 | minor | New Document: More Settings has Save Preset beside the name, as Preset Details does, and saves its settings (#4893); scores and estimates unchanged |