Compare commits

..

2 Commits

Author SHA1 Message Date
Aitor Moreno
7c1ddd3d7d Merge pull request #8382 from penpot/alotor-fix-components-propagation
🐛 Fix problem with text change component propagation and undo
2026-02-18 10:37:06 +01:00
alonso.torres
30106f8524 🐛 Fix problem with text change component propagation and undo 2026-02-17 11:54:33 +01:00
4 changed files with 47 additions and 76 deletions

View File

@@ -1150,3 +1150,24 @@
[changes]
(::page-id (meta changes)))
(defn set-text-content
[changes id content prev-content]
(assert-page-id! changes)
(let [page-id (::page-id (meta changes))
redo-change
{:type :mod-obj
:page-id page-id
:id id
:operations [{:type :set :attr :content :val content}]}
undo-change
{:type :mod-obj
:page-id page-id
:id id
:operations [{:type :set :attr :content :val prev-content}]}]
(-> changes
(update :redo-changes conj redo-change)
(update :undo-changes conj undo-change))))

View File

@@ -10,6 +10,7 @@
[app.common.attrs :as attrs]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
@@ -19,6 +20,7 @@
[app.common.types.shape.layout :as ctl]
[app.common.types.text :as txt]
[app.common.uuid :as uuid]
[app.main.data.changes :as dch]
[app.main.data.event :as ev]
[app.main.data.helpers :as dsh]
[app.main.data.workspace.common :as dwc]
@@ -916,11 +918,11 @@
(update-in state [:workspace-text-modifier shape-id] {:position-data position-data}))))
(defn v2-update-text-shape-content
[id content & {:keys [update-name? name finalize? save-undo?]
:or {update-name? false name nil finalize? false save-undo? true}}]
[id content & {:keys [update-name? name finalize? save-undo? original-content]
:or {update-name? false name nil finalize? false save-undo? true original-content nil}}]
(ptk/reify ::v2-update-text-shape-content
ptk/WatchEvent
(watch [_ state _]
(watch [it state _]
(if (features/active-feature? state "render-wasm/v1")
(let [objects (dsh/lookup-page-objects state)
shape (get objects id)
@@ -970,7 +972,13 @@
{:save-undo? false}))
(dws/deselect-shape id)
(dwsh/delete-shapes #{id})))
(rx/of (dwt/finish-transform))))))
(rx/of
;; This commit is necesary for undo and component propagation
;; on finalization
(dch/commit-changes
(-> (pcb/empty-changes it (:current-page-id state))
(pcb/set-text-content id content original-content)))
(dwt/finish-transform))))))
(let [objects (dsh/lookup-page-objects state)
shape (get objects id)

View File

@@ -118,7 +118,8 @@
:update-name? update-name?
:name generated-name
:finalize? true
:save-undo? false))))
:save-undo? false
:original-content original-content))))
(let [container-node (mf/ref-val container-ref)]
(dom/set-style! container-node "opacity" 0)))

View File

@@ -1573,22 +1573,16 @@ impl RenderState {
return;
}
// Special-case: blur=0 and spread=0 shadows are just an offset silhouette.
//
// For zoom > 1.0, rendering via the offscreen filter surface and then compositing it back
// with sampling can introduce unintended resampling/softening. Drawing the offset geometry
// directly keeps edges crisp at any zoom level.
if scale > 1.0
&& combined_blur.is_none()
&& shadow.blur <= 0.0
&& shadow.spread <= 0.0
{
{
let drop_canvas = self.surfaces.canvas(SurfaceId::DropShadows);
drop_canvas.save();
drop_canvas.scale((scale, scale));
drop_canvas.translate(translation);
}
if use_low_zoom_path {
let mut shadow_paint = skia::Paint::default();
shadow_paint.set_image_filter(drop_filter);
shadow_paint.set_blend_mode(skia::BlendMode::SrcOver);
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&shadow_paint);
let drop_canvas = self.surfaces.canvas(SurfaceId::DropShadows);
drop_canvas.save_layer(&layer_rec);
drop_canvas.scale((scale, scale));
drop_canvas.translate(translation);
self.with_nested_blurs_suppressed(|state| {
state.render_shape(
@@ -1608,66 +1602,13 @@ impl RenderState {
return;
}
if use_low_zoom_path {
// For low zoom, put offset in filter to avoid corner artifacts (like text shadows).
// transformed_shadow already has blur/spread scaled by scale, and offset needs to be
// scaled by scale to match the scaled canvas space.
let mut shadow_with_offset = transformed_shadow.clone();
shadow_with_offset.to_mut().offset = (world_offset.0 * scale, world_offset.1 * scale);
let Some(filter_with_offset) = shadow_with_offset.get_drop_shadow_filter() else {
return;
};
let mut shadow_paint = skia::Paint::default();
shadow_paint.set_image_filter(filter_with_offset);
shadow_paint.set_blend_mode(skia::BlendMode::SrcOver);
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&shadow_paint);
let drop_canvas = self.surfaces.canvas(SurfaceId::DropShadows);
drop_canvas.save_layer(&layer_rec);
drop_canvas.scale((scale, scale));
drop_canvas.translate(translation);
self.with_nested_blurs_suppressed(|state| {
state.render_shape(
&plain_shape,
clip_bounds,
SurfaceId::DropShadows,
SurfaceId::DropShadows,
SurfaceId::DropShadows,
SurfaceId::DropShadows,
false,
None,
None,
);
});
self.surfaces.canvas(SurfaceId::DropShadows).restore();
return;
}
// For scale > 1.0 without combined_blur, put offset in filter to avoid corner artifacts.
// For combined_blur, use offset as transformation (needed for correct composition).
let filter_result =
filters::render_into_filter_surface(self, bounds, |state, temp_surface| {
{
let canvas = state.surfaces.canvas(temp_surface);
// When scale > 1.0 and no combined_blur, put offset in filter to avoid artifacts.
let filter_to_use = if scale > 1.0 && combined_blur.is_none() {
let mut shadow_with_offset = transformed_shadow.clone();
shadow_with_offset.to_mut().offset = world_offset;
shadow_with_offset.get_drop_shadow_filter()
} else {
Some(drop_filter.clone())
};
let Some(filter) = filter_to_use else {
return;
};
let mut shadow_paint = skia::Paint::default();
shadow_paint.set_image_filter(filter);
shadow_paint.set_image_filter(drop_filter.clone());
shadow_paint.set_blend_mode(skia::BlendMode::SrcOver);
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&shadow_paint);
@@ -1683,7 +1624,7 @@ impl RenderState {
temp_surface,
temp_surface,
false,
if scale > 1.0 && combined_blur.is_none() { None } else { Some(shadow.offset) },
Some(shadow.offset),
None,
);
});