diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index d526bfb8b4..c088b3aa55 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -36,7 +36,9 @@ [app.main.data.workspace.undo :as dwu] [app.main.features :as features] [app.main.snap :as snap] + [app.main.store :as st] [app.main.streams :as ms] + [app.render-wasm.api :as wasm.api] [app.util.array :as array] [app.util.dom :as dom] [app.util.keyboard :as kbd] @@ -602,6 +604,15 @@ (rx/take 1) (rx/map #(start-move from-position)))))) +(defn get-drop-cell + [target-frame objects position] + (if (features/active-feature? @st/state "render-wasm/v1") + (do + (wasm.api/use-shape target-frame) + (let [cell (wasm.api/get-grid-coords position)] + (when (not= cell [-1 -1]) cell))) + (gslg/get-drop-cell target-frame objects position))) + (defn set-ghost-displacement [move-vector] (ptk/reify ::set-ghost-displacement @@ -621,7 +632,8 @@ ptk/WatchEvent (watch [_ state stream] - (let [page-id (:current-page-id state) + (let [prev-cell-data (volatile! nil) + page-id (:current-page-id state) objects (dsh/lookup-page-objects state page-id) selected (dsh/lookup-selected state {:omit-blocked? true}) ids (if (nil? ids) selected ids) @@ -685,7 +697,7 @@ flex-layout? (ctl/flex-layout? objects target-frame) grid-layout? (ctl/grid-layout? objects target-frame) drop-index (when flex-layout? (gslf/get-drop-index target-frame objects position)) - cell-data (when (and grid-layout? (not mod?)) (gslg/get-drop-cell target-frame objects position))] + cell-data (when (and grid-layout? (not mod?)) (get-drop-cell target-frame objects position))] (array move-vector target-frame drop-index cell-data)))) (rx/take-until stopper)) @@ -693,9 +705,15 @@ modifiers-stream (->> move-stream (rx/with-latest-from array/conj ms/mouse-position-shift) + (rx/tap + (fn [[_ _ _ cell-data _]] + (when (some? cell-data) + (vreset! prev-cell-data cell-data)))) + (rx/map (fn [[move-vector target-frame drop-index cell-data shift?]] - (let [x-disp? (> (mth/abs (:x move-vector)) (mth/abs (:y move-vector))) + (let [cell-data (or cell-data @prev-cell-data) + x-disp? (> (mth/abs (:x move-vector)) (mth/abs (:y move-vector))) [move-vector snap-ignore-axis] (cond (and shift? x-disp?) diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index f93a43ede2..b598dd36f7 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -1045,6 +1045,18 @@ (h/call wasm/internal-module "_hide_grid") (request-render "clear-grid")) +(defn get-grid-coords + [position] + (let [offset (h/call wasm/internal-module + "_get_grid_coords" + (get position :x) + (get position :y)) + heapi32 (mem/get-heap-i32) + row (aget heapi32 (mem/ptr8->ptr32 (+ offset 0))) + column (aget heapi32 (mem/ptr8->ptr32 (+ offset 4)))] + (h/call wasm/internal-module "_free_bytes") + [row column])) + (defonce module (delay (if (exists? js/dynamicImport) diff --git a/frontend/src/app/render_wasm/mem.cljs b/frontend/src/app/render_wasm/mem.cljs index 4e5e2bad1b..ddfaf77c23 100644 --- a/frontend/src/app/render_wasm/mem.cljs +++ b/frontend/src/app/render_wasm/mem.cljs @@ -50,6 +50,11 @@ [] (unchecked-get ^js wasm/internal-module "HEAPU32")) +(defn get-heap-i32 + "Returns a Uint32Array view of the heap" + [] + (unchecked-get ^js wasm/internal-module "HEAP32")) + (defn get-heap-f32 "Returns a Float32Array view of the heap" [] diff --git a/render-wasm/_build_env b/render-wasm/_build_env index bfd8142844..ec5e070abb 100644 --- a/render-wasm/_build_env +++ b/render-wasm/_build_env @@ -35,7 +35,7 @@ EMCC_CFLAGS="--no-entry \ -sMAX_WEBGL_VERSION=2 \ -sMODULARIZE=1 \ -sEXPORT_NAME=createRustSkiaModule \ - -sEXPORTED_RUNTIME_METHODS=GL,stringToUTF8,HEAPU8,HEAPU32,HEAPF32 \ + -sEXPORTED_RUNTIME_METHODS=GL,stringToUTF8,HEAPU8,HEAP32,HEAPU32,HEAPF32 \ -sEXPORT_ES6=1" export EM_CACHE="/tmp/emsdk_cache"; diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 5cba381bb5..d224d1acfb 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -748,6 +748,19 @@ pub extern "C" fn hide_grid() { }); } +#[no_mangle] +pub extern "C" fn get_grid_coords(pos_x: f32, pos_y: f32) -> *mut u8 { + let row: i32; + let col: i32; + with_state!(state, { + (row, col) = state.get_grid_coords(pos_x, pos_y); + }); + let mut bytes = vec![0; 8]; + bytes[0..4].clone_from_slice(&row.to_le_bytes()); + bytes[4..8].clone_from_slice(&col.to_le_bytes()); + mem::write_bytes(bytes) +} + fn main() { #[cfg(target_arch = "wasm32")] init_gl!(); diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 9e7534fb20..d730243780 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -21,7 +21,7 @@ use options::RenderOptions; use surfaces::{SurfaceId, Surfaces}; use crate::performance; -use crate::shapes::{modified_children_ids, Corners, Fill, Shape, StructureEntry, Type}; +use crate::shapes::{Corners, Fill, Shape, StructureEntry, Type}; use crate::tiles::{self, PendingTiles, TileRect}; use crate::uuid::Uuid; use crate::view::Viewbox; @@ -929,7 +929,7 @@ impl RenderState { node_render_state.get_children_clip_bounds(element, modifiers.get(&element.id)); let mut children_ids = - modified_children_ids(element, structure.get(&element.id), false); + element.modified_children_ids(structure.get(&element.id), false); // Z-index ordering on Layouts if element.has_layout() { @@ -1019,7 +1019,7 @@ impl RenderState { let Some(root) = tree.get(&Uuid::nil()) else { return Err(String::from("Root shape not found")); }; - let root_ids = modified_children_ids(root, structure.get(&root.id), false); + let root_ids = root.modified_children_ids(structure.get(&root.id), false); // If we finish processing every node rendering is complete // let's check if there are more pending nodes @@ -1118,7 +1118,7 @@ impl RenderState { self.update_tile_for(&shape); } else { // We only need to rebuild tiles from the first level. - let children = modified_children_ids(&shape, structure.get(&shape.id), false); + let children = shape.modified_children_ids(structure.get(&shape.id), false); for child_id in children.iter() { nodes.push(*child_id); } @@ -1148,7 +1148,7 @@ impl RenderState { self.update_tile_for(&shape); } - let children = modified_children_ids(&shape, structure.get(&shape.id), false); + let children = shape.modified_children_ids(structure.get(&shape.id), false); for child_id in children.iter() { nodes.push(*child_id); } diff --git a/render-wasm/src/render/grid_layout.rs b/render-wasm/src/render/grid_layout.rs index 04c732355e..c1c6fae987 100644 --- a/render-wasm/src/render/grid_layout.rs +++ b/render-wasm/src/render/grid_layout.rs @@ -1,11 +1,9 @@ use skia_safe::{self as skia}; use std::collections::HashMap; -use crate::shapes::modifiers::common::GetBounds; - -use crate::math::{Bounds, Matrix, Rect}; -use crate::shapes::modifiers::grid_layout::{calculate_tracks, create_cell_data}; -use crate::shapes::{modified_children_ids, Frame, Layout, Shape, StructureEntry, Type}; +use crate::math::{Matrix, Rect}; +use crate::shapes::modifiers::grid_layout::grid_cell_data; +use crate::shapes::{Shape, StructureEntry}; use crate::uuid::Uuid; pub fn render_overlay( @@ -16,67 +14,7 @@ pub fn render_overlay( modifiers: &HashMap, structure: &HashMap>, ) { - let Type::Frame(Frame { - layout: Some(Layout::GridLayout(layout_data, grid_data)), - .. - }) = &shape.shape_type - else { - return; - }; - - let bounds = &mut HashMap::::new(); - - let shape = &mut shape.clone(); - if let Some(modifiers) = modifiers.get(&shape.id) { - shape.apply_transform(modifiers); - } - - let layout_bounds = shape.bounds(); - let children = modified_children_ids(shape, structure.get(&shape.id), false); - - for child_id in children.iter() { - let Some(child) = shapes.get(child_id) else { - continue; - }; - - if let Some(modifier) = modifiers.get(child_id) { - let mut b = bounds.find(child); - b.transform_mut(modifier); - bounds.insert(*child_id, b); - } - } - - let column_tracks = calculate_tracks( - true, - shape, - layout_data, - grid_data, - &layout_bounds, - &grid_data.cells, - shapes, - bounds, - ); - - let row_tracks = calculate_tracks( - false, - shape, - layout_data, - grid_data, - &layout_bounds, - &grid_data.cells, - shapes, - bounds, - ); - - let cells = create_cell_data( - &layout_bounds, - &children, - shapes, - &grid_data.cells, - &column_tracks, - &row_tracks, - true, - ); + let cells = grid_cell_data(shape.clone(), shapes, modifiers, structure, true); let mut paint = skia::Paint::default(); paint.set_style(skia::PaintStyle::Stroke); diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index b17224b90e..b7b4c70041 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -967,42 +967,42 @@ impl Shape { pub fn has_inner_strokes(&self) -> bool { self.strokes.iter().any(|s| s.kind == StrokeKind::Inner) } -} -/* - Returns the list of children taking into account the structure modifiers -*/ -pub fn modified_children_ids( - element: &Shape, - structure: Option<&Vec>, - include_hidden: bool, -) -> IndexSet { - if let Some(structure) = structure { - let mut result: Vec = - Vec::from_iter(element.children_ids(include_hidden).iter().copied()); - let mut to_remove = HashSet::<&Uuid>::new(); + /* + Returns the list of children taking into account the structure modifiers + */ + pub fn modified_children_ids( + &self, + structure: Option<&Vec>, + include_hidden: bool, + ) -> IndexSet { + if let Some(structure) = structure { + let mut result: Vec = + Vec::from_iter(self.children_ids(include_hidden).iter().copied()); + let mut to_remove = HashSet::<&Uuid>::new(); - for st in structure { - match st.entry_type { - StructureEntryType::AddChild => { - result.insert(result.len() - st.index as usize, st.id); + for st in structure { + match st.entry_type { + StructureEntryType::AddChild => { + result.insert(result.len() - st.index as usize, st.id); + } + StructureEntryType::RemoveChild => { + to_remove.insert(&st.id); + } + _ => {} } - StructureEntryType::RemoveChild => { - to_remove.insert(&st.id); - } - _ => {} } + + let ret: IndexSet = result + .iter() + .filter(|id| !to_remove.contains(id)) + .copied() + .collect(); + + ret + } else { + self.children_ids(include_hidden) } - - let ret: IndexSet = result - .iter() - .filter(|id| !to_remove.contains(id)) - .copied() - .collect(); - - ret - } else { - element.children_ids(include_hidden) } } diff --git a/render-wasm/src/shapes/modifiers.rs b/render-wasm/src/shapes/modifiers.rs index c010e4bc99..e7de1650fc 100644 --- a/render-wasm/src/shapes/modifiers.rs +++ b/render-wasm/src/shapes/modifiers.rs @@ -8,8 +8,8 @@ use common::GetBounds; use crate::math::{self as math, identitish, Bounds, Matrix, Point}; use crate::shapes::{ - auto_height, modified_children_ids, set_paragraphs_width, ConstraintH, ConstraintV, Frame, - Group, GrowType, Layout, Modifier, Shape, StructureEntry, TransformEntry, Type, + auto_height, set_paragraphs_width, ConstraintH, ConstraintV, Frame, Group, GrowType, Layout, + Modifier, Shape, StructureEntry, TransformEntry, Type, }; use crate::state::State; use crate::uuid::Uuid; @@ -25,7 +25,7 @@ fn propagate_children( structure: &HashMap>, scale_content: &HashMap, ) -> VecDeque { - let children_ids = modified_children_ids(shape, structure.get(&shape.id), true); + let children_ids = shape.modified_children_ids(structure.get(&shape.id), true); if children_ids.is_empty() || identitish(transform) { return VecDeque::new(); @@ -95,7 +95,7 @@ fn calculate_group_bounds( let shape_bounds = bounds.find(shape); let mut result = Vec::::new(); - let children_ids = modified_children_ids(shape, structure.get(&shape.id), true); + let children_ids = shape.modified_children_ids(structure.get(&shape.id), true); for child_id in children_ids.iter() { let Some(child) = shapes.get(child_id) else { continue; @@ -263,7 +263,7 @@ fn propagate_reflow( } } Type::Group(Group { masked: true }) => { - let children_ids = modified_children_ids(shape, state.structure.get(&shape.id), true); + let children_ids = shape.modified_children_ids(state.structure.get(&shape.id), true); if let Some(child) = shapes.get(&children_ids[0]) { let child_bounds = bounds.find(child); bounds.insert(shape.id, child_bounds); diff --git a/render-wasm/src/shapes/modifiers/flex_layout.rs b/render-wasm/src/shapes/modifiers/flex_layout.rs index 7099b31aee..78661d360f 100644 --- a/render-wasm/src/shapes/modifiers/flex_layout.rs +++ b/render-wasm/src/shapes/modifiers/flex_layout.rs @@ -1,8 +1,8 @@ #![allow(dead_code)] use crate::math::{self as math, Bounds, Matrix, Point, Vector, VectorExt}; use crate::shapes::{ - modified_children_ids, AlignContent, AlignItems, AlignSelf, FlexData, JustifyContent, - LayoutData, LayoutItem, Modifier, Shape, StructureEntry, + AlignContent, AlignItems, AlignSelf, FlexData, JustifyContent, LayoutData, LayoutItem, + Modifier, Shape, StructureEntry, }; use crate::uuid::Uuid; @@ -184,7 +184,7 @@ fn initialize_tracks( ) -> Vec { let mut tracks = Vec::::new(); let mut current_track = TrackData::default(); - let mut children = modified_children_ids(shape, structure.get(&shape.id), true); + let mut children = shape.modified_children_ids(structure.get(&shape.id), true); let mut first = true; if flex_data.is_reverse() { diff --git a/render-wasm/src/shapes/modifiers/grid_layout.rs b/render-wasm/src/shapes/modifiers/grid_layout.rs index 065c2ec801..3dcd18f6b6 100644 --- a/render-wasm/src/shapes/modifiers/grid_layout.rs +++ b/render-wasm/src/shapes/modifiers/grid_layout.rs @@ -1,8 +1,8 @@ use crate::math::{self as math, intersect_rays, Bounds, Matrix, Point, Ray, Vector, VectorExt}; use crate::shapes::{ - modified_children_ids, AlignContent, AlignItems, AlignSelf, GridCell, GridData, GridTrack, - GridTrackType, JustifyContent, JustifyItems, JustifySelf, LayoutData, LayoutItem, Modifier, - Shape, StructureEntry, + AlignContent, AlignItems, AlignSelf, Frame, GridCell, GridData, GridTrack, GridTrackType, + JustifyContent, JustifyItems, JustifySelf, Layout, LayoutData, LayoutItem, Modifier, Shape, + StructureEntry, Type, }; use crate::uuid::Uuid; use indexmap::IndexSet; @@ -21,6 +21,8 @@ pub struct CellData<'a> { pub height: f32, pub align_self: Option, pub justify_self: Option, + pub row: usize, + pub column: usize, } #[derive(Debug)] @@ -590,12 +592,84 @@ pub fn create_cell_data<'a>( height: cell_bounds.height(), align_self: cell.align_self, justify_self: cell.justify_self, + row: row_start, + column: column_start, }); } result } +pub fn grid_cell_data<'a>( + shape: Shape, + shapes: &'a HashMap, + modifiers: &HashMap, + structure: &HashMap>, + allow_empty: bool, +) -> Vec> { + let Type::Frame(Frame { + layout: Some(Layout::GridLayout(layout_data, grid_data)), + .. + }) = &shape.shape_type + else { + return vec![]; + }; + + let bounds = &mut HashMap::::new(); + + let shape = &mut shape.clone(); + if let Some(modifiers) = modifiers.get(&shape.id) { + shape.apply_transform(modifiers); + } + + let layout_bounds = shape.bounds(); + let children = shape.modified_children_ids(structure.get(&shape.id), false); + + for child_id in children.iter() { + let Some(child) = shapes.get(child_id) else { + continue; + }; + + if let Some(modifier) = modifiers.get(child_id) { + let mut b = bounds.find(child); + b.transform_mut(modifier); + bounds.insert(*child_id, b); + } + } + + let column_tracks = calculate_tracks( + true, + shape, + layout_data, + grid_data, + &layout_bounds, + &grid_data.cells, + shapes, + bounds, + ); + + let row_tracks = calculate_tracks( + false, + shape, + layout_data, + grid_data, + &layout_bounds, + &grid_data.cells, + shapes, + bounds, + ); + + create_cell_data( + &layout_bounds, + &children, + shapes, + &grid_data.cells, + &column_tracks, + &row_tracks, + allow_empty, + ) +} + fn child_position( child: &Shape, layout_bounds: &Bounds, @@ -655,7 +729,7 @@ pub fn reflow_grid_layout( ) -> VecDeque { let mut result = VecDeque::new(); let layout_bounds = bounds.find(shape); - let children = modified_children_ids(shape, structure.get(&shape.id), true); + let children = shape.modified_children_ids(structure.get(&shape.id), true); let column_tracks = calculate_tracks( true, diff --git a/render-wasm/src/state.rs b/render-wasm/src/state.rs index cdd5c5bf00..9514950de8 100644 --- a/render-wasm/src/state.rs +++ b/render-wasm/src/state.rs @@ -1,7 +1,7 @@ use std::collections::{hash_map::Entry, HashMap}; use std::{iter, vec}; -use skia_safe as skia; +use skia_safe::{self as skia, Path, Point}; use crate::performance; use crate::render::RenderState; @@ -10,6 +10,8 @@ use crate::shapes::StructureEntry; use crate::tiles; use crate::uuid::Uuid; +use crate::shapes::modifiers::grid_layout::grid_cell_data; + const SHAPES_POOL_ALLOC_MULTIPLIER: f32 = 1.3; /// A pool allocator for `Shape` objects that attempts to minimize memory reallocations. @@ -224,4 +226,38 @@ impl<'a> State<'a> { self.render_state .rebuild_modifier_tiles(&mut self.shapes, &self.modifiers); } + + pub fn get_grid_coords(&mut self, pos_x: f32, pos_y: f32) -> (i32, i32) { + let Some(shape) = self.current_shape() else { + return (-1, -1); + }; + + let bounds = shape.bounds(); + let position = Point::new(pos_x, pos_y); + + let cells = grid_cell_data( + shape.clone(), + &self.shapes, + &self.modifiers, + &self.structure, + true, + ); + + for cell in cells { + let points = &[ + cell.anchor, + cell.anchor + bounds.hv(cell.width), + cell.anchor + bounds.hv(cell.width) + bounds.vv(cell.height), + cell.anchor + bounds.vv(cell.height), + ]; + + let polygon = Path::polygon(points, true, None, None); + + if polygon.contains(position) { + return (cell.row as i32 + 1, cell.column as i32 + 1); + } + } + + (-1, -1) + } }