mirror of
https://github.com/penpot/penpot.git
synced 2026-09-15 08:28:38 +00:00
🐛 Fix async text rendering on tiles (#10504)
This commit is contained in:
parent
05e9c68a74
commit
c8f586a197
@ -1424,18 +1424,20 @@
|
|||||||
(set-shape-layout shape)
|
(set-shape-layout shape)
|
||||||
(set-layout-data shape)
|
(set-layout-data shape)
|
||||||
(let [is-text? (= type :text)
|
(let [is-text? (= type :text)
|
||||||
pending_thumbnails (into [] (concat
|
text-content-pending (when is-text? (set-shape-text-content id content))
|
||||||
(when is-text? (set-shape-text-content id content))
|
pending-thumbnails (into [] (concat
|
||||||
|
text-content-pending
|
||||||
(when is-text? (set-shape-text-images id content true))
|
(when is-text? (set-shape-text-images id content true))
|
||||||
(set-shape-fills id fills true)
|
(set-shape-fills id fills true)
|
||||||
(set-shape-strokes id strokes true)))
|
(set-shape-strokes id strokes true)))
|
||||||
pending_full (into [] (concat
|
pending-full (into [] (concat
|
||||||
(when is-text? (set-shape-text-images id content false))
|
(when is-text? (set-shape-text-images id content false))
|
||||||
(set-shape-fills id fills false)
|
(set-shape-fills id fills false)
|
||||||
(set-shape-strokes id strokes false)))]
|
(set-shape-strokes id strokes false)))]
|
||||||
(perf/end-measure "set-object")
|
(perf/end-measure "set-object")
|
||||||
{:thumbnails pending_thumbnails
|
{:thumbnails pending-thumbnails
|
||||||
:full pending_full}))))
|
:full pending-full
|
||||||
|
:font-pending-ids (if (some :callback text-content-pending) [id] [])}))))
|
||||||
|
|
||||||
(defn- update-text-layouts
|
(defn- update-text-layouts
|
||||||
"Synchronously update text layouts for all shapes and send rect updates
|
"Synchronously update text layouts for all shapes and send rect updates
|
||||||
@ -1443,8 +1445,29 @@
|
|||||||
[text-ids]
|
[text-ids]
|
||||||
(run! f/update-text-layout text-ids))
|
(run! f/update-text-layout text-ids))
|
||||||
|
|
||||||
|
(defn- force-update-text-layouts
|
||||||
|
"Like update-text-layouts but forces a relayout. Use after pending fonts
|
||||||
|
resolve so layouts (and the extrect/tiles derived from them) use real glyph
|
||||||
|
metrics instead of fallback-font estimates."
|
||||||
|
[text-ids]
|
||||||
|
(run! f/force-update-text-layout text-ids))
|
||||||
|
|
||||||
|
(defn- relayout-after-fonts!
|
||||||
|
"Relayout text shapes once their pending fonts have resolved. Shapes in
|
||||||
|
`font-pending-ids` had a font fetched, so they get a forced relayout to pick
|
||||||
|
up the real glyph metrics; the remaining text shapes get a normal layout."
|
||||||
|
[shapes font-pending-ids]
|
||||||
|
(let [force-ids (set font-pending-ids)
|
||||||
|
text-ids (into [] (comp (filter cfh/text-shape?) (map :id)) shapes)
|
||||||
|
forced (filterv force-ids text-ids)
|
||||||
|
rest-ids (filterv (complement force-ids) text-ids)]
|
||||||
|
(when (seq forced)
|
||||||
|
(force-update-text-layouts forced))
|
||||||
|
(when (seq rest-ids)
|
||||||
|
(update-text-layouts rest-ids))))
|
||||||
|
|
||||||
(defn process-pending
|
(defn process-pending
|
||||||
[shapes thumbnails full on-complete]
|
[shapes thumbnails full font-pending-ids on-complete]
|
||||||
(let [pending-thumbnails
|
(let [pending-thumbnails
|
||||||
(d/index-by :key :callback thumbnails)
|
(d/index-by :key :callback thumbnails)
|
||||||
|
|
||||||
@ -1468,11 +1491,7 @@
|
|||||||
(rx/catch #(rx/empty))))
|
(rx/catch #(rx/empty))))
|
||||||
(rx/subs!
|
(rx/subs!
|
||||||
(fn [_]
|
(fn [_]
|
||||||
;; Fonts are now loaded — recompute text layouts so Skia
|
(relayout-after-fonts! shapes font-pending-ids)
|
||||||
;; uses the real metrics instead of fallback-font estimates.
|
|
||||||
(let [text-ids (into [] (comp (filter cfh/text-shape?) (map :id)) shapes)]
|
|
||||||
(when (seq text-ids)
|
|
||||||
(update-text-layouts text-ids)))
|
|
||||||
(request-render "images-loaded"))
|
(request-render "images-loaded"))
|
||||||
noop-fn
|
noop-fn
|
||||||
(fn [] (when (fn? on-complete) (on-complete)))))
|
(fn [] (when (fn? on-complete) (on-complete)))))
|
||||||
@ -1481,8 +1500,8 @@
|
|||||||
|
|
||||||
(defn process-object
|
(defn process-object
|
||||||
[shape]
|
[shape]
|
||||||
(let [{:keys [thumbnails full]} (set-object shape)]
|
(let [{:keys [thumbnails full font-pending-ids]} (set-object shape)]
|
||||||
(process-pending [shape] thumbnails full noop-fn)))
|
(process-pending [shape] thumbnails full font-pending-ids noop-fn)))
|
||||||
|
|
||||||
(defn process-objects
|
(defn process-objects
|
||||||
"Like process-object but for multiple shapes at once. Accumulates all
|
"Like process-object but for multiple shapes at once. Accumulates all
|
||||||
@ -1491,37 +1510,43 @@
|
|||||||
just the first shape that triggered the fetch."
|
just the first shape that triggered the fetch."
|
||||||
[shapes]
|
[shapes]
|
||||||
(let [total-shapes (count shapes)
|
(let [total-shapes (count shapes)
|
||||||
{:keys [thumbnails full]}
|
{:keys [thumbnails full font-pending-ids]}
|
||||||
(loop [index 0 thumbnails-acc (transient []) full-acc (transient [])]
|
(loop [index 0 thumbnails-acc (transient []) full-acc (transient []) font-acc (transient [])]
|
||||||
(if (< index total-shapes)
|
(if (< index total-shapes)
|
||||||
(let [shape (nth shapes index)
|
(let [shape (nth shapes index)
|
||||||
{:keys [thumbnails full]} (set-object shape)]
|
{:keys [thumbnails full font-pending-ids]} (set-object shape)]
|
||||||
(recur (inc index)
|
(recur (inc index)
|
||||||
(reduce conj! thumbnails-acc thumbnails)
|
(reduce conj! thumbnails-acc thumbnails)
|
||||||
(reduce conj! full-acc full)))
|
(reduce conj! full-acc full)
|
||||||
{:thumbnails (persistent! thumbnails-acc) :full (persistent! full-acc)}))]
|
(reduce conj! font-acc font-pending-ids)))
|
||||||
(process-pending shapes thumbnails full noop-fn)))
|
{:thumbnails (persistent! thumbnails-acc)
|
||||||
|
:full (persistent! full-acc)
|
||||||
|
:font-pending-ids (persistent! font-acc)}))]
|
||||||
|
(process-pending shapes thumbnails full font-pending-ids noop-fn)))
|
||||||
|
|
||||||
(defn- process-shapes-chunk
|
(defn- process-shapes-chunk
|
||||||
"Process shapes starting at `start-index` until the time budget is exhausted.
|
"Process shapes starting at `start-index` until the time budget is exhausted.
|
||||||
Returns {:thumbnails [...] :full [...] :next-index n}"
|
Returns {:thumbnails [...] :full [...] :font-pending-ids [...] :next-index n}"
|
||||||
[shapes start-index thumbnails-acc full-acc]
|
[shapes start-index thumbnails-acc full-acc font-pending-acc]
|
||||||
(let [total (count shapes)
|
(let [total (count shapes)
|
||||||
deadline (+ (js/performance.now) CHUNK_TIME_BUDGET_MS)]
|
deadline (+ (js/performance.now) CHUNK_TIME_BUDGET_MS)]
|
||||||
(loop [index start-index
|
(loop [index start-index
|
||||||
t-acc (transient thumbnails-acc)
|
t-acc (transient thumbnails-acc)
|
||||||
f-acc (transient full-acc)]
|
f-acc (transient full-acc)
|
||||||
|
fp-acc (transient font-pending-acc)]
|
||||||
(if (and (< index total)
|
(if (and (< index total)
|
||||||
;; Check performance.now every 8 shapes to reduce overhead
|
;; Check performance.now every 8 shapes to reduce overhead
|
||||||
(or (pos? (bit-and (- index start-index) 7))
|
(or (pos? (bit-and (- index start-index) 7))
|
||||||
(<= (js/performance.now) deadline)))
|
(<= (js/performance.now) deadline)))
|
||||||
(let [shape (nth shapes index)
|
(let [shape (nth shapes index)
|
||||||
{:keys [thumbnails full]} (set-object shape)]
|
{:keys [thumbnails full font-pending-ids]} (set-object shape)]
|
||||||
(recur (inc index)
|
(recur (inc index)
|
||||||
(reduce conj! t-acc thumbnails)
|
(reduce conj! t-acc thumbnails)
|
||||||
(reduce conj! f-acc full)))
|
(reduce conj! f-acc full)
|
||||||
|
(reduce conj! fp-acc font-pending-ids)))
|
||||||
{:thumbnails (persistent! t-acc)
|
{:thumbnails (persistent! t-acc)
|
||||||
:full (persistent! f-acc)
|
:full (persistent! f-acc)
|
||||||
|
:font-pending-ids (persistent! fp-acc)
|
||||||
:next-index index}))))
|
:next-index index}))))
|
||||||
|
|
||||||
(defn- set-objects-async
|
(defn- set-objects-async
|
||||||
@ -1532,16 +1557,16 @@
|
|||||||
(let [total-shapes (count shapes)]
|
(let [total-shapes (count shapes)]
|
||||||
(p/create
|
(p/create
|
||||||
(fn [resolve _reject]
|
(fn [resolve _reject]
|
||||||
(letfn [(process-next-chunk [index thumbnails-acc full-acc]
|
(letfn [(process-next-chunk [index thumbnails-acc full-acc font-pending-acc]
|
||||||
(if (< index total-shapes)
|
(if (< index total-shapes)
|
||||||
;; Process one time-budgeted chunk
|
;; Process one time-budgeted chunk
|
||||||
(let [{:keys [thumbnails full next-index]}
|
(let [{:keys [thumbnails full font-pending-ids next-index]}
|
||||||
(process-shapes-chunk shapes index
|
(process-shapes-chunk shapes index
|
||||||
thumbnails-acc full-acc)]
|
thumbnails-acc full-acc font-pending-acc)]
|
||||||
;; Yield to browser, then continue with next chunk
|
;; Yield to browser, then continue with next chunk
|
||||||
(-> (yield-to-browser)
|
(-> (yield-to-browser)
|
||||||
(p/then (fn [_]
|
(p/then (fn [_]
|
||||||
(process-next-chunk next-index thumbnails full)))))
|
(process-next-chunk next-index thumbnails full font-pending-ids)))))
|
||||||
;; All chunks done - finalize
|
;; All chunks done - finalize
|
||||||
(do
|
(do
|
||||||
(perf/end-measure "set-objects")
|
(perf/end-measure "set-objects")
|
||||||
@ -1588,13 +1613,11 @@
|
|||||||
(rx/reduce conj [])))
|
(rx/reduce conj [])))
|
||||||
(rx/subs!
|
(rx/subs!
|
||||||
(fn [_]
|
(fn [_]
|
||||||
(let [text-ids (into [] (comp (filter cfh/text-shape?) (map :id)) shapes)]
|
(relayout-after-fonts! shapes font-pending-acc)
|
||||||
(when (seq text-ids)
|
|
||||||
(update-text-layouts text-ids)))
|
|
||||||
(request-render "images-loaded"))
|
(request-render "images-loaded"))
|
||||||
noop-fn
|
noop-fn
|
||||||
noop-fn)))))))]
|
noop-fn)))))))]
|
||||||
(process-next-chunk 0 [] []))))))
|
(process-next-chunk 0 [] [] []))))))
|
||||||
|
|
||||||
|
|
||||||
;; This is a version of process-pending that doesn't have sideffects
|
;; This is a version of process-pending that doesn't have sideffects
|
||||||
@ -1647,22 +1670,25 @@
|
|||||||
"Synchronously process all shapes (for small shape counts)."
|
"Synchronously process all shapes (for small shape counts)."
|
||||||
[shapes render-callback on-shapes-ready]
|
[shapes render-callback on-shapes-ready]
|
||||||
(let [total-shapes (count shapes)
|
(let [total-shapes (count shapes)
|
||||||
{:keys [thumbnails full]}
|
{:keys [thumbnails full font-pending-ids]}
|
||||||
(loop [index 0 thumbnails-acc (transient []) full-acc (transient [])]
|
(loop [index 0 thumbnails-acc (transient []) full-acc (transient []) font-acc (transient [])]
|
||||||
(if (< index total-shapes)
|
(if (< index total-shapes)
|
||||||
(let [shape (nth shapes index)
|
(let [shape (nth shapes index)
|
||||||
{:keys [thumbnails full]} (set-object shape)]
|
{:keys [thumbnails full font-pending-ids]} (set-object shape)]
|
||||||
(recur (inc index)
|
(recur (inc index)
|
||||||
(reduce conj! thumbnails-acc thumbnails)
|
(reduce conj! thumbnails-acc thumbnails)
|
||||||
(reduce conj! full-acc full)))
|
(reduce conj! full-acc full)
|
||||||
{:thumbnails (persistent! thumbnails-acc) :full (persistent! full-acc)}))]
|
(reduce conj! font-acc font-pending-ids)))
|
||||||
|
{:thumbnails (persistent! thumbnails-acc)
|
||||||
|
:full (persistent! full-acc)
|
||||||
|
:font-pending-ids (persistent! font-acc)}))]
|
||||||
(perf/end-measure "set-objects")
|
(perf/end-measure "set-objects")
|
||||||
(when on-shapes-ready (on-shapes-ready))
|
(when on-shapes-ready (on-shapes-ready))
|
||||||
;; Rebuild the tile index so _render knows which shapes
|
;; Rebuild the tile index so _render knows which shapes
|
||||||
;; map to which tiles after a page switch.
|
;; map to which tiles after a page switch.
|
||||||
(h/call wasm/internal-module "_set_view_end")
|
(h/call wasm/internal-module "_set_view_end")
|
||||||
(reset! view-interaction-active? false)
|
(reset! view-interaction-active? false)
|
||||||
(process-pending shapes thumbnails full
|
(process-pending shapes thumbnails full font-pending-ids
|
||||||
(fn []
|
(fn []
|
||||||
(if render-callback
|
(if render-callback
|
||||||
(render-callback)
|
(render-callback)
|
||||||
|
|||||||
@ -119,6 +119,16 @@
|
|||||||
(aget shape-id-buffer 2)
|
(aget shape-id-buffer 2)
|
||||||
(aget shape-id-buffer 3)))))
|
(aget shape-id-buffer 3)))))
|
||||||
|
|
||||||
|
(defn force-update-text-layout
|
||||||
|
[id]
|
||||||
|
(when wasm/context-initialized?
|
||||||
|
(let [shape-id-buffer (uuid/get-u32 id)]
|
||||||
|
(h/call wasm/internal-module "_force_update_shape_text_layout_for"
|
||||||
|
(aget shape-id-buffer 0)
|
||||||
|
(aget shape-id-buffer 1)
|
||||||
|
(aget shape-id-buffer 2)
|
||||||
|
(aget shape-id-buffer 3)))))
|
||||||
|
|
||||||
;; IMPORTANT: Only TTF fonts can be stored.
|
;; IMPORTANT: Only TTF fonts can be stored.
|
||||||
(defn- store-font-buffer
|
(defn- store-font-buffer
|
||||||
[font-data font-array-buffer emoji? fallback?]
|
[font-data font-array-buffer emoji? fallback?]
|
||||||
|
|||||||
@ -250,13 +250,15 @@
|
|||||||
(api/set-shape-svg-raw-content (api/get-static-markup shape))
|
(api/set-shape-svg-raw-content (api/get-static-markup shape))
|
||||||
|
|
||||||
(cfh/text-shape? shape)
|
(cfh/text-shape? shape)
|
||||||
(let [pending-thumbnails (into [] (concat (api/set-shape-text-content id v)))
|
(let [text-content-pending (api/set-shape-text-content id v)
|
||||||
pending-full (into [] (concat (api/set-shape-text-images id v)))]
|
pending-thumbnails (vec text-content-pending)
|
||||||
|
pending-full (vec (api/set-shape-text-images id v))
|
||||||
|
font-pending-ids (when (some :callback text-content-pending) [id])]
|
||||||
;; FIXME: this is a hack to process the pending tasks
|
;; FIXME: this is a hack to process the pending tasks
|
||||||
;; asynchronously we should probably modify set-wasm-attr!
|
;; asynchronously we should probably modify set-wasm-attr!
|
||||||
;; to return a list of callbacks to be executed in a
|
;; to return a list of callbacks to be executed in a
|
||||||
;; second pass.
|
;; second pass.
|
||||||
(api/process-pending [shape] pending-thumbnails pending-full api/noop-fn)
|
(api/process-pending [shape] pending-thumbnails pending-full font-pending-ids api/noop-fn)
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
:grow-type
|
:grow-type
|
||||||
|
|||||||
@ -51,7 +51,7 @@
|
|||||||
(let [shapes [(make-shape :text) (make-shape :text) (make-shape :rect)]
|
(let [shapes [(make-shape :text) (make-shape :text) (make-shape :rect)]
|
||||||
visited-ids (atom [])
|
visited-ids (atom [])
|
||||||
mock-set (fn [s] (swap! visited-ids conj (:id s)) {:thumbnails [] :full []})
|
mock-set (fn [s] (swap! visited-ids conj (:id s)) {:thumbnails [] :full []})
|
||||||
mock-pend (fn [_sh _t _f _cb] nil)]
|
mock-pend (fn [_sh _t _f _fp _cb] nil)]
|
||||||
|
|
||||||
(with-mocks* mock-set mock-pend #(wasm.api/process-objects shapes))
|
(with-mocks* mock-set mock-pend #(wasm.api/process-objects shapes))
|
||||||
|
|
||||||
@ -66,7 +66,7 @@
|
|||||||
(let [shapes [(make-shape :text) (make-shape :text)]
|
(let [shapes [(make-shape :text) (make-shape :text)]
|
||||||
captured (atom nil)
|
captured (atom nil)
|
||||||
mock-set (fn [_s] {:thumbnails [] :full []})
|
mock-set (fn [_s] {:thumbnails [] :full []})
|
||||||
mock-pend (fn [sh t f cb] (reset! captured {:shapes sh :thumbnails t :full f :cb cb}))]
|
mock-pend (fn [sh t f _fp cb] (reset! captured {:shapes sh :thumbnails t :full f :cb cb}))]
|
||||||
|
|
||||||
(with-mocks* mock-set mock-pend #(wasm.api/process-objects shapes))
|
(with-mocks* mock-set mock-pend #(wasm.api/process-objects shapes))
|
||||||
|
|
||||||
@ -99,7 +99,7 @@
|
|||||||
{:thumbnails [] :full []}))
|
{:thumbnails [] :full []}))
|
||||||
|
|
||||||
mock-pend
|
mock-pend
|
||||||
(fn [sh t f _cb] (reset! captured {:shapes sh :thumbnails t :full f}))]
|
(fn [sh t f _fp _cb] (reset! captured {:shapes sh :thumbnails t :full f}))]
|
||||||
|
|
||||||
(with-mocks* mock-set mock-pend #(wasm.api/process-objects shapes))
|
(with-mocks* mock-set mock-pend #(wasm.api/process-objects shapes))
|
||||||
|
|
||||||
|
|||||||
@ -410,6 +410,11 @@ pub(crate) struct RenderState {
|
|||||||
/// GPU crops from `Backbuffer` or tile atlas keyed by shape id. Filled on full-frame completion; during
|
/// GPU crops from `Backbuffer` or tile atlas keyed by shape id. Filled on full-frame completion; during
|
||||||
/// drag, entries for the moved top-level selection are ensured here
|
/// drag, entries for the moved top-level selection are ensured here
|
||||||
pub backbuffer_crop_cache: HashMap<Uuid, InteractiveDragCrop>,
|
pub backbuffer_crop_cache: HashMap<Uuid, InteractiveDragCrop>,
|
||||||
|
/// Whether we've already forced a GPU flush+submit before a tile-atlas
|
||||||
|
/// snapshot this render. The first snapshot of a pass can otherwise capture
|
||||||
|
/// a tile before its text glyph uploads complete (blank first/center tile).
|
||||||
|
/// One explicit flush warms the submit path for the rest of the pass.
|
||||||
|
pub tile_atlas_flushed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InteractiveDragCrop {
|
pub struct InteractiveDragCrop {
|
||||||
@ -592,6 +597,7 @@ impl RenderState {
|
|||||||
interactive_target_seeded: false,
|
interactive_target_seeded: false,
|
||||||
preserve_target_during_render: false,
|
preserve_target_during_render: false,
|
||||||
backbuffer_crop_cache: HashMap::default(),
|
backbuffer_crop_cache: HashMap::default(),
|
||||||
|
tile_atlas_flushed: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1020,6 +1026,12 @@ impl RenderState {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(Error::CriticalError("Current tile not found".to_string()))?;
|
.ok_or(Error::CriticalError("Current tile not found".to_string()))?;
|
||||||
|
|
||||||
|
// Force pending GPU work (text glyph-atlas uploads)
|
||||||
|
if !self.tile_atlas_flushed {
|
||||||
|
crate::get_gpu_state().context.flush_and_submit();
|
||||||
|
self.tile_atlas_flushed = true;
|
||||||
|
}
|
||||||
|
|
||||||
self.surfaces.draw_current_tile_into_tile_atlas(
|
self.surfaces.draw_current_tile_into_tile_atlas(
|
||||||
&self.tile_viewbox,
|
&self.tile_viewbox,
|
||||||
¤t_tile,
|
¤t_tile,
|
||||||
@ -2090,6 +2102,7 @@ impl RenderState {
|
|||||||
self.surfaces.atlas.set_doc_bounds(doc_bounds);
|
self.surfaces.atlas.set_doc_bounds(doc_bounds);
|
||||||
|
|
||||||
self.cache_cleared_this_render = false;
|
self.cache_cleared_this_render = false;
|
||||||
|
self.tile_atlas_flushed = false;
|
||||||
let preserve_target = self.preserve_target_during_render;
|
let preserve_target = self.preserve_target_during_render;
|
||||||
self.preserve_target_during_render = false;
|
self.preserve_target_during_render = false;
|
||||||
|
|
||||||
|
|||||||
@ -888,6 +888,11 @@ impl TextContent {
|
|||||||
.copy_finite_size(result.2, default_width, default_height);
|
.copy_finite_size(result.2, default_width, default_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn force_next_layout_update(&mut self) {
|
||||||
|
self.layout_width = None;
|
||||||
|
self.layout.cached_extrect.set(None);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_layout(&mut self, selrect: Rect) -> TextContentSize {
|
pub fn update_layout(&mut self, selrect: Rect) -> TextContentSize {
|
||||||
if !self.layout.needs_update()
|
if !self.layout.needs_update()
|
||||||
&& self.layout_version == self.content_version
|
&& self.layout_version == self.content_version
|
||||||
|
|||||||
@ -370,8 +370,11 @@ pub extern "C" fn intersect_position_in_shape(
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_text_layout(shape: &mut Shape) {
|
fn update_text_layout(shape: &mut Shape, force: bool) {
|
||||||
if let Type::Text(text_content) = &mut shape.shape_type {
|
if let Type::Text(text_content) = &mut shape.shape_type {
|
||||||
|
if force {
|
||||||
|
text_content.force_next_layout_update();
|
||||||
|
}
|
||||||
text_content.update_layout(shape.selrect);
|
text_content.update_layout(shape.selrect);
|
||||||
shape.invalidate_extrect();
|
shape.invalidate_extrect();
|
||||||
}
|
}
|
||||||
@ -380,7 +383,7 @@ fn update_text_layout(shape: &mut Shape) {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn update_shape_text_layout() {
|
pub extern "C" fn update_shape_text_layout() {
|
||||||
with_current_shape_mut!(state, |shape: &mut Shape| {
|
with_current_shape_mut!(state, |shape: &mut Shape| {
|
||||||
update_text_layout(shape);
|
update_text_layout(shape, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +392,18 @@ pub extern "C" fn update_shape_text_layout_for(a: u32, b: u32, c: u32, d: u32) {
|
|||||||
with_state!(state, {
|
with_state!(state, {
|
||||||
let shape_id = uuid_from_u32_quartet(a, b, c, d);
|
let shape_id = uuid_from_u32_quartet(a, b, c, d);
|
||||||
if let Some(shape) = state.shapes.get_mut(&shape_id) {
|
if let Some(shape) = state.shapes.get_mut(&shape_id) {
|
||||||
update_text_layout(shape);
|
update_text_layout(shape, false);
|
||||||
|
}
|
||||||
|
state.touch_shape(shape_id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn force_update_shape_text_layout_for(a: u32, b: u32, c: u32, d: u32) {
|
||||||
|
with_state!(state, {
|
||||||
|
let shape_id = uuid_from_u32_quartet(a, b, c, d);
|
||||||
|
if let Some(shape) = state.shapes.get_mut(&shape_id) {
|
||||||
|
update_text_layout(shape, true);
|
||||||
}
|
}
|
||||||
state.touch_shape(shape_id);
|
state.touch_shape(shape_id);
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user