Thumbnails for clipped and nested artboards

This commit is contained in:
alonso.torres 2022-06-08 17:50:40 +02:00
parent 0bb0063be4
commit a4cc57886b
8 changed files with 119 additions and 53 deletions

View file

@ -14,6 +14,8 @@
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[cuerdas.core :as str])) [cuerdas.core :as str]))
(declare reduce-objects)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; GENERIC SHAPE SELECTORS AND PREDICATES ;; GENERIC SHAPE SELECTORS AND PREDICATES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -187,12 +189,21 @@
function of `get-immediate-children` for performance reasons. This function of `get-immediate-children` for performance reasons. This
function is executed in the render hot path." function is executed in the render hot path."
[objects] [objects]
(let [lookup (d/getf objects) (let [add-frame
xform (comp (keep lookup) (fn [result shape]
(filter frame-shape?) (cond-> result
(map :id))] (frame-shape? shape)
(->> (:shapes (lookup uuid/zero)) (conj (:id shape))))]
(into [] xform)))) (reduce-objects objects (complement frame-shape?) add-frame [])))
(defn get-root-shapes-ids
[objects]
(let [add-shape
(fn [result shape]
(cond-> result
(not (frame-shape? shape))
(conj (:id shape))))]
(reduce-objects objects (complement frame-shape?) add-shape [])))
(defn get-root-frames (defn get-root-frames
"Retrieves all frame objects as vector. It is not implemented in "Retrieves all frame objects as vector. It is not implemented in
@ -631,3 +642,28 @@
[objects parent-id candidate-child-id] [objects parent-id candidate-child-id]
(let [parents (get-parents-seq objects candidate-child-id)] (let [parents (get-parents-seq objects candidate-child-id)]
(some? (d/seek #(= % parent-id) parents)))) (some? (d/seek #(= % parent-id) parents))))
(defn reduce-objects
([objects reducer-fn init-val]
(reduce-objects objects nil reducer-fn init-val))
([objects check-children? reducer-fn init-val]
(let [root-children (get-in objects [uuid/zero :shapes])]
(if (empty? root-children)
init-val
(loop [current-val init-val
current-id (first root-children)
pending-ids (rest root-children)]
(let [current-shape (get objects current-id)
next-val (reducer-fn current-val current-shape)
next-pending-ids
(if (or (nil? check-children?) (check-children? current-shape))
(concat (or (:shapes current-shape) []) pending-ids)
pending-ids)]
(if (empty? next-pending-ids)
next-val
(recur next-val (first next-pending-ids) (rest next-pending-ids)))))))))

View file

@ -270,6 +270,14 @@
(into [] (keep (d/getf objects)) children-ids))) (into [] (keep (d/getf objects)) children-ids)))
workspace-page-objects =)) workspace-page-objects =))
(defn all-children-objects
[id]
(l/derived
(fn [objects]
(let [children-ids (cph/get-children-ids objects id)]
(into [] (keep (d/getf objects)) children-ids)))
workspace-page-objects =))
(def workspace-page-options (def workspace-page-options
(l/derived :options workspace-page)) (l/derived :options workspace-page))

View file

@ -21,4 +21,5 @@
(def current-project-id (mf/create-context nil)) (def current-project-id (mf/create-context nil))
(def current-page-id (mf/create-context nil)) (def current-page-id (mf/create-context nil))
(def current-file-id (mf/create-context nil)) (def current-file-id (mf/create-context nil))
(def scroll-ctx (mf/create-context nil)) (def scroll-ctx (mf/create-context nil))
(def active-frames-ctx (mf/create-context nil))

View file

@ -45,7 +45,8 @@
(mf/defc frame-thumbnail (mf/defc frame-thumbnail
{::mf/wrap-props false} {::mf/wrap-props false}
[props] [props]
(let [shape (obj/get props "shape")] (let [shape (obj/get props "shape")
bounds (or (obj/get props "bounds") (:selrect shape))]
(when (:thumbnail shape) (when (:thumbnail shape)
(let [{:keys [x y width height show-content]} shape (let [{:keys [x y width height show-content]} shape
transform (gsh/transform-str shape) transform (gsh/transform-str shape)
@ -72,10 +73,10 @@
[:image.frame-thumbnail [:image.frame-thumbnail
{:id (dm/str "thumbnail-" (:id shape)) {:id (dm/str "thumbnail-" (:id shape))
:href (:thumbnail shape) :href (:thumbnail shape)
:x (:x shape) :x (:x bounds)
:y (:y shape) :y (:y bounds)
:width (:width shape) :width (:width bounds)
:height (:height shape) :height (:height bounds)
;; DEBUG ;; DEBUG
:style {:filter (when (debug? :thumbnails) "sepia(1)")}}]] :style {:filter (when (debug? :thumbnails) "sepia(1)")}}]]

View file

@ -6,9 +6,9 @@
(ns app.main.ui.viewer.comments (ns app.main.ui.viewer.comments
(:require (:require
[app.common.geom.shapes :as gsh]
[app.common.geom.matrix :as gmt] [app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.main.data.comments :as dcm] [app.main.data.comments :as dcm]
[app.main.data.events :as ev] [app.main.data.events :as ev]
[app.main.refs :as refs] [app.main.refs :as refs]

View file

@ -13,6 +13,7 @@
common." common."
(:require (:require
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.main.ui.context :as ctx]
[app.main.ui.shapes.circle :as circle] [app.main.ui.shapes.circle :as circle]
[app.main.ui.shapes.image :as image] [app.main.ui.shapes.image :as image]
[app.main.ui.shapes.rect :as rect] [app.main.ui.shapes.rect :as rect]
@ -52,7 +53,8 @@
(mf/use-memo (mf/use-memo
(mf/deps objects) (mf/deps objects)
#(cph/objects-by-frame objects))] #(cph/objects-by-frame objects))]
[:*
[:& (mf/provider ctx/active-frames-ctx) {:value active-frames}
;; Render font faces only for shapes that are part of the root ;; Render font faces only for shapes that are part of the root
;; frame but don't belongs to any other frame. ;; frame but don't belongs to any other frame.
(let [xform (comp (let [xform (comp
@ -75,7 +77,15 @@
::mf/wrap-props false} ::mf/wrap-props false}
[props] [props]
(let [shape (obj/get props "shape") (let [shape (obj/get props "shape")
opts #js {:shape shape}]
active-frames
(when (cph/root-frame? shape) (mf/use-ctx ctx/active-frames-ctx))
thumbnail?
(and (some? active-frames)
(not (contains? active-frames (:id shape))))
opts #js {:shape shape :thumbnail? thumbnail?}]
(when (and (some? shape) (not (:hidden shape))) (when (and (some? shape) (not (:hidden shape)))
[:* [:*
(case (:type shape) (case (:type shape)

View file

@ -8,6 +8,7 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.geom.shapes :as gsh]
[app.common.math :as mth] [app.common.math :as mth]
[app.main.data.workspace.thumbnails :as dwt] [app.main.data.workspace.thumbnails :as dwt]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -54,7 +55,7 @@
(defn use-render-thumbnail (defn use-render-thumbnail
"Hook that will create the thumbnail thata" "Hook that will create the thumbnail thata"
[page-id {:keys [id x y width height] :as shape} node-ref rendered? disable? force-render] [page-id {:keys [id] :as shape} node-ref rendered? disable? force-render]
(let [frame-canvas-ref (mf/use-ref nil) (let [frame-canvas-ref (mf/use-ref nil)
frame-image-ref (mf/use-ref nil) frame-image-ref (mf/use-ref nil)
@ -63,13 +64,21 @@
regenerate-thumbnail (mf/use-var false) regenerate-thumbnail (mf/use-var false)
fixed-width (mth/clamp (:width shape) 250 2000) all-children-ref (mf/use-memo (mf/deps id) #(refs/all-children-objects id))
fixed-height (/ (* (:height shape) fixed-width) (:width shape)) all-children (mf/deref all-children-ref)
{:keys [x y width height] :as shape-bb}
(if (:show-content shape)
(gsh/selection-rect all-children)
(-> shape :points gsh/points->selrect))
fixed-width (mth/clamp width 250 2000)
fixed-height (/ (* height fixed-width) width)
image-url (mf/use-state nil) image-url (mf/use-state nil)
observer-ref (mf/use-var nil) observer-ref (mf/use-var nil)
shape-ref (hooks/use-update-var shape) shape-bb-ref (hooks/use-update-var shape-bb)
updates-str (mf/use-memo #(rx/subject)) updates-str (mf/use-memo #(rx/subject))
@ -101,7 +110,8 @@
(fn [] (fn []
(let [node @node-ref (let [node @node-ref
frame-html (dom/node->xml node) frame-html (dom/node->xml node)
{:keys [x y width height]} @shape-ref
{:keys [x y width height]} @shape-bb-ref
style-node (dom/query (dm/str "#frame-container-" (:id shape) " style")) style-node (dom/query (dm/str "#frame-container-" (:id shape) " style"))
style-str (or (-> style-node dom/node->xml) "") style-str (or (-> style-node dom/node->xml) "")
@ -201,6 +211,7 @@
(mf/html (mf/html
[:* [:*
[:> frame/frame-thumbnail {:key (dm/str (:id shape)) [:> frame/frame-thumbnail {:key (dm/str (:id shape))
:bounds shape-bb
:shape (cond-> shape :shape (cond-> shape
(some? thumbnail-data) (some? thumbnail-data)
(assoc :thumbnail thumbnail-data))}] (assoc :thumbnail thumbnail-data))}]
@ -220,9 +231,9 @@
(when (some? @image-url) (when (some? @image-url)
[:image {:ref frame-image-ref [:image {:ref frame-image-ref
:x (:x shape) :x x
:y (:y shape) :y y
:href @image-url :href @image-url
:width (:width shape) :width width
:height (:height shape) :height height
:on-load on-image-load}])])])) :on-load on-image-load}])])]))

View file

@ -10,7 +10,6 @@
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.pages :as cp] [app.common.pages :as cp]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.uuid :as uuid]
[app.main.data.shortcuts :as dsc] [app.main.data.shortcuts :as dsc]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.path.shortcuts :as psc] [app.main.data.workspace.path.shortcuts :as psc]
@ -189,23 +188,28 @@
grouped? (fn [id] (contains? #{:group :bool} (get-in objects [id :type]))) grouped? (fn [id] (contains? #{:group :bool} (get-in objects [id :type])))
selected-with-parents
(into #{} (mapcat #(cph/get-parent-ids objects %)) selected)
remove-xfm (mapcat #(cph/get-parent-ids objects %)) root-frame-with-data? #(and (cph/root-frame? objects %) (d/not-empty? (get-in objects [% :shapes])))
remove-id? (cond-> (into #{} remove-xfm selected)
(not mod?)
(into
(filter #(or (and (cph/root-frame? objects %) (d/not-empty? (get-in objects [% :shapes])))
(group-empty-space? % objects ids)))
ids)
mod? ;; Set with the elements to remove from the hover list
(into (filter grouped?) ids)) remove-id?
(cond-> selected-with-parents
(not mod?)
(into (filter #(or (root-frame-with-data? %)
(group-empty-space? % objects ids)))
ids)
hover-shape (->> ids mod?
(remove remove-id?) (into (filter grouped?) ids))
(filter #(or (empty? focus) (cp/is-in-focus? objects focus %)))
(first) hover-shape
(get objects))] (->> ids
(remove remove-id?)
(filter #(or (empty? focus) (cp/is-in-focus? objects focus %)))
(first)
(get objects))]
(reset! hover hover-shape) (reset! hover hover-shape)
(reset! hover-ids ids)))))) (reset! hover-ids ids))))))
@ -214,13 +218,7 @@
(let [root-frame-ids (let [root-frame-ids
(mf/use-memo (mf/use-memo
(mf/deps objects) (mf/deps objects)
(fn [] #(cph/get-root-shapes-ids objects))
(let [frame? (into #{} (cph/get-frames-ids objects))
;; Removes from zero/shapes attribute all the frames so we can ask only for
;; the non-frame children
objects (-> objects
(update-in [uuid/zero :shapes] #(filterv (comp not frame?) %)))]
(cph/get-children-ids objects uuid/zero))))
modifiers (select-keys modifiers root-frame-ids)] modifiers (select-keys modifiers root-frame-ids)]
(sfd/use-dynamic-modifiers objects globals/document modifiers))) (sfd/use-dynamic-modifiers objects globals/document modifiers)))
@ -238,14 +236,13 @@
selected-shapes-frames (mf/use-memo (mf/deps selected) #(into #{} xf-selected-frame selected)) selected-shapes-frames (mf/use-memo (mf/deps selected) #(into #{} xf-selected-frame selected))
active-selection (when (and (not= transform :move) (= (count selected-frames) 1)) (first selected-frames)) active-selection (when (and (not= transform :move) (= (count selected-frames) 1)) (first selected-frames))
hover-frame (last @hover-ids) last-hover-ids (mf/use-var nil)]
last-hover-frame (mf/use-var nil)]
(mf/use-effect (mf/use-effect
(mf/deps hover-frame) (mf/deps @hover-ids)
(fn [] (fn []
(when (some? hover-frame) (when (d/not-empty? @hover-ids)
(reset! last-hover-frame hover-frame)))) (reset! last-hover-ids (set @hover-ids)))))
(mf/use-effect (mf/use-effect
(mf/deps objects @hover-ids selected zoom transform vbox) (mf/deps objects @hover-ids selected zoom transform vbox)
@ -258,7 +255,9 @@
;; - If no hovering over any frames we keep the previous active one ;; - If no hovering over any frames we keep the previous active one
;; - Check always that the active frames are inside the vbox ;; - Check always that the active frames are inside the vbox
(let [is-active-frame? (let [hover-ids? (set @hover-ids)
is-active-frame?
(fn [id] (fn [id]
(or (or
;; Zoom > 130% shows every frame ;; Zoom > 130% shows every frame
@ -267,7 +266,7 @@
;; Zoom >= 25% will show frames hovering ;; Zoom >= 25% will show frames hovering
(and (and
(>= zoom 0.25) (>= zoom 0.25)
(or (= id hover-frame) (= id @last-hover-frame))) (or (contains? hover-ids? id) (contains? @last-hover-ids id)))
;; Otherwise, if it's a selected frame ;; Otherwise, if it's a selected frame
(= id active-selection) (= id active-selection)