diff --git a/crates/ui-egui/src/dialogs/form.rs b/crates/ui-egui/src/dialogs/form.rs index 40329c8c..fd14ff40 100644 --- a/crates/ui-egui/src/dialogs/form.rs +++ b/crates/ui-egui/src/dialogs/form.rs @@ -49,7 +49,7 @@ pub(super) fn length(ui: &mut egui::Ui, d: &mut Dialog, key: &str, unit: Unit, w fn lengths(kind: &str) -> &'static [&'static str] { match kind { "move" => &["dx", "dy"], - "rectangle" | "ellipse" | "rectangularGrid" | "artboardOptions" => &["width", "height"], + "rectangle" | "ellipse" | "rectangularGrid" | "polarGrid" | "artboardOptions" => &["width", "height"], "roundedRectangle" => &["width", "height", "radius"], "polygon" => &["radius"], "star" => &["radius1", "radius2"], @@ -68,6 +68,7 @@ fn order(kind: &str) -> &'static [&'static str] { "offsetPath" => &["offset", "joins", "miterLimit"], "splitIntoGrid" => &["rows", "columns", "gutter"], "rectangularGrid" => &["width", "height", "rows", "columns"], + "polarGrid" => &["width", "height", "concentric", "radial"], "artboardOptions" => &["name", "width", "height"], _ => &[], } @@ -182,6 +183,8 @@ pub(super) fn grid(ui: &mut egui::Ui, d: &mut Dialog, unit: Unit) { let label = match (d.kind.as_str(), k.as_str()) { ("rectangularGrid", "rows") => tl!("Horizontal Dividers:").into(), ("rectangularGrid", "columns") => tl!("Vertical Dividers:").into(), + ("polarGrid", "concentric") => tl!("Concentric Dividers:").into(), + ("polarGrid", "radial") => tl!("Radial Dividers:").into(), _ => humanized(&k), }; field(ui, d, &k, &label); diff --git a/crates/ui-egui/src/dialogs/mod.rs b/crates/ui-egui/src/dialogs/mod.rs index 7ff56acb..85dff13b 100644 --- a/crates/ui-egui/src/dialogs/mod.rs +++ b/crates/ui-egui/src/dialogs/mod.rs @@ -209,7 +209,7 @@ macro_rules! registry { registry! { NewDocument: [new_document::KIND] => new_document::SPEC, - Shape: ["rectangle", "roundedRectangle", "ellipse", "polygon", "star", "lineSegment", "rectangularGrid"] => shapes::SPEC, + Shape: ["rectangle", "roundedRectangle", "ellipse", "polygon", "star", "lineSegment", "rectangularGrid", "polarGrid"] => shapes::SPEC, Transform: ["move", "rotate", "scale", "reflect", "shear"] => transform::SPEC, PathOp: ["average", "offsetPath", "simplify", "splitIntoGrid"] => path_ops::SPEC, DocumentSetup: ["documentSetup"] => document_setup::SPEC, diff --git a/crates/ui-egui/src/dialogs/shapes.rs b/crates/ui-egui/src/dialogs/shapes.rs index 56680c12..ac479732 100644 --- a/crates/ui-egui/src/dialogs/shapes.rs +++ b/crates/ui-egui/src/dialogs/shapes.rs @@ -17,6 +17,7 @@ fn title(kind: &str) -> &'static str { "polygon" => tl!("Polygon"), "star" => tl!("Star"), "rectangularGrid" => tl!("Rectangular Grid"), + "polarGrid" => tl!("Polar Grid"), _ => tl!("Line Segment Tool Options"), } } @@ -35,6 +36,11 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result { json!({"x": x, "y": y, "width": d.f64("width", 100.0), "height": d.f64("height", 100.0), "rows": (d.f64("rows", 5.0) as u64).min(999), "columns": (d.f64("columns", 5.0) as u64).min(999)}), ), + "polarGrid" => ( + "shape.polarGrid", + json!({"x": x, "y": y, "width": d.f64("width", 100.0), "height": d.f64("height", 100.0), + "concentric": (d.f64("concentric", 5.0) as u64).min(999), "radial": (d.f64("radial", 5.0) as u64).min(999)}), + ), "lineSegment" => { let (l, a) = (d.f64("length", 100.0), d.f64("angle", 0.0).to_radians()); ("shape.line", json!({"x1": x, "y1": y, "x2": x + l * a.cos(), "y2": y - l * a.sin()})) @@ -45,13 +51,13 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result { ), }; // On the active plane while the perspective grid shows, sized in plane units from the click. - let options = (d.kind == "rectangularGrid").then(|| json!({"rows": params["rows"], "columns": params["columns"]})); + let options = matches!(d.kind.as_str(), "rectangularGrid" | "polarGrid").then(|| params.clone()); let result = match vectorcraft_engine::perspective_click(&app.session, id, ¶ms, vectorcraft_geom::Point::new(x, y)) { Some((id, params)) => run_and_close(app, &id, params), None => run_and_close(app, id, params), }?; if let Some(options) = options { - app.run("tool.setOption", json!({"tool": "rectangularGrid", "values": options}))?; + app.run("tool.setOption", json!({"tool": d.kind, "values": options}))?; } Ok(result) } diff --git a/crates/ui-egui/src/dialogs/tests.rs b/crates/ui-egui/src/dialogs/tests.rs index 5095b7c0..cfba1dcb 100644 --- a/crates/ui-egui/src/dialogs/tests.rs +++ b/crates/ui-egui/src/dialogs/tests.rs @@ -41,6 +41,7 @@ fn kinds_map_to_their_dialogs() { ("roundedRectangle", DialogKind::Shape), ("lineSegment", DialogKind::Shape), ("rectangularGrid", DialogKind::Shape), + ("polarGrid", DialogKind::Shape), ("shear", DialogKind::Transform), ("splitIntoGrid", DialogKind::PathOp), ("artboardOptions", DialogKind::ArtboardOptions), @@ -87,6 +88,7 @@ fn every_dialog_draws() { "star", "lineSegment", "rectangularGrid", + "polarGrid", "rotate", "reflect", "scale", @@ -650,3 +652,72 @@ fn zig_zag_has_sliders_and_radio_buttons() { } assert!(!lines.contains(&"Ridges:"), "{lines:?}"); } + +#[test] +fn polar_grid_shared_canvas_click_defaults_units_counts_and_history() { + use vectorcraft_doc::NodeId; + use vectorcraft_tools::{PointerEvent, PointerKind}; + let mut app = app(); + app.select_tool("polarGrid"); + app.run("tool.setOption", json!({"tool": "polarGrid", "values": {"concentric": 2, "radial": 3}})).unwrap(); + let before = app.session.doc().unwrap().doc.clone(); + let undo = app.session.doc().unwrap().history.undo.len(); + for kind in [PointerKind::Down, PointerKind::Up] { + crate::canvas::dispatch(&mut app, &PointerEvent::new(kind, 30.0, 40.0), Default::default()); + } + assert_eq!(DialogKind::of("polarGrid"), Some(DialogKind::Shape)); + let d = app.ui.dialog.as_mut().expect("canvas click opens the shared shape form"); + assert_eq!(d.fields["width"], json!("100 pt")); + assert_eq!(d.fields["height"], json!("100 pt")); + assert_eq!((d.fields["concentric"].clone(), d.fields["radial"].clone()), (json!(2), json!(3))); + d.fields.insert("width".into(), json!("25.4 mm")); + d.fields.insert("height".into(), json!("0.5 in")); + frame(&mut app, Default::default()); + let result = confirm(&mut app).unwrap(); + assert!(app.ui.dialog.is_none()); + let s = app.session.doc().unwrap(); + let group = s.doc.node(NodeId(result["id"].as_u64().unwrap())).unwrap(); + let children = group.children().unwrap(); + assert_eq!(children.len(), 6); + assert_eq!(children.iter().filter(|n| n.path_data().unwrap().is_closed()).count(), 3); + assert_eq!(s.history.undo.len(), undo + 1); + let made = s.doc.clone(); + app.run("edit.undo", json!({})).unwrap(); + assert_eq!(app.session.doc().unwrap().doc, before); + app.run("edit.redo", json!({})).unwrap(); + assert_eq!(app.session.doc().unwrap().doc, made); +} + +#[test] +fn polar_grid_shared_cancel_reopen_zero_counts_and_failed_creation() { + let mut app = app(); + app.run("tool.setOption", json!({"tool": "polarGrid", "values": {"concentric": 2, "radial": 3}})).unwrap(); + let before = app.session.doc().unwrap().doc.clone(); + open_tool_dialog(&mut app, "polarGrid", json!({"x": 10, "y": 20})); + let d = app.ui.dialog.as_mut().expect("shared Polar Grid form opens"); + d.fields.insert("concentric".into(), json!(8)); + cancel(&mut app); + assert_eq!(app.session.doc().unwrap().doc, before); + open_tool_dialog(&mut app, "polarGrid", json!({"x": 10, "y": 20})); + assert_eq!(app.ui.dialog.as_ref().unwrap().fields["concentric"], json!(2)); + for key in ["concentric", "radial"] { + app.ui.dialog.as_mut().unwrap().fields.insert(key.into(), json!(0)); + } + let result = confirm(&mut app).unwrap(); + let group = app.session.doc().unwrap().doc.node(vectorcraft_doc::NodeId(result["id"].as_u64().unwrap())).unwrap(); + assert_eq!(group.children().unwrap().len(), 1); + assert!(group.children().unwrap()[0].path_data().unwrap().is_closed()); + open_tool_dialog(&mut app, "polarGrid", json!({"x": 10, "y": 20})); + assert_eq!(app.ui.dialog.as_ref().unwrap().fields["radial"], json!(0)); + cancel(&mut app); + let layer = app.session.doc().unwrap().doc.layers[0].id; + app.run("layer.setProps", json!({"ids": [layer.0], "locked": true})).unwrap(); + let options = app.session.tool_options_of("polarGrid"); + let before = app.session.doc().unwrap().doc.clone(); + open_tool_dialog(&mut app, "polarGrid", json!({"x": 10, "y": 20})); + app.ui.dialog.as_mut().unwrap().fields.insert("radial".into(), json!(9)); + assert!(confirm(&mut app).is_err()); + assert!(app.ui.dialog.is_none()); + assert_eq!(app.session.tool_options_of("polarGrid"), options); + assert_eq!(app.session.doc().unwrap().doc, before); +} diff --git a/crates/ui-egui/src/dialogs/tools.rs b/crates/ui-egui/src/dialogs/tools.rs index 88013a34..2697a48d 100644 --- a/crates/ui-egui/src/dialogs/tools.rs +++ b/crates/ui-egui/src/dialogs/tools.rs @@ -20,6 +20,13 @@ pub fn open_tool_dialog(app: &mut VectorcraftApp, kind: &str, p: Value) { let options = app.session.tool_options_of(kind); Dialog::new(kind, json!({"x": x, "y": y, "width": "100 pt", "height": "100 pt", "rows": options["rows"], "columns": options["columns"]})) } + "polarGrid" => { + let options = app.session.tool_options_of(kind); + Dialog::new( + kind, + json!({"x": x, "y": y, "width": "100 pt", "height": "100 pt", "concentric": options["concentric"], "radial": options["radial"]}), + ) + } // Graph tool click: the graph's size. "graph" => Dialog::new( "command", diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 440777bf..defd6191 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -4498,3 +4498,5 @@ mark-style Japanese Japonské @msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) komprimovaný soubor se rozbalí na více, než VectorCraft přečte ({_1} MB dokumentu, {_2} MB na obrázek) @msg the document is too large to save compressed ({0}): save it with Use Compression off dokument je příliš velký pro komprimované uložení ({0}): uložte ho s vypnutou volbou Použít kompresi @msg the file stopped being read (please report this bug) čtení souboru se přerušilo (nahlaste prosím tuto chybu) + Concentric Dividers: Soustředné dělicí čáry: + Radial Dividers: Radiální dělicí čáry: diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index e09d40f2..9f347288 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -4149,3 +4149,5 @@ theme Appearance Erscheinungsbild @msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) die komprimierte Datei entpackt sich auf mehr, als VectorCraft liest ({_1} MB Dokument, {_2} MB pro Bild) @msg the document is too large to save compressed ({0}): save it with Use Compression off das Dokument ist zu groß, um komprimiert gespeichert zu werden ({0}): speichern Sie es mit deaktivierter Option „Komprimierung verwenden“ @msg the file stopped being read (please report this bug) das Lesen der Datei wurde abgebrochen (bitte melden Sie diesen Fehler) + Concentric Dividers: Konzentrische Trennlinien: + Radial Dividers: Radiale Trennlinien: diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 833308da..4954d10a 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -4192,3 +4192,5 @@ lighting Light Luz @msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) el archivo comprimido ocupa al descomprimirse más de lo que VectorCraft lee ({_1} MB de documento, {_2} MB por imagen) @msg the document is too large to save compressed ({0}): save it with Use Compression off el documento es demasiado grande para guardarlo comprimido ({0}): guárdelo con Usar compresión desactivado @msg the file stopped being read (please report this bug) se dejó de leer el archivo (informe de este error) + Concentric Dividers: Divisiones concéntricas: + Radial Dividers: Divisiones radiales: diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 5ad2bd5f..49139253 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -4202,3 +4202,5 @@ lighting Light Lumière @msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) le fichier compressé dépasse une fois décompressé ce que VectorCraft lit ({_1} Mo de document, {_2} Mo par image) @msg the document is too large to save compressed ({0}): save it with Use Compression off le document est trop volumineux pour être enregistré compressé ({0}) : enregistrez-le avec Utiliser la compression désactivé @msg the file stopped being read (please report this bug) la lecture du fichier s’est interrompue (veuillez signaler ce bogue) + Concentric Dividers: Divisions concentriques : + Radial Dividers: Divisions radiales : diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 0cde4d46..2e924593 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -4194,3 +4194,5 @@ lighting Light Luce @msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) il file compresso, una volta decompresso, supera ciò che VectorCraft legge ({_1} MB di documento, {_2} MB per immagine) @msg the document is too large to save compressed ({0}): save it with Use Compression off il documento è troppo grande per essere salvato compresso ({0}): salvalo con Usa compressione disattivato @msg the file stopped being read (please report this bug) la lettura del file si è interrotta (segnala questo errore) + Concentric Dividers: Divisori concentrici: + Radial Dividers: Divisori radiali: diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 6b05d121..4125621d 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -4188,3 +4188,5 @@ lighting Light ライト @msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) 圧縮ファイルを展開すると VectorCraft が読み込める量を超えます (ドキュメント {_1} MB、画像 1 枚あたり {_2} MB) @msg the document is too large to save compressed ({0}): save it with Use Compression off ドキュメントが大きすぎて圧縮保存できません ({0}): 「圧縮を使用」をオフにして保存してください @msg the file stopped being read (please report this bug) ファイルの読み込みが中断されました (このバグを報告してください) + Concentric Dividers: 同心円の分割線: + Radial Dividers: 放射状の分割線: diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index c9d86310..04e66fd0 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -3677,3 +3677,5 @@ axis Both Ambos @msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) o arquivo compactado se expande para mais do que o VectorCraft lê ({_1} MB de documento, {_2} MB por imagem) @msg the document is too large to save compressed ({0}): save it with Use Compression off o documento é grande demais para ser salvo compactado ({0}): salve-o com Usar Compressão desativado @msg the file stopped being read (please report this bug) a leitura do arquivo foi interrompida (informe este erro) + Concentric Dividers: Divisórias concêntricas: + Radial Dividers: Divisórias radiais: diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 4bab5e25..f95584f4 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -4190,3 +4190,5 @@ lighting Light Свет @msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) сжатый файл распаковывается в объём больше, чем читает VectorCraft ({_1} МБ документа, {_2} МБ на изображение) @msg the document is too large to save compressed ({0}): save it with Use Compression off документ слишком велик для сохранения со сжатием ({0}): сохраните его с выключенным параметром «Использовать сжатие» @msg the file stopped being read (please report this bug) чтение файла прервалось (пожалуйста, сообщите об этой ошибке) + Concentric Dividers: Концентрические разделители: + Radial Dividers: Радиальные разделители: diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index ea77f63b..489a369f 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -4174,3 +4174,5 @@ lighting Light Світло @msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) стислий файл розпаковується в обсяг більший, ніж читає VectorCraft ({_1} МБ документа, {_2} МБ на зображення) @msg the document is too large to save compressed ({0}): save it with Use Compression off документ завеликий для збереження зі стиском ({0}): збережіть його з вимкненим параметром «Використовувати стиск» @msg the file stopped being read (please report this bug) читання файлу перервалося (будь ласка, повідомте про цю помилку) + Concentric Dividers: Концентричні розділювачі: + Radial Dividers: Радіальні розділювачі: diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 3a7a15b0..836b4259 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -3786,3 +3786,5 @@ lighting Light 光源 Artistic 艺术效果 Film Grain… 胶片颗粒… Highlight Area 高光区域 + Concentric Dividers: 同心分隔线: + Radial Dividers: 径向分隔线: diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 3bb7a640..2c962956 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -3567,3 +3567,5 @@ lighting Light 光源 Artistic 藝術效果 Film Grain… 底片顆粒… Highlight Area 亮部範圍 + Concentric Dividers: 同心分隔線: + Radial Dividers: 徑向分隔線: