diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index 1afd3e352..d8cc7cf22 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -30,6 +30,7 @@ [app.main.data.workspace.undo :as dwu] [app.main.features :as features] [app.render-wasm.api :as wasm.api] + [app.render-wasm.shape :as wasm.shape] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -162,17 +163,46 @@ change-to-fixed? (assoc :grow-type :fixed)))) +(defn- set-wasm-props! + [objects prev-wasm-props wasm-props] + (let [;; Set old value for previous properties + clean-props + (->> prev-wasm-props + (map (fn [[id {:keys [property] :as change}]] + (let [shape (get objects id)] + [id (assoc change :value (get shape property))])))) + + wasm-props + (concat clean-props wasm-props) + + wasm-props + (-> (group-by first wasm-props) + (update-vals #(map second %)))] + + ;; Props are grouped by id and then assoc to the shape the new value + (doseq [[id properties] wasm-props] + (let [shape + (->> properties + (reduce + (fn [shape {:keys [property value]}] + (assoc shape property value)) + (get objects id)))] + + ;; With the new values to the shape change multi props + (wasm.shape/set-wasm-multi-attrs! shape (->> properties (map :property))))))) + (defn clear-local-transform [] (ptk/reify ::clear-local-transform ptk/EffectEvent (effect [_ state _] (when (features/active-feature? state "render-wasm/v1") - (wasm.api/clean-modifiers))) + (wasm.api/clean-modifiers) + (set-wasm-props! (dsh/lookup-page-objects state) (:wasm-props state) []))) ptk/UpdateEvent (update [_ state] (-> state - (dissoc :workspace-modifiers) + (dissoc :workspace-modifiers :wasm-props :prev-wasm-props) (dissoc :app.main.data.workspace.transforms/current-move-selected))))) (defn create-modif-tree @@ -459,6 +489,15 @@ :transform (ctm/modifiers->transform (:modifiers data))}))) modif-tree)) +(defn- extract-property-changes + [modif-tree] + (->> modif-tree + (mapcat (fn [[id {:keys [modifiers]}]] + (->> (:structure-parent modifiers) + (map #(vector id %))))) + (filter (fn [[_ {:keys [type]}]] + (= type :change-property))))) + (defn set-wasm-modifiers ([modif-tree] (set-wasm-modifiers modif-tree false)) @@ -471,15 +510,31 @@ ([modif-tree _ignore-constraints _ignore-snap-pixel _params] (ptk/reify ::set-wasm-modifiers + ptk/UpdateEvent + (update [_ state] + (let [property-changes + (extract-property-changes modif-tree)] + + (-> state + (assoc :prev-wasm-props (:wasm-props state)) + (assoc :wasm-props property-changes)))) + ptk/EffectEvent - (effect [_ _ _] + (effect [_ state _] (wasm.api/clean-modifiers) - (let [structure-entries (parse-structure-modifiers modif-tree)] - (wasm.api/set-structure-modifiers structure-entries) - (let [geometry-entries (parse-geometry-modifiers modif-tree) - modifiers-new - (wasm.api/propagate-modifiers geometry-entries)] - (wasm.api/set-modifiers modifiers-new))))))) + + (let [prev-wasm-props (:prev-wasm-props state) + wasm-props (:wasm-props state) + objects (dsh/lookup-page-objects state)] + + (set-wasm-props! objects prev-wasm-props wasm-props) + + (let [structure-entries (parse-structure-modifiers modif-tree)] + (wasm.api/set-structure-modifiers structure-entries) + (let [geometry-entries (parse-geometry-modifiers modif-tree) + modifiers-new + (wasm.api/propagate-modifiers geometry-entries)] + (wasm.api/set-modifiers modifiers-new)))))))) (defn set-selrect-transform [modifiers] diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index e0e70639c..e28cc6c77 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -409,9 +409,8 @@ padding-bottom padding-left))) -(defn set-grid-layout +(defn set-grid-layout-data [shape] - (let [dir (-> (or (dm/get-prop shape :layout-grid-dir) :row) sr/translate-layout-grid-dir) gap (dm/get-prop shape :layout-gap) row-gap (or (dm/get-prop gap :row-gap) 0) @@ -440,11 +439,11 @@ padding-top padding-right padding-bottom - padding-left)) + padding-left))) - ;; Send Rows - (let [entries (:layout-grid-rows shape) - size (grid-layout-get-row-entries-size entries) +(defn set-grid-layout-rows + [entries] + (let [size (grid-layout-get-row-entries-size entries) offset (mem/alloc-bytes size) heap @@ -459,11 +458,11 @@ (.set heap (sr/u8 (sr/translate-grid-track-type type)) (+ current-offset 0)) (.set heap (sr/f32->u8 value) (+ current-offset 1)) (recur (rest entries) (+ current-offset GRID-LAYOUT-ROW-ENTRY-SIZE))))) - (h/call wasm/internal-module "_set_grid_rows")) + (h/call wasm/internal-module "_set_grid_rows"))) - ;; Send Columns - (let [entries (:layout-grid-columns shape) - size (grid-layout-get-column-entries-size entries) +(defn set-grid-layout-columns + [entries] + (let [size (grid-layout-get-column-entries-size entries) offset (mem/alloc-bytes size) heap @@ -478,10 +477,11 @@ (.set heap (sr/u8 (sr/translate-grid-track-type type)) (+ current-offset 0)) (.set heap (sr/f32->u8 value) (+ current-offset 1)) (recur (rest entries) (+ current-offset GRID-LAYOUT-COLUMN-ENTRY-SIZE))))) - (h/call wasm/internal-module "_set_grid_columns")) + (h/call wasm/internal-module "_set_grid_columns"))) - ;; Send cells - (let [entries (-> shape :layout-grid-cells vals) +(defn set-grid-layout-cells + [cells] + (let [entries (vals cells) size (grid-layout-get-cell-entries-size entries) offset (mem/alloc-bytes size) @@ -533,6 +533,13 @@ (h/call wasm/internal-module "_set_grid_cells"))) +(defn set-grid-layout + [shape] + (set-grid-layout-data shape) + (set-grid-layout-rows (:layout-grid-rows shape)) + (set-grid-layout-columns (:layout-grid-columns shape)) + (set-grid-layout-cells (:layout-grid-cells shape))) + (defn set-layout-child [shape] (let [margins (dm/get-prop shape :layout-item-margin) @@ -868,4 +875,4 @@ (p/merr (fn [cause] (js/console.error cause) (p/resolved false))))) - (p/resolved false)))) \ No newline at end of file + (p/resolved false)))) diff --git a/frontend/src/app/render_wasm/shape.cljs b/frontend/src/app/render_wasm/shape.cljs index f2eef1899..6f80a6598 100644 --- a/frontend/src/app/render_wasm/shape.cljs +++ b/frontend/src/app/render_wasm/shape.cljs @@ -108,106 +108,135 @@ ;; --- SHAPE IMPL -(defn- set-wasm-attrs +(defn set-wasm-single-attr! + [shape k] + (let [v (get shape k)] + (case k + :parent-id (api/set-parent-id v) + :type (api/set-shape-type v) + :bool-type (api/set-shape-bool-type v) + :selrect (api/set-shape-selrect v) + :show-content (if (= (:type shape) :frame) + (api/set-shape-clip-content (not v)) + (api/set-shape-clip-content false)) + :rotation (api/set-shape-rotation v) + :transform (api/set-shape-transform v) + :fills (into [] (api/set-shape-fills v)) + :strokes (into [] (api/set-shape-strokes v)) + :blend-mode (api/set-shape-blend-mode v) + :opacity (api/set-shape-opacity v) + :hidden (api/set-shape-hidden v) + :shapes (api/set-shape-children v) + :blur (api/set-shape-blur v) + :shadow (api/set-shape-shadows v) + :constraints-h (api/set-constraints-h v) + :constraints-v (api/set-constraints-v v) + + :svg-attrs + (when (= (:type shape) :path) + (api/set-shape-path-attrs v)) + + :masked-group + (when (and (= (:type shape) :group) (:masked-group shape)) + (api/set-masked (:masked-group shape))) + + :content + (cond + (or (= (:type shape) :path) + (= (:type shape) :bool)) + (api/set-shape-path-content v) + + (= (:type shape) :svg-raw) + (api/set-shape-svg-raw-content (api/get-static-markup shape)) + + (= (:type shape) :text) + (into [] (api/set-shape-text-content v))) + + (:layout-item-margin + :layout-item-margin-type + :layout-item-h-sizing + :layout-item-v-sizing + :layout-item-max-h + :layout-item-min-h + :layout-item-max-w + :layout-item-min-w + :layout-item-absolute + :layout-item-z-index) + (api/set-layout-child shape) + + :layout-grid-rows + (api/set-grid-layout-rows v) + + :layout-grid-columns + (api/set-grid-layout-columns v) + + :layout-grid-cells + (api/set-grid-layout-cells v) + + (:layout + :layout-flex-dir + :layout-gap-type + :layout-gap + :layout-align-items + :layout-align-content + :layout-justify-items + :layout-justify-content + :layout-wrap-type + :layout-padding-type + :layout-padding) + (cond + (ctl/grid-layout? shape) + (api/set-grid-layout-data shape) + + (ctl/flex-layout? shape) + (api/set-flex-layout shape)) + + nil))) + +(defn set-wasm-multi-attrs! + [shape properties] + (api/use-shape (:id shape)) + (let [pending + (->> properties + (mapcat #(set-wasm-single-attr! shape %)))] + (if (and pending (seq pending)) + (->> (rx/from pending) + (rx/mapcat identity) + (rx/reduce conj []) + (rx/subs! + (fn [_] + (api/update-shape-tiles) + (api/clear-drawing-cache) + (api/request-render "set-wasm-attrs-pending")))) + (do + (api/update-shape-tiles) + (api/request-render "set-wasm-attrs"))))) + +(defn set-wasm-attrs! + [shape k v] + (let [shape (assoc shape k v)] + (api/use-shape (:id shape)) + (let [pending (set-wasm-single-attr! shape k)] + ;; TODO: set-wasm-attrs is called twice with every set + (if (and pending (seq pending)) + (->> (rx/from pending) + (rx/mapcat identity) + (rx/reduce conj []) + (rx/subs! + (fn [_] + (api/update-shape-tiles) + (api/clear-drawing-cache) + (api/request-render "set-wasm-attrs-pending")))) + (do + (api/update-shape-tiles) + (api/request-render "set-wasm-attrs")))))) + +(defn- impl-assoc [self k v] (when ^boolean shape/*wasm-sync* (binding [shape/*wasm-sync* false] - (let [self (assoc self k v)] - (api/use-shape (:id self)) - (let [pending (case k - :parent-id (api/set-parent-id v) - :type (api/set-shape-type v) - :bool-type (api/set-shape-bool-type v) - :selrect (api/set-shape-selrect v) - :show-content (if (= (:type self) :frame) - (api/set-shape-clip-content (not v)) - (api/set-shape-clip-content false)) - :rotation (api/set-shape-rotation v) - :transform (api/set-shape-transform v) - :fills (into [] (api/set-shape-fills v)) - :strokes (into [] (api/set-shape-strokes v)) - :blend-mode (api/set-shape-blend-mode v) - :opacity (api/set-shape-opacity v) - :hidden (api/set-shape-hidden v) - :shapes (api/set-shape-children v) - :blur (api/set-shape-blur v) - :shadow (api/set-shape-shadows v) - :constraints-h (api/set-constraints-h v) - :constraints-v (api/set-constraints-v v) + (set-wasm-attrs! self k v))) - :svg-attrs - (when (= (:type self) :path) - (api/set-shape-path-attrs v)) - - :masked-group - (when (and (= (:type self) :group) (:masked-group self)) - (api/set-masked (:masked-group self))) - - :content - (cond - (or (= (:type self) :path) - (= (:type self) :bool)) - (api/set-shape-path-content v) - - (= (:type self) :svg-raw) - (api/set-shape-svg-raw-content (api/get-static-markup self)) - - (= (:type self) :text) - (into [] (api/set-shape-text-content v))) - - (:layout-item-margin - :layout-item-margin-type - :layout-item-h-sizing - :layout-item-v-sizing - :layout-item-max-h - :layout-item-min-h - :layout-item-max-w - :layout-item-min-w - :layout-item-absolute - :layout-item-z-index) - (api/set-layout-child self) - - (:layout-grid-rows - :layout-grid-columns - :layout-grid-cells) - (when (ctl/grid-layout? self) - (api/set-grid-layout self)) - - (:layout - :layout-flex-dir - :layout-gap-type - :layout-gap - :layout-align-items - :layout-align-content - :layout-justify-items - :layout-justify-content - :layout-wrap-type - :layout-padding-type - :layout-padding) - (cond - (ctl/grid-layout? self) - (api/set-grid-layout self) - - (ctl/flex-layout? self) - (api/set-flex-layout self)) - - nil)] - - ;; TODO: set-wasm-attrs is called twice with every set - (if (and pending (seq pending)) - (->> (rx/from pending) - (rx/mapcat identity) - (rx/reduce conj []) - (rx/subs! (fn [_] - (api/update-shape-tiles) - (api/clear-drawing-cache) - (api/request-render "set-wasm-attrs-pending")))) - (do - (api/update-shape-tiles) - (api/request-render "set-wasm-attrs")))))))) -(defn- impl-assoc - [self k v] - (set-wasm-attrs self k v) (case k :id (ShapeProxy. v @@ -228,7 +257,10 @@ (defn- impl-dissoc [self k] - (set-wasm-attrs self k nil) + (when ^boolean shape/*wasm-sync* + (binding [shape/*wasm-sync* false] + (set-wasm-attrs! self k nil))) + (case k :id (ShapeProxy. nil diff --git a/render-wasm/src/shapes/modifiers.rs b/render-wasm/src/shapes/modifiers.rs index 53173dad3..491b5ba7e 100644 --- a/render-wasm/src/shapes/modifiers.rs +++ b/render-wasm/src/shapes/modifiers.rs @@ -279,6 +279,7 @@ pub fn propagate_modifiers(state: &State, modifiers: Vec) -> Vec grid_data, shapes, &mut bounds, + &state.structure, ); entries.append(&mut children); } diff --git a/render-wasm/src/shapes/modifiers/grid_layout.rs b/render-wasm/src/shapes/modifiers/grid_layout.rs index 23c40284d..bbac9efa2 100644 --- a/render-wasm/src/shapes/modifiers/grid_layout.rs +++ b/render-wasm/src/shapes/modifiers/grid_layout.rs @@ -1,9 +1,11 @@ use crate::math::{self as math, intersect_rays, Bounds, Matrix, Point, Ray, Vector, VectorExt}; use crate::shapes::{ - AlignContent, AlignItems, AlignSelf, GridCell, GridData, GridTrack, GridTrackType, - JustifyContent, JustifyItems, JustifySelf, LayoutData, LayoutItem, Modifier, Shape, + modified_children_ids, AlignContent, AlignItems, AlignSelf, GridCell, GridData, GridTrack, + GridTrackType, JustifyContent, JustifyItems, JustifySelf, LayoutData, LayoutItem, Modifier, + Shape, StructureEntry, }; use crate::uuid::Uuid; +use indexmap::IndexSet; use std::collections::{HashMap, VecDeque}; use super::common::GetBounds; @@ -113,7 +115,7 @@ fn set_auto_base_size( (cell.row, cell.row_span) }; - if prop_span != 1 { + if prop_span != 1 || (prop as usize) >= tracks.len() { continue; } @@ -506,6 +508,7 @@ fn cell_bounds( fn create_cell_data<'a>( layout_bounds: &Bounds, + children: &IndexSet, shapes: &'a HashMap, cells: &Vec, column_tracks: &Vec, @@ -517,6 +520,11 @@ fn create_cell_data<'a>( let Some(shape_id) = cell.shape else { continue; }; + + if !children.contains(&shape_id) { + continue; + } + let Some(shape) = shapes.get(&shape_id) else { continue; }; @@ -525,6 +533,15 @@ fn create_cell_data<'a>( let column_end = (cell.column + cell.column_span - 2) as usize; let row_start = (cell.row - 1) as usize; let row_end = (cell.row + cell.row_span - 2) as usize; + + if column_start >= column_tracks.len() + || column_end >= column_tracks.len() + || row_start >= row_tracks.len() + || row_end >= row_tracks.len() + { + continue; + } + let Some(cell_bounds) = cell_bounds( layout_bounds, column_tracks[column_start].anchor_start, @@ -603,9 +620,11 @@ pub fn reflow_grid_layout<'a>( grid_data: &GridData, shapes: &'a HashMap, bounds: &mut HashMap, + structure: &HashMap>, ) -> VecDeque { let mut result = VecDeque::new(); let layout_bounds = bounds.find(shape); + let children = modified_children_ids(shape, structure.get(&shape.id)); let column_tracks = calculate_tracks( true, @@ -631,6 +650,7 @@ pub fn reflow_grid_layout<'a>( let cells = create_cell_data( &layout_bounds, + &children, shapes, &grid_data.cells, &column_tracks,