diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index 87274e79c5..f67f3f35c5 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -529,17 +529,15 @@ ptk/WatchEvent (watch [_ state _] (wasm.api/clean-modifiers) - (let [prev-wasm-props (:prev-wasm-props state) wasm-props (:wasm-props state) - objects (dsh/lookup-page-objects state)] - + objects (dsh/lookup-page-objects state) + pixel-precision false] (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) - selrect (wasm.api/propagate-apply geometry-entries)] + selrect (wasm.api/propagate-apply geometry-entries pixel-precision)] (rx/of (set-temporary-selrect selrect)))))))) #_:clj-kondo/ignore @@ -549,15 +547,18 @@ :as params}] (ptk/reify ::apply-wasm-modifiesr ptk/WatchEvent - (watch [_ _ _] + (watch [_ state _] (let [geometry-entries (parse-geometry-modifiers modif-tree) + snap-pixel? + (and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid)) + transforms (into {} (map (fn [{:keys [id transform]}] [id transform])) - (wasm.api/propagate-modifiers geometry-entries)) + (wasm.api/propagate-modifiers geometry-entries snap-pixel?)) ids (into (set (keys modif-tree)) (keys transforms)) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs index 50c0327a0e..1af9cae6a8 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs @@ -262,10 +262,10 @@ [x y width height] (if (features/active-feature? @st/state "render-wasm/v1") - (let [{:keys [width height]} (wasm.api/text-dimensions shape-id) + (let [{:keys [max-width height]} (wasm.api/text-dimensions shape-id) {:keys [x y]} (:selrect shape)] - [x y width height]) + [x y max-width height]) (let [bounds (gst/shape->rect shape) x (mth/min (dm/get-prop bounds :x) diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index c597ae0639..9899b58388 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -654,9 +654,10 @@ (let [offset (h/call wasm/internal-module "_get_text_dimensions") heapf32 (mem/get-heap-f32) width (aget heapf32 (mem/ptr8->ptr32 offset)) - height (aget heapf32 (mem/ptr8->ptr32 (+ offset 4)))] + height (aget heapf32 (mem/ptr8->ptr32 (+ offset 4))) + max-width (aget heapf32 (mem/ptr8->ptr32 (+ offset 8)))] (h/call wasm/internal-module "_free_bytes") - {:width width :height height}))) + {:width width :height height :max-width max-width}))) (defn set-view-box [zoom vbox] @@ -803,7 +804,7 @@ (h/call wasm/internal-module "_set_structure_modifiers")))) (defn propagate-modifiers - [entries] + [entries pixel-precision] (when (d/not-empty? entries) (let [offset (mem/alloc-bytes-32 (modifier-get-entries-size entries)) heapf32 (mem/get-heap-f32) @@ -817,7 +818,7 @@ (sr/heapf32-set-matrix transform heapf32 (+ current-offset (mem/ptr8->ptr32 MODIFIER-ENTRY-TRANSFORM-OFFSET))) (recur (rest entries) (+ current-offset (mem/ptr8->ptr32 MODIFIER-ENTRY-SIZE)))))) - (let [result-offset (h/call wasm/internal-module "_propagate_modifiers") + (let [result-offset (h/call wasm/internal-module "_propagate_modifiers" pixel-precision) heapf32 (mem/get-heap-f32) heapu32 (mem/get-heap-u32) len (aget heapu32 (mem/ptr8->ptr32 result-offset)) @@ -829,7 +830,7 @@ result)))) (defn propagate-apply - [entries] + [entries pixel-precision] (when (d/not-empty? entries) (let [offset (mem/alloc-bytes-32 (modifier-get-entries-size entries)) heapf32 (mem/get-heap-f32) @@ -843,7 +844,7 @@ (sr/heapf32-set-matrix transform heapf32 (+ current-offset (mem/ptr8->ptr32 MODIFIER-ENTRY-TRANSFORM-OFFSET))) (recur (rest entries) (+ current-offset (mem/ptr8->ptr32 MODIFIER-ENTRY-SIZE)))))) - (let [offset (h/call wasm/internal-module "_propagate_apply") + (let [offset (h/call wasm/internal-module "_propagate_apply" pixel-precision) heapf32 (mem/get-heap-f32) width (aget heapf32 (mem/ptr8->ptr32 (+ offset 0))) height (aget heapf32 (mem/ptr8->ptr32 (+ offset 4))) diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 602ea625f8..a850a9bafc 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -389,7 +389,7 @@ pub extern "C" fn set_shape_path_attrs(num_attrs: u32) { } #[no_mangle] -pub extern "C" fn propagate_modifiers() -> *mut u8 { +pub extern "C" fn propagate_modifiers(pixel_precision: bool) -> *mut u8 { let bytes = mem::bytes(); let entries: Vec<_> = bytes @@ -398,13 +398,13 @@ pub extern "C" fn propagate_modifiers() -> *mut u8 { .collect(); with_state!(state, { - let (result, _) = shapes::propagate_modifiers(state, &entries); + let (result, _) = shapes::propagate_modifiers(state, &entries, pixel_precision); mem::write_vec(result) }) } #[no_mangle] -pub extern "C" fn propagate_apply() -> *mut u8 { +pub extern "C" fn propagate_apply(pixel_precision: bool) -> *mut u8 { let bytes = mem::bytes(); let entries: Vec<_> = bytes @@ -413,7 +413,7 @@ pub extern "C" fn propagate_apply() -> *mut u8 { .collect(); with_state!(state, { - let (result, bounds) = shapes::propagate_modifiers(state, &entries); + let (result, bounds) = shapes::propagate_modifiers(state, &entries, pixel_precision); for entry in result { state.modifiers.insert(entry.id, entry.transform); diff --git a/render-wasm/src/shapes/modifiers.rs b/render-wasm/src/shapes/modifiers.rs index a1d5cd00d2..6b33dd3f65 100644 --- a/render-wasm/src/shapes/modifiers.rs +++ b/render-wasm/src/shapes/modifiers.rs @@ -103,9 +103,35 @@ fn calculate_group_bounds( shape_bounds.with_points(result) } +fn set_pixel_precision(transform: &mut Matrix, bounds: &mut Bounds) { + let tr = bounds.transform_matrix().unwrap_or_default(); + let tr_inv = tr.invert().unwrap_or_default(); + + let x = bounds.min_x().round(); + let y = bounds.min_y().round(); + + let mut round_transform = Matrix::scale(( + bounds.width().round() / bounds.width(), + bounds.height().round() / bounds.height(), + )); + round_transform.post_concat(&tr); + round_transform.pre_concat(&tr_inv); + + transform.post_concat(&round_transform); + bounds.transform_mut(&round_transform); + + let dx = x - bounds.min_x(); + let dy = y - bounds.min_y(); + + let round_transform = Matrix::translate((dx, dy)); + transform.post_concat(&round_transform); + bounds.transform_mut(&round_transform); +} + pub fn propagate_modifiers( state: &State, modifiers: &[TransformEntry], + pixel_precision: bool, ) -> (Vec, HashMap) { let shapes = &state.shapes; @@ -161,6 +187,10 @@ pub fn propagate_modifiers( } } + if pixel_precision { + set_pixel_precision(&mut transform, &mut shape_bounds_after); + } + if entry.propagate { let mut children = propagate_children( shape, @@ -180,6 +210,10 @@ pub fn propagate_modifiers( shape_modif.post_concat(&transform); modifiers.insert(shape.id, shape_modif); + if shape.has_layout() { + entries.push_back(Modifier::reflow(shape.id)); + } + if let Some(parent) = shape.parent_id.and_then(|id| shapes.get(&id)) { if parent.has_layout() || parent.is_group_like() { entries.push_back(Modifier::reflow(parent.id)); diff --git a/render-wasm/src/shapes/modifiers/flex_layout.rs b/render-wasm/src/shapes/modifiers/flex_layout.rs index 2dcccda1d8..8e7a47a456 100644 --- a/render-wasm/src/shapes/modifiers/flex_layout.rs +++ b/render-wasm/src/shapes/modifiers/flex_layout.rs @@ -187,7 +187,7 @@ fn initialize_tracks( let mut children = modified_children_ids(shape, structure.get(&shape.id)); let mut first = true; - if !flex_data.is_reverse() { + if flex_data.is_reverse() { children.reverse(); } @@ -497,6 +497,14 @@ fn next_anchor( prev_anchor: Point, total_shapes_size: f32, ) -> Point { + if layout_axis.is_auto_main { + let delta = child_axis.margin_main_start + + child_axis.margin_main_end + + child_axis.main_size + + layout_axis.gap_main; + return prev_anchor + layout_axis.main_v * delta; + } + let delta = child_axis.margin_main_start + child_axis.margin_main_end + match layout_data.justify_content { diff --git a/render-wasm/src/shapes/text.rs b/render-wasm/src/shapes/text.rs index 510406469e..e63b1df29a 100644 --- a/render-wasm/src/shapes/text.rs +++ b/render-wasm/src/shapes/text.rs @@ -594,6 +594,13 @@ pub fn auto_width(paragraphs: &[Vec]) -> f32 { }) } +pub fn max_width(paragraphs: &[Vec]) -> f32 { + paragraphs + .iter() + .flatten() + .fold(0.0, |max_width, p| f32::max(p.max_width(), max_width)) +} + pub fn auto_height(paragraphs: &[Vec]) -> f32 { paragraphs .iter() diff --git a/render-wasm/src/wasm/text.rs b/render-wasm/src/wasm/text.rs index 7642216daa..6619bf0cf8 100644 --- a/render-wasm/src/wasm/text.rs +++ b/render-wasm/src/wasm/text.rs @@ -1,5 +1,5 @@ use crate::mem; -use crate::shapes::{auto_height, auto_width, GrowType, RawTextData, Type}; +use crate::shapes::{auto_height, auto_width, max_width, GrowType, RawTextData, Type}; use crate::STATE; use crate::{with_current_shape, with_state}; @@ -42,6 +42,7 @@ pub extern "C" fn get_text_dimensions() -> *mut u8 { let mut width = 0.01; let mut height = 0.01; + let mut m_width = 0.01; with_current_shape!(state, |shape: &mut Shape| { width = shape.selrect.width(); height = shape.selrect.height(); @@ -49,14 +50,16 @@ pub extern "C" fn get_text_dimensions() -> *mut u8 { if let Type::Text(content) = &shape.shape_type { let paragraphs = content.get_skia_paragraphs(font_col); height = auto_height(¶graphs).ceil(); + m_width = max_width(¶graphs); if content.grow_type() == GrowType::AutoWidth { width = auto_width(¶graphs).ceil(); } } }); - let mut bytes = vec![0; 8]; + let mut bytes = vec![0; 12]; bytes[0..4].clone_from_slice(&width.to_le_bytes()); bytes[4..8].clone_from_slice(&height.to_le_bytes()); + bytes[8..12].clone_from_slice(&m_width.to_le_bytes()); mem::write_bytes(bytes) }