penpot/frontend/src/app/render_wasm/shape.cljs
2024-11-25 09:26:49 +01:00

146 lines
3.2 KiB
Clojure

;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.render-wasm.shape
(:require
[app.common.transit :as t]
[app.common.types.shape :as shape]
[app.render-wasm.api :as api]
[clojure.core :as c]
[cuerdas.core :as str]))
(declare ^:private impl-assoc)
(declare ^:private impl-conj)
(declare ^:private impl-dissoc)
(deftype ShapeProxy [delegate]
Object
(toString [coll]
(str "{" (str/join ", " (for [[k v] coll] (str k " " v))) "}"))
(equiv [this other]
(-equiv this other))
;; Marker protocol
shape/IShape
IWithMeta
(-with-meta [_ meta]
(ShapeProxy. (with-meta delegate meta)))
IMeta
(-meta [_] (meta delegate))
ICollection
(-conj [coll entry]
(impl-conj coll entry))
IEquiv
(-equiv [coll other]
(c/equiv-map coll other))
IHash
(-hash [coll] (hash (into {} coll)))
ISequential
ISeqable
(-seq [_]
(c/-seq delegate))
ICounted
(-count [_]
(+ 1 (count delegate)))
ILookup
(-lookup [coll k]
(-lookup coll k nil))
(-lookup [_ k not-found]
(c/-lookup delegate k not-found))
IFind
(-find [_ k]
(c/-find delegate k))
IAssociative
(-assoc [coll k v]
(impl-assoc coll k v))
(-contains-key? [_ k]
(contains? delegate k))
IMap
(-dissoc [coll k]
(impl-dissoc coll k))
IFn
(-invoke [coll k]
(-lookup coll k))
(-invoke [coll k not-found]
(-lookup coll k not-found))
IPrintWithWriter
(-pr-writer [_ writer _]
(-write writer (str "#penpot/shape " (:id delegate)))))
;; --- SHAPE IMPL
(defn- impl-assoc
[self k v]
(when ^boolean shape/*wasm-sync*
(api/use-shape (:id self))
(case k
:selrect (api/set-shape-selrect v)
:rotation (api/set-shape-rotation v)
:transform (api/set-shape-transform v)
:fills (api/set-shape-fills v)
:blend-mode (api/set-shape-blend-mode v)
:opacity (api/set-shape-opacity v)
:shapes (api/set-shape-children v)
nil)
;; when something synced with wasm
;; is modified, we need to request
;; a new render.
(api/request-render))
(let [delegate (.-delegate ^ShapeProxy self)
delegate' (assoc delegate k v)]
(if (identical? delegate' delegate)
self
(ShapeProxy. delegate'))))
(defn- impl-dissoc
[self k]
(let [delegate (.-delegate ^ShapeProxy self)
delegate' (dissoc delegate k)]
(if (identical? delegate delegate')
self
(ShapeProxy. delegate'))))
(defn- impl-conj
[self entry]
(if (vector? entry)
(-assoc self (-nth entry 0) (-nth entry 1))
(loop [ret self es (seq entry)]
(if (nil? es)
ret
(let [e (first es)]
(if (vector? e)
(recur (-assoc ret (-nth e 0) (-nth e 1))
(next es))
(throw (js/Error. "conj on a map takes map entries or seqables of map entries"))))))))
(defn create-shape
"Instanciate a shape from a map"
[attrs]
(ShapeProxy. attrs))
(t/add-handlers!
;; We only add a write handler, read handler uses the dynamic dispatch
{:id "shape"
:class ShapeProxy
:wfn #(into {} %)})