mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-22 19:08:39 +00:00
Export: a progress dialog with Cancel for background exports
While an export runs in the background (Background Export), a dialog shows what is being exported and how far it has got: artboard by artboard for a raster export of several artboards, busy otherwise. Cancel (also Esc) stops it and writes no file; Continue in Background leaves it to the status bar. It opens after 0.4 s and never over another dialog. Built on the shared vectorcraft_doc::cancel (#2546): fileio::progress::Progress wraps its CancelToken and a progress sink, so a cancelled export stops drawing and keeps no half-drawn pattern tile or effect raster. - ui: background jobs carry their Progress; export_progress draws the dialog; UI commands file.cancelExport and file.exportInBackground. - i18n: the strings in every language with complete menus, Danish, Dutch and Indonesian included; "{n} more exports waiting" is a plural (trn). - docs: ui-parity, gaps (G12) and ROADMAP rows. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Dt49z1ubK9x1bPq51APgz3
This commit is contained in:
1 parent
a275496ef4
commit
ec410552cf
34 files changed
+560
-15
No files matched your search
+3
-1
@@ -9,7 +9,7 @@
|
||||
|
||||
**Stage: alpha** · next: beta, ~13 points (ready for real work ~62% → ~75%, and reliable `.ai` exchange) and ~170–270 h away
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-18 · **Change:** minor (about 40 community PRs of 2026-10-18: Word .docx placed as area type, CCITT Group 3 TIFF, Roman Hanging Punctuation, blend spines edited with the Anchor Point tool, shadows and glows in stack order, coloured 3D lights, a Dutch catalog completed; 3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; about 145 community PRs of 2026-10-17: image-decode overflow guards, opaque floating panels, MCP export cancel and progress, editable Illustrator EPS import, scoped `doc_inspect`, Dutch, Danish and Indonesian, Simplified Chinese messages, screen-reader names; headline numbers unchanged; about 160 community PRs of 2026-10-15/16: PDF import crash fixes, Dimension tool, legacy `.ai` save, Intertwine, web pen input, Image Trace fixes, `.ai`/EPS/PDF import fidelity, Bulgarian, Greek, Swedish and Finnish catalogs; headline numbers unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-18 · **Change:** minor (Background Export progress dialog with Cancel, #2564; about 40 community PRs of 2026-10-18: Word .docx placed as area type, CCITT Group 3 TIFF, Roman Hanging Punctuation, blend spines edited with the Anchor Point tool, shadows and glows in stack order, coloured 3D lights, a Dutch catalog completed; 3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; about 145 community PRs of 2026-10-17: image-decode overflow guards, opaque floating panels, MCP export cancel and progress, editable Illustrator EPS import, scoped `doc_inspect`, Dutch, Danish and Indonesian, Simplified Chinese messages, screen-reader names; headline numbers unchanged; about 160 community PRs of 2026-10-15/16: PDF import crash fixes, Dimension tool, legacy `.ai` save, Intertwine, web pen input, Image Trace fixes, `.ai`/EPS/PDF import fidelity, Bulgarian, Greek, Swedish and Finnish catalogs; headline numbers unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
VectorCraft is a clean-room, open-source, pure-Rust reimplementation of the Adobe Illustrator workflow. It runs
|
||||
on macOS, Windows, Linux, FreeBSD and the web (WASM), and agents can drive all of it over MCP, the CLI and a JSON
|
||||
@@ -135,6 +135,7 @@ Ranked; detail in [docs/roadmap.md](docs/roadmap.md) and [docs/gaps.md](docs/gap
|
||||
| Date | What landed |
|
||||
|---|---|
|
||||
| 2026-10-18 | About 40 community PRs landed in one batch through the full gate, with our fixes on top. New: Word `.docx` files place as area type with their formatting (#2766), CCITT Group 3 TIFF files open (#2770), Paragraph panel menu › Roman Hanging Punctuation (#2775), the Anchor Point tool edits blend spine points and handles (#2643, fixes #2544), shadows and glows each draw from the art with the effects before them, without the old exponential cost (#2395), one to ten coloured lights in 3D effects (#2706), Artboard Options X and Y with a reference point (#2739), more Contextual Task Bar actions (#2471), Optical Margin Alignment for a whole thread (#2773), History panel Step Backward/Forward and Set History Limit (#2764), Edit › Undo and Redo name the step as the History panel does (#2774), bounding-box handles snap to the grid, pixels and guides (#2715), Ctrl/Cmd+A in the Artboards panel (#2761), Tabs panel menu items (#2742), Import Data Sets defines variables from header cells (#2736), a Linux option to use X11 when a pen is attached (#2717). Fidelity and robustness: variable fonts named by their default weight and fallback glyphs keep bold and italic (#2733, #2732), legacy `.ai` layer names in Windows Latin-1 (#2722), WMF window mapping (#2721), Magic Wand inside isolation (#2720), type values out of range in files are clamped (#2728, #2757), raster effects stay responsive in drags (#2745), exports and Save for Web report undecodable images (#2762, #2737, #2684), Windows-safe exported file names (#2738), a marquee skips a Shaper Group's originals (#2772). Languages: the Dutch catalog completed (#2730). Headline numbers unchanged (estimated). |
|
||||
| 2026-10-17 | Background Export shows a progress dialog: the file being exported, the artboard count and a bar, Cancel (Esc) and Continue in Background. It runs on the shared cancel token of #2546, so a cancelled export writes no file and keeps no half-drawn pattern tile or effect raster; translated in every language with complete menus (#2564). Headline numbers unchanged. |
|
||||
| 2026-10-17 | About 145 community PRs landed in six batches, each through the full gate, plus our fixes on top. Crashes and hostile input: an image's RGBA size is checked from its header before decoding, without overflowing, in the renderer, sampling, recoloring and Crop Image (#2442, #2468, #2407), PDF Add Around and huge art are clamped (#2440), graph axis labels past i64, `+` in hex colours and XML ids are handled (#2610, #2470, #2447), Document Raster Effects resolutions open in range (#2579), Batch names stay in their folder (#2504). Regressions: floating panel groups are opaque again while the docked card keeps its rounded corners (#2499), the Wayland pointer returns over panels (#2502). New: MCP cancels long exports, answers while one runs and reports progress (#2546), `doc_inspect` takes ids, kind and paging (#2543), raster export of a page rectangle (#2511), `text.create` returns bounds (#2478), PDF presets and ICC profiles persist (#2547), screen-reader names for widgets (#2611), Expand on Intertwine (#2622, fixes #2540), Duplicate/Copy/Cut of several artboards (#2567), print presets by name (#2607), TSV data sets (#2608), planar TIFF (#2598), Alt-click a swatch onto a gradient stop (#2623), Shift-click ranges in Links, Graphic Styles and Libraries (#2612, #2613, #2618), Space+Shift rotates the view (#2601), Presentation Mode keys (#2583), Small Caps and super/subscript buttons (#2508). Fidelity: editable vector art from Illustrator EPS (#2571), `.ai` Drop Shadow Darkness (#2513), overprint preview with Darken (#2514), Generic CMYK black round-trips (#2503), PNG sRGB chunk (#2519), kerning at a caret (#2518), tab widths in justified area type (#2487), Transform copies keep their own gradients (#2486), smooth Zig Zag (#2475), Live Corners round up to their neighbours (#2484), kerning across style runs (#2554), SVG letter spacing on justified lines (#2574), Dimension tool snapping (#2482), gradient chips (#2495), Transform panel flips and percentages (#2400, #2404). Later the same day: PostScript `.ps` and `.epsf` files open (#2461), EPS images with CCITTFax compression draw (#2445), Illustrator 3 EPS without layers (#2436), `.ai` type opacity and blend modes (#2420), envelope meshes and compound holes in `.ai` (#2410, #2444), CMYK image soft masks in PDF export (#2448), Show Transparency Grid saved with the document (#2488), Save Selection in the Layers panel menu (#2483), Character and Paragraph Reset Panel (#2479, #2438), Cmd/Ctrl+Shift double-click releases a guide (#2467), Shift+Enter keeps a field active (#2431), Shift keeps reshaped handles perpendicular (#2425), emptied type keeps its style (#2464), Smart Blur radius (#2443), freeform gradients recolour (#2397), mesh slivers (#2456), Recolor wheel handles in CMYK (#2454), and six Image Trace fixes (#2387, #2584, #2586–#2589). Fifth batch: Optical Margin Alignment (#2563), Window › Blend panel (#2573), graph column designs (#2406), Live Paint Gap Options (#2424), the SVG Interactivity panel (#2437), RTF placed as area type (#2524), the Touch Type tool (#2469), pattern fills expand into clipped tiles (#2616), Save and Export ask before replacing a file an added extension names (#2619), the Artboard tool's Control bar and Contextual Task Bar (#2559, #2556), Alt-click New Artboard for its options (#2496), Shift-click in Brushes (#2562), the Move dialog remembers the last move (#2542), PDF export uses the document's assigned profiles (#2536), Printer Setup opens the selected printer's preferences on Windows (#2577). Sixth batch: Effect › Artistic › Cutout, Neon Glow and Poster Edges (#2453), View › Rulers › Change to Global / Artboard Rulers (#2416), Symbol Options 9-slice guides (#2512), Create Gradient Mesh on an embedded image (#2538), Rotate View cursors and the Mac trackpad rotation gesture (#2594, #2644), choice parameters as dropdowns in the inline effect editor (#2582), Group Selection picks and moves one path of a compound path (part of #2441), Shift-click ranges in the Artboards panel (#2626), Lock Variables (#2631), a layer dropped under the last row goes to the bottom (#2632), and robustness work: huge selections draw bounded overlays (#2635, #2645, part of #2460), values out of range in presets and files are clamped (#2656), whole-number checks for counts (#2638, #2659), exports warn about overset type (#2628), symbol instances measured by their art (#2614), colour-managed Eyedropper in CMYK documents (#2664). Later: Effect › Rasterize (#2409), variable-font axis sliders in the Character panel (#2710), Image Trace Create Strokes on an exact distance transform with Guo–Hall thinning and a skeleton graph (#2703–#2705), Auto Color keeps rare colours (#2702), large images trace with a warning (#2700), a 1e9 pt input bound on Reshape Segment, Convert Anchor Point and the Pencil (#2709), task-bar Connect, Corner and Smooth for selected anchors (#2697), PDF 0 w strokes import as hairlines (#2696), threading text from theLine truncated
|
||||
| 2026-10-16 | Symbol Options › Export Type (Graphic or Movie Clip, default Movie Clip) with Registration; `symbol.options` / `symbol.list` (G5).
|
||||
| 2026-10-15 | Cursors: 36 more tool states get cursors of their own. Node editing follows the public manual (Direct Selection's anchor squares, Reshape Segment, Group Selection's plus, the arrow hollow on a snap, the Alt-copy arrowheads), the Anchor Point caret, the Curvature pen and Free Transform's side-shear arrows are drawn; Lasso, the freehand, symbolism and other tools borrow a drawn cursor until theirs is drawn. `crates/tools/src/cursors.toml` lists every cursor for whoever draws them, and `cargo xtask cursors` warns about one missing from it. Scores unchanged. |
|
||||
@@ -183,6 +184,7 @@ The full pre-standard record of what landed is kept in [docs/roadmap.md](docs/ro
|
||||
| 2026-10-17 | minor | Colored Inflate: shared membrane normals and smooth RGB vector ramps replace flat polygon bands; channel groups are batched per solid-fill surface. White-light Inflate retains its original polygons, paint composition and seam joins; the 0.05 seam minimum is restored. Scores and hour estimates unchanged. |
|
||||
| 2026-10-16 | minor | 3D lighting: one to ten directional lights, a selectable light list, Add Light and Delete Light, independent settings and artboard gestures; ambient applied once and smooth RGB vector export; scores and hour estimates unchanged |
|
||||
| 2026-10-16 | minor | 3D lighting: opaque RGB Color for the shared directional light, with neutral ambient, preview/reset controls and smooth vector Revolve shading; scores and hour estimates unchanged |
|
||||
| 2026-10-17 | minor | Progress log row for the Background Export progress dialog (#2564); headline numbers unchanged. |
|
||||
| 2026-10-14 | minor | G11: open-path anchor Delete removes adjoining segments without bridging the gap; explicit Remove Anchors keeps its reconnect/refit behavior. Scores and hours unchanged. |
|
||||
| 2026-10-14 | minor | Document fonts register every explicitly supplied craft-fonts family after the existing Japanese, Arabic and Thai prefix; optional empty input remains supported. Headline scores and estimates are unchanged. |
|
||||
| 2026-10-14 | minor | Progress log rows for the later merges of 2026-10-14 (#1630–#1875 as listed there); headline numbers unchanged. |
|
||||
|
||||
@@ -40,6 +40,7 @@ pub mod pdf;
|
||||
mod pdfimport;
|
||||
pub mod pngtext;
|
||||
pub mod ppi;
|
||||
pub mod progress;
|
||||
pub mod psdread;
|
||||
mod save;
|
||||
mod screens;
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
//! Progress and Cancel of an export running off the UI thread (Background Export), on the shared
|
||||
//! [`vectorcraft_doc::cancel`] mechanism.
|
||||
//!
|
||||
//! The frontend makes a [`Progress`], runs the export inside [`scope`] on its worker and reads the
|
||||
//! counts from the UI thread to draw them; its Cancel button calls [`Progress::cancel`]. Inside the
|
||||
//! scope the encoders that work in steps (raster formats: one artboard at a time) report each step
|
||||
//! through [`vectorcraft_doc::cancel::progress`] and stop at the next check once cancelled; the
|
||||
//! renderer stops drawing and keeps no half-drawn pattern tile or effect raster. Encoders that
|
||||
//! make one file in one go report nothing: the frontend shows them as busy and checks
|
||||
//! [`Progress::cancelled`] itself before writing, so a cancelled export never writes a file.
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use vectorcraft_doc::cancel::{self, CancelToken, ProgressSink};
|
||||
|
||||
/// How far an export has got, shared between the worker and the UI thread.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Progress {
|
||||
token: CancelToken,
|
||||
done: AtomicU64,
|
||||
total: AtomicU64,
|
||||
}
|
||||
|
||||
impl Progress {
|
||||
pub fn new() -> Arc<Self> {
|
||||
Arc::new(Self::default())
|
||||
}
|
||||
|
||||
/// `(steps done, steps in all)`; `total` is 0 while the export can't say (one file made in one go).
|
||||
pub fn counts(&self) -> (u64, u64) {
|
||||
let total = self.total.load(Ordering::Relaxed);
|
||||
(self.done.load(Ordering::Relaxed).min(total), total)
|
||||
}
|
||||
|
||||
/// The share of the export done, 0 to 1; `None` while the export can't say.
|
||||
pub fn fraction(&self) -> Option<f64> {
|
||||
let (done, total) = self.counts();
|
||||
(total > 0).then(|| done as f64 / total as f64)
|
||||
}
|
||||
|
||||
/// Ask the export to stop: it writes nothing more, and a render under way stops drawing.
|
||||
pub fn cancel(&self) {
|
||||
self.token.cancel();
|
||||
}
|
||||
|
||||
pub fn cancelled(&self) -> bool {
|
||||
self.token.is_cancelled()
|
||||
}
|
||||
}
|
||||
|
||||
/// Run `f` with `progress`'s token in effect on this thread and the steps reported to it.
|
||||
pub fn scope<R>(progress: &Arc<Progress>, f: impl FnOnce() -> R) -> R {
|
||||
let to = Arc::clone(progress);
|
||||
let sink: ProgressSink = Rc::new(move |done, total, _| {
|
||||
to.total.store(total, Ordering::Relaxed);
|
||||
to.done.store(done, Ordering::Relaxed);
|
||||
});
|
||||
cancel::scope(&progress.token, Some(sink), f)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn a_raster_export_counts_its_artboards_and_stops_once_cancelled() {
|
||||
let mut doc = vectorcraft_doc::Document::new(100.0, 100.0);
|
||||
let second = doc.artboards[0].clone();
|
||||
doc.artboards.push(second);
|
||||
let p = Progress::new();
|
||||
assert_eq!(p.fraction(), None, "nothing reported yet");
|
||||
let png = scope(&p, || super::super::encode_all(&doc, "png", &serde_json::json!({"useArtboards": true})));
|
||||
assert!(png.is_ok(), "{:?}", png.err());
|
||||
assert_eq!(p.counts(), (1, 2), "the step under way when the last artboard started");
|
||||
p.cancel();
|
||||
let r = scope(&p, || super::super::encode_all(&doc, "png", &serde_json::json!({"useArtboards": true})));
|
||||
assert!(matches!(r, Err(crate::EngineError::Cancelled)), "{:?}", r.err());
|
||||
assert!(!cancel::is_cancelled(), "the scope ended");
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,11 @@
|
||||
//! It needs a thread-safe writer from the host ([`crate::Services::write_shared`], desktop);
|
||||
//! without one (the web, which downloads) the work runs at once, as with the preference off.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_engine::cmd::fileio::progress::Progress;
|
||||
use vectorcraft_engine::file_access::ScopeHandle;
|
||||
|
||||
use crate::VectorcraftApp;
|
||||
@@ -50,9 +52,24 @@ pub struct Job {
|
||||
pub label: String,
|
||||
/// The document the job reads (`DocState::uid`), if any.
|
||||
pub doc: Option<u64>,
|
||||
/// How far an export has got, and its Cancel ([`crate::export_progress`] shows it).
|
||||
pub progress: Option<Arc<Progress>>,
|
||||
/// Continue in Background: the progress dialog no longer shows this job.
|
||||
pub hidden: bool,
|
||||
then: Then,
|
||||
}
|
||||
|
||||
impl Job {
|
||||
pub fn id(&self) -> u64 {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Was the job cancelled (it writes nothing more; its result is still on its way)?
|
||||
pub fn cancelled(&self) -> bool {
|
||||
self.progress.as_ref().is_some_and(|p| p.cancelled())
|
||||
}
|
||||
}
|
||||
|
||||
/// The worker and the jobs it has not finished.
|
||||
#[derive(Default)]
|
||||
pub struct Background {
|
||||
@@ -93,6 +110,26 @@ impl Background {
|
||||
pub fn busy_with(&self, uid: u64) -> bool {
|
||||
self.jobs.iter().any(|j| j.doc == Some(uid))
|
||||
}
|
||||
|
||||
/// Cancel every export in progress → how many there were.
|
||||
pub fn cancel_exports(&mut self) -> usize {
|
||||
let mut n = 0;
|
||||
for p in self.jobs.iter().filter(|j| !j.cancelled()).filter_map(|j| j.progress.as_ref()) {
|
||||
p.cancel();
|
||||
n += 1;
|
||||
}
|
||||
n
|
||||
}
|
||||
|
||||
/// Hide the exports in progress from the progress dialog (they go on) → how many there were.
|
||||
pub fn hide_exports(&mut self) -> usize {
|
||||
let mut n = 0;
|
||||
for j in self.jobs.iter_mut().filter(|j| j.progress.is_some() && !j.hidden && !j.cancelled()) {
|
||||
j.hidden = true;
|
||||
n += 1;
|
||||
}
|
||||
n
|
||||
}
|
||||
}
|
||||
|
||||
/// Run `work` on a worker thread when `enabled` and the host can write from one, else now; `then`
|
||||
@@ -106,6 +143,20 @@ pub fn run(
|
||||
doc: Option<u64>,
|
||||
work: impl FnOnce(Writer) -> Result<Value, String> + Send + 'static,
|
||||
then: impl FnOnce(&mut VectorcraftApp, Result<Value, String>) -> Result<Value, String> + 'static,
|
||||
) -> Result<Value, String> {
|
||||
run_with_progress(app, enabled, label, doc, None, work, then)
|
||||
}
|
||||
|
||||
/// [`run`] for work that reports to `progress` (an export): the progress dialog shows it while it
|
||||
/// runs in the background, with Cancel.
|
||||
pub fn run_with_progress(
|
||||
app: &mut VectorcraftApp,
|
||||
enabled: bool,
|
||||
label: String,
|
||||
doc: Option<u64>,
|
||||
progress: Option<Arc<Progress>>,
|
||||
work: impl FnOnce(Writer) -> Result<Value, String> + Send + 'static,
|
||||
then: impl FnOnce(&mut VectorcraftApp, Result<Value, String>) -> Result<Value, String> + 'static,
|
||||
) -> Result<Value, String> {
|
||||
let mut work: Work = Box::new(work);
|
||||
let shared = app.services.write_shared.clone().filter(|_| enabled && app.services.download.is_none());
|
||||
@@ -116,7 +167,7 @@ pub fn run(
|
||||
Ok(()) => {
|
||||
let bg = &mut app.background;
|
||||
bg.next_id += 1;
|
||||
bg.jobs.push(Job { id, label: label.clone(), doc, then: Box::new(then) });
|
||||
bg.jobs.push(Job { id, label: label.clone(), doc, progress, hidden: false, then: Box::new(then) });
|
||||
if label.is_empty() {
|
||||
return Ok(json!({ "background": true }));
|
||||
}
|
||||
|
||||
@@ -739,7 +739,7 @@ pub fn status_bar(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
.background
|
||||
.jobs
|
||||
.iter()
|
||||
.filter(|j| !j.label.is_empty())
|
||||
.filter(|j| !j.label.is_empty() && !j.cancelled())
|
||||
.map(|j| j.label.as_str())
|
||||
.chain(app.opening.pending.iter().map(|p| p.label.as_str()))
|
||||
.collect();
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
//! The export progress dialog: while an export runs in the background (Background Export) a modal
|
||||
//! window shows what is being exported, how far it has got (artboard by artboard for raster
|
||||
//! exports of several artboards; busy for one artboard and for formats made in one go, which
|
||||
//! report no steps) and two buttons: Cancel (`file.cancelExport`, also
|
||||
//! Esc) stops it and writes nothing, Continue in Background (`file.exportInBackground`) closes the
|
||||
//! dialog and leaves the export to the status bar.
|
||||
//!
|
||||
//! It opens after [`DELAY`] so a quick export doesn't flash it, and waits while another dialog is
|
||||
//! open (the overwrite question, an options dialog): never two modals at once.
|
||||
|
||||
use serde_json::json;
|
||||
|
||||
use crate::dialogs::modal;
|
||||
use crate::theme::Tokens;
|
||||
use crate::{VectorcraftApp, widgets};
|
||||
|
||||
/// Seconds an export runs before the dialog opens.
|
||||
pub const DELAY: f64 = 0.4;
|
||||
|
||||
/// Where the job a frame first saw is remembered (its id, the time it was first seen).
|
||||
fn seen_id() -> egui::Id {
|
||||
egui::Id::new("export-progress-seen")
|
||||
}
|
||||
|
||||
/// What the dialog shows: (label, steps done, steps in all, share done, exports after this one).
|
||||
fn shown(app: &VectorcraftApp, now: f64, ctx: &egui::Context) -> Option<(String, u64, u64, f32, usize)> {
|
||||
let jobs: Vec<_> = app.background.jobs.iter().filter(|j| !j.hidden && !j.cancelled()).filter(|j| j.progress.is_some()).collect();
|
||||
let first = jobs.first()?;
|
||||
let since = ctx.data_mut(|m| {
|
||||
let seen = m.get_temp_mut_or_insert_with(seen_id(), || (first.id(), now));
|
||||
if seen.0 != first.id() {
|
||||
*seen = (first.id(), now);
|
||||
}
|
||||
seen.1
|
||||
});
|
||||
if now - since < DELAY {
|
||||
return None;
|
||||
}
|
||||
let (done, total) = first.progress.as_ref().map(|p| p.counts()).unwrap_or_default();
|
||||
let frac = first.progress.as_ref().and_then(|p| p.fraction()).unwrap_or(0.0) as f32;
|
||||
Some((first.label.clone(), done, total, frac, jobs.len() - 1))
|
||||
}
|
||||
|
||||
pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
|
||||
let now = ctx.input(|i| i.time);
|
||||
let Some((label, done, total, frac, more)) = shown(app, now, ctx) else { return };
|
||||
if app.ui.dialog.is_some() {
|
||||
return;
|
||||
}
|
||||
let t = Tokens::get(ctx);
|
||||
let heading = format!("{}…", crate::i18n::msg(&label));
|
||||
let mut action = None;
|
||||
modal::show(ctx, &heading, egui::Id::new("export-progress"), -40.0, 20, |ui| {
|
||||
ui.set_width(360.0);
|
||||
modal::heading(ui, &heading);
|
||||
ui.add_space(12.0);
|
||||
// One step says nothing a spinner doesn't: the bar would sit at 0 % until the file is written.
|
||||
if total > 1 {
|
||||
let step = crate::i18n::fmt(tl!("Artboard {n} of {total}"), &[("n", &(done + 1).min(total).to_string()), ("total", &total.to_string())]);
|
||||
// The count on its own line: drawn over the bar it would straddle the fill's edge.
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(egui::RichText::new(step).color(t.text_dim));
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
ui.label(egui::RichText::new(format!("{:.0} %", frac * 100.0)).color(t.text_dim));
|
||||
});
|
||||
});
|
||||
ui.add_space(4.0);
|
||||
ui.add(egui::ProgressBar::new(frac).desired_width(360.0).desired_height(6.0).fill(t.accent));
|
||||
} else {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(egui::Spinner::new().size(14.0).color(t.accent));
|
||||
ui.label(egui::RichText::new(tl!("Encoding and writing the file…")).color(t.text_dim));
|
||||
});
|
||||
}
|
||||
if more > 0 {
|
||||
ui.add_space(6.0);
|
||||
let queued = crate::i18n::tn(more as u64, "{n} more export waiting", "{n} more exports waiting");
|
||||
ui.label(egui::RichText::new(queued).size(11.5).color(t.text_dim));
|
||||
}
|
||||
ui.add_space(16.0);
|
||||
let row = egui::vec2(ui.min_rect().width(), ui.spacing().interact_size.y);
|
||||
ui.allocate_ui_with_layout(row, egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
if widgets::secondary_button(ui, "Cancel").clicked() {
|
||||
action = Some("file.cancelExport");
|
||||
}
|
||||
ui.add_space(8.0);
|
||||
if widgets::secondary_button(ui, "Continue in Background").clicked() {
|
||||
action = Some("file.exportInBackground");
|
||||
}
|
||||
});
|
||||
});
|
||||
if ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
|
||||
action = Some("file.cancelExport");
|
||||
}
|
||||
if let Some(id) = action
|
||||
&& let Err(e) = app.run(id, json!({}))
|
||||
{
|
||||
app.status(e);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use serde_json::json;
|
||||
use vectorcraft_engine::Session;
|
||||
use vectorcraft_engine::cmd::fileio::progress::Progress;
|
||||
|
||||
use crate::{Services, VectorcraftApp, background};
|
||||
|
||||
/// An app whose background writer waits until `go` is set.
|
||||
fn app(go: Arc<std::sync::atomic::AtomicBool>) -> VectorcraftApp {
|
||||
let services = Services {
|
||||
write_shared: Some(Arc::new(move |_: &str, _: &[u8]| {
|
||||
while !go.load(std::sync::atomic::Ordering::SeqCst) {
|
||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||
}
|
||||
Ok(())
|
||||
})),
|
||||
..Default::default()
|
||||
};
|
||||
VectorcraftApp::new(Session::new(), services)
|
||||
}
|
||||
|
||||
/// Draws one frame of the dialog at `time` seconds → the texts it drew.
|
||||
fn frame(app: &mut VectorcraftApp, ctx: &egui::Context, time: f64) -> String {
|
||||
let mut out = ctx.run_ui(egui::RawInput { time: Some(time), ..Default::default() }, |ui| super::show(app, ui.ctx()));
|
||||
out.textures_delta.clear();
|
||||
let mut texts = String::new();
|
||||
for shape in out.shapes {
|
||||
if let egui::epaint::Shape::Text(t) = shape.shape {
|
||||
texts.push_str(t.galley.text());
|
||||
texts.push('\n');
|
||||
}
|
||||
}
|
||||
texts
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opens_after_the_delay_and_cancel_closes_it() {
|
||||
let go = Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
let mut app = app(go.clone());
|
||||
let p = Progress::new();
|
||||
let r = background::run_with_progress(
|
||||
&mut app,
|
||||
true,
|
||||
"Exporting a.png".into(),
|
||||
None,
|
||||
Some(p.clone()),
|
||||
|w| w("/a.png", b"x").map(|()| json!({})),
|
||||
|_, r| r,
|
||||
);
|
||||
assert_eq!(r.unwrap()["background"], true);
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
assert!(!frame(&mut app, &ctx, 0.0).contains("Exporting a.png"), "not before the delay");
|
||||
frame(&mut app, &ctx, 1.0); // a new window measures itself unseen first
|
||||
let shown = frame(&mut app, &ctx, 1.1);
|
||||
assert!(shown.contains("Exporting a.png") && shown.contains("Cancel") && shown.contains("Continue in Background"), "{shown}");
|
||||
app.run("file.cancelExport", json!({})).unwrap();
|
||||
assert!(p.cancelled());
|
||||
assert_eq!(app.ui.status, crate::io::EXPORT_CANCELLED);
|
||||
assert!(!frame(&mut app, &ctx, 2.0).contains("Exporting a.png"), "a cancelled export isn't shown");
|
||||
go.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
background::wait_all(&mut app);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn continue_in_background_hides_it_and_the_export_goes_on() {
|
||||
let go = Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
let mut app = app(go.clone());
|
||||
let p = Progress::new();
|
||||
background::run_with_progress(
|
||||
&mut app,
|
||||
true,
|
||||
"Exporting b.png".into(),
|
||||
None,
|
||||
Some(p.clone()),
|
||||
|w| w("/b.png", b"x").map(|()| json!({})),
|
||||
|_, r| r,
|
||||
)
|
||||
.unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
frame(&mut app, &ctx, 0.0);
|
||||
frame(&mut app, &ctx, 1.0);
|
||||
assert!(frame(&mut app, &ctx, 1.1).contains("Exporting b.png"));
|
||||
assert_eq!(app.run("file.exportInBackground", json!({})).unwrap()["hidden"], 1);
|
||||
assert!(!frame(&mut app, &ctx, 2.0).contains("Exporting b.png"));
|
||||
assert!(!p.cancelled());
|
||||
go.store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
background::wait_all(&mut app);
|
||||
assert!(app.background.jobs.is_empty());
|
||||
}
|
||||
}
|
||||
@@ -5153,6 +5153,12 @@ theme Appearance Външен вид
|
||||
Enable Guides for 9-Slice Scaling Включване на водачи за 9-slice мащабиране
|
||||
Persistent Постоянна
|
||||
Library Panel Options Опции на панела с библиотека
|
||||
Artboard {n} of {total} Работна област {n} от {total}
|
||||
Encoding and writing the file… Кодиране и записване на файла…
|
||||
@plural {n} more export waiting|{n} more exports waiting Още {n} експортиране в изчакване|Още {n} експортирания в изчакване
|
||||
Continue in Background Продължаване във фонов режим
|
||||
Cancel Export Отказ от експортирането
|
||||
@msg Export cancelled Експортирането е отказано
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Желано
|
||||
Word Spacing: Разстояние между думите:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5188,6 +5188,12 @@ mark-style Japanese Japonské
|
||||
@msg too many library panels were opened in this session v této relaci bylo otevřeno příliš mnoho panelů knihoven
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Obraz má {_1} × {_2} pixelů, na jedné straně více než {LARGE_TRACE_SIDE}: vektorizuje se, ale pomaleji. Rychleji se vektorizuje s předvolbou nižší věrnosti nebo menší kopie obrazu.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Obraz byl vektorizován v rozlišení {_1} × {_2} pixelů, zmenšený z {_3} × {_4} (nejvýše {_5} megapixelů).
|
||||
Artboard {n} of {total} Pracovní plocha {n} z {total}
|
||||
Encoding and writing the file… Kódování a zápis souboru…
|
||||
@plural {n} more export waiting|{n} more exports waiting Čeká ještě {n} export|Čekají ještě {n} exporty|Čeká ještě {n} exportů
|
||||
Continue in Background Pokračovat na pozadí
|
||||
Cancel Export Zrušit export
|
||||
@msg Export cancelled Export byl zrušen
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Požadované
|
||||
Word Spacing: Mezery mezi slovy:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5172,6 +5172,12 @@ swatch Bright Violet Kraftig violet
|
||||
Typing Indtastning
|
||||
Persistent Vedvarende
|
||||
Library Panel Options Indstillinger for bibliotekspanel
|
||||
Artboard {n} of {total} Tegneflade {n} af {total}
|
||||
Encoding and writing the file… Koder og skriver filen…
|
||||
@plural {n} more export waiting|{n} more exports waiting {n} eksport mere venter|{n} eksporter mere venter
|
||||
Continue in Background Fortsæt i baggrunden
|
||||
Cancel Export Annullér eksport
|
||||
@msg Export cancelled Eksport annulleret
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Ønsket
|
||||
Word Spacing: Ordafstand:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4763,6 +4763,12 @@ effect Regular Regelmäßig
|
||||
@msg too many library panels were opened in this session in dieser Sitzung wurden zu viele Bibliotheksbedienfelder geöffnet
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Das Bild hat {_1} × {_2} Pixel, mehr als {LARGE_TRACE_SIDE} auf einer Seite: Es wird nachgezeichnet, aber langsamer. Eine Vorgabe mit geringerer Detailtreue oder eine kleinere Kopie des Bildes geht schneller.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Das Bild wurde mit {_1} × {_2} Pixeln nachgezeichnet, verkleinert von {_3} × {_4} (höchstens {_5} Megapixel).
|
||||
Artboard {n} of {total} Zeichenfläche {n} von {total}
|
||||
Encoding and writing the file… Datei wird codiert und geschrieben…
|
||||
@plural {n} more export waiting|{n} more exports waiting {n} weiterer Export wartet|{n} weitere Exporte warten
|
||||
Continue in Background Im Hintergrund fortsetzen
|
||||
Cancel Export Export abbrechen
|
||||
@msg Export cancelled Export abgebrochen
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Optimal
|
||||
Word Spacing: Wortabstand:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5140,6 +5140,12 @@ effect Points Σημεία
|
||||
@msg too many library panels were opened in this session άνοιξαν πάρα πολλοί πίνακες βιβλιοθηκών σε αυτή τη συνεδρία
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Η εικόνα είναι {_1} × {_2} εικονοστοιχεία, πάνω από {LARGE_TRACE_SIDE} σε μία πλευρά: ανιχνεύεται, αλλά πιο αργά. Μια προρύθμιση χαμηλότερης πιστότητας ή ένα μικρότερο αντίγραφο της εικόνας ανιχνεύεται γρηγορότερα.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Η εικόνα ανιχνεύτηκε σε {_1} × {_2} εικονοστοιχεία, σμικρυμένη από {_3} × {_4} (το πολύ {_5} megapixel).
|
||||
Artboard {n} of {total} Πίνακας σχεδίασης {n} από {total}
|
||||
Encoding and writing the file… Κωδικοποίηση και εγγραφή του αρχείου…
|
||||
@plural {n} more export waiting|{n} more exports waiting {n} ακόμη εξαγωγή σε αναμονή|{n} ακόμη εξαγωγές σε αναμονή
|
||||
Continue in Background Συνέχεια στο παρασκήνιο
|
||||
Cancel Export Ακύρωση εξαγωγής
|
||||
@msg Export cancelled Η εξαγωγή ακυρώθηκε
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Επιθυμητό
|
||||
Word Spacing: Απόσταση λέξεων:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2006 and column 19.
|
@@ -5130,6 +5130,12 @@ mark-style Japanese Japonesas
|
||||
@msg too many library panels were opened in this session se abrieron demasiados paneles de biblioteca en esta sesión
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. La imagen tiene {_1} × {_2} píxeles, más de {LARGE_TRACE_SIDE} en un lado: se calcará, pero más despacio. Un ajuste preestablecido de menor fidelidad o una copia más pequeña de la imagen se calcan más rápido.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). La imagen se calcó a {_1} × {_2} píxeles, reducida desde {_3} × {_4} ({_5} megapíxeles como máximo).
|
||||
Artboard {n} of {total} Mesa de trabajo {n} de {total}
|
||||
Encoding and writing the file… Codificando y escribiendo el archivo…
|
||||
@plural {n} more export waiting|{n} more exports waiting {n} exportación más en espera|{n} exportaciones más en espera
|
||||
Continue in Background Continuar en segundo plano
|
||||
Cancel Export Cancelar exportación
|
||||
@msg Export cancelled Exportación cancelada
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Deseado
|
||||
Word Spacing: Espaciado entre palabras:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5152,6 +5152,12 @@ mark-style Japanese Japanilainen
|
||||
@msg too many library panels were opened in this session tässä istunnossa on avattu liian monta kirjastopaneelia
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Kuva on {_1} × {_2} kuvapistettä, yhdeltä sivultaan yli {LARGE_TRACE_SIDE}: se jäljitetään, mutta hitaammin. Pienemmän tarkkuuden esiasetus tai kuvan pienempi kopio jäljittyy nopeammin.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Kuva jäljitettiin kokoon {_1} × {_2} kuvapistettä pienennettynä koosta {_3} × {_4} (enintään {_5} megapikseliä).
|
||||
Artboard {n} of {total} Piirtoalusta {n}/{total}
|
||||
Encoding and writing the file… Tiedostoa koodataan ja kirjoitetaan…
|
||||
@plural {n} more export waiting|{n} more exports waiting {n} vienti odottaa|{n} vientiä odottaa
|
||||
Continue in Background Jatka taustalla
|
||||
Cancel Export Peruuta vienti
|
||||
@msg Export cancelled Vienti peruutettiin
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Haluttu
|
||||
Word Spacing: Sanaväli:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5187,6 +5187,12 @@ theme Appearance Apparence
|
||||
@msg too many library panels were opened in this session trop de panneaux de bibliothèque ont été ouverts dans cette session
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. L’image fait {_1} × {_2} pixels, plus de {LARGE_TRACE_SIDE} sur un côté : elle est vectorisée, mais plus lentement. Un paramètre prédéfini moins fidèle ou une copie plus petite de l’image se vectorise plus vite.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). L’image a été vectorisée à {_1} × {_2} pixels, réduite de {_3} × {_4} ({_5} mégapixels au plus).
|
||||
Artboard {n} of {total} Plan de travail {n} sur {total}
|
||||
Encoding and writing the file… Encodage et écriture du fichier…
|
||||
@plural {n} more export waiting|{n} more exports waiting {n} autre exportation en attente|{n} autres exportations en attente
|
||||
Continue in Background Continuer en arrière-plan
|
||||
Cancel Export Annuler l’exportation
|
||||
@msg Export cancelled Exportation annulée
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Optimal
|
||||
Word Spacing: Espacement des mots :
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5174,6 +5174,12 @@ imageTrace Outline Körvonal
|
||||
@msg too many library panels were opened in this session ebben a munkamenetben túl sok könyvtárpanel nyílt meg
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. A kép {_1} × {_2} képpont, egyik oldalán több mint {LARGE_TRACE_SIDE}: a vektorizálás megtörténik, de lassabban. Kisebb hűségű előbeállítással vagy a kép kisebb másolatával gyorsabb.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). A kép vektorizálása {_1} × {_2} képponton történt, {_3} × {_4} méretről kicsinyítve (legfeljebb {_5} megapixel).
|
||||
Artboard {n} of {total} Rajztábla: {n} / {total}
|
||||
Encoding and writing the file… A fájl kódolása és írása…
|
||||
@plural {n} more export waiting|{n} more exports waiting Még {n} exportálás várakozik
|
||||
Continue in Background Folytatás a háttérben
|
||||
Cancel Export Exportálás megszakítása
|
||||
@msg Export cancelled Az exportálás megszakítva
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Kívánt
|
||||
Word Spacing: Szóköz:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5172,6 +5172,12 @@ swatch Bright Violet Violet cerah
|
||||
Typing Mengetik
|
||||
Persistent Persisten
|
||||
Library Panel Options Opsi panel pustaka
|
||||
Artboard {n} of {total} Artboard {n} dari {total}
|
||||
Encoding and writing the file… Mengodekan dan menulis berkas…
|
||||
@plural {n} more export waiting|{n} more exports waiting {n} ekspor lagi menunggu
|
||||
Continue in Background Lanjutkan di latar belakang
|
||||
Cancel Export Batalkan ekspor
|
||||
@msg Export cancelled Ekspor dibatalkan
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Diinginkan
|
||||
Word Spacing: Spasi Kata:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 3768 and column 19.
|
@@ -4801,6 +4801,12 @@ lighting Light Luce
|
||||
@msg too many library panels were opened in this session in questa sessione sono stati aperti troppi pannelli libreria
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. L’immagine è di {_1} × {_2} pixel, più di {LARGE_TRACE_SIDE} su un lato: viene vettorializzata, ma più lentamente. Un predefinito a fedeltà più bassa o una copia più piccola dell’immagine è più veloce.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). L’immagine è stata vettorializzata a {_1} × {_2} pixel, ridotta da {_3} × {_4} (al massimo {_5} megapixel).
|
||||
Artboard {n} of {total} Tavola da disegno {n} di {total}
|
||||
Encoding and writing the file… Codifica e scrittura del file…
|
||||
@plural {n} more export waiting|{n} more exports waiting {n} altra esportazione in attesa|{n} altre esportazioni in attesa
|
||||
Continue in Background Continua in background
|
||||
Cancel Export Annulla esportazione
|
||||
@msg Export cancelled Esportazione annullata
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Desiderato
|
||||
Word Spacing: Spaziatura parole:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4841,6 +4841,12 @@ lighting Light ライト
|
||||
@msg too many library panels were opened in this session このセッションで開いたライブラリパネルが多すぎます
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. 画像は {_1} × {_2} ピクセルで、一辺が {LARGE_TRACE_SIDE} を超えています。トレースはできますが、時間がかかります。忠実度の低いプリセットや画像の小さいコピーを使うと速くなります。
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). 画像は {_3} × {_4} から縮小した {_1} × {_2} ピクセルでトレースされました (最大 {_5} メガピクセル)。
|
||||
Artboard {n} of {total} アートボード {n} / {total}
|
||||
Encoding and writing the file… ファイルをエンコードして書き込み中…
|
||||
@plural {n} more export waiting|{n} more exports waiting 待機中の書き出し: あと {n} 件
|
||||
Continue in Background バックグラウンドで続行
|
||||
Cancel Export 書き出しをキャンセル
|
||||
@msg Export cancelled 書き出しをキャンセルしました
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired 最適
|
||||
Word Spacing: 単語間隔:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4764,6 +4764,12 @@ lighting Light 광원
|
||||
@msg too many library panels were opened in this session 이 세션에서 라이브러리 패널을 너무 많이 열었습니다
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. 이미지가 {_1} × {_2}픽셀로 한 변이 {LARGE_TRACE_SIDE}을 넘습니다. 추적은 되지만 더 느립니다. 충실도가 낮은 사전 설정이나 이미지의 작은 사본을 사용하면 더 빠릅니다.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). 이미지를 {_3} × {_4}에서 축소한 {_1} × {_2}픽셀로 추적했습니다(최대 {_5}메가픽셀).
|
||||
Artboard {n} of {total} 아트보드 {n} / {total}
|
||||
Encoding and writing the file… 파일을 인코딩하고 쓰는 중…
|
||||
@plural {n} more export waiting|{n} more exports waiting 대기 중인 내보내기 {n}개 더
|
||||
Continue in Background 백그라운드에서 계속
|
||||
Cancel Export 내보내기 취소
|
||||
@msg Export cancelled 내보내기를 취소했습니다
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired 희망
|
||||
Word Spacing: 단어 간격:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5215,7 +5215,12 @@ swatch Bright Violet Felviolet
|
||||
@msg too many library panels were opened in this session er zijn in deze sessie te veel bibliotheekpanelen geopend
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. De afbeelding is {_1} × {_2} pixels, meer dan {LARGE_TRACE_SIDE} aan één zijde: ze wordt getraceerd, maar langzamer. Een voorinstelling met minder detail of een kleinere kopie van de afbeelding traceert sneller.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). De afbeelding is getraceerd op {_1} × {_2} pixels, verkleind van {_3} × {_4} (maximaal {_5} megapixels).
|
||||
|
||||
Artboard {n} of {total} Tekengebied {n} van {total}
|
||||
Encoding and writing the file… Bestand coderen en schrijven…
|
||||
@plural {n} more export waiting|{n} more exports waiting Nog {n} export in de wachtrij|Nog {n} exports in de wachtrij
|
||||
Continue in Background Doorgaan op achtergrond
|
||||
Cancel Export Export annuleren
|
||||
@msg Export cancelled Export geannuleerd
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Gewenst
|
||||
Word Spacing: Woordafstand:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5040,6 +5040,12 @@ mark-style Japanese Japońskie
|
||||
@msg too many library panels were opened in this session w tej sesji otwarto zbyt wiele paneli bibliotek
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Obraz ma {_1} × {_2} pikseli, ponad {LARGE_TRACE_SIDE} na jednym boku: zostanie obrysowany, ale wolniej. Ustawienie wstępne o niższej wierności lub mniejsza kopia obrazu obrysuje się szybciej.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Obraz obrysowano w rozdzielczości {_1} × {_2} pikseli, po zmniejszeniu z {_3} × {_4} (najwyżej {_5} megapikseli).
|
||||
Artboard {n} of {total} Obszar kompozycji {n} z {total}
|
||||
Encoding and writing the file… Kodowanie i zapisywanie pliku…
|
||||
@plural {n} more export waiting|{n} more exports waiting Czeka jeszcze {n} eksport|Czekają jeszcze {n} eksporty|Czeka jeszcze {n} eksportów
|
||||
Continue in Background Kontynuuj w tle
|
||||
Cancel Export Anuluj eksport
|
||||
@msg Export cancelled Eksport anulowany
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Optymalne
|
||||
Word Spacing: Odstępy między wyrazami:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5105,6 +5105,12 @@ mark-style Japanese Japonesas
|
||||
@msg too many library panels were opened in this session foram abertos painéis de biblioteca demais nesta sessão
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. A imagem tem {_1} × {_2} pixels, mais de {LARGE_TRACE_SIDE} em um lado: ela é traçada, mas mais devagar. Uma predefinição de menor fidelidade ou uma cópia menor da imagem traça mais rápido.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). A imagem foi traçada em {_1} × {_2} pixels, reduzida de {_3} × {_4} (no máximo {_5} megapixels).
|
||||
Artboard {n} of {total} Mesa de Trabalho {n} de {total}
|
||||
Encoding and writing the file… Codificando e gravando o arquivo…
|
||||
@plural {n} more export waiting|{n} more exports waiting Mais {n} exportação aguardando|Mais {n} exportações aguardando
|
||||
Continue in Background Continuar em Segundo Plano
|
||||
Cancel Export Cancelar Exportação
|
||||
@msg Export cancelled Exportação cancelada
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Desejado
|
||||
Word Spacing: Espaçamento entre Palavras:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -4797,6 +4797,12 @@ lighting Light Свет
|
||||
@msg too many library panels were opened in this session в этом сеансе открыто слишком много панелей библиотек
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Изображение {_1} × {_2} пикселей, больше {LARGE_TRACE_SIDE} по одной стороне: трассировка выполняется, но медленнее. Стиль с меньшей точностью или уменьшенная копия изображения трассируются быстрее.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Изображение трассировано в размере {_1} × {_2} пикселей, уменьшенное с {_3} × {_4} (не более {_5} мегапикселей).
|
||||
Artboard {n} of {total} Монтажная область {n} из {total}
|
||||
Encoding and writing the file… Кодирование и запись файла…
|
||||
@plural {n} more export waiting|{n} more exports waiting Ещё {n} экспорт в очереди|Ещё {n} экспорта в очереди|Ещё {n} экспортов в очереди
|
||||
Continue in Background Продолжить в фоне
|
||||
Cancel Export Отменить экспорт
|
||||
@msg Export cancelled Экспорт отменён
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Желательно
|
||||
Word Spacing: Интервал между словами:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5144,6 +5144,12 @@ theme Appearance Utseende
|
||||
@msg too many library panels were opened in this session för många bibliotekspaneler har öppnats i den här sessionen
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Bilden är {_1} × {_2} pixlar, mer än {LARGE_TRACE_SIDE} på en sida: den vektoriseras, men långsammare. En förinställning med lägre återgivning eller en mindre kopia av bilden går snabbare.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Bilden vektoriserades vid {_1} × {_2} pixlar, nedskalad från {_3} × {_4} (högst {_5} megapixlar).
|
||||
Artboard {n} of {total} Rityta {n} av {total}
|
||||
Encoding and writing the file… Kodar och skriver filen…
|
||||
@plural {n} more export waiting|{n} more exports waiting {n} export till väntar|{n} exporter till väntar
|
||||
Continue in Background Fortsätt i bakgrunden
|
||||
Cancel Export Avbryt export
|
||||
@msg Export cancelled Exporten avbröts
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Önskat
|
||||
Word Spacing: Ordavstånd:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -4781,6 +4781,12 @@ lighting Light Світло
|
||||
@msg too many library panels were opened in this session у цьому сеансі відкрито забагато панелей бібліотек
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Зображення {_1} × {_2} пікселів, понад {LARGE_TRACE_SIDE} з одного боку: трасування виконується, але повільніше. Стиль із нижчою точністю або менша копія зображення трасуються швидше.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Зображення трасовано в розмірі {_1} × {_2} пікселів, зменшене з {_3} × {_4} (щонайбільше {_5} мегапікселів).
|
||||
Artboard {n} of {total} Монтажна область {n} з {total}
|
||||
Encoding and writing the file… Кодування та запис файлу…
|
||||
@plural {n} more export waiting|{n} more exports waiting Ще {n} експорт в очікуванні|Ще {n} експорти в очікуванні|Ще {n} експортів в очікуванні
|
||||
Continue in Background Продовжити у фоні
|
||||
Cancel Export Скасувати експорт
|
||||
@msg Export cancelled Експорт скасовано
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Бажане
|
||||
Word Spacing: Інтервал між словами:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 725 and column 40.
|
@@ -4781,6 +4781,12 @@ theme Appearance Giao diện
|
||||
@msg too many library panels were opened in this session đã mở quá nhiều bảng thư viện trong phiên này
|
||||
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Ảnh có kích thước {_1} × {_2} điểm ảnh, hơn {LARGE_TRACE_SIDE} ở một cạnh: ảnh vẫn được dò, nhưng chậm hơn. Một thiết lập sẵn có độ trung thực thấp hơn hoặc một bản sao nhỏ hơn của ảnh sẽ dò nhanh hơn.
|
||||
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Ảnh đã được dò ở {_1} × {_2} điểm ảnh, thu nhỏ từ {_3} × {_4} (tối đa {_5} megapixel).
|
||||
Artboard {n} of {total} Bảng vẽ {n} / {total}
|
||||
Encoding and writing the file… Đang mã hóa và ghi tệp…
|
||||
@plural {n} more export waiting|{n} more exports waiting Còn {n} lượt xuất đang chờ
|
||||
Continue in Background Tiếp tục trong nền
|
||||
Cancel Export Hủy xuất
|
||||
@msg Export cancelled Đã hủy xuất
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired Mong muốn
|
||||
Word Spacing: Khoảng cách từ:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 3948 and column 10.
|
@@ -5023,7 +5023,12 @@ lighting Light 光源
|
||||
@msg only http, https and mailto URLs open in the browser, not “{url}” 浏览器中只会打开 http、https 和 mailto 的 URL,不会打开 “{url}”
|
||||
@msg {id} opens another app: only the person at the keyboard can run it {id} 会打开其他应用程序:只有使用键盘的人才能运行它
|
||||
@msg “{name}” is not a file “{name}” 不是文件
|
||||
|
||||
Artboard {n} of {total} 画板 {n} / {total}
|
||||
Encoding and writing the file… 正在编码并写入文件…
|
||||
@plural {n} more export waiting|{n} more exports waiting 另有 {n} 个导出等待中
|
||||
Continue in Background 在后台继续
|
||||
Cancel Export 取消导出
|
||||
@msg Export cancelled 已取消导出
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired 所需
|
||||
Word Spacing: 字间距:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -4077,6 +4077,12 @@ lighting Light 光源
|
||||
Enable Guides for 9-Slice Scaling 啟用 9-slice 縮放參考線
|
||||
Persistent 持久
|
||||
Library Panel Options 資料庫面板選項
|
||||
Artboard {n} of {total} 工作區域 {n} / {total}
|
||||
Encoding and writing the file… 正在編碼並寫入檔案…
|
||||
@plural {n} more export waiting|{n} more exports waiting 另有 {n} 個匯出等待中
|
||||
Continue in Background 在背景繼續
|
||||
Cancel Export 取消匯出
|
||||
@msg Export cancelled 已取消匯出
|
||||
# Paragraph panel menu › Justification…
|
||||
Desired 所需
|
||||
Word Spacing: 字間距:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -3,7 +3,9 @@
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_doc::SavedView;
|
||||
use vectorcraft_doc::cancel;
|
||||
use vectorcraft_engine::EngineError;
|
||||
use vectorcraft_engine::cmd::fileio::progress::{self, Progress};
|
||||
use vectorcraft_engine::cmd::fileio::{self, Format, SAVE_FORMATS, SaveMode, SavePlan, TEMPLATE_FORMATS};
|
||||
|
||||
use crate::background::{self, Writer};
|
||||
@@ -675,6 +677,25 @@ pub fn recent_files(app: &VectorcraftApp) -> &[String] {
|
||||
&r[..r.len().min(app.session.prefs.recent_files_count as usize)]
|
||||
}
|
||||
|
||||
/// The status once an export was cancelled.
|
||||
pub const EXPORT_CANCELLED: &str = "Export cancelled";
|
||||
|
||||
/// File → Cancel Export (the progress dialog's Cancel): every export running in the background
|
||||
/// stops and writes nothing more → `{cancelled: n}`.
|
||||
pub fn cancel_exports(app: &mut VectorcraftApp) -> Value {
|
||||
let n = app.background.cancel_exports();
|
||||
if n > 0 {
|
||||
app.status(EXPORT_CANCELLED);
|
||||
}
|
||||
json!({ "cancelled": n })
|
||||
}
|
||||
|
||||
/// The progress dialog's Continue in Background: the exports go on, shown in the status bar only
|
||||
/// → `{hidden: n}`.
|
||||
pub fn hide_exports(app: &mut VectorcraftApp) -> Value {
|
||||
json!({ "hidden": app.background.hide_exports() })
|
||||
}
|
||||
|
||||
/// Export the active document in `format` (default: the path's extension, else PNG) with the
|
||||
/// `document.export` options in `params` (artboard, range, useArtboards, selectedOnly, ppi, SVG
|
||||
/// options…): every file it writes (one per artboard, linked images) goes next to `path`. The
|
||||
@@ -704,18 +725,31 @@ pub fn export(app: &mut VectorcraftApp, format: Option<&str>, path: Option<Strin
|
||||
// Background Export: encoded and written from this snapshot while editing goes on.
|
||||
let label = format!("Exporting {}", fileio::file_name(&path));
|
||||
let (params, target) = (params.clone(), path.clone());
|
||||
// The progress dialog's counts and Cancel: a cancelled export stops drawing and never starts
|
||||
// writing.
|
||||
let progress = Progress::new();
|
||||
let (reporting, asked) = (progress.clone(), progress.clone());
|
||||
let work = move |write: Writer| {
|
||||
let enc = fileio::encode_all(&doc, f.id, ¶ms).map_err(|e| e.to_string())?;
|
||||
let files = write_encoded(write, &doc, &path, &enc)?;
|
||||
let mut out = json!({ "path": files.first().unwrap_or(&path), "warnings": enc.warnings });
|
||||
if files.len() > 1 {
|
||||
out["files"] = json!(files);
|
||||
}
|
||||
Ok(out)
|
||||
progress::scope(&reporting, || {
|
||||
cancel::check().map_err(|e| e.to_string())?;
|
||||
let enc = fileio::encode_all(&doc, f.id, ¶ms).map_err(|e| e.to_string())?;
|
||||
cancel::check().map_err(|e| e.to_string())?;
|
||||
let files = write_encoded(write, &doc, &path, &enc)?;
|
||||
let mut out = json!({ "path": files.first().unwrap_or(&path), "warnings": enc.warnings });
|
||||
if files.len() > 1 {
|
||||
out["files"] = json!(files);
|
||||
}
|
||||
Ok(out)
|
||||
})
|
||||
};
|
||||
let enabled = app.session.prefs.background_export;
|
||||
let failed = target.clone();
|
||||
let mut r = background::run(app, enabled, label, None, work, move |app, r| {
|
||||
let mut r = background::run_with_progress(app, enabled, label, None, Some(progress), work, move |app, r| {
|
||||
// A file written before the Cancel arrived is reported as written.
|
||||
if r.is_err() && asked.cancelled() {
|
||||
app.status(EXPORT_CANCELLED);
|
||||
return Ok(json!({ "cancelled": true }));
|
||||
}
|
||||
let r = r.inspect_err(|e| report_write_error(app, true, &failed, e))?;
|
||||
let what = match r["files"].as_array() {
|
||||
Some(files) => format!("{} files", files.len()),
|
||||
|
||||
@@ -25,6 +25,7 @@ pub mod cursors;
|
||||
pub mod dialogs;
|
||||
pub mod dock;
|
||||
mod edit_toolbar;
|
||||
pub mod export_progress;
|
||||
pub mod find_font;
|
||||
pub mod floating;
|
||||
pub mod font_menu;
|
||||
@@ -1440,6 +1441,7 @@ impl VectorcraftApp {
|
||||
dock::floating_panel(self, &ctx);
|
||||
floating::show(self, &ctx);
|
||||
dialogs::show(self, &ctx);
|
||||
export_progress::show(self, &ctx);
|
||||
palette::show(self, &ctx);
|
||||
if self.custom_titlebar {
|
||||
titlebar::resize_zones(ui);
|
||||
|
||||
@@ -747,6 +747,18 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
|
||||
("file.openRecent29", "Open Recent File 29", "", "{}"),
|
||||
("file.openRecent30", "Open Recent File 30", "", "{}"),
|
||||
("file.reveal", "Show in Folder", "", "{} show the document's file in the system file manager (desktop) → {path}"),
|
||||
(
|
||||
"file.cancelExport",
|
||||
"Cancel Export",
|
||||
"",
|
||||
"{} stop every export running in the background (Background Export): an export stops between artboards and writes no file once cancelled → {cancelled: n}",
|
||||
),
|
||||
(
|
||||
"file.exportInBackground",
|
||||
"Continue in Background",
|
||||
"",
|
||||
"{} close the export progress dialog; the exports go on, shown in the status bar → {hidden: n}",
|
||||
),
|
||||
(
|
||||
"ui.dxfOptionsDialog",
|
||||
"DXF Options Dialog",
|
||||
@@ -1101,6 +1113,8 @@ pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<R
|
||||
"file.newFromTemplate" if p.get("dataBase64").is_none() => io::new_from_template(app, s("path")),
|
||||
"file.revert" if p.get("confirmed").and_then(Value::as_bool) != Some(true) => io::ask_revert(app),
|
||||
"file.reveal" => io::reveal(app),
|
||||
"file.cancelExport" => Ok(io::cancel_exports(app)),
|
||||
"file.exportInBackground" => Ok(io::hide_exports(app)),
|
||||
id if id.starts_with("file.openRecent") => match recent_slot(app, id).cloned() {
|
||||
Some(path) => io::open_reporting(app, &path),
|
||||
None => Err("no such recent file".into()),
|
||||
@@ -2225,6 +2239,7 @@ pub fn enabled(app: &VectorcraftApp, id: &str) -> bool {
|
||||
"type.bold" | "type.italic" => crate::panels::character::text_style(app).is_some(),
|
||||
id if crate::panels::paragraph::ALIGN_COMMANDS.iter().any(|c| c.0 == id) => crate::panels::character::text_style(app).is_some(),
|
||||
"file.reveal" => app.services.reveal.is_some() && app.session.active().is_some_and(|d| d.path.is_some()),
|
||||
"file.cancelExport" | "file.exportInBackground" => app.background.jobs.iter().any(|j| j.progress.is_some() && !j.cancelled()),
|
||||
"file.place"
|
||||
| "file.export.svg"
|
||||
| "file.export.png"
|
||||
|
||||
@@ -121,6 +121,34 @@ fn exports_run_in_the_background_and_the_preference_turns_both_off() {
|
||||
assert!(app.background.jobs.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_cancelled_export_writes_nothing() {
|
||||
let (gate, writing) = (Gate::default(), Gate::default());
|
||||
let files = Files::default();
|
||||
let (f, g, w) = (files.clone(), gate.clone(), writing.clone());
|
||||
let services = Services {
|
||||
write_shared: Some(Arc::new(move |p: &str, b: &[u8]| {
|
||||
w.open();
|
||||
g.wait();
|
||||
f.lock().unwrap().push((p.to_string(), b.to_vec()));
|
||||
Ok(())
|
||||
})),
|
||||
..Default::default()
|
||||
};
|
||||
let mut app = VectorcraftApp::new(Session::new(), services);
|
||||
app.run("file.new", json!({"width": 100, "height": 100})).unwrap();
|
||||
// The first export holds the worker at the gate, past its last check: it still writes.
|
||||
io::export(&mut app, Some("svg"), Some("/out/first.svg".into()), &json!({})).unwrap();
|
||||
writing.wait();
|
||||
io::export(&mut app, Some("png"), Some("/out/second.png".into()), &json!({})).unwrap();
|
||||
assert_eq!(app.run("file.cancelExport", json!({})).unwrap()["cancelled"], 2);
|
||||
assert_eq!(app.run("file.cancelExport", json!({})).unwrap()["cancelled"], 0, "nothing left to cancel");
|
||||
gate.open();
|
||||
background::wait_all(&mut app);
|
||||
assert_eq!(written(&files), ["/out/first.svg"]);
|
||||
assert_eq!(app.ui.status, io::EXPORT_CANCELLED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quitting_waits_for_background_saves() {
|
||||
let gate = Gate::default();
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
# Gaps
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; Blend spine Anchor Point conversions, selection/stacking priority and Shift regression coverage (#2544); scores and hours unchanged; transparency grid visibility saved in native documents, #1968; G6: MCP cancellation, progress notifications and busy answers during long exports, #2165, #2171, #2172; EPS editing data: legacy mesh corners, vector opacity masks and compound member paint retained; raster page comparison filters minification without relaxing limits; scores and hours unchanged; correction: quick Image Trace presets leave the panel as it was, the Image Trace button opens it; batch exporting, #1935; G2: Effect › Sketch › Halftone Pattern; Photoshop filters 30/57, 27 remaining (22–38 h); G5: Symbol Options Export Type (Graphic / Movie Clip); G1: Smart Guides line extension, #2120; G16: Brazilian Portuguese catalog complete under the tests, #1914; Localization 71% → 74%, hours 55–90 → 50–85; G6: image variables landed, estimate 8–14 → 6–12 h; Warp Options controls verified by focused checks and original-image review; estimates unchanged; existing-guide Escape cancellation validated 2026-10-14; scores and hours unchanged; open-path anchor Delete keeps surviving runs separate; estimates unchanged; Artboard tab-switch follow-up historically validated on `a0671777`; scores and hours unchanged; Artboard clean active-tab close/Delete follow-up validated 2026-10-15; G13: Object › Intertwine landed, #1763; no entry closed, estimates unchanged; MCP `add_text` exposes the existing `vertical` option for point, area and path type; horizontal remains the default; scores and hours unchanged; Vertical fullwidth question and exclamation marks keep their nominal glyph upright when the font has no vertical alternate; scores and hours unchanged.; G1: Curvature curve geometry, #1236 in part; Width-point finite-arithmetic follow-up validated; scores and hours unchanged; Image Trace adjustment discovery; G9: legacy `.ai` of version 7 saved with type as text and hidden objects kept, legacy type read, #868; estimate unchanged; Revolve: automatic smooth shading and default-on Cap with continuous rims; later 2026-10-14: G8 untrusted files, #1852, #1862–#1873; G11 anchor X/Y/W/H, #1824, Snap to Grid over Snap to Pixel, #1816, redo during a drag, #1719, Dim Images, #1683, failed-save dialog, #1685; gaps noted for reporters: G1 Warp Options for Effect › Warp, #1760, Color Picker Tab order, #1692, key anchor, #1636; G7 custom toolbars, #1759, workspace panel widths, #1753; G11 Shape Builder gaps between closed shapes, #1691, interior anchor delete; G13 Intertwine, #1763, gradient mesh from an image, #1676, Image Trace radial gradients, #1849; G12 duplicated line removed; no entry closed, estimates unchanged; verified PngSuite downloads, #1729; synthetic control-input budgets, #1709; 2026-10-14 fixes: G11 anchor Align and Distribute, #1636, Delete and selection, #1674, #1666, #1743, #1686, Roughen and Scribble, #1643, #1648; G8 file and input hardening, #1776–#1783, #1786, #1807; G10 Wayland cursors and the Windows title bar, unconfirmed, #1645, #1758; no entry closed, estimates unchanged; later 2026-10-13 fixes, #1570, #1578, #1590, #1613, #1615, #1616, #1620 in part, #1602 in part; no entry closed, estimates unchanged; TIFF is available in Export for Screens and Asset Export with shared format settings; scores and hours unchanged; G1/M11: Artboard deletion and history selection normalization; history regressions and full workspace CI pass; scores and hours unchanged; Character styles retain explicit Auto leading, propagate resets and preserve local leading overrides; scores and hours unchanged; Width Point Edit: Shift-double-click enables adjoining adjustment; plain double-click keeps it off; scores and hours unchanged; G12: Identify Objects By acts, #394; ~16 → ~15 prefs still inert; G12: Appearance of Black › Printing / Exporting acts, #394; ~16 → ~15 prefs still inert; control transport deadlines, #1726; G1: Average and Join in one step with a corner or smooth choice added as missing (it was listed as done in ui-parity.md); Join after Average, #1533; guide objects, #1534; Keyboard Shortcuts, #1539; G9 and G11 fixes, #1528, #1530, #1544, #1553, #1512, #1537; estimates unchanged; Direct Selection anchor precedence, #1508; dialog slider rails, #1483; template layers in Outline view, #1486; Tabs panel stop drags, #1458; type that does not fit its frame is reported, #1471; 3D on outlined type, #1481, Twist range, #1524, twisted walls, #1525; print-size images, #1517; French catalog, #1511; estimates unchanged; G1: Split Into Grid options, #1462, Transform Objects and Reflect's angle, #1448 and #1460, Pathfinder on a group, #1450, valueLine truncated
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (G12: Background Export progress dialog with Cancel, #2564; no entry closed, estimates unchanged; 3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; Blend spine Anchor Point conversions, selection/stacking priority and Shift regression coverage (#2544); scores and hours unchanged; transparency grid visibility saved in native documents, #1968; G6: MCP cancellation, progress notifications and busy answers during long exports, #2165, #2171, #2172; EPS editing data: legacy mesh corners, vector opacity masks and compound member paint retained; raster page comparison filters minification without relaxing limits; scores and hours unchanged; correction: quick Image Trace presets leave the panel as it was, the Image Trace button opens it; batch exporting, #1935; G2: Effect › Sketch › Halftone Pattern; Photoshop filters 30/57, 27 remaining (22–38 h); G5: Symbol Options Export Type (Graphic / Movie Clip); G1: Smart Guides line extension, #2120; G16: Brazilian Portuguese catalog complete under the tests, #1914; Localization 71% → 74%, hours 55–90 → 50–85; G6: image variables landed, estimate 8–14 → 6–12 h; Warp Options controls verified by focused checks and original-image review; estimates unchanged; existing-guide Escape cancellation validated 2026-10-14; scores and hours unchanged; open-path anchor Delete keeps surviving runs separate; estimates unchanged; Artboard tab-switch follow-up historically validated on `a0671777`; scores and hours unchanged; Artboard clean active-tab close/Delete follow-up validated 2026-10-15; G13: Object › Intertwine landed, #1763; no entry closed, estimates unchanged; MCP `add_text` exposes the existing `vertical` option for point, area and path type; horizontal remains the default; scores and hours unchanged; Vertical fullwidth question and exclamation marks keep their nominal glyph upright when the font has no vertical alternate; scores and hours unchanged.; G1: Curvature curve geometry, #1236 in part; Width-point finite-arithmetic follow-up validated; scores and hours unchanged; Image Trace adjustment discovery; G9: legacy `.ai` of version 7 saved with type as text and hidden objects kept, legacy type read, #868; estimate unchanged; Revolve: automatic smooth shading and default-on Cap with continuous rims; later 2026-10-14: G8 untrusted files, #1852, #1862–#1873; G11 anchor X/Y/W/H, #1824, Snap to Grid over Snap to Pixel, #1816, redo during a drag, #1719, Dim Images, #1683, failed-save dialog, #1685; gaps noted for reporters: G1 Warp Options for Effect › Warp, #1760, Color Picker Tab order, #1692, key anchor, #1636; G7 custom toolbars, #1759, workspace panel widths, #1753; G11 Shape Builder gaps between closed shapes, #1691, interior anchor delete; G13 Intertwine, #1763, gradient mesh from an image, #1676, Image Trace radial gradients, #1849; G12 duplicated line removed; no entry closed, estimates unchanged; verified PngSuite downloads, #1729; synthetic control-input budgets, #1709; 2026-10-14 fixes: G11 anchor Align and Distribute, #1636, Delete and selection, #1674, #1666, #1743, #1686, Roughen and Scribble, #1643, #1648; G8 file and input hardening, #1776–#1783, #1786, #1807; G10 Wayland cursors and the Windows title bar, unconfirmed, #1645, #1758; no entry closed, estimates unchanged; later 2026-10-13 fixes, #1570, #1578, #1590, #1613, #1615, #1616, #1620 in part, #1602 in part; no entry closed, estimates unchanged; TIFF is available in Export for Screens and Asset Export with shared format settings; scores and hours unchanged; G1/M11: Artboard deletion and history selection normalization; history regressions and full workspace CI pass; scores and hours unchanged; Character styles retain explicit Auto leading, propagate resets and preserve local leading overrides; scores and hours unchanged; Width Point Edit: Shift-double-click enables adjoining adjustment; plain double-click keeps it off; scores and hours unchanged; G12: Identify Objects By acts, #394; ~16 → ~15 prefs still inert; G12: Appearance of Black › Printing / Exporting acts, #394; ~16 → ~15 prefs still inert; control transport deadlines, #1726; G1: Average and Join in one step with a corner or smooth choice added as missing (it was listed as done in ui-parity.md); Join after Average, #1533; guide objects, #1534; Keyboard Shortcuts, #1539; G9 and G11 fixes, #1528, #1530, #1544, #1553, #1512, #1537; estimates unchanged; Direct Selection anchor precedence, #1508; dialog slider rails, #1483; template layers in Outline view, #1486; Tabs panel stop drags, #1458; type that does not fit its frame is reported, #1471; 3D on outlined type, #1481, Twist range, #1524, twisted walls, #1525; print-size images, #1517; French catalog, #1511; estimates unchanged; G1: Split Into Grid options, #Line truncated
|
||||
|
||||
Every known shortfall against Illustrator, one entry each, **ranked by how much it stops a professional from
|
||||
switching**. This is the work list: unless you were given a task, pick the highest-ranked gap you can make
|
||||
@@ -202,6 +202,7 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
|
||||
|
||||
## G12 Preferences and panels that don't act yet
|
||||
|
||||
- **Done (2026-10-17):** Background Export shows its progress in a dialog with Cancel and Continue in Background (#2564), on the shared cancel token (#2546); a cancelled export writes no file. Save As, Export for Screens and Export Selection don't show it yet. Estimate unchanged.
|
||||
- **Done:** quick Image Trace presets trace at once and leave the panel as it was; the Image Trace button (Control bar, Properties) opens the controls with Threshold and Advanced (Paths, Corners, Noise), as the public documentation describes (<https://helpx.adobe.com/illustrator/using/image-trace-results-optimization.html>). Other context-specific Properties gaps remain.
|
||||
|
||||
- **Missing:** ~14 Preferences options stored but not acted on (#394: Open
|
||||
@@ -349,6 +350,7 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-17 | minor | Blend spine Anchor Point conversions, selection/stacking priority and Shift regression coverage (#2544); scores and hours unchanged. Windows native canvas checks and screenshots reviewed in PR #2643. |
|
||||
| 2026-10-17 | minor | G12: Background Export progress dialog with Cancel and Continue in Background (#2564); no entry closed, estimates unchanged |
|
||||
| 2026-10-16 | minor | Native documents restore transparency grid visibility (#1968); save/open regression covers on/off, old files and SVG; scores and hours unchanged (estimated) |
|
||||
| 2026-10-16 | minor | G6: MCP cancellation, progress notifications and a busy answer during long exports (#2165, #2171, #2172); no entry closed, estimates unchanged |
|
||||
| 2026-10-16 | minor | G11: PDF export of a CMYK document reports its RGB colours and non-CMYK images, with a count, in the export warnings (#2162); no entry closed, estimates unchanged |
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
# UI and interaction parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; Blend spine Anchor Point conversions, selection/stacking priority and Shift regression coverage (#2544); scores and hours unchanged; Artboard Options' X and Y with a reference point, #1876; scores unchanged; library panels: one per library, dockable and tabbable, with Persistent, #2603; the Selection tool threads text from the out port, #2572 in part; scores unchanged; Group Selection picks and moves one path of a compound path, #2441 in part; the Move dialog opens on the last move and its last checkboxes; scores unchanged; transparency grid visibility saved in native documents, #1968; Live Corners radius up to the neighbouring cuts, #2332; Image Trace quick presets: wording corrected to the shipped behaviour, the Image Trace button opens the controls; batch exporting, #1935; document window scroll bars, #2322; Anchor Point tool: a click on a handle end retracts that handle only, #2267; scores unchanged; Smart Guides: the dragged end of a straight segment keeps to its extension at any angle, #2120; Warp Options controls verified by focused checks and original-image review; estimates unchanged; existing-guide Escape cancellation validated 2026-10-14; scores and hours unchanged; open-path anchor Delete keeps surviving runs separate; estimates unchanged; Artboard tab-switch follow-up historically validated on `a0671777`; scores and hours unchanged; Artboard clean active-tab close/Delete follow-up validated 2026-10-15; Curvature curve geometry, #1236 in part; Width-point finite-arithmetic follow-up validated; scores and hours unchanged; Image Trace adjustment discovery; later 2026-10-14 merges: macOS save sheet Format menu back to missing, #1855, #866 reopened; anchor X/Y/W/H in the Transform panel, Properties and Control bar, #1824; Snap to Grid over Snap to Pixel, #1816; toolbar labels, columns and the All Tools drawer, #1751; Actions panel selection, #1629 in part; scores unchanged; 2026-10-14 merges: anchor Align and Distribute, #1636; Delete on closed paths and whole objects, #1674; selection inside isolation, #1666; Escape in a Direct Selection drag and while typing, #1743, #1686; Pen clicks under 3 px, #1663; [ and ] on more tools, #1661 in part; fields commit on click away, #1787; command search keys, #1757; a guide's position in the Transform panel is a convenience with no parity claim, #1659; scores unchanged; intersect Smart Guides for drawn points and dragged anchors, #1602 in part; a ruler guide's position in the Transform panel, #1597 in part; Symbol Options before New Symbol, #1560; tool flyout widths, #1616; Pathfinder Trim, Merge and Crop strokes, #1570; scores unchanged; G1/M11: Artboard deletion and history selection normalization; history regressions and full workspace CI pass; scores and hours unchanged; Width Point Edit: Shift-double-click enables adjoining adjustment; plain double-click keeps it off; scores and hours unchanged; per-corner radius fields with a link toggle, split from #1594; Join merges anchors lying on one another, #1533; correction: Average and Join with a corner or smooth choice is missing, not done; guide objects take the guide colour and style and Lock Guides, #1534; Keyboard Shortcuts lists commands outside the menus, #1539; round corners next to curves and the Smooth tool's reach, #1512; Make Compound Path on a group, #1537; dialog slider rails, #1483; template layers stay in Preview in Outline view, #1486; Direct Selection takes the selected object's anchor where anchors coincide, #1508; Tabs panel stop drags, #1458; Split Into Grid dialog, #1462; Transform Objects and Reflect's angle, #1448 and #1460; Pathfinder panel on a group, #1450; value fields: readable selection, #1465 in part, and a focused field follows its value, #1457; scores unchanged; Transform dialogs have a live Preview and step with the arrow keys, #1412; transform tools, dialogs and Move act on direct-selected anchors, #1408; the bounding box hides during a corner drag, #1229; scores unchanged; dialogs focus their first field on every open, #1367, and a first field that starts greyed out once it is enabled, #1395; scores unchanged; Rectangular Grid and Polar Grid click dialogs take the skew, frame, compound path and Fill Grid options, #1377 in part, score unchanged; Live Corners: a Direct Selection click on a widget selects its corner, #1239; custom ruler origin, #1212; Artboard tool clicks fit the topmost editable object in one undo step (#1227); scores unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; Document Setup: Edit Artboards closes the modal after applying valid settings; pointer-click regressions; scores and hours unchanged; Artboards: Escape Line truncated
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** minor (Background Export progress dialog with Cancel, #2564; scores unchanged; 3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; Blend spine Anchor Point conversions, selection/stacking priority and Shift regression coverage (#2544); scores and hours unchanged; Artboard Options' X and Y with a reference point, #1876; scores unchanged; library panels: one per library, dockable and tabbable, with Persistent, #2603; the Selection tool threads text from the out port, #2572 in part; scores unchanged; Group Selection picks and moves one path of a compound path, #2441 in part; the Move dialog opens on the last move and its last checkboxes; scores unchanged; transparency grid visibility saved in native documents, #1968; Live Corners radius up to the neighbouring cuts, #2332; Image Trace quick presets: wording corrected to the shipped behaviour, the Image Trace button opens the controls; batch exporting, #1935; document window scroll bars, #2322; Anchor Point tool: a click on a handle end retracts that handle only, #2267; scores unchanged; Smart Guides: the dragged end of a straight segment keeps to its extension at any angle, #2120; Warp Options controls verified by focused checks and original-image review; estimates unchanged; existing-guide Escape cancellation validated 2026-10-14; scores and hours unchanged; open-path anchor Delete keeps surviving runs separate; estimates unchanged; Artboard tab-switch follow-up historically validated on `a0671777`; scores and hours unchanged; Artboard clean active-tab close/Delete follow-up validated 2026-10-15; Curvature curve geometry, #1236 in part; Width-point finite-arithmetic follow-up validated; scores and hours unchanged; Image Trace adjustment discovery; later 2026-10-14 merges: macOS save sheet Format menu back to missing, #1855, #866 reopened; anchor X/Y/W/H in the Transform panel, Properties and Control bar, #1824; Snap to Grid over Snap to Pixel, #1816; toolbar labels, columns and the All Tools drawer, #1751; Actions panel selection, #1629 in part; scores unchanged; 2026-10-14 merges: anchor Align and Distribute, #1636; Delete on closed paths and whole objects, #1674; selection inside isolation, #1666; Escape in a Direct Selection drag and while typing, #1743, #1686; Pen clicks under 3 px, #1663; [ and ] on more tools, #1661 in part; fields commit on click away, #1787; command search keys, #1757; a guide's position in the Transform panel is a convenience with no parity claim, #1659; scores unchanged; intersect Smart Guides for drawn points and dragged anchors, #1602 in part; a ruler guide's position in the Transform panel, #1597 in part; Symbol Options before New Symbol, #1560; tool flyout widths, #1616; Pathfinder Trim, Merge and Crop strokes, #1570; scores unchanged; G1/M11: Artboard deletion and history selection normalization; history regressions and full workspace CI pass; scores and hours unchanged; Width Point Edit: Shift-double-click enables adjoining adjustment; plain double-click keeps it off; scores and hours unchanged; per-corner radius fields with a link toggle, split from #1594; Join merges anchors lying on one another, #1533; correction: Average and Join with a corner or smooth choice is missing, not done; guide objects take the guide colour and style and Lock Guides, #1534; Keyboard Shortcuts lists commands outside the menus, #1539; round corners next to curves and the Smooth tool's reach, #1512; Make Compound Path on a group, #1537; dialog slider rails, #1483; template layers stay in Preview in Outline view, #1486; Direct Selection takes the selected object's anchor where anchors coincide, #1508; Tabs panel stop drags, #1458; Split Into Grid dialog, #1462; Transform Objects and Reflect's angle, #1448 and #1460; Pathfinder panel on a group, #1450; value fields: readable selection, #1465 in part, and a focused field follows its value, #1457; scores unchanged; Transform dialogs have a live Preview and step with the arrow keys, #1412; transform tools, dialogs and Move act on direct-selected anchors, #1408; the bounding box hides during a corner drag, #1229; scores unchanged; dialogs focus their first field on every open, #1367, and a first field that starts greyed out once it is enabled, #1395; scores unchanged; Rectangular Grid and Polar Grid click dialogs take the skew, frame, compound path and Fill Grid options, #1377 in part, score unchanged; Live Corners: a Direct Selection click on a widget selects its corner, #1239; custom ruler origin, #1212; Artboard tool clicks fit the topmost editable object in one undo step (#1227); scores unchanged; Pathfinder single-result source names and SVG IDs retained, #1293; DXF control-point SPLINE closure read, #1299 follow-up; Document Setup: Edit Artboards closes the modal after applying valid settings; pLine truncated
|
||||
|
||||
How VectorCraft's tools, handles, snapping and panels behave next to Illustrator's. A vector illustrator's
|
||||
trust is in the details: where a handle lands, what Shift and Alt do mid-drag, what a smart guide snaps to,
|
||||
@@ -226,6 +226,7 @@ panel-menu commands such as Break Link to Symbol), so they can be given shortcut
|
||||
| Blend panel (30.7) | partial | Window › Blend: Make, Spacing, Orientation, Reverse Front to Back, Reverse Spine, Replace Spine, Expand and Release, running the Object › Blend commands; the easing and color controls are missing |
|
||||
| SVG Interactivity, Retype, Comments, Version History panels | missing | the last two are cloud features (out of scope) |
|
||||
| Workspaces (save, reset, switch) | done | |
|
||||
| Background Export: a progress dialog with the file name, the artboard count and a bar (busy for one artboard and for formats made in one go), Cancel (Esc) and Continue in Background; it opens after 0.4 s and never over another dialog. A cancelled export writes no file | done | #2564; on the shared `vectorcraft_doc::cancel` token (#2546), so a cancelled render keeps no half-drawn pattern tile or effect raster. The bar moves between artboards, not within one. Save As, Export for Screens and Export Selection don't show it yet |
|
||||
| Swatch and graphic style library panels: each library opens in a panel of its own, several at once, that floats, groups, docks and closes like any panel; opening an open library brings its panel forward; Previous / Next Library change the library in place; the panel menu's Persistent reopens the panel at startup with its view and find field (a library loaded from a file is read again, a missing file is skipped with a status-bar note) | done | #2603; panel ids `library:<kind>:<id>`, `ui.libraryPanel {panel, library?, view?, showFind?, persistent?}`; the library panels have no sort option, as before |
|
||||
| DXF export/import preserves intended curve closure | done | #1299; R13–2018 closed SPLINE bit set/read; an open curve ending at its start stays open; knots and control points unchanged; native CIRCLE/ARC export and CNC preset remain pending |
|
||||
| macOS save sheet: choose the format from a menu | done | #866; the save panel is VectorCraft's own `NSSavePanel` with its Format menu set before the sheet opens (`apps/vectorcraft/src/mac_save_format.rs`); #1855 had withdrawn the menu that was added after the sheet began, which made Save As, Save a Copy and the first Save show no sheet |
|
||||
@@ -248,6 +249,7 @@ re-measure of the panels added since. Slider rails show on Medium Dark (#1808, #
|
||||
|---|---|---|
|
||||
| 2026-10-17 | minor | Blend spine Anchor Point conversions, selection/stacking priority and Shift regression coverage (#2544); scores and hours unchanged. Windows native canvas checks and screenshots reviewed in PR #2643. |
|
||||
| 2026-10-17 | minor | Artboard Options has X and Y beside a reference point locator, counted from the ruler origin as the Transform panel's are; a typed X or Y moves the artboard's reference point there and a new Width or Height keeps it in place (#1876); scores unchanged (estimated) |
|
||||
| 2026-10-17 | minor | Background Export shows a progress dialog with Cancel and Continue in Background (#2564), built on the shared cancel token of #2546; scores and hours unchanged (estimated) |
|
||||
| 2026-10-17 | minor | Library panels (#2603): each swatch or graphic style library opens in its own dockable, tabbable panel, several at once; the panel menu's Persistent reopens a panel at startup with its view and find field, a library loaded from a file read again from its path; scores unchanged (estimated) |
|
||||
| 2026-10-17 | minor | Selection tool: clicking the red out port of overflowing area type, then a closed path, empty canvas or a dragged rectangle threads the text there (#2572 in part); Escape cancels. Scores and hours unchanged. |
|
||||
| 2026-10-17 | minor | Group Selection picks one path of a compound path (the outer shape or a hole) and moves it on its own; a second click adds the compound path (#2441, in part) |
|
||||
|
||||
Reference in new issue
Block a user