mirror of
https://github.com/penpot/penpot.git
synced 2026-08-01 19:06:18 +00:00
🐛 Fix text selrect size (#10566)
* 🐛 Fix text selrect size * 🐛 Fix blinks on complex files --------- Co-authored-by: Belén Albeza <belen@hey.com>
This commit is contained in:
parent
88186eaec1
commit
d656d342fe
@ -65,6 +65,7 @@
|
|||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
[app.main.data.workspace.variants :as dwva]
|
[app.main.data.workspace.variants :as dwva]
|
||||||
[app.main.data.workspace.viewport :as dwv]
|
[app.main.data.workspace.viewport :as dwv]
|
||||||
|
[app.main.data.workspace.wasm-text :as dwwt]
|
||||||
[app.main.data.workspace.zoom :as dwz]
|
[app.main.data.workspace.zoom :as dwz]
|
||||||
[app.main.errors]
|
[app.main.errors]
|
||||||
[app.main.features :as features]
|
[app.main.features :as features]
|
||||||
@ -441,6 +442,14 @@
|
|||||||
;; Keep comment thread positions in sync on undo/redo
|
;; Keep comment thread positions in sync on undo/redo
|
||||||
(rx/of (dwcm/watch-comment-thread-position-changes stoper-s))
|
(rx/of (dwcm/watch-comment-thread-position-changes stoper-s))
|
||||||
|
|
||||||
|
;; Resize auto-grow text shapes whose selrect does not match
|
||||||
|
;; the WASM text layout once their fonts finish loading.
|
||||||
|
(->> stream
|
||||||
|
(rx/filter (ptk/type? :app.render-wasm.api/stale-text-selrects))
|
||||||
|
(rx/map deref)
|
||||||
|
(rx/map (fn [{:keys [ids]}]
|
||||||
|
(dwwt/resize-wasm-text-all ids))))
|
||||||
|
|
||||||
(let [local-commits-s
|
(let [local-commits-s
|
||||||
(->> stream
|
(->> stream
|
||||||
(rx/filter dch/commit?)
|
(rx/filter dch/commit?)
|
||||||
|
|||||||
@ -42,12 +42,14 @@
|
|||||||
(wasm.api/set-shape-text-images id content)
|
(wasm.api/set-shape-text-images id content)
|
||||||
(let [dimension (when (not= :fixed grow-type)
|
(let [dimension (when (not= :fixed grow-type)
|
||||||
(wasm.api/get-text-dimensions))]
|
(wasm.api/get-text-dimensions))]
|
||||||
{:width (if (#{:fixed :auto-height} grow-type)
|
;; nil dimension = shape not present in WASM state; skip the resize.
|
||||||
(:width selrect)
|
(when (or (= :fixed grow-type) (some? dimension))
|
||||||
(:width dimension))
|
{:width (if (#{:fixed :auto-height} grow-type)
|
||||||
:height (if (= :fixed grow-type)
|
(:width selrect)
|
||||||
(:height selrect)
|
(:width dimension))
|
||||||
(:height dimension))}))))
|
:height (if (= :fixed grow-type)
|
||||||
|
(:height selrect)
|
||||||
|
(:height dimension))})))))
|
||||||
|
|
||||||
(defn resize-wasm-text-modifiers
|
(defn resize-wasm-text-modifiers
|
||||||
([shape]
|
([shape]
|
||||||
|
|||||||
@ -573,15 +573,19 @@
|
|||||||
(wasm.api/push-ruler-theme-colors!)
|
(wasm.api/push-ruler-theme-colors!)
|
||||||
(wasm.api/request-render "rulers-colors-theme")))))
|
(wasm.api/request-render "rulers-colors-theme")))))
|
||||||
|
|
||||||
|
;; Ruler overlay updates below only change the UI surface, not the shapes.
|
||||||
|
;; They use `render-from-cache!` (cached tiles + UI, atomic) instead of a full
|
||||||
|
;; `request-render`, which would kick off a progressive tile-by-tile shape
|
||||||
|
;; re-render that flashes on zoomed-in views (see penpot ruler-selection flash).
|
||||||
(mf/with-effect [@canvas-init? frame-visible?]
|
(mf/with-effect [@canvas-init? frame-visible?]
|
||||||
(when @canvas-init?
|
(when @canvas-init?
|
||||||
(wasm.api/set-rulers-frame-visible! frame-visible?)
|
(wasm.api/set-rulers-frame-visible! frame-visible?)
|
||||||
(wasm.api/request-render "rulers-frame")))
|
(wasm.api/render-from-cache!)))
|
||||||
|
|
||||||
(mf/with-effect [@canvas-init? show-rulers?]
|
(mf/with-effect [@canvas-init? show-rulers?]
|
||||||
(when @canvas-init?
|
(when @canvas-init?
|
||||||
(wasm.api/set-rulers-visible! show-rulers?)
|
(wasm.api/set-rulers-visible! show-rulers?)
|
||||||
(wasm.api/request-render "rulers-visible")))
|
(wasm.api/render-from-cache!)))
|
||||||
|
|
||||||
(mf/with-effect [@canvas-init? show-rulers? offset-x offset-y]
|
(mf/with-effect [@canvas-init? show-rulers? offset-x offset-y]
|
||||||
(when (and @canvas-init? show-rulers?)
|
(when (and @canvas-init? show-rulers?)
|
||||||
@ -592,7 +596,7 @@
|
|||||||
(some-> ruler-selection :width) (some-> ruler-selection :height)]
|
(some-> ruler-selection :width) (some-> ruler-selection :height)]
|
||||||
(when (and @canvas-init? show-rulers?)
|
(when (and @canvas-init? show-rulers?)
|
||||||
(wasm.api/set-rulers-selection! ruler-selection)
|
(wasm.api/set-rulers-selection! ruler-selection)
|
||||||
(wasm.api/request-render "rulers-selection")))
|
(wasm.api/render-from-cache!)))
|
||||||
|
|
||||||
;; Paint background + rulers instantly, before shapes finish loading. Runs
|
;; Paint background + rulers instantly, before shapes finish loading. Runs
|
||||||
;; after the ruler push effects so the WASM ruler state is already set.
|
;; after the ruler push effects so the WASM ruler state is already set.
|
||||||
|
|||||||
@ -58,6 +58,7 @@
|
|||||||
[app.util.timers :as timers]
|
[app.util.timers :as timers]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
|
[potok.v2.core :as ptk]
|
||||||
[promesa.core :as p]
|
[promesa.core :as p]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
@ -459,6 +460,20 @@
|
|||||||
(when (and wasm/context-initialized? (not @wasm/context-lost?))
|
(when (and wasm/context-initialized? (not @wasm/context-lost?))
|
||||||
(h/call wasm/internal-module "_render_ui_only")))
|
(h/call wasm/internal-module "_render_ui_only")))
|
||||||
|
|
||||||
|
(defn render-from-cache!
|
||||||
|
"Blit the shapes from the cached tile atlas and redraw the UI overlay
|
||||||
|
(rulers, selection band) fresh on top, in a single atomic frame. The
|
||||||
|
*shapes* are the cached part (already-rasterized tiles, not rebuilt); the UI
|
||||||
|
is re-rendered every call, which is what lets it reflect a new selection.
|
||||||
|
|
||||||
|
Use for UI-only updates that don't change shapes (e.g. the ruler selection
|
||||||
|
band): unlike `request-render`, it never kicks off a progressive,
|
||||||
|
tile-by-tile shape re-render, so it does not flash on zoomed-in views where
|
||||||
|
the scene spans multiple tiles."
|
||||||
|
[]
|
||||||
|
(when (and wasm/context-initialized? (not @wasm/context-lost?))
|
||||||
|
(h/call wasm/internal-module "_render_from_cache" 0)))
|
||||||
|
|
||||||
;; CSS-pixel blur radius for the page-transition snapshot (DPR-scaled in WASM).
|
;; CSS-pixel blur radius for the page-transition snapshot (DPR-scaled in WASM).
|
||||||
(def ^:private TRANSITION_BLUR_RADIUS 4.0)
|
(def ^:private TRANSITION_BLUR_RADIUS 4.0)
|
||||||
|
|
||||||
@ -1282,17 +1297,19 @@
|
|||||||
([]
|
([]
|
||||||
(if-not (initialized?)
|
(if-not (initialized?)
|
||||||
{:x 0 :y 0 :width 0 :height 0 :max-width 0}
|
{:x 0 :y 0 :width 0 :height 0 :max-width 0}
|
||||||
(let [offset (-> (h/call wasm/internal-module "_get_text_dimensions")
|
(let [ptr (h/call wasm/internal-module "_get_text_dimensions")]
|
||||||
(mem/->offset-32))
|
;; NULL pointer when there is no current shape or it is not a text.
|
||||||
heapf32 (mem/get-heap-f32)
|
(when-not (zero? ptr)
|
||||||
width (aget heapf32 (+ offset 0))
|
(let [offset (mem/->offset-32 ptr)
|
||||||
height (aget heapf32 (+ offset 1))
|
heapf32 (mem/get-heap-f32)
|
||||||
max-width (aget heapf32 (+ offset 2))
|
width (aget heapf32 (+ offset 0))
|
||||||
|
height (aget heapf32 (+ offset 1))
|
||||||
|
max-width (aget heapf32 (+ offset 2))
|
||||||
|
|
||||||
x (aget heapf32 (+ offset 3))
|
x (aget heapf32 (+ offset 3))
|
||||||
y (aget heapf32 (+ offset 4))]
|
y (aget heapf32 (+ offset 4))]
|
||||||
(mem/free)
|
(mem/free)
|
||||||
{:x x :y y :width width :height height :max-width max-width}))))
|
{:x x :y y :width width :height height :max-width max-width}))))))
|
||||||
|
|
||||||
(defn intersect-position-in-shape
|
(defn intersect-position-in-shape
|
||||||
[id position]
|
[id position]
|
||||||
@ -1452,19 +1469,45 @@
|
|||||||
[text-ids]
|
[text-ids]
|
||||||
(run! f/force-update-text-layout text-ids))
|
(run! f/force-update-text-layout text-ids))
|
||||||
|
|
||||||
|
(defn- text-selrect-stale?
|
||||||
|
"Check if the WASM-measured dimensions of an auto-grow text shape differ
|
||||||
|
from its stored selrect (same 0.1px tolerance as the classic renderer)."
|
||||||
|
[{:keys [id selrect grow-type]}]
|
||||||
|
(when-let [{:keys [width height]} (get-text-dimensions id)]
|
||||||
|
(case grow-type
|
||||||
|
:auto-width (or (not (mth/close? width (:width selrect) 0.1))
|
||||||
|
(not (mth/close? height (:height selrect) 0.1)))
|
||||||
|
:auto-height (not (mth/close? height (:height selrect) 0.1))
|
||||||
|
false)))
|
||||||
|
|
||||||
|
(defn- sync-stale-text-selrects!
|
||||||
|
"Emit the ids of auto-grow text shapes whose selrect no longer matches the
|
||||||
|
measured layout, so the workspace resizes them (data-event instead of a
|
||||||
|
direct call to avoid a circular dependency; see the watcher in
|
||||||
|
`app.main.data.workspace/initialize-workspace`)."
|
||||||
|
[shapes]
|
||||||
|
(let [stale-ids (into []
|
||||||
|
(comp (filter cfh/text-shape?)
|
||||||
|
(filter (comp #{:auto-width :auto-height} :grow-type))
|
||||||
|
(filter text-selrect-stale?)
|
||||||
|
(map :id))
|
||||||
|
shapes)]
|
||||||
|
(when (seq stale-ids)
|
||||||
|
(st/emit! (ptk/data-event ::stale-text-selrects {:ids stale-ids})))))
|
||||||
|
|
||||||
(defn- relayout-after-fonts!
|
(defn- relayout-after-fonts!
|
||||||
"Relayout text shapes once their pending fonts have resolved. Shapes in
|
"Relayout text shapes once their pending fonts have resolved. Font fetches
|
||||||
`font-pending-ids` had a font fetched, so they get a forced relayout to pick
|
are deduped per URL and storing a font does not invalidate cached layouts,
|
||||||
up the real glyph metrics; the remaining text shapes get a normal layout."
|
so every text shape (not only the fetch triggers in `font-pending-ids`)
|
||||||
|
needs a forced relayout; then re-sync selrects that drifted."
|
||||||
[shapes font-pending-ids]
|
[shapes font-pending-ids]
|
||||||
(let [force-ids (set font-pending-ids)
|
(let [text-ids (into [] (comp (filter cfh/text-shape?) (map :id)) shapes)]
|
||||||
text-ids (into [] (comp (filter cfh/text-shape?) (map :id)) shapes)
|
(when (seq text-ids)
|
||||||
forced (filterv force-ids text-ids)
|
(if (seq font-pending-ids)
|
||||||
rest-ids (filterv (complement force-ids) text-ids)]
|
(do
|
||||||
(when (seq forced)
|
(force-update-text-layouts text-ids)
|
||||||
(force-update-text-layouts forced))
|
(sync-stale-text-selrects! shapes))
|
||||||
(when (seq rest-ids)
|
(update-text-layouts text-ids)))))
|
||||||
(update-text-layouts rest-ids))))
|
|
||||||
|
|
||||||
(defn process-pending
|
(defn process-pending
|
||||||
[shapes thumbnails full font-pending-ids on-complete]
|
[shapes thumbnails full font-pending-ids on-complete]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user