mirror of
https://github.com/penpot/penpot.git
synced 2026-10-28 21:40:34 +00:00
🐛 Relayout text whose font is fetched for another shape
Font fetches are shared per URL, so a text that asks for a face already being fetched gets no fetch callback. process-pending only relaid out text after its own callbacks, so such a text kept its fallback-font layout. Auto-grow text recovered through the reflow resize, but fixed text stayed wrong until another change. Wait for the text's pending faces to be stored or to fail before the relayout, whoever started their fetch. AI-assisted-by: claude-opus-5-5
This commit is contained in:
parent
e97b85cf1b
commit
4a25c0bf94
@ -1788,29 +1788,48 @@
|
||||
(sync-stale-text-selrects! affected-shapes))
|
||||
(update-text-layouts text-ids)))))
|
||||
|
||||
(defn- await-pending-faces
|
||||
"Completes once every face in `pending-faces` is stored or has failed.
|
||||
Waits on the faces, not on fetch callbacks: a face whose URL is already
|
||||
being fetched for another shape yields no callback for this one."
|
||||
[pending-faces]
|
||||
(if (empty? pending-faces)
|
||||
(rx/empty)
|
||||
(->> (rx/merge f/font-stored-stream f/font-storage-failed-stream)
|
||||
(rx/filter pending-faces)
|
||||
(rx/scan disj pending-faces)
|
||||
(rx/filter empty?)
|
||||
(rx/take 1)
|
||||
(rx/ignore))))
|
||||
|
||||
(defn process-pending
|
||||
[shapes thumbnails full text-font-state on-complete]
|
||||
(let [pending-thumbnails
|
||||
(d/index-by :key :callback thumbnails)
|
||||
|
||||
pending-full
|
||||
(d/index-by :key :callback full)]
|
||||
(d/index-by :key :callback full)
|
||||
|
||||
pending-faces
|
||||
(:pending-faces text-font-state)]
|
||||
|
||||
;; Run text layouts synchronously so shapes are immediately correct.
|
||||
(let [text-ids (into [] (comp (filter cfh/text-shape?) (map :id)) shapes)]
|
||||
(when (seq text-ids)
|
||||
(update-text-layouts text-ids)))
|
||||
|
||||
(if (or (seq pending-thumbnails) (seq pending-full))
|
||||
(->> (rx/concat
|
||||
(->> (rx/from (vals pending-thumbnails))
|
||||
(rx/merge-map (fn [callback] (if (fn? callback) (callback) (rx/empty))))
|
||||
(rx/reduce conj [])
|
||||
(rx/catch #(rx/empty)))
|
||||
(->> (rx/from (vals pending-full))
|
||||
(rx/mapcat (fn [callback] (if (fn? callback) (callback) (rx/empty))))
|
||||
(rx/reduce conj [])
|
||||
(rx/catch #(rx/empty))))
|
||||
(if (or (seq pending-thumbnails) (seq pending-full) (seq pending-faces))
|
||||
(->> (rx/merge
|
||||
(rx/concat
|
||||
(->> (rx/from (vals pending-thumbnails))
|
||||
(rx/merge-map (fn [callback] (if (fn? callback) (callback) (rx/empty))))
|
||||
(rx/reduce conj [])
|
||||
(rx/catch #(rx/empty)))
|
||||
(->> (rx/from (vals pending-full))
|
||||
(rx/mapcat (fn [callback] (if (fn? callback) (callback) (rx/empty))))
|
||||
(rx/reduce conj [])
|
||||
(rx/catch #(rx/empty))))
|
||||
(await-pending-faces pending-faces))
|
||||
(rx/subs!
|
||||
noop-fn
|
||||
noop-fn
|
||||
@ -1818,7 +1837,7 @@
|
||||
(relayout-after-fonts! shapes text-font-state)
|
||||
(request-render "images-loaded")
|
||||
(when (fn? on-complete) (on-complete)))))
|
||||
;; No pending images — complete immediately.
|
||||
;; No pending images or faces: complete immediately.
|
||||
(when on-complete (on-complete)))))
|
||||
|
||||
(defn process-objects
|
||||
|
||||
@ -18,8 +18,10 @@
|
||||
[app.common.render-wasm.mem :as mem]
|
||||
[app.common.render-wasm.wasm :as wasm]
|
||||
[app.render-wasm.api :as wasm.api]
|
||||
[app.render-wasm.api.fonts :as wasm.fonts]
|
||||
[beicon.v2.core :as rx]
|
||||
[cljs.test :as t :include-macros true]))
|
||||
[cljs.test :as t :include-macros true]
|
||||
[frontend-tests.helpers.async :as async]))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Helpers
|
||||
@ -111,6 +113,22 @@
|
||||
(t/is (= 2 (count (:shapes @captured)))
|
||||
"Both shapes are in process-pending so font-load covers all of them")))
|
||||
|
||||
(t/deftest ^:async process-pending-waits-for-faces-fetched-for-other-shapes
|
||||
"A text whose face is already being fetched for another shape gets no fetch
|
||||
callback. process-pending must still wait for that face, so the relayout
|
||||
that follows uses the real font instead of the fallback."
|
||||
(let [face "face-key"
|
||||
shape (assoc (make-shape :text) :grow-type :fixed)
|
||||
completed (atom false)]
|
||||
(wasm.api/process-pending [shape] [] []
|
||||
{:font-index {face #{(:id shape)}}
|
||||
:pending-faces #{face}}
|
||||
#(reset! completed true))
|
||||
(await (async/settle))
|
||||
(t/is (not @completed) "the shape waits for its pending face")
|
||||
(rx/push! wasm.fonts/font-stored-stream face)
|
||||
(await (async/wait-for #(deref completed) "storing the face completes the wait"))))
|
||||
|
||||
(t/deftest empty-grid-tracks-do-not-allocate-zero-bytes
|
||||
(let [calls (atom [])
|
||||
;; `h/call` is a macro that resolves the wasm function off the module
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user