mirror of
https://github.com/penpot/penpot.git
synced 2025-07-12 10:17:28 +02:00
🐛 Fix focus mode for wasm render
This commit is contained in:
parent
01311225c7
commit
1c7aea4b84
5 changed files with 145 additions and 5 deletions
|
@ -320,6 +320,12 @@
|
|||
(wasm.api/initialize base-objects zoom vbox background)
|
||||
(reset! initialized? true)))
|
||||
|
||||
(mf/with-effect [focus]
|
||||
(when (and @canvas-init? @initialized?)
|
||||
(if (empty? focus)
|
||||
(wasm.api/clear-focus-mode)
|
||||
(wasm.api/set-focus-mode focus))))
|
||||
|
||||
(mf/with-effect [vbox zoom]
|
||||
(when (and @canvas-init? initialized?)
|
||||
(wasm.api/set-view-box zoom vbox)))
|
||||
|
|
|
@ -248,7 +248,7 @@
|
|||
heap (mem/get-heap-u8)
|
||||
dview (js/DataView. (.-buffer heap))]
|
||||
|
||||
;; write fill data to heap
|
||||
;; write fill data to heap
|
||||
(loop [fills (seq fills)
|
||||
current-offset offset]
|
||||
(when-not (empty? fills)
|
||||
|
@ -256,10 +256,10 @@
|
|||
new-offset (sr-fills/write-fill! current-offset dview fill)]
|
||||
(recur (rest fills) new-offset))))
|
||||
|
||||
;; send fills to wasm
|
||||
;; send fills to wasm
|
||||
(h/call wasm/internal-module "_set_shape_fills")
|
||||
|
||||
;; load images for image fills if not cached
|
||||
;; load images for image fills if not cached
|
||||
(keep (fn [fill]
|
||||
(let [image (:fill-image fill)
|
||||
id (dm/get-prop image :id)
|
||||
|
@ -802,6 +802,28 @@
|
|||
(request-render "set-objects")
|
||||
(process-pending pending)))
|
||||
|
||||
(defn clear-focus-mode
|
||||
[]
|
||||
(h/call wasm/internal-module "_clear_focus_mode")
|
||||
(clear-drawing-cache)
|
||||
(request-render "clear-focus-mode"))
|
||||
|
||||
(defn set-focus-mode
|
||||
[entries]
|
||||
(let [offset (mem/alloc-bytes-32 (* (count entries) 16))
|
||||
heapu32 (mem/get-heap-u32)]
|
||||
|
||||
(loop [entries (seq entries)
|
||||
current-offset offset]
|
||||
(when-not (empty? entries)
|
||||
(let [id (first entries)]
|
||||
(sr/heapu32-set-uuid id heapu32 current-offset)
|
||||
(recur (rest entries) (+ current-offset (mem/ptr8->ptr32 16))))))
|
||||
|
||||
(h/call wasm/internal-module "_set_focus_mode")
|
||||
(clear-drawing-cache)
|
||||
(request-render "set-focus-mode")))
|
||||
|
||||
(defn set-structure-modifiers
|
||||
[entries]
|
||||
(when-not (empty? entries)
|
||||
|
|
|
@ -161,6 +161,27 @@ pub extern "C" fn set_view(zoom: f32, x: f32, y: f32) {
|
|||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn clear_focus_mode() {
|
||||
with_state!(state, {
|
||||
state.clear_focus_mode();
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_focus_mode() {
|
||||
let bytes = mem::bytes();
|
||||
|
||||
let entries: Vec<Uuid> = bytes
|
||||
.chunks(size_of::<<Uuid as SerializableResult>::BytesType>())
|
||||
.map(|data| Uuid::from_bytes(data.try_into().unwrap()))
|
||||
.collect();
|
||||
|
||||
with_state!(state, {
|
||||
state.set_focus_mode(entries);
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn init_shapes_pool(capacity: usize) {
|
||||
with_state!(state, {
|
||||
|
|
|
@ -81,6 +81,73 @@ impl NodeRenderState {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents the "focus mode" state used during rendering.
|
||||
///
|
||||
/// Focus mode allows selectively highlighting or isolating specific shapes (UUIDs)
|
||||
/// during the render pass. It maintains a list of shapes to focus and tracks
|
||||
/// whether the current rendering context is inside a focused element.
|
||||
///
|
||||
/// # Focus Propagation
|
||||
/// If a shape is in focus, all its nested content
|
||||
/// is also considered to be in focus for the duration of the render traversal. Focus
|
||||
/// state propagates *downward* through the tree while rendering.
|
||||
///
|
||||
/// # Usage
|
||||
/// - `set_shapes(...)` to activate focus mode for specific elements and their anidated content.
|
||||
/// - `clear()` to disable focus mode.
|
||||
/// - `reset()` should be called at the beginning of the render loop.
|
||||
/// - `enter(...)` / `exit(...)` should be called when entering and leaving shape
|
||||
/// render contexts.
|
||||
/// - `is_active()` returns whether the current shape is being rendered in focus.
|
||||
pub struct FocusMode {
|
||||
shapes: Vec<Uuid>,
|
||||
active: bool,
|
||||
}
|
||||
|
||||
impl FocusMode {
|
||||
pub fn new() -> Self {
|
||||
FocusMode {
|
||||
shapes: Vec::new(),
|
||||
active: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.shapes.clear();
|
||||
self.active = false;
|
||||
}
|
||||
|
||||
pub fn set_shapes(&mut self, shapes: Vec<Uuid>) {
|
||||
self.shapes = shapes;
|
||||
}
|
||||
|
||||
/// Returns `true` if the given shape ID should be focused.
|
||||
/// If the `shapes` list is empty, focus applies to all shapes.
|
||||
pub fn should_focus(&self, id: &Uuid) -> bool {
|
||||
self.shapes.is_empty() || self.shapes.contains(id)
|
||||
}
|
||||
|
||||
pub fn enter(&mut self, id: &Uuid) {
|
||||
if !self.active && self.should_focus(id) {
|
||||
self.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exit(&mut self, id: &Uuid) {
|
||||
if self.active && self.should_focus(id) {
|
||||
self.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.active
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RenderState {
|
||||
gpu_state: GpuState,
|
||||
pub options: RenderOptions,
|
||||
|
@ -109,6 +176,7 @@ pub(crate) struct RenderState {
|
|||
// migration to remove group-level fills is completed, this code should be removed.
|
||||
pub nested_fills: Vec<Vec<Fill>>,
|
||||
pub show_grid: Option<Uuid>,
|
||||
pub focus_mode: FocusMode,
|
||||
}
|
||||
|
||||
pub fn get_cache_size(viewbox: Viewbox, scale: f32) -> skia::ISize {
|
||||
|
@ -176,6 +244,7 @@ impl RenderState {
|
|||
pending_tiles: PendingTiles::new_empty(),
|
||||
nested_fills: vec![],
|
||||
show_grid: None,
|
||||
focus_mode: FocusMode::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,6 +380,14 @@ impl RenderState {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn clear_focus_mode(&mut self) {
|
||||
self.focus_mode.clear();
|
||||
}
|
||||
|
||||
pub fn set_focus_mode(&mut self, shapes: Vec<Uuid>) {
|
||||
self.focus_mode.set_shapes(shapes);
|
||||
}
|
||||
|
||||
pub fn render_shape(
|
||||
&mut self,
|
||||
shape: &Shape,
|
||||
|
@ -569,6 +646,8 @@ impl RenderState {
|
|||
let scale = self.get_scale();
|
||||
self.tile_viewbox.update(self.viewbox, scale);
|
||||
|
||||
self.focus_mode.reset();
|
||||
|
||||
performance::begin_measure!("render");
|
||||
performance::begin_measure!("start_render_loop");
|
||||
|
||||
|
@ -683,6 +762,8 @@ impl RenderState {
|
|||
self.surfaces
|
||||
.canvas(SurfaceId::Current)
|
||||
.save_layer(&layer_rec);
|
||||
|
||||
self.focus_mode.enter(&element.id);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -728,6 +809,8 @@ impl RenderState {
|
|||
self.nested_fills.pop();
|
||||
}
|
||||
self.surfaces.canvas(SurfaceId::Current).restore();
|
||||
|
||||
self.focus_mode.exit(&element.id);
|
||||
}
|
||||
|
||||
pub fn get_current_tile_bounds(&mut self) -> Rect {
|
||||
|
@ -823,7 +906,7 @@ impl RenderState {
|
|||
}
|
||||
|
||||
self.render_shape_enter(element, mask);
|
||||
if !node_render_state.is_root() {
|
||||
if !node_render_state.is_root() && self.focus_mode.is_active() {
|
||||
self.render_shape(
|
||||
element,
|
||||
modifiers.get(&element.id),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::iter;
|
||||
use std::{iter, vec};
|
||||
|
||||
use skia_safe as skia;
|
||||
|
||||
|
@ -135,6 +135,14 @@ impl<'a> State<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clear_focus_mode(&mut self) {
|
||||
self.render_state.clear_focus_mode();
|
||||
}
|
||||
|
||||
pub fn set_focus_mode(&mut self, shapes: Vec<Uuid>) {
|
||||
self.render_state.set_focus_mode(shapes);
|
||||
}
|
||||
|
||||
pub fn init_shapes_pool(&mut self, capacity: usize) {
|
||||
self.shapes_pool.initialize(capacity);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue