mirror of
https://github.com/penpot/penpot.git
synced 2025-07-12 17:57:17 +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
|
@ -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