|
|
Donner SVG Editor & Engine
SVG-native editor and embeddable SVG2 + CSS3 engine in C++20, with GPU (WebGPU) and compact CPU renderers, built for correctness, security, and performance.
|
ImGui Layers panel backed by a LayerTreeModel snapshot. More...
#include "donner/editor/LayersPanel.h"
Classes | |
| struct | ThumbnailTexture |
| Maps a per-row rendered thumbnail bitmap to an ImGui texture handle for display. The editor shell supplies an implementation backed by GlTextureCache (the same Donner-bitmap -> GL/WGPU texture path the render pane uses); when null (e.g. in headless unit tests) the panel falls back to the deterministic swatch. Donner renders the thumbnail pixels; ImGui only blits the resulting texture - see CLAUDE.md "No Rendering Vector Graphics
With ImGui". More... | |
| struct | IconTexture |
| Maps a static layer-affordance icon bitmap to an ImGui texture handle for display. The icon bitmaps are rendered from embedded Bootstrap SVG resources through Donner; ImGui only receives the final raster texture for the image button. More... | |
| struct | ThumbnailRefreshStats |
| Diagnostics for the most recent thumbnail refresh. More... | |
| struct | ClickModifiers |
| Modifier state for a row click. Mirrors the ImGui click path so tests can drive selection without a live ImGui frame. More... | |
Public Types | |
| enum class | ThumbnailRefreshMode { Render , SwatchesOnly } |
| using | ThumbnailTextureProvider |
| using | IconTextureProvider |
Public Member Functions | |
| void | refreshSnapshot (const EditorApp &app, svg::RendererInterface *renderer=nullptr, ThumbnailRefreshMode mode=ThumbnailRefreshMode::Render) |
| Rebuild the row snapshot, per-row preview swatches, and per-row rendered thumbnail bitmaps from live editor state. Safe to call only when the async renderer is idle (delegates to LayerTreeModel::refresh and renders each row's element subtree through svg::Renderer::renderElementToBitmap, which takes document write access). | |
| void | render (EditorApp *liveApp, const ThumbnailTextureProvider &textureProvider={}, const IconTextureProvider &iconTextureProvider={}, float minimumInteractionHeight=0.0f) |
Render the panel into the current ImGui window. Must be called inside an ImGui::Begin(...) / End() pair. When liveApp is null (the worker owns the document) mutating clicks are dropped. | |
| void | setLockedRejectionFlash (std::optional< LayersLockedRejectionFlash > flash) |
| Set the current locked-rejection flash (or clear it with std::nullopt). When set with a positive intensity and the flashed element matches a visible row, render paints that row's background red with alpha scaled by intensity, in sync with the canvas outline flash. EditorShell calls this each frame with SelectTool::lockedRejectionFlash() mapped into the input struct, before render. | |
| std::optional< std::size_t > | flashedRowIndex () const |
| The visible row index that the active locked-rejection flash maps to (an exact element match against a visible row), or std::nullopt when no flash is active, its intensity is non-positive, or its element is not a visible row. The non-ImGui seam the row-flash test asserts on. | |
| float | lockedRejectionFlashIntensity () const |
| The fade intensity of the active locked-rejection flash, or 0 when none is set. Testing/diagnostics accessor paired with flashedRowIndex. | |
| const std::vector< LayerTreeRow > & | rows () const |
| The current flat row list (testing/diagnostics accessor). | |
| const ThumbnailRefreshStats & | thumbnailRefreshStats () const |
| Diagnostics for the most recent thumbnail refresh. | |
| std::size_t | visibleRowCount () const |
| Number of currently-visible rows (rows in the flat list). | |
| void | handleRowClick (EditorApp &app, std::size_t rowIndex, ClickModifiers mods) |
Apply the selection logic for a click on the visible row at rowIndex. This is the exact logic the ImGui click path uses, factored out so it is unit-testable without an ImGui context. | |
| void | handleEyeClick (EditorApp &app, std::size_t rowIndex) |
Toggle the visibility of the row at rowIndex via the shared EditorApp::setElementVisible path (the same path the context-menu Hide/Show items use). Factored out so the eye-button affordance is unit-testable without an ImGui frame. No-op for an out-of-range index. | |
| void | handleLockClick (EditorApp &app, std::size_t rowIndex) |
Toggle the lock state of the row at rowIndex via the shared EditorApp::setElementLocked path (the same path the context-menu Lock/Unlock items use). Factored out so the lock-button affordance is unit-testable without an ImGui frame. No-op for an out-of-range index. | |
| bool | handleRowRename (EditorApp &app, std::size_t rowIndex, std::string_view newId) |
Rename the element at rowIndex to newId via the shared EditorApp::renameSelectedElement path - a DOM-level id change that also repoints url(#...) / href references and <style> selectors. The row's element is selected first (renaming the thing you double-clicked), then the engine runs. Factored out so the inline-edit affordance is unit-testable without an ImGui frame. | |
| bool | handleRowReorder (EditorApp &app, std::size_t fromIndex, std::size_t toIndex) |
Move the element at fromIndex so it sits at the toIndex row's position, among the same parent's children, via the shared EditorApp::reorderElementBeforeSibling DOM move. Cross-parent drops and locked elements are rejected. Factored out so drag-to-reorder is unit-testable without an ImGui frame. | |
| bool | handleRowZOrder (EditorApp &app, std::size_t rowIndex, EditorApp::ZOrder direction) |
Reorder the element at rowIndex in paint order (z-order) via the shared EditorApp::reorderSelectedElement engine - the same path as the canvas Arrange menu and the Cmd+[ / Cmd+] shortcuts. The row's element is selected first, then the move runs. Factored out so the Layers context-menu Arrange items are unit-testable without an ImGui frame. | |
| void | beginRename (std::uint64_t stableId) |
Begin an inline rename of the row identified by stableId (the render loop draws an edit field in place of the row label). No-op if the row is not present. Exposed so the context-menu "Rename" item and tests can start an edit; the double-click path calls it internally. | |
| std::optional< std::uint64_t > | renamingStableId () const |
| The stable id of the row currently being inline-renamed, or std::nullopt. | |
| bool | hasThumbnailOrSwatch (std::uint64_t stableId) const |
Whether the row identified by stableId has a non-null thumbnail handle or a deterministic fallback swatch. Always true for every visible row. | |
| std::optional< css::RGBA > | rowFallbackSwatch (std::uint64_t stableId) const |
The fallback preview swatch color for the row identified by stableId, or std::nullopt if the row is not present. | |
| const svg::RendererBitmap * | rowThumbnail (std::uint64_t stableId) const |
The Donner-rendered preview thumbnail bitmap for the row identified by stableId, or nullptr when the row has no real raster preview (it then uses the fallback swatch). This is the non-ImGui data seam: tests assert on the rendered pixels here without needing a GL context or an ImGui frame. | |
| bool | consumeSelectionChanged () |
| Whether the most recent render / handleRowClick changed the editor selection. Consumes the flag. Used by EditorShell to fire the existing canvas/source selection-sync plumbing when a Layers row drives selection. | |
| bool | consumeQueuedMutation () |
| Whether the most recent render queued a DOM mutation (show/hide, lock, rename, reorder, z-order) that is not reflected by a selection change. Consumes the flag. EditorShell flushes the queued mutation and re-renders when this is set; without it a show/hide toggle would leave the canvas showing the pre-mutation frame (the "hidden layer ghost" report). | |
| void | noteRowHovered (std::optional< std::size_t > rowIndex) |
| Record which visible row (if any) is under the mouse cursor. Called from render with the hovered row index, and directly by tests. Drives the canvas hover-highlight chrome the same way the source pane does. Passing std::nullopt (or an out-of-range index) clears the hover. | |
| std::optional< svg::SVGElement > | hoveredElement () const |
| The element under the mouse cursor as of the most recent render / noteRowHovered, or std::nullopt when no row is hovered. | |
| const LayerTreeModel & | model () const |
| Access the underlying model (testing/diagnostics). | |
ImGui Layers panel backed by a LayerTreeModel snapshot.
| struct donner::editor::LayersPanel::ThumbnailTexture |
Maps a per-row rendered thumbnail bitmap to an ImGui texture handle for display. The editor shell supplies an implementation backed by GlTextureCache (the same Donner-bitmap -> GL/WGPU texture path the render pane uses); when null (e.g. in headless unit tests) the panel falls back to the deterministic swatch. Donner renders the thumbnail pixels; ImGui only blits the resulting texture - see CLAUDE.md "No Rendering Vector Graphics With ImGui".
| stableId | Stable id of the row whose thumbnail is being uploaded. |
| bitmap | The Donner-rendered RGBA thumbnail bitmap. |
| Class Members | ||
|---|---|---|
| ImTextureID | texture = 0 | ImGui texture handle. |
| Vector2d | uvBottomRight = Vector2d(1.0, 1.0) | Bottom-right valid payload UV. |
| struct donner::editor::LayersPanel::IconTexture |
Maps a static layer-affordance icon bitmap to an ImGui texture handle for display. The icon bitmaps are rendered from embedded Bootstrap SVG resources through Donner; ImGui only receives the final raster texture for the image button.
| stableId | Stable id of the static icon resource being uploaded. |
| bitmap | The Donner-rendered RGBA icon bitmap. |
| Class Members | ||
|---|---|---|
| ImTextureID | texture = 0 | ImGui texture handle. |
| Vector2d | uvBottomRight = Vector2d(1.0, 1.0) | Bottom-right valid payload UV. |
| struct donner::editor::LayersPanel::ThumbnailRefreshStats |
Diagnostics for the most recent thumbnail refresh.
| struct donner::editor::LayersPanel::ClickModifiers |
| using donner::editor::LayersPanel::IconTextureProvider |
| using donner::editor::LayersPanel::ThumbnailTextureProvider |
| void donner::editor::LayersPanel::handleEyeClick | ( | EditorApp & | app, |
| std::size_t | rowIndex ) |
Toggle the visibility of the row at rowIndex via the shared EditorApp::setElementVisible path (the same path the context-menu Hide/Show items use). Factored out so the eye-button affordance is unit-testable without an ImGui frame. No-op for an out-of-range index.
| app | Live editor app the mutation is applied to. |
| rowIndex | Index into rows(). |
| void donner::editor::LayersPanel::handleLockClick | ( | EditorApp & | app, |
| std::size_t | rowIndex ) |
Toggle the lock state of the row at rowIndex via the shared EditorApp::setElementLocked path (the same path the context-menu Lock/Unlock items use). Factored out so the lock-button affordance is unit-testable without an ImGui frame. No-op for an out-of-range index.
| app | Live editor app the mutation is applied to. |
| rowIndex | Index into rows(). |
| void donner::editor::LayersPanel::handleRowClick | ( | EditorApp & | app, |
| std::size_t | rowIndex, | ||
| ClickModifiers | mods ) |
Apply the selection logic for a click on the visible row at rowIndex. This is the exact logic the ImGui click path uses, factored out so it is unit-testable without an ImGui context.
| app | Live editor app the selection request is applied to. |
| rowIndex | Index into rows(). |
| mods | Modifier-key state for the click. |
| bool donner::editor::LayersPanel::handleRowRename | ( | EditorApp & | app, |
| std::size_t | rowIndex, | ||
| std::string_view | newId ) |
Rename the element at rowIndex to newId via the shared EditorApp::renameSelectedElement path - a DOM-level id change that also repoints url(#...) / href references and <style> selectors. The row's element is selected first (renaming the thing you double-clicked), then the engine runs. Factored out so the inline-edit affordance is unit-testable without an ImGui frame.
| app | Live editor app the mutation is applied to. |
| rowIndex | Index into rows(). |
| newId | The requested new element id. |
| bool donner::editor::LayersPanel::handleRowReorder | ( | EditorApp & | app, |
| std::size_t | fromIndex, | ||
| std::size_t | toIndex ) |
Move the element at fromIndex so it sits at the toIndex row's position, among the same parent's children, via the shared EditorApp::reorderElementBeforeSibling DOM move. Cross-parent drops and locked elements are rejected. Factored out so drag-to-reorder is unit-testable without an ImGui frame.
| app | Live editor app the mutation is applied to. |
| fromIndex | Index of the dragged row. |
| toIndex | Index of the drop-target row. |
| bool donner::editor::LayersPanel::handleRowZOrder | ( | EditorApp & | app, |
| std::size_t | rowIndex, | ||
| EditorApp::ZOrder | direction ) |
Reorder the element at rowIndex in paint order (z-order) via the shared EditorApp::reorderSelectedElement engine - the same path as the canvas Arrange menu and the Cmd+[ / Cmd+] shortcuts. The row's element is selected first, then the move runs. Factored out so the Layers context-menu Arrange items are unit-testable without an ImGui frame.
| app | Live editor app the mutation is applied to. |
| rowIndex | Index into rows(). |
| direction | Which way to move the element in paint order. |
| void donner::editor::LayersPanel::refreshSnapshot | ( | const EditorApp & | app, |
| svg::RendererInterface * | renderer = nullptr, | ||
| ThumbnailRefreshMode | mode = ThumbnailRefreshMode::Render ) |
Rebuild the row snapshot, per-row preview swatches, and per-row rendered thumbnail bitmaps from live editor state. Safe to call only when the async renderer is idle (delegates to LayerTreeModel::refresh and renders each row's element subtree through svg::Renderer::renderElementToBitmap, which takes document write access).
| app | Live editor app to snapshot. |
| renderer | Optional renderer to use for thumbnail rasterization. |
| mode | Whether to render thumbnails or refresh only the row swatches. |
| void donner::editor::LayersPanel::render | ( | EditorApp * | liveApp, |
| const ThumbnailTextureProvider & | textureProvider = {}, | ||
| const IconTextureProvider & | iconTextureProvider = {}, | ||
| float | minimumInteractionHeight = 0.0f ) |
Render the panel into the current ImGui window. Must be called inside an ImGui::Begin(...) / End() pair. When liveApp is null (the worker owns the document) mutating clicks are dropped.
| liveApp | Live editor app for selection/mutation, or null. |
| textureProvider | Uploads a row's rendered thumbnail bitmap to an ImGui texture for display, or null to fall back to the swatch. |
| iconTextureProvider | Uploads the static Donner-rendered layer icon bitmaps to ImGui textures for display, or null to keep a blank hit area in headless tests. |
| minimumInteractionHeight | Minimum row and icon-button target height. Compact touch sheets pass 44 logical pixels; desktop keeps the dense default by passing zero. |
|
nodiscard |
The Donner-rendered preview thumbnail bitmap for the row identified by stableId, or nullptr when the row has no real raster preview (it then uses the fallback swatch). This is the non-ImGui data seam: tests assert on the rendered pixels here without needing a GL context or an ImGui frame.
| stableId | Stable id of the row. |