mirror of
https://github.com/penpot/penpot.git
synced 2025-12-31 18:38:39 -05:00
Compare commits
17 Commits
superalex-
...
staging-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75860afe57 | ||
|
|
824ca1bbca | ||
|
|
48e3f35bb3 | ||
|
|
6b794c9d12 | ||
|
|
d3ee50daf5 | ||
|
|
a948e49e51 | ||
|
|
d635f5a8dc | ||
|
|
ab3a3ef43b | ||
|
|
9c21fd3359 | ||
|
|
44b70cf1d4 | ||
|
|
a8bd74b392 | ||
|
|
3d3e3582d6 | ||
|
|
de052b5161 | ||
|
|
6ebd48b94c | ||
|
|
8a3b33797f | ||
|
|
13fd20f76f | ||
|
|
417cd80564 |
14
.github/workflows/build-staging-render.yml
vendored
Normal file
14
.github/workflows/build-staging-render.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: _STAGING RENDER
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '36 5-20 * * 1-5'
|
||||
|
||||
jobs:
|
||||
build-bundle:
|
||||
uses: ./.github/workflows/build-bundle.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
gh_ref: "staging-render"
|
||||
build_wasm: "yes"
|
||||
build_storybook: "yes"
|
||||
@@ -1,6 +1,12 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 2.12.0 (Unreleased)
|
||||
## 2.12.1
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix setting a portion of text as bold or underline messes things up [Github #7980](https://github.com/penpot/penpot/issues/7980)
|
||||
|
||||
## 2.12.0
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
|
||||
@@ -23,30 +23,25 @@ tmux -2 new-session -d -s penpot
|
||||
tmux rename-window -t penpot:0 'frontend watch'
|
||||
tmux select-window -t penpot:0
|
||||
tmux send-keys -t penpot 'cd penpot/frontend' enter C-l
|
||||
tmux send-keys -t penpot 'yarn run watch' enter
|
||||
tmux send-keys -t penpot './scripts/watch app' enter
|
||||
|
||||
tmux new-window -t penpot:1 -n 'frontend shadow'
|
||||
tmux new-window -t penpot:1 -n 'frontend storybook'
|
||||
tmux select-window -t penpot:1
|
||||
tmux send-keys -t penpot 'cd penpot/frontend' enter C-l
|
||||
tmux send-keys -t penpot 'yarn run watch:app' enter
|
||||
tmux send-keys -t penpot './scripts/watch storybook' enter
|
||||
|
||||
tmux new-window -t penpot:2 -n 'frontend storybook'
|
||||
tmux new-window -t penpot:2 -n 'exporter'
|
||||
tmux select-window -t penpot:2
|
||||
tmux send-keys -t penpot 'cd penpot/frontend' enter C-l
|
||||
tmux send-keys -t penpot 'yarn run watch:storybook' enter
|
||||
|
||||
tmux new-window -t penpot:3 -n 'exporter'
|
||||
tmux select-window -t penpot:3
|
||||
tmux send-keys -t penpot 'cd penpot/exporter' enter C-l
|
||||
tmux send-keys -t penpot 'rm -f target/app.js*' enter C-l
|
||||
tmux send-keys -t penpot 'yarn run watch' enter
|
||||
tmux send-keys -t penpot './scripts/watch' enter
|
||||
|
||||
tmux split-window -v
|
||||
tmux send-keys -t penpot 'cd penpot/exporter' enter C-l
|
||||
tmux send-keys -t penpot './scripts/wait-and-start.sh' enter
|
||||
|
||||
tmux new-window -t penpot:4 -n 'backend'
|
||||
tmux select-window -t penpot:4
|
||||
tmux new-window -t penpot:3 -n 'backend'
|
||||
tmux select-window -t penpot:3
|
||||
tmux send-keys -t penpot 'cd penpot/backend' enter C-l
|
||||
tmux send-keys -t penpot './scripts/start-dev' enter
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clear:shadow-cache": "rm -rf .shadow-cljs && rm -rf target",
|
||||
"watch:app": "clojure -M:dev:shadow-cljs watch main",
|
||||
"watch": "yarn run clear:shadow-cache && yarn run watch:app",
|
||||
"watch:app": "yarn run clear:shadow-cache && clojure -M:dev:shadow-cljs watch main",
|
||||
"watch": "yarn run watch:app",
|
||||
"build:app": "clojure -M:dev:shadow-cljs release main",
|
||||
"build": "yarn run clear:shadow-cache && yarn run build:app",
|
||||
"fmt:clj:check": "cljfmt check --parallel=false src/",
|
||||
|
||||
7
exporter/scripts/watch
Executable file
7
exporter/scripts/watch
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
TARGET=${1:-app};
|
||||
|
||||
set -ex
|
||||
|
||||
exec yarn run watch:$TARGET
|
||||
@@ -47,10 +47,9 @@
|
||||
"watch:app:libs": "node ./scripts/build-libs.js --watch",
|
||||
"watch:app:main": "clojure -M:dev:shadow-cljs watch main worker storybook",
|
||||
"clear:shadow-cache": "rm -rf .shadow-cljs",
|
||||
"watch:app": "yarn run clear:shadow-cache && concurrently \"yarn run watch:app:main\" \"yarn run watch:app:libs\"",
|
||||
"watch": "yarn run watch:app:assets",
|
||||
"watch:storybook": "yarn run build:storybook:assets && concurrently \"storybook dev -p 6006 --no-open\" \"yarn run watch:storybook:assets\"",
|
||||
"watch:storybook:assets": "node ./scripts/watch-storybook.js"
|
||||
"watch": "exit 0",
|
||||
"watch:app": "yarn run clear:shadow-cache && concurrently --kill-others-on-fail \"yarn run watch:app:assets\" \"yarn run watch:app:main\" \"yarn run watch:app:libs\"",
|
||||
"watch:storybook": "yarn run build:storybook:assets && concurrently --kill-others-on-fail \"storybook dev -p 6006 --no-open\" \"node ./scripts/watch-storybook.js\""
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.52.0",
|
||||
|
||||
@@ -73,7 +73,7 @@ export function isJsFile(path) {
|
||||
export async function compileSass(worker, path, options) {
|
||||
path = ph.resolve(path);
|
||||
|
||||
log.info("compile:", path);
|
||||
// log.info("compile:", path);
|
||||
return worker.exec("compileSass", [path, options]);
|
||||
}
|
||||
|
||||
|
||||
7
frontend/scripts/watch
Executable file
7
frontend/scripts/watch
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
TARGET=${1:-app};
|
||||
|
||||
set -ex
|
||||
|
||||
exec yarn run watch:$TARGET
|
||||
@@ -50,7 +50,8 @@
|
||||
touched? (and (contains? (:data @form) input-name)
|
||||
(get-in @form [:touched input-name]))
|
||||
|
||||
error (get-in @form [:errors input-name])
|
||||
error (or (get-in @form [:errors input-name])
|
||||
(get-in @form [:extra-errors input-name]))
|
||||
|
||||
value (get-in @form [:data input-name] "")
|
||||
|
||||
|
||||
@@ -18,16 +18,18 @@
|
||||
|
||||
(defn- on-error
|
||||
[form error]
|
||||
(case (:code (ex-data error))
|
||||
:old-password-not-match
|
||||
(swap! form assoc-in [:errors :password-old]
|
||||
{:message (tr "errors.wrong-old-password")})
|
||||
:email-as-password
|
||||
(swap! form assoc-in [:errors :password-1]
|
||||
{:message (tr "errors.email-as-password")})
|
||||
(let [data (ex-data error)]
|
||||
(case (:code data)
|
||||
:old-password-not-match
|
||||
(swap! form assoc-in [:extra-errors :password-old]
|
||||
{:message (tr "errors.wrong-old-password")})
|
||||
|
||||
(let [msg (tr "generic.error")]
|
||||
(st/emit! (ntf/error msg)))))
|
||||
:email-as-password
|
||||
(swap! form assoc-in [:extra-errors :password-1]
|
||||
{:message (tr "errors.email-as-password")})
|
||||
|
||||
(let [msg (tr "generic.error")]
|
||||
(st/emit! (ntf/error msg))))))
|
||||
|
||||
(defn- on-success
|
||||
[form]
|
||||
|
||||
@@ -106,9 +106,11 @@
|
||||
:overflowWrap "initial"
|
||||
:lineBreak "auto"
|
||||
:whiteSpace "break-spaces"
|
||||
:textRendering "geometricPrecision"
|
||||
:display "inline-block"
|
||||
:verticalAlign "top"}
|
||||
:textRendering "geometricPrecision"}
|
||||
base (cond-> base
|
||||
(= (:line-height data) "0")
|
||||
(-> (obj/set! "display" "inline-block")
|
||||
(obj/set! "verticalAlign" "top")))
|
||||
fills
|
||||
(cond
|
||||
;; DEPRECATED: still here for backward compatibility with
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.logging :as log]
|
||||
[app.common.types.color :as clr]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.path :as path]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.common :as dcm]
|
||||
[app.main.data.workspace.transforms :as dwt]
|
||||
[app.main.data.workspace.variants :as dwv]
|
||||
[app.main.features :as features]
|
||||
@@ -55,7 +55,6 @@
|
||||
[app.main.ui.workspace.viewport.widgets :as widgets]
|
||||
[app.render-wasm.api :as wasm.api]
|
||||
[app.util.debug :as dbg]
|
||||
[app.util.globals :as ug]
|
||||
[app.util.text-editor :as ted]
|
||||
[beicon.v2.core :as rx]
|
||||
[promesa.core :as p]
|
||||
@@ -135,9 +134,6 @@
|
||||
canvas-init? (mf/use-state false)
|
||||
initialized? (mf/use-state false)
|
||||
|
||||
;; Refs to store current values for context restoration
|
||||
viewport-state-ref (mf/use-ref nil)
|
||||
|
||||
;; REFS
|
||||
[viewport-ref
|
||||
on-viewport-ref] (create-viewport-ref)
|
||||
@@ -306,15 +302,19 @@
|
||||
;; We think moving this out to a handler will make the render code
|
||||
;; harder to follow through.
|
||||
(mf/with-effect [page-id]
|
||||
;; Reset initialized state when page changes
|
||||
(reset! initialized? false)
|
||||
(when-let [canvas (mf/ref-val canvas-ref)]
|
||||
(->> wasm.api/module
|
||||
(p/fmap (fn [ready?]
|
||||
(when ready?
|
||||
(let [init? (wasm.api/init-canvas-context canvas)]
|
||||
(let [init? (try
|
||||
(wasm.api/init-canvas-context canvas)
|
||||
(catch :default e
|
||||
(js/console.error "Error initializing canvas context:" e)
|
||||
false))]
|
||||
(reset! canvas-init? init?)
|
||||
(when-not init? (js/alert "WebGL not supported")))))))
|
||||
(when-not init?
|
||||
(js/alert "WebGL not supported")
|
||||
(st/emit! (dcm/go-to-dashboard-recent))))))))
|
||||
(fn []
|
||||
(wasm.api/clear-canvas))))
|
||||
|
||||
@@ -338,33 +338,11 @@
|
||||
(when (and @canvas-init? preview-blend)
|
||||
(wasm.api/request-render "with-effect")))
|
||||
|
||||
;; Update viewport state ref whenever values change
|
||||
(mf/with-effect [base-objects zoom vbox background]
|
||||
(mf/set-ref-val! viewport-state-ref {:base-objects base-objects
|
||||
:zoom zoom
|
||||
:vbox vbox
|
||||
:background background}))
|
||||
|
||||
(mf/with-effect [@canvas-init? zoom vbox background]
|
||||
(when (and @canvas-init? (not @initialized?))
|
||||
(wasm.api/initialize-viewport base-objects zoom vbox background)
|
||||
(reset! initialized? true)))
|
||||
|
||||
;; Listen for context restoration events - register only once
|
||||
(mf/with-effect []
|
||||
(let [listener (fn [_]
|
||||
(log/info :hint "Context restored event received, resetting viewport initialization")
|
||||
(let [state (mf/ref-val viewport-state-ref)]
|
||||
(when state
|
||||
(reset! initialized? false)
|
||||
(wasm.api/initialize-viewport (:base-objects state)
|
||||
(:zoom state)
|
||||
(:vbox state)
|
||||
(:background state))
|
||||
(wasm.api/request-render "context-restored"))))]
|
||||
(.addEventListener ug/document "penpot:wasm:context-restored" listener)
|
||||
#(.removeEventListener ug/document "penpot:wasm:context-restored" listener)))
|
||||
|
||||
(mf/with-effect [focus]
|
||||
(when (and @canvas-init? @initialized?)
|
||||
(if (empty? focus)
|
||||
|
||||
@@ -96,20 +96,20 @@
|
||||
;; This should never be called from the outside.
|
||||
(defn- render
|
||||
[timestamp]
|
||||
(when wasm/context-initialized?
|
||||
(when (and wasm/context-initialized? (not @wasm/context-lost?))
|
||||
(h/call wasm/internal-module "_render" timestamp)
|
||||
(set! wasm/internal-frame-id nil)
|
||||
(ug/dispatch! (ug/event "penpot:wasm:render"))))
|
||||
|
||||
(defn render-sync
|
||||
[]
|
||||
(when wasm/context-initialized?
|
||||
(when (and wasm/context-initialized? (not @wasm/context-lost?))
|
||||
(h/call wasm/internal-module "_render_sync")
|
||||
(set! wasm/internal-frame-id nil)))
|
||||
|
||||
(defn render-sync-shape
|
||||
[id]
|
||||
(when wasm/context-initialized?
|
||||
(when (and wasm/context-initialized? (not @wasm/context-lost?))
|
||||
(let [buffer (uuid/get-u32 id)]
|
||||
(h/call wasm/internal-module "_render_sync_shape"
|
||||
(aget buffer 0)
|
||||
@@ -123,7 +123,7 @@
|
||||
|
||||
(defn request-render
|
||||
[_requester]
|
||||
(when (not @pending-render)
|
||||
(when (and wasm/context-initialized? (not @pending-render) (not @wasm/context-lost?))
|
||||
(reset! pending-render true)
|
||||
(js/requestAnimationFrame
|
||||
(fn [ts]
|
||||
@@ -759,13 +759,8 @@
|
||||
(h/call wasm/internal-module "_clear_shape_layout"))
|
||||
|
||||
(defn- set-shape-layout
|
||||
[shape objects]
|
||||
[shape]
|
||||
(clear-layout)
|
||||
(when (or (ctl/any-layout? shape)
|
||||
(ctl/any-layout-immediate-child? objects shape)
|
||||
(has-any-layout-prop? shape))
|
||||
(set-layout-data shape))
|
||||
|
||||
(when (ctl/flex-layout? shape)
|
||||
(set-flex-layout shape))
|
||||
|
||||
@@ -916,7 +911,7 @@
|
||||
(perf/end-measure "set-view-box::zoom")))))
|
||||
|
||||
(defn set-object
|
||||
[objects shape]
|
||||
[shape]
|
||||
(perf/begin-measure "set-object")
|
||||
(let [shape (svg-filters/apply-svg-derived shape)
|
||||
id (dm/get-prop shape :id)
|
||||
@@ -981,7 +976,7 @@
|
||||
(when (= type :text)
|
||||
(set-shape-grow-type grow-type))
|
||||
|
||||
(set-shape-layout shape objects)
|
||||
(set-shape-layout shape)
|
||||
(set-shape-selrect selrect)
|
||||
|
||||
(let [pending_thumbnails (into [] (concat
|
||||
@@ -1035,7 +1030,7 @@
|
||||
|
||||
(defn process-object
|
||||
[shape]
|
||||
(let [{:keys [thumbnails full]} (set-object [] shape)]
|
||||
(let [{:keys [thumbnails full]} (set-object shape)]
|
||||
(process-pending [shape] thumbnails full noop-fn)))
|
||||
|
||||
(defn set-objects
|
||||
@@ -1050,7 +1045,7 @@
|
||||
(loop [index 0 thumbnails-acc [] full-acc []]
|
||||
(if (< index total-shapes)
|
||||
(let [shape (nth shapes index)
|
||||
{:keys [thumbnails full]} (set-object objects shape)]
|
||||
{:keys [thumbnails full]} (set-object shape)]
|
||||
(recur (inc index)
|
||||
(into thumbnails-acc thumbnails)
|
||||
(into full-acc full)))
|
||||
@@ -1234,97 +1229,6 @@
|
||||
(re-find #"(?i)edge" user-agent) :edge
|
||||
:else :unknown)))))
|
||||
|
||||
(defn- initialize-wasm-context
|
||||
"Initializes the WebGL context and WASM render engine.
|
||||
Registers the context with Emscripten, sets up extensions, and initializes the render module."
|
||||
[canvas context flags]
|
||||
(let [gl (unchecked-get wasm/internal-module "GL")
|
||||
handle (.registerContext ^js gl context #js {"majorVersion" 2})]
|
||||
(.makeContextCurrent ^js gl handle)
|
||||
(set! wasm/gl-context-handle handle)
|
||||
(set! wasm/gl-context context)
|
||||
|
||||
;; Force the WEBGL_debug_renderer_info extension as emscripten does not enable it
|
||||
(.getExtension context "WEBGL_debug_renderer_info")
|
||||
|
||||
;; Initialize Wasm Render Engine
|
||||
(h/call wasm/internal-module "_init" (/ (.-width ^js canvas) dpr) (/ (.-height ^js canvas) dpr))
|
||||
(h/call wasm/internal-module "_set_render_options" flags dpr)))
|
||||
|
||||
(defn- handle-context-lost
|
||||
"Handler for WebGL context loss events. Attempts to restore the context manually if possible."
|
||||
[canvas event]
|
||||
(.preventDefault event)
|
||||
(log/warn :hint "WebGL context lost")
|
||||
;; Only mark as lost if this is the same canvas (not a page change)
|
||||
;; During page changes, clear-canvas handles the cleanup
|
||||
(when (exists? js/window)
|
||||
(let [saved-canvas (.-penpotCanvas js/window)]
|
||||
(when (identical? canvas saved-canvas)
|
||||
(set! wasm/context-initialized? false)
|
||||
(st/emit! (drw/context-lost))
|
||||
;; Only try to restore manually if we have the lose extension
|
||||
(when-let [lose-ext (.-penpotLoseContextExt js/window)]
|
||||
;; Use setTimeout to allow the browser to process the context loss event
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(try
|
||||
;; Check if context is actually lost before trying to restore
|
||||
(let [current-context (.getContext ^js canvas "webgl2" default-context-options)]
|
||||
(cond
|
||||
(nil? current-context)
|
||||
;; No context available, try to restore
|
||||
(do
|
||||
(log/info :hint "No context available, attempting to restore manually")
|
||||
(.restoreContext ^js lose-ext))
|
||||
|
||||
(.-isContextLost ^js current-context)
|
||||
;; Context exists but is lost, try to restore
|
||||
(do
|
||||
(log/info :hint "Context is lost, attempting to restore manually")
|
||||
(.restoreContext ^js lose-ext))
|
||||
|
||||
:else
|
||||
;; Context is already restored, skip
|
||||
(log/info :hint "Context already restored, skipping manual restoration")))
|
||||
;; After restoreContext(), the browser should fire webglcontextrestored
|
||||
;; which will trigger our restoration handler
|
||||
(catch :default err
|
||||
(log/warn :hint "Could not restore context manually" :error err))))
|
||||
0))))))
|
||||
|
||||
(defn- handle-context-restored
|
||||
"Handler for WebGL context restoration events. Re-initializes the context and WASM module."
|
||||
[canvas gl flags]
|
||||
(log/info :hint "WebGL context restored by browser, re-initializing")
|
||||
;; Re-initialize the context after browser restoration
|
||||
;; Use setTimeout to ensure the browser has fully restored the context
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(let [restored-context (.getContext ^js canvas "webgl2" default-context-options)]
|
||||
(when-not (nil? restored-context)
|
||||
(try
|
||||
;; Clean up old handle if exists
|
||||
(when-let [old-handle wasm/gl-context-handle]
|
||||
(try
|
||||
(.deleteContext ^js gl old-handle)
|
||||
(catch :default _)))
|
||||
|
||||
;; Re-initialize the context and WASM module
|
||||
(initialize-wasm-context canvas restored-context flags)
|
||||
|
||||
(set! wasm/context-initialized? true)
|
||||
|
||||
(log/info :hint "WebGL context restored successfully")
|
||||
(st/emit! (drw/context-restored))
|
||||
(ug/dispatch! (ug/event "penpot:wasm:context-restored"))
|
||||
;; Force a render after context restoration
|
||||
(request-render "context-restored")
|
||||
(catch :default err
|
||||
(log/error :hint "Failed to restore WebGL context"
|
||||
:error err)))))
|
||||
0)))
|
||||
|
||||
(defn init-canvas-context
|
||||
[canvas]
|
||||
(let [gl (unchecked-get wasm/internal-module "GL")
|
||||
@@ -1335,88 +1239,67 @@
|
||||
browser (get-browser)
|
||||
browser (sr/translate-browser browser)]
|
||||
(when-not (nil? context)
|
||||
;; Initialize WebGL context and WASM render engine
|
||||
(initialize-wasm-context canvas context flags)
|
||||
(let [handle (.registerContext ^js gl context #js {"majorVersion" 2})]
|
||||
(.makeContextCurrent ^js gl handle)
|
||||
(set! wasm/gl-context-handle handle)
|
||||
(set! wasm/gl-context context)
|
||||
|
||||
;; Store reference to lose extension for manual restoration
|
||||
(let [lose-ext (.getExtension context "WEBGL_lose_context")]
|
||||
(when (and lose-ext (exists? js/window))
|
||||
(set! (.-penpotLoseContextExt js/window) lose-ext)
|
||||
(set! (.-penpotCanvas js/window) canvas)))
|
||||
;; Force the WEBGL_debug_renderer_info extension as emscripten does not enable it
|
||||
(.getExtension context "WEBGL_debug_renderer_info")
|
||||
|
||||
;; Remove existing event listeners if any (cleanup from previous initialization)
|
||||
(when wasm/context-lost-handler
|
||||
(.removeEventListener canvas "webglcontextlost" wasm/context-lost-handler))
|
||||
(when wasm/context-restored-handler
|
||||
(.removeEventListener canvas "webglcontextrestored" wasm/context-restored-handler))
|
||||
;; Initialize Wasm Render Engine
|
||||
(h/call wasm/internal-module "_init" (/ (.-width ^js canvas) dpr) (/ (.-height ^js canvas) dpr))
|
||||
(h/call wasm/internal-module "_set_render_options" flags dpr)
|
||||
|
||||
;; Create and store event handlers for context loss and restoration
|
||||
(let [lost-handler (fn [event]
|
||||
(handle-context-lost canvas event))
|
||||
restored-handler (fn [event]
|
||||
(handle-context-restored canvas gl flags))]
|
||||
(set! wasm/context-lost-handler lost-handler)
|
||||
(set! wasm/context-restored-handler restored-handler)
|
||||
;; Set browser and canvas size only after initialization
|
||||
(h/call wasm/internal-module "_set_browser" browser)
|
||||
(set-canvas-size canvas)
|
||||
|
||||
;; Add event listeners for context loss and restoration
|
||||
(.addEventListener canvas "webglcontextlost" lost-handler)
|
||||
(.addEventListener canvas "webglcontextrestored" restored-handler))
|
||||
;; Add event listeners for WebGL context lost
|
||||
(let [handler (fn [event]
|
||||
(.preventDefault event)
|
||||
(reset! wasm/context-lost? true)
|
||||
(log/warn :hint "WebGL context lost")
|
||||
(st/emit! (drw/context-lost)))]
|
||||
(set! wasm/context-lost-handler handler)
|
||||
(set! wasm/context-lost-canvas canvas)
|
||||
(.addEventListener canvas "webglcontextlost" handler))
|
||||
(set! wasm/context-initialized? true)))
|
||||
|
||||
;; Add debug helpers for reproducing context loss issues
|
||||
(when (exists? js/window)
|
||||
(set! (.-penpotDebugPanning js/window)
|
||||
(fn []
|
||||
(let [lose-ext (.getExtension context "WEBGL_lose_context")]
|
||||
(when lose-ext
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(log/warn :hint "Forcing context loss for testing")
|
||||
(.loseContext lose-ext))
|
||||
1000))))))
|
||||
|
||||
(set! wasm/context-initialized? true))
|
||||
|
||||
(h/call wasm/internal-module "_set_browser" browser)
|
||||
|
||||
(h/call wasm/internal-module "_set_render_options" flags dpr)
|
||||
(set-canvas-size canvas)
|
||||
context-init?))
|
||||
|
||||
(defn clear-canvas
|
||||
[]
|
||||
(try
|
||||
;; TODO: perform corresponding cleaning
|
||||
(set! wasm/context-initialized? false)
|
||||
(h/call wasm/internal-module "_clean_up")
|
||||
(when wasm/context-initialized?
|
||||
(try
|
||||
;; TODO: perform corresponding cleaning
|
||||
(set! wasm/context-initialized? false)
|
||||
(h/call wasm/internal-module "_clean_up")
|
||||
|
||||
;; Remove event listeners before cleaning up context
|
||||
(when (and wasm/gl-context (.-canvas wasm/gl-context))
|
||||
(let [canvas (.-canvas wasm/gl-context)]
|
||||
(when wasm/context-lost-handler
|
||||
(.removeEventListener canvas "webglcontextlost" wasm/context-lost-handler)
|
||||
(set! wasm/context-lost-handler nil))
|
||||
(when wasm/context-restored-handler
|
||||
(.removeEventListener canvas "webglcontextrestored" wasm/context-restored-handler)
|
||||
(set! wasm/context-restored-handler nil))))
|
||||
;; Remove event listener for WebGL context lost
|
||||
(when (and wasm/context-lost-handler wasm/context-lost-canvas)
|
||||
(.removeEventListener wasm/context-lost-canvas "webglcontextlost" wasm/context-lost-handler)
|
||||
(set! wasm/context-lost-canvas nil)
|
||||
(set! wasm/context-lost-handler nil))
|
||||
|
||||
;; Ensure the WebGL context is properly disposed so browsers do not keep
|
||||
;; accumulating active contexts between page switches.
|
||||
(when-let [gl (unchecked-get wasm/internal-module "GL")]
|
||||
(when-let [handle wasm/gl-context-handle]
|
||||
(try
|
||||
;; Ask the browser to release resources explicitly if available.
|
||||
(when-let [ctx wasm/gl-context]
|
||||
(when-let [lose-ext (.getExtension ^js ctx "WEBGL_lose_context")]
|
||||
(.loseContext ^js lose-ext)))
|
||||
(.deleteContext ^js gl handle)
|
||||
(finally
|
||||
(set! wasm/gl-context-handle nil)
|
||||
(set! wasm/gl-context nil)))))
|
||||
;; Ensure the WebGL context is properly disposed so browsers do not keep
|
||||
;; accumulating active contexts between page switches.
|
||||
(when-let [gl (unchecked-get wasm/internal-module "GL")]
|
||||
(when-let [handle wasm/gl-context-handle]
|
||||
(try
|
||||
;; Ask the browser to release resources explicitly if available.
|
||||
(when-let [ctx wasm/gl-context]
|
||||
(when-let [lose-ext (.getExtension ^js ctx "WEBGL_lose_context")]
|
||||
(.loseContext ^js lose-ext)))
|
||||
(.deleteContext ^js gl handle)
|
||||
(finally
|
||||
(set! wasm/gl-context-handle nil)
|
||||
(set! wasm/gl-context nil)))))
|
||||
|
||||
;; If this calls panics we don't want to crash. This happens sometimes
|
||||
;; with hot-reload in develop
|
||||
(catch :default error
|
||||
(.error js/console error))))
|
||||
;; If this calls panics we don't want to crash. This happens sometimes
|
||||
;; with hot-reload in develop
|
||||
(catch :default error
|
||||
(.error js/console error)))))
|
||||
|
||||
(defn show-grid
|
||||
[id]
|
||||
|
||||
@@ -354,32 +354,32 @@
|
||||
:is-fallback true}))
|
||||
|
||||
(def noto-fonts
|
||||
{:japanese {:font-id "gfont-noto-sans-jp" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:chinese {:font-id "gfont-noto-sans-sc" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:korean {:font-id "gfont-noto-sans-kr" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:arabic {:font-id "gfont-noto-sans-arabic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:cyrillic {:font-id "gfont-noto-sans-cyrillic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:greek {:font-id "gfont-noto-sans-greek" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:hebrew {:font-id "gfont-noto-sans-hebrew" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:thai {:font-id "gfont-noto-sans-thai" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:devanagari {:font-id "gfont-noto-sans-devanagari" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:tamil {:font-id "gfont-noto-sans-tamil" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:latin-ext {:font-id "gfont-noto-sans" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:vietnamese {:font-id "gfont-noto-sans" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:armenian {:font-id "gfont-noto-sans-armenian" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:bengali {:font-id "gfont-noto-sans-bengali" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:cherokee {:font-id "gfont-noto-sans-cherokee" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:ethiopic {:font-id "gfont-noto-sans-ethiopic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:georgian {:font-id "gfont-noto-sans-georgian" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:gujarati {:font-id "gfont-noto-sans-gujarati" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:gurmukhi {:font-id "gfont-noto-sans-gurmukhi" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:khmer {:font-id "gfont-noto-sans-khmer" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:lao {:font-id "gfont-noto-sans-lao" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:malayalam {:font-id "gfont-noto-sans-malayalam" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:myanmar {:font-id "gfont-noto-sans-myanmar" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:sinhala {:font-id "gfont-noto-sans-sinhala" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:telugu {:font-id "gfont-noto-sans-telugu" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:tibetan {:font-id "gfont-noto-sans-tibetan" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
{:japanese {:font-id "gfont-noto-sans-jp" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:chinese {:font-id "gfont-noto-sans-sc" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:korean {:font-id "gfont-noto-sans-kr" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:arabic {:font-id "gfont-noto-sans-arabic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:cyrillic {:font-id "gfont-noto-sans" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:greek {:font-id "gfont-noto-sans" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:hebrew {:font-id "gfont-noto-sans-hebrew" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:thai {:font-id "gfont-noto-sans-thai" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:devanagari {:font-id "gfont-noto-sans" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:tamil {:font-id "gfont-noto-sans-tamil" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:latin-ext {:font-id "gfont-noto-sans" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:vietnamese {:font-id "gfont-noto-sans" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:armenian {:font-id "gfont-noto-sans-armenian" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:bengali {:font-id "gfont-noto-sans-bengali" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:cherokee {:font-id "gfont-noto-sans-cherokee" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:ethiopic {:font-id "gfont-noto-sans-ethiopic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:georgian {:font-id "gfont-noto-sans-georgian" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:gujarati {:font-id "gfont-noto-sans-gujarati" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:gurmukhi {:font-id "gfont-noto-sans-gurmukhi" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:khmer {:font-id "gfont-noto-sans-khmer" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:lao {:font-id "gfont-noto-sans-lao" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:malayalam {:font-id "gfont-noto-sans-malayalam" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:myanmar {:font-id "gfont-noto-sans-myanmar" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:sinhala {:font-id "gfont-noto-sans-sinhala" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:telugu {:font-id "gfont-noto-sans-telugu" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:tibetan {:font-id "gfont-noto-serif-tibetan" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:javanese {:font-id "gfont-noto-sans-javanese" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:kannada {:font-id "gfont-noto-sans-kannada" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:oriya {:font-id "gfont-noto-sans-oriya" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
@@ -399,8 +399,8 @@
|
||||
:bamum {:font-id "gfont-noto-sans-bamum" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:meroitic {:font-id "gfont-noto-sans-meroitic" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:symbols {:font-id "gfont-noto-sans-symbols" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:symbols-2 {:font-id "gfont-noto-sans-symbols-2" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:music {:font-id "gfont-noto-music" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}})
|
||||
:symbols-2 {:font-id "gfont-noto-sans-symbols-2" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
:music {:font-id "gfont-noto-music" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}})
|
||||
|
||||
(defn add-noto-fonts [fonts languages]
|
||||
(reduce (fn [acc lang]
|
||||
|
||||
@@ -280,8 +280,18 @@
|
||||
:layout-grid-cells
|
||||
(api/set-grid-layout-cells v)
|
||||
|
||||
(:layout
|
||||
:layout-flex-dir
|
||||
:layout
|
||||
(do
|
||||
(api/clear-layout)
|
||||
(cond
|
||||
(ctl/grid-layout? shape)
|
||||
(api/set-grid-layout shape)
|
||||
|
||||
(ctl/flex-layout? shape)
|
||||
(api/set-flex-layout shape))
|
||||
(api/set-layout-data shape))
|
||||
|
||||
(:layout-flex-dir
|
||||
:layout-gap-type
|
||||
:layout-gap
|
||||
:layout-align-items
|
||||
@@ -291,15 +301,12 @@
|
||||
:layout-wrap-type
|
||||
:layout-padding-type
|
||||
:layout-padding)
|
||||
(do
|
||||
(api/clear-layout)
|
||||
(cond
|
||||
(ctl/grid-layout? shape)
|
||||
(api/set-grid-layout-data shape)
|
||||
(cond
|
||||
(ctl/grid-layout? shape)
|
||||
(api/set-grid-layout-data shape)
|
||||
|
||||
(ctl/flex-layout? shape)
|
||||
(api/set-flex-layout shape))
|
||||
(api/set-layout-data shape))
|
||||
(ctl/flex-layout? shape)
|
||||
(api/set-flex-layout shape))
|
||||
|
||||
;; Property not in WASM
|
||||
nil))))
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
(defonce internal-module #js {})
|
||||
(defonce gl-context-handle nil)
|
||||
(defonce gl-context nil)
|
||||
(defonce context-lost-handler nil)
|
||||
(defonce context-restored-handler nil)
|
||||
(defonce serializers
|
||||
#js {:blur-type shared/RawBlurType
|
||||
:blend-mode shared/RawBlendMode
|
||||
@@ -48,3 +46,6 @@
|
||||
:fill-rule shared/RawFillRule})
|
||||
|
||||
(defonce context-initialized? false)
|
||||
(defonce context-lost? (atom false))
|
||||
(defonce context-lost-handler nil)
|
||||
(defonce context-lost-canvas nil)
|
||||
@@ -48,7 +48,11 @@
|
||||
(let [props (m/properties schema)
|
||||
tprops (m/type-properties schema)
|
||||
field (or (first in)
|
||||
(:error/field props))]
|
||||
(:error/field props))
|
||||
|
||||
field (if (vector? field)
|
||||
field
|
||||
[field])]
|
||||
|
||||
(if (contains? acc field)
|
||||
acc
|
||||
@@ -58,30 +62,30 @@
|
||||
|
||||
(or (= type :malli.core/missing-key)
|
||||
(nil? value))
|
||||
(assoc acc field {:message (tr "errors.field-missing")})
|
||||
(assoc-in acc field {:message (tr "errors.field-missing")})
|
||||
|
||||
;; --- CHECK on schema props
|
||||
(contains? props :error/fn)
|
||||
(assoc acc field (handle-error-fn props problem))
|
||||
(assoc-in acc field (handle-error-fn props problem))
|
||||
|
||||
(contains? props :error/message)
|
||||
(assoc acc field (handle-error-message props))
|
||||
(assoc-in acc field (handle-error-message props))
|
||||
|
||||
(contains? props :error/code)
|
||||
(assoc acc field (handle-error-code props))
|
||||
(assoc-in acc field (handle-error-code props))
|
||||
|
||||
;; --- CHECK on type props
|
||||
(contains? tprops :error/fn)
|
||||
(assoc acc field (handle-error-fn tprops problem))
|
||||
(assoc-in acc field (handle-error-fn tprops problem))
|
||||
|
||||
(contains? tprops :error/message)
|
||||
(assoc acc field (handle-error-message tprops))
|
||||
(assoc-in acc field (handle-error-message tprops))
|
||||
|
||||
(contains? tprops :error/code)
|
||||
(assoc acc field (handle-error-code tprops))
|
||||
(assoc-in acc field (handle-error-code tprops))
|
||||
|
||||
:else
|
||||
(assoc acc field {:message (tr "errors.invalid-data")})))))
|
||||
(assoc-in acc field {:message (tr "errors.invalid-data")})))))
|
||||
|
||||
(defn- use-rerender-fn
|
||||
[]
|
||||
@@ -114,20 +118,35 @@
|
||||
[f {:keys [schema validators]}]
|
||||
(fn [& args]
|
||||
(let [state (apply f args)
|
||||
cleaned (sm/decode schema (:data state) sm/string-transformer)
|
||||
cleaned (sm/decode schema (:data state) sm/json-transformer)
|
||||
valid? (sm/validate schema cleaned)
|
||||
errors (when-not valid?
|
||||
(collect-schema-errors schema validators state))]
|
||||
|
||||
errors
|
||||
(when-not valid?
|
||||
(collect-schema-errors schema validators state))
|
||||
|
||||
extra-errors
|
||||
(not-empty (:extra-errors state))]
|
||||
|
||||
(assoc state
|
||||
:errors errors
|
||||
:clean-data (when valid? cleaned)
|
||||
:valid (and (not errors) valid?)))))
|
||||
:valid (and (not errors)
|
||||
(not extra-errors)
|
||||
valid?)))))
|
||||
|
||||
|
||||
(defn- make-initial-state
|
||||
[initial-data]
|
||||
(let [initial (if (fn? initial-data) (initial-data) initial-data)
|
||||
initial (d/nilv initial {})]
|
||||
{:initial initial
|
||||
:data initial
|
||||
:errors {}
|
||||
:touched {}}))
|
||||
|
||||
(defn- create-form-mutator
|
||||
[internal-state rerender-fn wrap-update-fn initial opts]
|
||||
(mf/set-ref-val! internal-state initial)
|
||||
|
||||
[internal-state rerender-fn wrap-update-fn opts]
|
||||
(reify
|
||||
IDeref
|
||||
(-deref [_]
|
||||
@@ -136,7 +155,10 @@
|
||||
IReset
|
||||
(-reset! [_ new-value]
|
||||
(if (nil? new-value)
|
||||
(mf/set-ref-val! internal-state (if (fn? initial) (initial) initial))
|
||||
(let [initial (-> (mf/ref-val internal-state)
|
||||
(get :initial)
|
||||
(make-initial-state))]
|
||||
(mf/set-ref-val! internal-state initial))
|
||||
(mf/set-ref-val! internal-state new-value))
|
||||
(rerender-fn))
|
||||
|
||||
@@ -162,24 +184,25 @@
|
||||
(rerender-fn)))))
|
||||
|
||||
(defn use-form
|
||||
[& {:keys [initial] :as opts}]
|
||||
[& {:keys [initial schema validators] :as opts}]
|
||||
(let [rerender-fn (use-rerender-fn)
|
||||
|
||||
initial
|
||||
(mf/with-memo [initial]
|
||||
{:data (if (fn? initial) (initial) initial)
|
||||
:errors {}
|
||||
:touched {}})
|
||||
(make-initial-state initial))
|
||||
|
||||
internal-state
|
||||
(mf/use-ref nil)
|
||||
(mf/use-ref initial)
|
||||
|
||||
form-mutator
|
||||
(mf/with-memo [initial]
|
||||
(create-form-mutator internal-state rerender-fn wrap-update-schema-fn initial opts))]
|
||||
(mf/with-memo [schema validators]
|
||||
(let [mutator (create-form-mutator internal-state rerender-fn wrap-update-schema-fn
|
||||
(select-keys opts [:schema :validators]))]
|
||||
(swap! mutator identity)
|
||||
mutator))]
|
||||
|
||||
;; Initialize internal state once
|
||||
(mf/with-layout-effect []
|
||||
(mf/with-effect []
|
||||
(mf/set-ref-val! internal-state initial))
|
||||
|
||||
(mf/with-effect [initial]
|
||||
@@ -191,11 +214,16 @@
|
||||
([form field value]
|
||||
(on-input-change form field value false))
|
||||
([form field value trim?]
|
||||
(swap! form (fn [state]
|
||||
(-> state
|
||||
(assoc-in [:touched field] true)
|
||||
(assoc-in [:data field] (if trim? (str/trim value) value))
|
||||
(update :errors dissoc field))))))
|
||||
(letfn [(clean-errors [errors]
|
||||
(-> errors
|
||||
(dissoc field)
|
||||
(not-empty)))]
|
||||
(swap! form (fn [state]
|
||||
(-> state
|
||||
(assoc-in [:touched field] true)
|
||||
(assoc-in [:data field] (if trim? (str/trim value) value))
|
||||
(update :errors clean-errors)
|
||||
(update :extra-errors clean-errors)))))))
|
||||
|
||||
(defn update-input-value!
|
||||
[form field value]
|
||||
|
||||
@@ -275,6 +275,14 @@ pub extern "C" fn set_view_end() {
|
||||
}
|
||||
performance::end_measure!("set_view_end::rebuild_tiles");
|
||||
performance::end_timed_log!("rebuild_tiles", _rebuild_start);
|
||||
} else {
|
||||
// During pan, we only clear the tile index without
|
||||
// invalidating cached textures, which is more efficient.
|
||||
let _clear_start = performance::begin_timed_log!("clear_tile_index");
|
||||
performance::begin_measure!("set_view_end::clear_tile_index");
|
||||
state.clear_tile_index();
|
||||
performance::end_measure!("set_view_end::clear_tile_index");
|
||||
performance::end_timed_log!("clear_tile_index", _clear_start);
|
||||
}
|
||||
performance::end_measure!("set_view_end");
|
||||
performance::end_timed_log!("set_view_end", _end_start);
|
||||
|
||||
@@ -999,12 +999,13 @@ impl RenderState {
|
||||
|
||||
let viewbox_cache_size = get_cache_size(self.viewbox, scale);
|
||||
let cached_viewbox_cache_size = get_cache_size(self.cached_viewbox, scale);
|
||||
if viewbox_cache_size != cached_viewbox_cache_size {
|
||||
self.surfaces.resize_cache(
|
||||
&mut self.gpu_state,
|
||||
viewbox_cache_size,
|
||||
VIEWPORT_INTEREST_AREA_THRESHOLD,
|
||||
);
|
||||
// Only resize cache if the new size is larger than the cached size
|
||||
// This avoids unnecessary surface recreations when the cache size decreases
|
||||
if viewbox_cache_size.width > cached_viewbox_cache_size.width
|
||||
|| viewbox_cache_size.height > cached_viewbox_cache_size.height
|
||||
{
|
||||
self.surfaces
|
||||
.resize_cache(viewbox_cache_size, VIEWPORT_INTEREST_AREA_THRESHOLD);
|
||||
}
|
||||
|
||||
// FIXME - review debug
|
||||
@@ -1961,6 +1962,17 @@ impl RenderState {
|
||||
performance::end_measure!("rebuild_tiles_shallow");
|
||||
}
|
||||
|
||||
/// Clears the tile index without invalidating cached tile textures.
|
||||
/// This is useful when tile positions don't change (e.g., during pan operations)
|
||||
/// but the tile index needs to be synchronized. The cached tile textures remain
|
||||
/// valid since they don't depend on the current view position, only on zoom level.
|
||||
/// This is much more efficient than clearing the entire cache surface.
|
||||
pub fn clear_tile_index(&mut self) {
|
||||
performance::begin_measure!("clear_tile_index");
|
||||
self.surfaces.clear_tiles();
|
||||
performance::end_measure!("clear_tile_index");
|
||||
}
|
||||
|
||||
pub fn rebuild_tiles_from(&mut self, tree: ShapesPoolRef, base_id: Option<&Uuid>) {
|
||||
performance::begin_measure!("rebuild_tiles");
|
||||
|
||||
|
||||
@@ -108,6 +108,10 @@ impl Surfaces {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_tiles(&mut self) {
|
||||
self.tiles.clear();
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, gpu_state: &mut GpuState, new_width: i32, new_height: i32) {
|
||||
self.reset_from_target(gpu_state.create_target_surface(new_width, new_height));
|
||||
}
|
||||
@@ -248,13 +252,8 @@ impl Surfaces {
|
||||
// The rest are tile size surfaces
|
||||
}
|
||||
|
||||
pub fn resize_cache(
|
||||
&mut self,
|
||||
gpu_state: &mut GpuState,
|
||||
cache_dims: skia::ISize,
|
||||
interest_area_threshold: i32,
|
||||
) {
|
||||
self.cache = gpu_state.create_surface_with_isize("cache".to_string(), cache_dims);
|
||||
pub fn resize_cache(&mut self, cache_dims: skia::ISize, interest_area_threshold: i32) {
|
||||
self.cache = self.target.new_surface_with_dimensions(cache_dims).unwrap();
|
||||
self.cache.canvas().reset_matrix();
|
||||
self.cache.canvas().translate((
|
||||
(interest_area_threshold as f32 * TILE_SIZE),
|
||||
|
||||
@@ -197,6 +197,10 @@ impl<'a> State<'a> {
|
||||
self.render_state.rebuild_tiles_shallow(&self.shapes);
|
||||
}
|
||||
|
||||
pub fn clear_tile_index(&mut self) {
|
||||
self.render_state.clear_tile_index();
|
||||
}
|
||||
|
||||
pub fn rebuild_tiles(&mut self) {
|
||||
self.render_state.rebuild_tiles_from(&self.shapes, None);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user