Wraps an svg::SVGDocument and the editor's per-frame command queue. More...
#include "donner/editor/AsyncSVGDocument.h"
Classes | |
| struct | FlushResult |
| Outcome of applying queued document mutations. More... | |
Public Types | |
| enum class | ReplaceKind : uint8_t { FullReplace , Structural } |
| Outcome of setDocumentMaybeStructural. FullReplace means the new doc differs structurally from the current one (or there was no current one) and every consumer of the old entity space must treat their state as invalid - identical to setDocument's contract. Structural means the new doc has the same XML shape and element ids, and the entity remap carried via the next RenderRequest lets downstream consumers (the compositor) preserve their caches. | |
Public Member Functions | |
| AsyncSVGDocument (const AsyncSVGDocument &)=delete | |
| AsyncSVGDocument & | operator= (const AsyncSVGDocument &)=delete |
| AsyncSVGDocument (AsyncSVGDocument &&)=delete | |
| AsyncSVGDocument & | operator= (AsyncSVGDocument &&)=delete |
| void | setDocument (svg::SVGDocument document) |
| Replace the inner document. Clears any pending commands - they would reference now-invalid entities. Bumps the frame version. | |
| bool | usesEditorParserForSourceRecovery () const |
| Whether this document was loaded or fully reparsed with the editor's established policy. | |
| std::optional< EditorCommand::SourceRecoveryState > | sourceRecoveryState () const |
| Current source-pane recovery identity, available only while the original policy is intact. | |
| ReplaceKind | setDocumentMaybeStructural (svg::SVGDocument newDocument) |
| Like setDocument, but builds a structural entity remap against the current document first. If the remap is non-empty (trees match by tag name + id at every step), the replacement is tagged as Structural - the next RenderRequest carries the remap so the compositor can call remapAfterStructuralReplace instead of resetAllLayers(documentReplaced=true), preserving cached layer bitmaps and segments across the swap. The current editor canvas size is also carried forward for structural replacements so the next render does not dirty every cache with a redundant resize. If the trees differ (user edited the source pane to change shape, etc.) the remap is empty and we fall back to the standard setDocument path. | |
| std::unordered_map< Entity, Entity > | consumePendingStructuralRemap () |
| Take ownership of the pending structural remap produced by the most recent setDocumentMaybeStructural(Structural) call. Returns an empty map if the last replacement was a FullReplace or nothing has been replaced since the last consumption. Called by RenderCoordinator when assembling the next RenderRequest. | |
| bool | hasDocument () const |
| Whether a document has been loaded yet. | |
| svg::SVGDocument & | document () |
| Direct access to the inner document. UI thread only. The render thread should hold the snapshot returned by acquireRenderSnapshot() instead. | |
| const svg::SVGDocument & | document () const |
| Return the current parsed SVG document under the caller's document-access policy. | |
| void | applyMutation (EditorCommand command) |
| Push a command onto the per-frame queue. UI thread only. | |
| xml::ApplySourceEditResult | applySourceEdit (const xml::XMLEditIntent &intent) |
| Apply an incremental source edit directly to the current document. | |
| CommandQueue & | queue () |
| Direct access to the command queue. Tools push via applyMutation; this accessor exists for tests and for the main loop's flushFrame. | |
| bool | hasPendingMutations () const |
| Whether any document mutations are queued for the next frame flush. | |
| bool | flushFrame () |
| Drain and apply any pending commands. Called once per frame at the start of the main loop. Returns true if any commands were applied (so the caller can decide whether to re-render). | |
| const FlushResult & | lastFlushResult () const |
| Metadata from the most recent flushFrame() call. | |
| bool | hasUnmirroredSourceDeltas () const |
| True while flushed source deltas wait for the source pane to mirror them. | |
| std::optional< std::vector< xml::XMLSourceDelta > > | takeUnmirroredSourceDeltas () |
| Take the source deltas flushed since the previous call, in application order. | |
| std::uint64_t | nonTransformRevision () const |
| Advances on every change that can alter a clip guide without changing the selected element's transform. Unlike lastFlushResult(), survives no-op frame flushes. | |
| std::uint64_t | documentGeneration () const |
| Generation counter bumped only when setDocument replaces the inner SVGDocument (e.g. ReplaceDocumentCommand on source-pane edits). The inner document's storage address is stable across a replacement (the optional lives inside this object), so pointer-identity is NOT a reliable "is this the same document" check - consumers that cache per-document state (like the compositor's activeHints_ backing an entity space) need this counter to know when to tear that state down. | |
| std::uint64_t | currentFrameVersion () const |
| Monotonic frame version counter. Bumped on every state change visible to the renderer (mutation flush or document replacement). The render thread polls this to detect updates without locking the document. | |
| bool | refreshFontResources () |
| Adopt font resolutions while the renderer and preview worker are idle. The frame version advances, but document generation, authored source, and undo history do not change. | |
| bool | setAnimationTime (double seconds) |
| Advance playback without changing source, document identity, or the command queue. | |
| bool | restartAnimations () |
| Start a fresh animation session without source edits; retry when document access is busy. | |
| bool | dispatchMouseEvent (svg::EventType type, const svg::SVGElement &target) |
| Deliver a preview mouse event without an authored edit. Busy access is retried by the caller. | |
| std::uint64_t | fontResourceRevision () const |
| Frame-boundary font adoption identity, distinct from the manager's individual face counter. | |
| bool | loadFromString (std::string_view svgBytes) |
Test hook: re-parse svgBytes into a fresh document via SVGParser. On failure the existing document is left intact and lastParseError() returns the parser's diagnostic, so the caller can surface a line and reason in a text editor. | |
| const std::optional< ParseDiagnostic > & | lastParseError () const |
| The diagnostic from the most recent failed loadFromString / ReplaceDocumentCommand. Cleared on every successful parse - so has_value() is the live "is the source pane currently invalid?" signal. | |
| const std::vector< ParseDiagnostic > & | parseDiagnostics () const |
| All warnings and the fatal error, if any, published by the most recent parse operation. | |
| std::uint64_t | parseDiagnosticsRevision () const |
| Monotonic revision for parseDiagnostics. | |
Static Public Attributes | |
| static constexpr std::size_t | kMaxUnmirroredSourceDeltas = 4096 |
| Most flushed source deltas kept for takeUnmirroredSourceDeltas() before they are dropped. | |
Wraps an svg::SVGDocument and the editor's per-frame command queue.
Lifetime: owned by EditorApp (one per editor session). All public methods must be called from the UI thread; the only thread-safe operation is currentFrameVersion() which the render thread can poll.
| struct donner::editor::AsyncSVGDocument::FlushResult |
Outcome of applying queued document mutations.
| Class Members | ||
|---|---|---|
| bool | appliedCommands = false | |
| vector< Entity > | cacheInvalidatedElements | Entities whose existing rendered pixels may have changed because of an incremental command. |
| bool | hadPriorAppliedCommandsOnRollback = false | A prior command in this flush still changed source before that rollback. |
| bool | onlyTransformCommands = false | True only when every applied command changed an element transform. An overlay guide captured before a drag may be safely projected through these version advances without rereading clip resources; any other edit invalidates that immutable guide cache. |
| bool | preserveUndoOnReparse = false | |
| bool | removedElements = false | True when at least one flushed command removed an element from the live document. |
| bool | replacedDocument = false | |
| bool | rolledBackReplacementBatch = false | A failed replacement batch restored its exact pre-command source. |
| vector< XMLSourceDelta > | sourceDeltas | |
| xml::ApplySourceEditResult donner::editor::AsyncSVGDocument::applySourceEdit | ( | const xml::XMLEditIntent & | intent | ) |
Apply an incremental source edit directly to the current document.
Unlike EditorCommand::Kind::ReplaceDocument, this preserves document identity and routes the edit through donner/base/xml so the XML source store owns both source bytes and emitted SVG mutations.
| intent | Source edit request. |
| bool donner::editor::AsyncSVGDocument::dispatchMouseEvent | ( | svg::EventType | type, |
| const svg::SVGElement & | target ) |
Deliver a preview mouse event without an authored edit. Busy access is retried by the caller.
| type | Mouse event to deliver. |
| target | Hit element in the current document. |
|
inlinenodiscard |
Generation counter bumped only when setDocument replaces the inner SVGDocument (e.g. ReplaceDocumentCommand on source-pane edits). The inner document's storage address is stable across a replacement (the optional lives inside this object), so pointer-identity is NOT a reliable "is this the same document" check - consumers that cache per-document state (like the compositor's activeHints_ backing an entity space) need this counter to know when to tear that state down.
Distinct from frameVersion_: the frame counter bumps on every mutation (including every drag frame's SetTransformCommand), which would be catastrophic to treat as a document replacement.
| bool donner::editor::AsyncSVGDocument::flushFrame | ( | ) |
Drain and apply any pending commands. Called once per frame at the start of the main loop. Returns true if any commands were applied (so the caller can decide whether to re-render).
Bumps the frame version when commands are applied OR when the document itself was replaced via setDocument since the last flush.
|
nodiscard |
Test hook: re-parse svgBytes into a fresh document via SVGParser. On failure the existing document is left intact and lastParseError() returns the parser's diagnostic, so the caller can surface a line and reason in a text editor.
| svgBytes | SVG source bytes to parse. |
| bool donner::editor::AsyncSVGDocument::setAnimationTime | ( | double | seconds | ) |
Advance playback without changing source, document identity, or the command queue.
Returns false when document access is busy or the time is unchanged.
| seconds | Finite document time in seconds. |
|
inlinenodiscard |
Take the source deltas flushed since the previous call, in application order.
Every flushFrame() resets lastFlushResult(), including a no-op flush, so a tool that flushes its own edit and is followed by any other flush would otherwise lose the deltas before the source pane mirrors them. These accumulate until taken. Replacing the document discards them, because they describe source that no longer exists.
Hosts without a source pane never take them, so at most kMaxUnmirroredSourceDeltas are kept. Past that the list is dropped and this returns std::nullopt once: the caller must mirror the whole source instead.