🐛 Fix problem with selection and text shapes for new render

This commit is contained in:
alonso.torres
2025-11-17 17:57:56 +01:00
parent ae624b3728
commit 0a01fc8af9
11 changed files with 311 additions and 52 deletions

View File

@@ -16,6 +16,7 @@
[app.main.data.workspace :as-alias dw]
[app.main.router :as rt]
[app.main.store :as st]
[app.main.worker]
[app.util.globals :as glob]
[app.util.i18n :refer [tr]]
[app.util.timers :as ts]
@@ -94,6 +95,9 @@
(let [data (exception->error-data error)]
(ptk/handle-error data))))
;; Inject dependency to remove circular dependency
(set! app.main.worker/on-error on-error)
;; Set the main potok error handler
(reset! st/on-error on-error)

View File

@@ -29,6 +29,7 @@
[app.main.ui.workspace.viewport.actions :as actions]
[app.main.ui.workspace.viewport.utils :as utils]
[app.main.worker :as mw]
[app.render-wasm.api :as wasm.api]
[app.util.debug :as dbg]
[app.util.dom :as dom]
[app.util.globals :as globals]
@@ -311,7 +312,9 @@
ids)]
(filter #(or (root-frame-with-data? %)
(and (cfh/group-shape? objects %)
(not (contains? child-parent? %)))))))
(not (contains? child-parent? %)))
(and (cfh/text-shape? objects %)
(not (wasm.api/intersect-position % @last-point-ref)))))))
remove-measure-xf
(cond

View File

@@ -8,15 +8,17 @@
"Interface to communicate with the web worker"
(:require
[app.config :as cf]
[app.main.errors :as errors]
[app.util.worker :as uw]
[beicon.v2.core :as rx]))
;; Injected from `app.main.errors` to remove circular dependency
(defonce on-error nil)
(defonce instance nil)
(defn init!
[]
(let [worker (uw/init cf/worker-uri errors/on-error)]
(let [worker (uw/init cf/worker-uri on-error)]
(uw/ask! worker {:cmd :configure
:config {:public-uri cf/public-uri
:build-data cf/build-date

View File

@@ -23,6 +23,8 @@
[app.main.fonts :as fonts]
[app.main.refs :as refs]
[app.main.render :as render]
[app.main.store :as st]
[app.main.worker :as mw]
[app.render-wasm.api.fonts :as f]
[app.render-wasm.api.texts :as t]
[app.render-wasm.deserializers :as dr]
@@ -107,6 +109,15 @@
(reset! pending-render false)
(render ts)))))
(declare get-text-dimensions)
(defn update-text-rect!
[id]
(mw/emit!
{:cmd :index/update-text-rect
:page-id (:current-page-id @st/state)
:shape-id id
:dimensions (get-text-dimensions id)}))
(defn- ensure-text-content
"Guarantee that the shape always sends a valid text tree to WASM. When the
@@ -813,9 +824,25 @@
heapf32 (mem/get-heap-f32)
width (aget heapf32 (+ offset 0))
height (aget heapf32 (+ offset 1))
max-width (aget heapf32 (+ offset 2))]
max-width (aget heapf32 (+ offset 2))
x (aget heapf32 (+ offset 3))
y (aget heapf32 (+ offset 4))]
(mem/free)
{:width width :height height :max-width max-width})))
{:x x :y y :width width :height height :max-width max-width})))
(defn intersect-position
[id position]
(let [buffer (uuid/get-u32 id)
result
(h/call wasm/internal-module "_intersect_position"
(aget buffer 0)
(aget buffer 1)
(aget buffer 2)
(aget buffer 3)
(:x position)
(:y position))]
(= result 1)))
(defn set-view-box
[zoom vbox]
@@ -922,7 +949,13 @@
(->> shapes
(filter cfh/text-shape?)
(map :id)
(run! f/update-text-layout)))
(run!
(fn [id]
(f/update-text-layout id)
(mw/emit! {:cmd :index/update-text-rect
:page-id (:current-page-id @st/state)
:shape-id id
:dimensions (get-text-dimensions id)})))))
(defn process-pending!
[shapes thumbnails full]

View File

@@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.transit :as t]
[app.common.types.shape :as shape]
[app.common.types.shape.layout :as ctl]
@@ -131,66 +132,116 @@
(let [v (get shape k)
id (get shape :id)]
(case k
:parent-id (api/set-parent-id v)
:type (do
(api/set-shape-type v)
(when (or (= v :path) (= v :bool))
(api/set-shape-path-content (:content shape))))
:bool-type (api/set-shape-bool-type 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))
:rotation (api/set-shape-rotation v)
:transform (api/set-shape-transform v)
:fills (into [] (api/set-shape-fills id v false))
:strokes (into [] (api/set-shape-strokes id v false))
: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)
:parent-id
(api/set-parent-id v)
:type
(do
(api/set-shape-type v)
(when (or (= v :path) (= v :bool))
(api/set-shape-path-content (:content shape))))
:bool-type
(api/set-shape-bool-type v)
:selrect
(do
(api/set-shape-selrect v)
(when (cfh/svg-raw-shape? shape)
(api/set-shape-svg-raw-content (api/get-static-markup shape))))
:show-content
(if (cfh/frame-shape? shape)
(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 id v false))
:strokes
(into [] (api/set-shape-strokes id v false))
: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)
:r1
(api/set-shape-corners [v (dm/get-prop shape :r2) (dm/get-prop shape :r3) (dm/get-prop shape :r4)])
(api/set-shape-corners
[v
(dm/get-prop shape :r2)
(dm/get-prop shape :r3)
(dm/get-prop shape :r4)])
:r2
(api/set-shape-corners [(dm/get-prop shape :r1) v (dm/get-prop shape :r3) (dm/get-prop shape :r4)])
(api/set-shape-corners
[(dm/get-prop shape :r1)
v
(dm/get-prop shape :r3)
(dm/get-prop shape :r4)])
:r3
(api/set-shape-corners [(dm/get-prop shape :r1) (dm/get-prop shape :r2) v (dm/get-prop shape :r4)])
(api/set-shape-corners
[(dm/get-prop shape :r1)
(dm/get-prop shape :r2)
v
(dm/get-prop shape :r4)])
:r4
(api/set-shape-corners [(dm/get-prop shape :r1) (dm/get-prop shape :r2) (dm/get-prop shape :r3) v])
(api/set-shape-corners
[(dm/get-prop shape :r1)
(dm/get-prop shape :r2)
(dm/get-prop shape :r3)
v])
:svg-attrs
(when (= (:type shape) :path)
(when (cfh/path-shape? shape)
(api/set-shape-path-attrs v))
:masked-group
(when (and (= (:type shape) :group) (:masked-group shape))
(when (cfh/mask-shape? shape)
(api/set-masked (:masked-group shape)))
:content
(cond
(or (= (:type shape) :path)
(= (:type shape) :bool))
(or (cfh/path-shape? shape)
(cfh/bool-shape? shape))
(api/set-shape-path-content v)
(= (:type shape) :svg-raw)
(cfh/svg-raw-shape? shape)
(api/set-shape-svg-raw-content (api/get-static-markup shape))
(= (:type shape) :text)
(cfh/text-shape? shape)
(let [pending-thumbnails (into [] (concat (api/set-shape-text-content id v)))
pending-full (into [] (concat (api/set-shape-text-images id v)))]
;; FIXME: this is a hack to process the pending tasks asynchronously
;; we should probably modify set-wasm-attr! to return a list of callbacks to be executed in a second pass.
;; FIXME: this is a hack to process the pending tasks asynchronously
;; we should probably modify set-wasm-attr! to return a list of callbacks
;;to be executed in a second pass.
(api/process-pending! [shape] pending-thumbnails pending-full)
nil))
@@ -239,7 +290,7 @@
(ctl/flex-layout? shape)
(api/set-flex-layout shape)))
;; Property not in WASM
;; Property not in WASM
nil))))
(defn process-shape!
@@ -254,7 +305,11 @@
(vals)
(rx/from)
(rx/mapcat (fn [callback] (callback)))
(rx/reduce conj [])))
(rx/reduce conj [])
(rx/tap
(fn []
(when (cfh/text-shape? shape)
(api/update-text-rect! (:id shape)))))))
(rx/empty))))
(defn process-shape-changes!

View File

@@ -7,9 +7,12 @@
(ns app.worker.index
"Page index management within the worker."
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes :as ch]
[app.common.geom.matrix :as gmt]
[app.common.geom.rect :as grc]
[app.common.geom.shapes :as gsh]
[app.common.logging :as log]
[app.common.time :as ct]
[app.worker.impl :as impl]
@@ -41,7 +44,17 @@
(let [old-page (dm/get-in @state [:pages-index page-id])
new-page (-> state
(swap! ch/process-changes changes false)
(dm/get-in [:pages-index page-id]))]
(dm/get-in [:pages-index page-id]))
text-rects (dm/get-in @state [::text-rect page-id])
;; Update page objects with the text data
new-page
(reduce-kv
(fn [page id data]
(update-in page [:objects id] d/patch-object data))
new-page
text-rects)]
(swap! state update ::snap snap/update-page old-page new-page)
(swap! state update ::selection selection/update-page old-page new-page))
@@ -50,6 +63,31 @@
(log/dbg :hint "page index updated" :id page-id :elapsed elapsed ::log/sync? true))))
nil))
(defmethod impl/handler :index/update-text-rect
[{:keys [page-id shape-id dimensions]}]
(let [page (dm/get-in @state [:pages-index page-id])
objects (get page :objects)
shape (get objects shape-id)
center (gsh/shape->center shape)
transform (:transform shape (gmt/matrix))
rect (-> (grc/make-rect dimensions)
(grc/rect->points))
points (gsh/transform-points rect center transform)
selrect (gsh/calculate-selrect points (gsh/points->center points))
data {:position-data nil
:points points
:selrect selrect}
shape (d/patch-object shape data)
objects
(assoc objects shape-id shape)]
(swap! state update-in [::text-rect page-id] assoc shape-id data)
(swap! state update-in [::selection page-id] selection/update-index-single objects shape)
nil))
;; FIXME: schema
(defmethod impl/handler :index/query-snap

View File

@@ -105,7 +105,7 @@
index (reduce-kv #(index-shape objects parents-index clip-index %1 %3)
(qdt/create (clj->js bounds))
(dissoc objects uuid/zero))]
{:index index :bounds bounds}))
{:index index :bounds bounds :parents-index parents-index :clip-index clip-index}))
;; FIXME: optimize
(defn- update-index
@@ -140,6 +140,12 @@
(qdt/remove-all index changed-ids)
shapes)]
(assoc data :index index :parents-index parents-index :clip-index clip-index)))
(defn update-index-single
[{index :index parents-index :parents-index clip-index :clip-index :as data} objects shape]
(let [index (qdt/remove-all index [(:id shape)])
index (index-shape objects parents-index clip-index index shape)]
(assoc data :index index)))
(defn- query-index