Fix problems with SVGraw and modifiers

This commit is contained in:
alonso.torres
2025-10-30 12:20:45 +01:00
parent 122619b197
commit fcc9282304
10 changed files with 101 additions and 56 deletions

View File

@@ -645,12 +645,16 @@
#_:clj-kondo/ignore
(defn apply-wasm-modifiers
[modif-tree & {:keys [ignore-constraints ignore-snap-pixel snap-ignore-axis undo-group]
:or {ignore-constraints false ignore-snap-pixel false snap-ignore-axis nil undo-group nil}
[modif-tree & {:keys [ignore-constraints ignore-snap-pixel snap-ignore-axis undo-transation?]
:or {ignore-constraints false ignore-snap-pixel false snap-ignore-axis nil undo-transation? true}
:as params}]
(ptk/reify ::apply-wasm-modifiesr
ptk/WatchEvent
(watch [_ state _]
(wasm.api/clean-modifiers)
(let [structure-entries (parse-structure-modifiers modif-tree)]
(wasm.api/set-structure-modifiers structure-entries))
(let [objects (dsh/lookup-page-objects state)
ignore-tree
@@ -670,8 +674,6 @@
snap-pixel?
(and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid))
_ (wasm.api/clean-geometry-modifiers)
transforms
(into {} (wasm.api/propagate-modifiers geometry-entries snap-pixel?))
@@ -696,16 +698,26 @@
(mapcat (partial cfh/get-parents-with-self objects))
(filter cfh/bool-shape?)
(map :id))
ids)]
(rx/of
(clear-local-transform)
(ptk/event ::dwg/move-frame-guides {:ids ids :transforms transforms})
(ptk/event ::dwcm/move-frame-comment-threads transforms)
(dwsh/update-shapes ids update-shape options)
ids)
;; The update to the bool path needs to be in a different operation because it
;; needs to have the updated children info
(dwsh/update-shapes bool-ids path/update-bool-shape (assoc options :with-objects? true)))))))
undo-id (js/Symbol)]
(rx/concat
(if undo-transation?
(rx/of (dwu/start-undo-transaction undo-id))
(rx/empty))
(rx/of
(clear-local-transform)
(ptk/event ::dwg/move-frame-guides {:ids ids :transforms transforms})
(ptk/event ::dwcm/move-frame-comment-threads transforms)
(dwsh/update-shapes ids update-shape options)
;; The update to the bool path needs to be in a different operation because it
;; needs to have the updated children info
(dwsh/update-shapes bool-ids path/update-bool-shape (assoc options :with-objects? true)))
(if undo-transation?
(rx/of (dwu/commit-undo-transaction undo-id))
(rx/empty)))))))
(def ^:private
xf-rotation-shape

View File

@@ -146,7 +146,7 @@
(defn start-resize
"Enter mouse resize mode, until mouse button is released."
[handler ids shape]
(letfn [(resize [shape initial layout [point lock? center? point-snap]]
(letfn [(resize [shape initial layout objects [point lock? center? point-snap]]
(let [selrect (dm/get-prop shape :selrect)
width (dm/get-prop selrect :width)
height (dm/get-prop selrect :height)
@@ -243,10 +243,14 @@
:always
(ctm/resize scalev resize-origin shape-transform shape-transform-inverse)
^boolean change-width?
(and (ctl/any-layout-immediate-child? objects shape)
(not= (:layout-item-h-sizing shape) :fix)
^boolean change-width?)
(ctm/change-property :layout-item-h-sizing :fix)
^boolean change-height?
(and (ctl/any-layout-immediate-child? objects shape)
(not= (:layout-item-v-sizing shape) :fix)
^boolean change-height?)
(ctm/change-property :layout-item-v-sizing :fix)
;; Set grow-type if it should change
@@ -298,7 +302,7 @@
(fn [[point _ _ :as current]]
(->> (snap/closest-snap-point page-id shapes objects layout zoom focus point)
(rx/map #(conj current %)))))
(rx/map #(resize shape initial-position layout %))
(rx/map #(resize shape initial-position layout objects %))
(rx/share))
modifiers-stream
@@ -332,8 +336,8 @@
(rx/take-until stopper)))]
(rx/concat
;; This initial stream waits for some pixels to be move before making the resize
;; if you make a click in the border will not make a resize
;; This initial stream waits for some pixels to be move before making the resize
;; if you make a click in the border will not make a resize
(->> ms/mouse-position
(rx/map #(gpt/to-vec initial-position %))
(rx/map #(gpt/length %))
@@ -745,12 +749,6 @@
(fn [[modifiers snap-ignore-axis]]
(dwm/set-wasm-modifiers modifiers :snap-ignore-axis snap-ignore-axis))))
(->> modifiers-stream
(rx/last)
(rx/map
(fn [[modifiers snap-ignore-axis]]
(dwm/apply-wasm-modifiers modifiers :snap-ignore-axis snap-ignore-axis))))
(->> move-stream
(rx/with-latest-from ms/mouse-position-alt)
(rx/filter (fn [[_ alt?]] alt?))
@@ -765,14 +763,18 @@
;; Last event will write the modifiers creating the changes
(->> move-stream
(rx/last)
(rx/with-latest-from modifiers-stream)
(rx/mapcat
(fn [[_ target-frame drop-index drop-cell]]
(fn [[[_ target-frame drop-index drop-cell] [modifiers snap-ignore-axis]]]
(let [undo-id (js/Symbol)]
(rx/of (dwu/start-undo-transaction undo-id)
;; (dwm/apply-modifiers {:undo-transation? false})
(move-shapes-to-frame ids target-frame drop-index drop-cell)
(finish-transform)
(dwu/commit-undo-transaction undo-id)))))))
(rx/of
(dwu/start-undo-transaction undo-id)
(dwm/apply-wasm-modifiers modifiers
:snap-ignore-axis snap-ignore-axis
:undo-transation? false)
(move-shapes-to-frame ids target-frame drop-index drop-cell)
(finish-transform)
(dwu/commit-undo-transaction undo-id)))))))
(rx/merge
(->> modifiers-stream

View File

@@ -135,7 +135,10 @@
(when (or (= v :path) (= v :bool))
(api/set-shape-path-content (:content shape))))
:bool-type (api/set-shape-bool-type v)
:selrect (api/set-shape-selrect v)
:selrect (do
(api/set-shape-selrect v)
(when (= (:type shape) :svg-raw)
(api/set-shape-svg-raw-content (api/get-static-markup shape))))
:show-content (if (= (:type shape) :frame)
(api/set-shape-clip-content (not v))
(api/set-shape-clip-content false))

View File

@@ -290,15 +290,21 @@ pub extern "C" fn add_shape_child(a: u32, b: u32, c: u32, d: u32) {
fn set_children_set(entries: IndexSet<Uuid>) {
let mut deleted = IndexSet::new();
let mut parent_id = None;
with_current_shape_mut!(state, |shape: &mut Shape| {
parent_id = Some(shape.id);
(_, deleted) = shape.compute_children_differences(&entries);
shape.children = entries.clone();
});
with_state_mut!(state, {
let Some(parent_id) = parent_id else {
return;
};
for id in deleted {
state.delete_shape(id);
state.delete_shape_children(parent_id, id);
}
});
}

View File

@@ -535,7 +535,12 @@ impl RenderState {
match &shape.shape_type {
Type::SVGRaw(sr) => {
if let Some(svg_transform) = shape.svg_transform() {
matrix.pre_concat(&svg_transform);
}
self.surfaces.canvas(fills_surface_id).concat(&matrix);
if let Some(svg) = shape.svg.as_ref() {
svg.render(self.surfaces.canvas(fills_surface_id))
} else {
@@ -1574,7 +1579,7 @@ impl RenderState {
while let Some(shape_id) = nodes.pop() {
if let Some(shape) = tree.get(&shape_id) {
if shape_id != Uuid::nil() {
self.update_tile_for(&shape, tree);
self.update_tile_for(shape, tree);
} else {
// We only need to rebuild tiles from the first level.
let children = shape.children_ids(false);
@@ -1595,7 +1600,7 @@ impl RenderState {
while let Some(shape_id) = nodes.pop() {
if let Some(shape) = tree.get(&shape_id) {
if shape_id != Uuid::nil() {
self.update_tile_for(&shape, tree);
self.update_tile_for(shape, tree);
}
let children = shape.children_ids(false);

View File

@@ -182,6 +182,7 @@ pub struct Shape {
pub layout_item: Option<LayoutItem>,
pub extrect: OnceCell<math::Rect>,
pub bounds: OnceCell<math::Bounds>,
pub svg_transform: Option<Matrix>,
}
// Returns all ancestor shapes of this shape, traversing up the parent hierarchy
@@ -263,6 +264,7 @@ impl Shape {
layout_item: None,
extrect: OnceCell::new(),
bounds: OnceCell::new(),
svg_transform: None,
}
}
@@ -393,6 +395,10 @@ impl Shape {
self.hidden = value;
}
pub fn svg_transform(&self) -> Option<Matrix> {
self.svg_transform
}
// FIXME: These arguments could be grouped or simplified
#[allow(clippy::too_many_arguments)]
pub fn set_flex_layout_child_data(
@@ -876,7 +882,7 @@ impl Shape {
}
Type::Text(text_content) => {
// FIXME: we need to recalculate the text bounds here because the shape's selrect
let text_bounds = text_content.calculate_bounds(&shape);
let text_bounds = text_content.calculate_bounds(shape);
text_bounds.to_rect()
}
_ => shape.bounds().to_rect(),
@@ -1160,6 +1166,8 @@ impl Shape {
}
} else if let Type::Text(text) = &mut self.shape_type {
text.transform(transform);
} else if let Type::SVGRaw(_) = &mut self.shape_type {
self.svg_transform = Some(*transform);
}
}

View File

@@ -95,7 +95,7 @@ fn calculate_group_bounds(
let mut result = Vec::<Point>::new();
for child_id in shape.children_ids_iter(true) {
let Some(child) = shapes.get(&child_id) else {
let Some(child) = shapes.get(child_id) else {
continue;
};
@@ -109,7 +109,7 @@ fn calculate_bool_bounds(
shape: &Shape,
shapes: ShapesPoolRef,
bounds: &HashMap<Uuid, Bounds>,
modifiers: &HashMap<Uuid, Matrix>
modifiers: &HashMap<Uuid, Matrix>,
) -> Option<Bounds> {
let shape_bounds = bounds.find(shape);
let children_ids = shape.children_ids(true);
@@ -258,7 +258,7 @@ fn propagate_reflow(
bounds: &mut HashMap<Uuid, Bounds>,
layout_reflows: &mut Vec<Uuid>,
reflown: &mut HashSet<Uuid>,
modifiers: &HashMap<Uuid, Matrix>
modifiers: &HashMap<Uuid, Matrix>,
) {
let Some(shape) = state.shapes.get(id) else {
return;
@@ -267,7 +267,7 @@ fn propagate_reflow(
let shapes = &state.shapes;
let mut reflow_parent = false;
if reflown.contains(&id) {
if reflown.contains(id) {
return;
}
@@ -403,7 +403,7 @@ pub fn propagate_modifiers(
&mut bounds,
&mut layout_reflows,
&mut reflown,
&mut modifiers,
&modifiers,
),
}
}
@@ -429,13 +429,14 @@ mod tests {
use crate::math::{Matrix, Point};
use crate::shapes::*;
use crate::state::ShapesPool;
#[test]
fn test_propagate_shape() {
let parent_id = Uuid::new_v4();
let shapes = {
let mut shapes = ShapesPoolRef::new();
let mut shapes = ShapesPool::new();
shapes.initialize(10);
let child_id = Uuid::new_v4();
@@ -468,7 +469,6 @@ mod tests {
transform,
&HashMap::new(),
&HashMap::new(),
&HashMap::new(),
);
assert_eq!(result.len(), 1);
@@ -478,7 +478,7 @@ mod tests {
fn test_group_bounds() {
let parent_id = Uuid::new_v4();
let shapes = {
let mut shapes = ShapesPoolRef::new();
let mut shapes = ShapesPool::new();
shapes.initialize(10);
let child1_id = Uuid::new_v4();
@@ -500,7 +500,7 @@ mod tests {
let parent = shapes.get(&parent_id).unwrap();
let bounds =
calculate_group_bounds(parent, &shapes, &HashMap::new(), &HashMap::new()).unwrap();
calculate_group_bounds(parent, &shapes, &HashMap::new()).unwrap();
assert_eq!(bounds.width(), 3.0);
assert_eq!(bounds.height(), 3.0);

View File

@@ -175,7 +175,7 @@ impl ToPath for Shape {
match &self.shape_type {
Type::Frame(ref frame) => {
let children = self.children_ids(true);
let mut result = Path::new(rect_segments(&self, frame.corners));
let mut result = Path::new(rect_segments(self, frame.corners));
for id in children {
let Some(shape) = shapes.get(&id) else {
continue;
@@ -202,11 +202,11 @@ impl ToPath for Shape {
Type::Bool(bool_data) => bool_data.path.clone(),
Type::Rect(ref rect) => Path::new(rect_segments(&self, rect.corners)),
Type::Rect(ref rect) => Path::new(rect_segments(self, rect.corners)),
Type::Path(path_data) => path_data.clone(),
Type::Circle => Path::new(circle_segments(&self)),
Type::Circle => Path::new(circle_segments(self)),
Type::SVGRaw(_) => Path::default(),
@@ -217,7 +217,7 @@ impl ToPath for Shape {
result = join_paths(result, Path::from_skia_path(path));
}
Path::new(transform_segments(result.segments().clone(), &self))
Path::new(transform_segments(result.segments().clone(), self))
}
}
}

View File

@@ -94,9 +94,14 @@ impl<'a> State<'a> {
self.current_id = Some(id);
}
pub fn delete_shape(&mut self, id: Uuid) {
pub fn delete_shape_children(&mut self, parent_id: Uuid, id: Uuid) {
// We don't really do a self.shapes.remove so that redo/undo keep working
if let Some(shape) = self.shapes.get(&id) {
let Some(shape) = self.shapes.get(&id) else {
return;
};
// Only remove the children when is being deleted from the owner
if shape.parent_id.is_none() || shape.parent_id == Some(parent_id) {
let tiles::TileRect(rsx, rsy, rex, rey) =
self.render_state.get_tiles_for_shape(shape, &self.shapes);
for x in rsx..=rex {

View File

@@ -217,7 +217,7 @@ impl<'a> ShapesPoolImpl<'a> {
Some(cell.get_or_init(|| {
let shape = &*shape_ptr;
shape.transformed(
&self,
self,
(*modifiers_ptr).get(&id_ref),
(*structure_ptr).get(&id_ref),
)
@@ -264,7 +264,7 @@ impl<'a> ShapesPoolImpl<'a> {
}
self.modifiers = modifiers_with_refs;
let all_ids = shapes::all_with_ancestors(&ids, &self, true);
let all_ids = shapes::all_with_ancestors(&ids, self, true);
for uuid in all_ids {
if let Some(uuid_ref) = self.get_uuid_ref(&uuid) {
self.modified_shape_cache.insert(uuid_ref, OnceCell::new());
@@ -287,7 +287,7 @@ impl<'a> ShapesPoolImpl<'a> {
}
self.structure = structure_with_refs;
let all_ids = shapes::all_with_ancestors(&ids, &self, true);
let all_ids = shapes::all_with_ancestors(&ids, self, true);
for uuid in all_ids {
if let Some(uuid_ref) = self.get_uuid_ref(&uuid) {
self.modified_shape_cache.insert(uuid_ref, OnceCell::new());
@@ -317,7 +317,9 @@ impl<'a> ShapesPoolImpl<'a> {
}
pub fn subtree(&self, id: &Uuid) -> ShapesPoolImpl<'a> {
let Some(shape) = self.get(id) else { panic!("Subtree not found"); };
let Some(shape) = self.get(id) else {
panic!("Subtree not found");
};
// TODO: Maybe create all_children_iter
let all_children = shape.all_children(self, true, true);
@@ -327,7 +329,9 @@ impl<'a> ShapesPoolImpl<'a> {
let mut shapes_uuid_to_idx = HashMap::default();
for id in all_children.iter() {
let Some(shape) = self.get(id) else { panic!("Not found"); };
let Some(shape) = self.get(id) else {
panic!("Not found");
};
shapes.push(shape.clone());
let id_ref: &'a Uuid = unsafe { &*(&self.shapes[idx].id as *const Uuid) };