mirror of
https://github.com/penpot/penpot.git
synced 2025-12-11 22:14:05 +01:00
🐛 Fix problem with selection and text shapes for new render
This commit is contained in:
@@ -72,9 +72,11 @@
|
|||||||
(= :bool (dm/get-prop shape :type))))
|
(= :bool (dm/get-prop shape :type))))
|
||||||
|
|
||||||
(defn text-shape?
|
(defn text-shape?
|
||||||
[shape]
|
([shape]
|
||||||
(and (some? shape)
|
(and (some? shape)
|
||||||
(= :text (dm/get-prop shape :type))))
|
(= :text (dm/get-prop shape :type))))
|
||||||
|
([objects id]
|
||||||
|
(text-shape? (get objects id))))
|
||||||
|
|
||||||
(defn rect-shape?
|
(defn rect-shape?
|
||||||
[shape]
|
[shape]
|
||||||
|
|||||||
@@ -162,6 +162,7 @@
|
|||||||
(dm/export gtr/inverse-transform-matrix)
|
(dm/export gtr/inverse-transform-matrix)
|
||||||
(dm/export gtr/transform-rect)
|
(dm/export gtr/transform-rect)
|
||||||
(dm/export gtr/calculate-geometry)
|
(dm/export gtr/calculate-geometry)
|
||||||
|
(dm/export gtr/calculate-selrect)
|
||||||
(dm/export gtr/update-group-selrect)
|
(dm/export gtr/update-group-selrect)
|
||||||
(dm/export gtr/update-mask-selrect)
|
(dm/export gtr/update-mask-selrect)
|
||||||
(dm/export gtr/apply-transform)
|
(dm/export gtr/apply-transform)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
[app.main.data.workspace :as-alias dw]
|
[app.main.data.workspace :as-alias dw]
|
||||||
[app.main.router :as rt]
|
[app.main.router :as rt]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.main.worker]
|
||||||
[app.util.globals :as glob]
|
[app.util.globals :as glob]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[app.util.timers :as ts]
|
[app.util.timers :as ts]
|
||||||
@@ -94,6 +95,9 @@
|
|||||||
(let [data (exception->error-data error)]
|
(let [data (exception->error-data error)]
|
||||||
(ptk/handle-error data))))
|
(ptk/handle-error data))))
|
||||||
|
|
||||||
|
;; Inject dependency to remove circular dependency
|
||||||
|
(set! app.main.worker/on-error on-error)
|
||||||
|
|
||||||
;; Set the main potok error handler
|
;; Set the main potok error handler
|
||||||
(reset! st/on-error on-error)
|
(reset! st/on-error on-error)
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
[app.main.ui.workspace.viewport.actions :as actions]
|
[app.main.ui.workspace.viewport.actions :as actions]
|
||||||
[app.main.ui.workspace.viewport.utils :as utils]
|
[app.main.ui.workspace.viewport.utils :as utils]
|
||||||
[app.main.worker :as mw]
|
[app.main.worker :as mw]
|
||||||
|
[app.render-wasm.api :as wasm.api]
|
||||||
[app.util.debug :as dbg]
|
[app.util.debug :as dbg]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.globals :as globals]
|
[app.util.globals :as globals]
|
||||||
@@ -311,7 +312,9 @@
|
|||||||
ids)]
|
ids)]
|
||||||
(filter #(or (root-frame-with-data? %)
|
(filter #(or (root-frame-with-data? %)
|
||||||
(and (cfh/group-shape? objects %)
|
(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
|
remove-measure-xf
|
||||||
(cond
|
(cond
|
||||||
|
|||||||
@@ -8,15 +8,17 @@
|
|||||||
"Interface to communicate with the web worker"
|
"Interface to communicate with the web worker"
|
||||||
(:require
|
(:require
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.main.errors :as errors]
|
|
||||||
[app.util.worker :as uw]
|
[app.util.worker :as uw]
|
||||||
[beicon.v2.core :as rx]))
|
[beicon.v2.core :as rx]))
|
||||||
|
|
||||||
|
;; Injected from `app.main.errors` to remove circular dependency
|
||||||
|
(defonce on-error nil)
|
||||||
|
|
||||||
(defonce instance nil)
|
(defonce instance nil)
|
||||||
|
|
||||||
(defn init!
|
(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
|
(uw/ask! worker {:cmd :configure
|
||||||
:config {:public-uri cf/public-uri
|
:config {:public-uri cf/public-uri
|
||||||
:build-data cf/build-date
|
:build-data cf/build-date
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
[app.main.fonts :as fonts]
|
[app.main.fonts :as fonts]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.render :as render]
|
[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.fonts :as f]
|
||||||
[app.render-wasm.api.texts :as t]
|
[app.render-wasm.api.texts :as t]
|
||||||
[app.render-wasm.deserializers :as dr]
|
[app.render-wasm.deserializers :as dr]
|
||||||
@@ -107,6 +109,15 @@
|
|||||||
(reset! pending-render false)
|
(reset! pending-render false)
|
||||||
(render ts)))))
|
(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
|
(defn- ensure-text-content
|
||||||
"Guarantee that the shape always sends a valid text tree to WASM. When the
|
"Guarantee that the shape always sends a valid text tree to WASM. When the
|
||||||
@@ -813,9 +824,25 @@
|
|||||||
heapf32 (mem/get-heap-f32)
|
heapf32 (mem/get-heap-f32)
|
||||||
width (aget heapf32 (+ offset 0))
|
width (aget heapf32 (+ offset 0))
|
||||||
height (aget heapf32 (+ offset 1))
|
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)
|
(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
|
(defn set-view-box
|
||||||
[zoom vbox]
|
[zoom vbox]
|
||||||
@@ -922,7 +949,13 @@
|
|||||||
(->> shapes
|
(->> shapes
|
||||||
(filter cfh/text-shape?)
|
(filter cfh/text-shape?)
|
||||||
(map :id)
|
(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!
|
(defn process-pending!
|
||||||
[shapes thumbnails full]
|
[shapes thumbnails full]
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.transit :as t]
|
[app.common.transit :as t]
|
||||||
[app.common.types.shape :as shape]
|
[app.common.types.shape :as shape]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
@@ -131,66 +132,116 @@
|
|||||||
(let [v (get shape k)
|
(let [v (get shape k)
|
||||||
id (get shape :id)]
|
id (get shape :id)]
|
||||||
(case k
|
(case k
|
||||||
:parent-id (api/set-parent-id v)
|
:parent-id
|
||||||
:type (do
|
(api/set-parent-id v)
|
||||||
(api/set-shape-type v)
|
|
||||||
(when (or (= v :path) (= v :bool))
|
:type
|
||||||
(api/set-shape-path-content (:content shape))))
|
(do
|
||||||
:bool-type (api/set-shape-bool-type v)
|
(api/set-shape-type v)
|
||||||
:selrect (do
|
(when (or (= v :path) (= v :bool))
|
||||||
(api/set-shape-selrect v)
|
(api/set-shape-path-content (:content shape))))
|
||||||
(when (= (:type shape) :svg-raw)
|
|
||||||
(api/set-shape-svg-raw-content (api/get-static-markup shape))))
|
:bool-type
|
||||||
:show-content (if (= (:type shape) :frame)
|
(api/set-shape-bool-type v)
|
||||||
(api/set-shape-clip-content (not v))
|
|
||||||
(api/set-shape-clip-content false))
|
:selrect
|
||||||
:rotation (api/set-shape-rotation v)
|
(do
|
||||||
:transform (api/set-shape-transform v)
|
(api/set-shape-selrect v)
|
||||||
:fills (into [] (api/set-shape-fills id v false))
|
(when (cfh/svg-raw-shape? shape)
|
||||||
:strokes (into [] (api/set-shape-strokes id v false))
|
(api/set-shape-svg-raw-content (api/get-static-markup shape))))
|
||||||
:blend-mode (api/set-shape-blend-mode v)
|
|
||||||
:opacity (api/set-shape-opacity v)
|
:show-content
|
||||||
:hidden (api/set-shape-hidden v)
|
(if (cfh/frame-shape? shape)
|
||||||
:shapes (api/set-shape-children v)
|
(api/set-shape-clip-content (not v))
|
||||||
:blur (api/set-shape-blur v)
|
(api/set-shape-clip-content false))
|
||||||
:shadow (api/set-shape-shadows v)
|
|
||||||
:constraints-h (api/set-constraints-h v)
|
:rotation
|
||||||
:constraints-v (api/set-constraints-v v)
|
(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
|
: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
|
: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
|
: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
|
: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
|
:svg-attrs
|
||||||
(when (= (:type shape) :path)
|
(when (cfh/path-shape? shape)
|
||||||
(api/set-shape-path-attrs v))
|
(api/set-shape-path-attrs v))
|
||||||
|
|
||||||
:masked-group
|
:masked-group
|
||||||
(when (and (= (:type shape) :group) (:masked-group shape))
|
(when (cfh/mask-shape? shape)
|
||||||
(api/set-masked (:masked-group shape)))
|
(api/set-masked (:masked-group shape)))
|
||||||
|
|
||||||
:content
|
:content
|
||||||
(cond
|
(cond
|
||||||
(or (= (:type shape) :path)
|
(or (cfh/path-shape? shape)
|
||||||
(= (:type shape) :bool))
|
(cfh/bool-shape? shape))
|
||||||
(api/set-shape-path-content v)
|
(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))
|
(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)))
|
(let [pending-thumbnails (into [] (concat (api/set-shape-text-content id v)))
|
||||||
pending-full (into [] (concat (api/set-shape-text-images id v)))]
|
pending-full (into [] (concat (api/set-shape-text-images id v)))]
|
||||||
;; FIXME: this is a hack to process the pending tasks asynchronously
|
;; 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.
|
;; 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)
|
(api/process-pending! [shape] pending-thumbnails pending-full)
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
@@ -239,7 +290,7 @@
|
|||||||
(ctl/flex-layout? shape)
|
(ctl/flex-layout? shape)
|
||||||
(api/set-flex-layout shape)))
|
(api/set-flex-layout shape)))
|
||||||
|
|
||||||
;; Property not in WASM
|
;; Property not in WASM
|
||||||
nil))))
|
nil))))
|
||||||
|
|
||||||
(defn process-shape!
|
(defn process-shape!
|
||||||
@@ -254,7 +305,11 @@
|
|||||||
(vals)
|
(vals)
|
||||||
(rx/from)
|
(rx/from)
|
||||||
(rx/mapcat (fn [callback] (callback)))
|
(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))))
|
(rx/empty))))
|
||||||
|
|
||||||
(defn process-shape-changes!
|
(defn process-shape-changes!
|
||||||
|
|||||||
@@ -7,9 +7,12 @@
|
|||||||
(ns app.worker.index
|
(ns app.worker.index
|
||||||
"Page index management within the worker."
|
"Page index management within the worker."
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.changes :as ch]
|
[app.common.files.changes :as ch]
|
||||||
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.rect :as grc]
|
[app.common.geom.rect :as grc]
|
||||||
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.time :as ct]
|
[app.common.time :as ct]
|
||||||
[app.worker.impl :as impl]
|
[app.worker.impl :as impl]
|
||||||
@@ -41,7 +44,17 @@
|
|||||||
(let [old-page (dm/get-in @state [:pages-index page-id])
|
(let [old-page (dm/get-in @state [:pages-index page-id])
|
||||||
new-page (-> state
|
new-page (-> state
|
||||||
(swap! ch/process-changes changes false)
|
(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 ::snap snap/update-page old-page new-page)
|
||||||
(swap! state update ::selection selection/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))))
|
(log/dbg :hint "page index updated" :id page-id :elapsed elapsed ::log/sync? true))))
|
||||||
nil))
|
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
|
;; FIXME: schema
|
||||||
|
|
||||||
(defmethod impl/handler :index/query-snap
|
(defmethod impl/handler :index/query-snap
|
||||||
|
|||||||
@@ -105,7 +105,7 @@
|
|||||||
index (reduce-kv #(index-shape objects parents-index clip-index %1 %3)
|
index (reduce-kv #(index-shape objects parents-index clip-index %1 %3)
|
||||||
(qdt/create (clj->js bounds))
|
(qdt/create (clj->js bounds))
|
||||||
(dissoc objects uuid/zero))]
|
(dissoc objects uuid/zero))]
|
||||||
{:index index :bounds bounds}))
|
{:index index :bounds bounds :parents-index parents-index :clip-index clip-index}))
|
||||||
|
|
||||||
;; FIXME: optimize
|
;; FIXME: optimize
|
||||||
(defn- update-index
|
(defn- update-index
|
||||||
@@ -140,6 +140,12 @@
|
|||||||
(qdt/remove-all index changed-ids)
|
(qdt/remove-all index changed-ids)
|
||||||
shapes)]
|
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)))
|
(assoc data :index index)))
|
||||||
|
|
||||||
(defn- query-index
|
(defn- query-index
|
||||||
|
|||||||
@@ -6,13 +6,16 @@ use crate::{
|
|||||||
|
|
||||||
use core::f32;
|
use core::f32;
|
||||||
use macros::ToJs;
|
use macros::ToJs;
|
||||||
|
use skia_safe::textlayout::{RectHeightStyle, RectWidthStyle};
|
||||||
use skia_safe::{
|
use skia_safe::{
|
||||||
self as skia,
|
self as skia,
|
||||||
paint::{self, Paint},
|
paint::{self, Paint},
|
||||||
textlayout::ParagraphBuilder,
|
textlayout::ParagraphBuilder,
|
||||||
textlayout::ParagraphStyle,
|
textlayout::ParagraphStyle,
|
||||||
textlayout::PositionWithAffinity,
|
textlayout::PositionWithAffinity,
|
||||||
|
Contains,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use super::FontFamily;
|
use super::FontFamily;
|
||||||
@@ -182,6 +185,24 @@ impl TextContentLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the current x,y (in paragraph relative coordinates) is inside
|
||||||
|
* the paragraph
|
||||||
|
*/
|
||||||
|
fn intersects(paragraph: &skia_safe::textlayout::Paragraph, x: f32, y: f32) -> bool {
|
||||||
|
if y < 0.0 || y > paragraph.height() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pos = paragraph.get_glyph_position_at_coordinate((x, y));
|
||||||
|
let idx = pos.position as usize;
|
||||||
|
|
||||||
|
let rects =
|
||||||
|
paragraph.get_rects_for_range(0..idx + 1, RectHeightStyle::Tight, RectWidthStyle::Tight);
|
||||||
|
|
||||||
|
rects.iter().any(|r| r.rect.contains(&Point::new(x, y)))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct TextContent {
|
pub struct TextContent {
|
||||||
pub paragraphs: Vec<Paragraph>,
|
pub paragraphs: Vec<Paragraph>,
|
||||||
@@ -304,6 +325,32 @@ impl TextContent {
|
|||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn content_rect(&self, selrect: &Rect, valign: VerticalAlign) -> Rect {
|
||||||
|
let x = selrect.x();
|
||||||
|
let mut y = selrect.y();
|
||||||
|
|
||||||
|
let width = if self.grow_type() == GrowType::AutoWidth {
|
||||||
|
self.size.width
|
||||||
|
} else {
|
||||||
|
selrect.width()
|
||||||
|
};
|
||||||
|
|
||||||
|
let height = if self.size.width.round() != width.round() {
|
||||||
|
self.get_height(width)
|
||||||
|
} else {
|
||||||
|
self.size.height
|
||||||
|
};
|
||||||
|
|
||||||
|
let offset_y = match valign {
|
||||||
|
VerticalAlign::Center => (selrect.height() - height) / 2.0,
|
||||||
|
VerticalAlign::Bottom => selrect.height() - height,
|
||||||
|
_ => 0.0,
|
||||||
|
};
|
||||||
|
y += offset_y;
|
||||||
|
|
||||||
|
Rect::from_xywh(x, y, width, height)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn transform(&mut self, transform: &Matrix) {
|
pub fn transform(&mut self, transform: &Matrix) {
|
||||||
let left = self.bounds.left();
|
let left = self.bounds.left();
|
||||||
let right = self.bounds.right();
|
let right = self.bounds.right();
|
||||||
@@ -645,6 +692,42 @@ impl TextContent {
|
|||||||
|
|
||||||
(fallback_width, fallback_height)
|
(fallback_width, fallback_height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn intersect_position(&self, shape: &Shape, x_pos: f32, y_pos: f32) -> bool {
|
||||||
|
let rect = self.content_rect(&shape.selrect, shape.vertical_align);
|
||||||
|
let mut matrix = Matrix::new_identity();
|
||||||
|
let center = shape.center();
|
||||||
|
let Some(inv_transform) = &shape.transform.invert() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
matrix.pre_translate(center);
|
||||||
|
matrix.pre_concat(inv_transform);
|
||||||
|
matrix.pre_translate(-center);
|
||||||
|
|
||||||
|
let result = matrix.map_point((x_pos, y_pos));
|
||||||
|
|
||||||
|
// Change coords to content space
|
||||||
|
let x_pos = result.x - rect.x();
|
||||||
|
let y_pos = result.y - rect.y();
|
||||||
|
|
||||||
|
let width = self.width();
|
||||||
|
let mut paragraph_builders = self.paragraph_builder_group_from_text(None);
|
||||||
|
let paragraphs =
|
||||||
|
self.build_paragraphs_from_paragraph_builders(&mut paragraph_builders, width);
|
||||||
|
|
||||||
|
paragraphs
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.scan(
|
||||||
|
(0 as f32, None::<skia::textlayout::Paragraph>),
|
||||||
|
|(height, _), p| {
|
||||||
|
let prev_height = *height;
|
||||||
|
*height += p.height();
|
||||||
|
Some((prev_height, p))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.any(|(height, p)| intersects(p, x_pos, y_pos - height))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TextContent {
|
impl Default for TextContent {
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ use crate::shapes::{
|
|||||||
self, GrowType, Shape, TextAlign, TextDecoration, TextDirection, TextTransform, Type,
|
self, GrowType, Shape, TextAlign, TextDecoration, TextDirection, TextTransform, Type,
|
||||||
};
|
};
|
||||||
use crate::utils::{uuid_from_u32, uuid_from_u32_quartet};
|
use crate::utils::{uuid_from_u32, uuid_from_u32_quartet};
|
||||||
use crate::{with_current_shape_mut, with_state_mut, with_state_mut_current_shape, STATE};
|
use crate::{
|
||||||
|
with_current_shape_mut, with_state, with_state_mut, with_state_mut_current_shape, STATE,
|
||||||
|
};
|
||||||
|
|
||||||
const RAW_SPAN_DATA_SIZE: usize = std::mem::size_of::<RawTextSpan>();
|
const RAW_SPAN_DATA_SIZE: usize = std::mem::size_of::<RawTextSpan>();
|
||||||
const RAW_PARAGRAPH_DATA_SIZE: usize = std::mem::size_of::<RawParagraphData>();
|
const RAW_PARAGRAPH_DATA_SIZE: usize = std::mem::size_of::<RawParagraphData>();
|
||||||
@@ -312,14 +314,23 @@ pub extern "C" fn set_shape_grow_type(grow_type: u8) {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn get_text_dimensions() -> *mut u8 {
|
pub extern "C" fn get_text_dimensions() -> *mut u8 {
|
||||||
let mut ptr = std::ptr::null_mut();
|
let mut ptr = std::ptr::null_mut();
|
||||||
|
|
||||||
with_current_shape_mut!(state, |shape: &mut Shape| {
|
with_current_shape_mut!(state, |shape: &mut Shape| {
|
||||||
if let Type::Text(content) = &mut shape.shape_type {
|
if let Type::Text(content) = &mut shape.shape_type {
|
||||||
let text_content_size = content.update_layout(shape.selrect);
|
let text_content_size = content.update_layout(shape.selrect);
|
||||||
|
|
||||||
let mut bytes = vec![0; 12];
|
// Sacar de aqui x, y, width, height
|
||||||
|
let rect = content.content_rect(&shape.selrect, shape.vertical_align);
|
||||||
|
|
||||||
|
let mut bytes = vec![0; 20];
|
||||||
bytes[0..4].clone_from_slice(&text_content_size.width.to_le_bytes());
|
bytes[0..4].clone_from_slice(&text_content_size.width.to_le_bytes());
|
||||||
bytes[4..8].clone_from_slice(&text_content_size.height.to_le_bytes());
|
bytes[4..8].clone_from_slice(&text_content_size.height.to_le_bytes());
|
||||||
bytes[8..12].clone_from_slice(&text_content_size.max_width.to_le_bytes());
|
bytes[8..12].clone_from_slice(&text_content_size.max_width.to_le_bytes());
|
||||||
|
|
||||||
|
// veamos
|
||||||
|
bytes[12..16].clone_from_slice(&rect.x().to_le_bytes());
|
||||||
|
bytes[16..20].clone_from_slice(&rect.y().to_le_bytes());
|
||||||
|
|
||||||
ptr = mem::write_bytes(bytes)
|
ptr = mem::write_bytes(bytes)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -329,6 +340,27 @@ pub extern "C" fn get_text_dimensions() -> *mut u8 {
|
|||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn intersect_position(
|
||||||
|
a: u32,
|
||||||
|
b: u32,
|
||||||
|
c: u32,
|
||||||
|
d: u32,
|
||||||
|
x_pos: f32,
|
||||||
|
y_pos: f32,
|
||||||
|
) -> bool {
|
||||||
|
with_state!(state, {
|
||||||
|
let id = uuid_from_u32_quartet(a, b, c, d);
|
||||||
|
let Some(shape) = state.shapes.get(&id) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if let Type::Text(content) = &shape.shape_type {
|
||||||
|
return content.intersect_position(shape, x_pos, y_pos);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn update_text_layout(shape: &mut Shape) {
|
fn update_text_layout(shape: &mut Shape) {
|
||||||
if let Type::Text(text_content) = &mut shape.shape_type {
|
if let Type::Text(text_content) = &mut shape.shape_type {
|
||||||
text_content.update_layout(shape.selrect);
|
text_content.update_layout(shape.selrect);
|
||||||
|
|||||||
Reference in New Issue
Block a user