diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index be95e3416..5158ed32e 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -43,13 +43,13 @@ (defn bounding-box "Returns a rect that wraps the shape after all transformations applied." [shape] - ; TODO: perhaps we need to store this calculation in a shape attribute + ;; TODO: perhaps we need to store this calculation in a shape attribute (gpr/points->rect (:points shape))) (defn left-bound "Returns the lowest x coord of the shape BEFORE applying transformations." - ; TODO: perhaps some day we want after transformations, but for the - ; moment it's enough as is now. + ;; TODO: perhaps some day we want after transformations, but for the + ;; moment it's enough as is now. [shape] (or (:x shape) (:x (:selrect shape)))) ; Paths don't have :x attribute @@ -106,8 +106,8 @@ ([attr val1 val2 precision] (let [close-val? (fn [num1 num2] - (when (and (number? num1) (number? num2)) - (< (mth/abs (- num1 num2)) precision)))] + (when (and (number? num1) (number? num2)) + (< (mth/abs (- num1 num2)) precision)))] (cond (and (number? val1) (number? val2)) (close-val? val1 val2) diff --git a/common/src/app/common/geom/shapes/constraints.cljc b/common/src/app/common/geom/shapes/constraints.cljc index 6cdf6c5d9..6c4f24cc3 100644 --- a/common/src/app/common/geom/shapes/constraints.cljc +++ b/common/src/app/common/geom/shapes/constraints.cljc @@ -210,7 +210,7 @@ ;; after-vec will contain the side length of the grown side ;; we scale the shape by the diference and translate it by the start ;; displacement (so its left+top position is constant) - scale (/ (gpt/length after-vec) (gpt/length before-vec)) + scale (/ (gpt/length after-vec) (max 0.01 (gpt/length before-vec))) resize-origin (gpo/origin child-points-after) @@ -268,11 +268,11 @@ scale-x (if (= :scale constraints-h) 1 - (/ (gpo/width-points child-bb-before) (gpo/width-points child-bb-after))) + (/ (gpo/width-points child-bb-before) (max 0.01 (gpo/width-points child-bb-after)))) scale-y (if (= :scale constraints-v) 1 - (/ (gpo/height-points child-bb-before) (gpo/height-points child-bb-after))) + (/ (gpo/height-points child-bb-before) (max 0.01 (gpo/height-points child-bb-after)))) resize-vector (gpt/point scale-x scale-y) resize-origin (gpo/origin transformed-child-bounds) diff --git a/common/src/app/common/geom/shapes/flex_layout/lines.cljc b/common/src/app/common/geom/shapes/flex_layout/lines.cljc index a31e13ca5..00349c995 100644 --- a/common/src/app/common/geom/shapes/flex_layout/lines.cljc +++ b/common/src/app/common/geom/shapes/flex_layout/lines.cljc @@ -238,6 +238,30 @@ (and col? (< total-min-width rest-layout-width total-max-width) (not (ctl/auto-width? parent))) (distribute-space :line-width :line-min-width :line-max-width total-min-width rest-layout-width)) + ;; Add information to limit the growth of width: 100% shapes to the bounds of the layout + layout-lines + (cond + row? + (->> layout-lines + (reduce + (fn [[result rest-layout-height] {:keys [line-height] :as line}] + [(conj result (assoc line :to-bound-height rest-layout-height)) + (- rest-layout-height line-height layout-gap-row)]) + [[] layout-height]) + (first)) + + col? + (->> layout-lines + (reduce + (fn [[result rest-layout-width] {:keys [line-width] :as line}] + [(conj result (assoc line :to-bound-width rest-layout-width)) + (- rest-layout-width line-width layout-gap-col)]) + [[] layout-width]) + (first)) + + :else + layout-lines) + [total-width total-height] (->> layout-lines (reduce add-lines [0 0])) base-p (flp/get-base-line parent layout-bounds total-width total-height num-lines)] diff --git a/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc b/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc index 072eed1b3..72994b873 100644 --- a/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc +++ b/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc @@ -20,7 +20,7 @@ transform-inverse child child-origin child-width - {:keys [children-data line-width] :as layout-data}] + {:keys [children-data line-width to-bound-width] :as layout-data}] (cond (ctl/row? parent) @@ -30,8 +30,9 @@ :modifiers (ctm/resize-modifiers (gpt/point fill-scale 1) child-origin transform transform-inverse)}) (ctl/col? parent) - (let [target-width (max (- line-width (ctl/child-width-margin child)) 0.01) - max-width (ctl/child-max-width child) + (let [line-width (min line-width (or to-bound-width line-width)) + target-width (max (- line-width (ctl/child-width-margin child)) 0.01) + max-width (max (ctl/child-max-width child) 0.01) target-width (min max-width target-width) fill-scale (/ target-width child-width)] {:width target-width @@ -43,7 +44,7 @@ transform transform-inverse child child-origin child-height - {:keys [children-data line-height] :as layout-data}] + {:keys [children-data line-height to-bound-height] :as layout-data}] (cond (ctl/col? parent) @@ -53,8 +54,9 @@ :modifiers (ctm/resize-modifiers (gpt/point 1 fill-scale) child-origin transform transform-inverse)}) (ctl/row? parent) - (let [target-height (max (- line-height (ctl/child-height-margin child)) 0.01) - max-height (ctl/child-max-height child) + (let [line-height (min line-height (or to-bound-height line-height)) + target-height (max (- line-height (ctl/child-height-margin child)) 0.01) + max-height (max (ctl/child-max-height child) 0.01) target-height (min max-height target-height) fill-scale (/ target-height child-height)] {:height target-height @@ -71,8 +73,13 @@ (when (or (ctl/fill-width? child) (ctl/fill-height? child)) (gtr/calculate-geometry @parent-bounds)) - fill-width (when (ctl/fill-width? child) (calc-fill-width-data parent transform transform-inverse child child-origin child-width layout-line)) - fill-height (when (ctl/fill-height? child) (calc-fill-height-data parent transform transform-inverse child child-origin child-height layout-line)) + fill-width + (when (ctl/fill-width? child) + (calc-fill-width-data parent transform transform-inverse child child-origin child-width layout-line)) + + fill-height + (when (ctl/fill-height? child) + (calc-fill-height-data parent transform transform-inverse child child-origin child-height layout-line)) child-width (or (:width fill-width) child-width) child-height (or (:height fill-height) child-height) diff --git a/common/src/app/common/math.cljc b/common/src/app/common/math.cljc index 7760a974c..d3f724ee9 100644 --- a/common/src/app/common/math.cljc +++ b/common/src/app/common/math.cljc @@ -34,7 +34,7 @@ (defn abs [v] #?(:cljs (js/Math.abs v) - :clj (Math/abs v))) + :clj (Math/abs (double v)))) (defn sin "Returns the sine of a number" diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index 973c1a714..f3f6f0dbb 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -109,6 +109,20 @@ (recur (conj result parent-id) parent-id) result)))) +(defn get-parent-ids-with-index + "Returns a tuple with the list of parents and a map with the position within each parent" + [objects shape-id] + (loop [parent-list [] + parent-indices {} + current shape-id] + (let [parent-id (dm/get-in objects [current :parent-id]) + parent (get objects parent-id)] + (if (and (some? parent) (not= parent-id current)) + (let [parent-list (conj parent-list parent-id) + parent-indices (assoc parent-indices parent-id (d/index-of (:shapes parent) current))] + (recur parent-list parent-indices parent-id)) + [parent-list parent-indices])))) + (defn get-siblings-ids [objects id] (let [parent (get-parent objects id)] diff --git a/common/src/app/common/types/shape_tree.cljc b/common/src/app/common/types/shape_tree.cljc index 4f13a5e8a..e6a41b5d8 100644 --- a/common/src/app/common/types/shape_tree.cljc +++ b/common/src/app/common/types/shape_tree.cljc @@ -132,43 +132,34 @@ (defn get-base [objects id-a id-b] - (let [parents-a (reverse (cons id-a (cph/get-parent-ids objects id-a))) - parents-b (reverse (cons id-b (cph/get-parent-ids objects id-b))) + (let [[parents-a parents-a-index] (cph/get-parent-ids-with-index objects id-a) + [parents-b parents-b-index] (cph/get-parent-ids-with-index objects id-b) - [base base-child-a base-child-b] - (loop [parents-a (rest parents-a) - parents-b (rest parents-b) - base uuid/zero] - (cond - (not= (first parents-a) (first parents-b)) - [base (first parents-a) (first parents-b)] + parents-a (cons id-a parents-a) + parents-b (into #{id-b} parents-b) - (or (empty? parents-a) (empty? parents-b)) - [uuid/zero (first parents-a) (first parents-b)] + ;; Search for the common frame in order + base (or (d/seek parents-b parents-a) uuid/zero) - :else - (recur (rest parents-a) (rest parents-b) (first parents-a)))) + idx-a (get parents-a-index base) + idx-b (get parents-b-index base)] - index-base-a (when base-child-a (cph/get-position-on-parent objects base-child-a)) - index-base-b (when base-child-b (cph/get-position-on-parent objects base-child-b))] - - [base index-base-a index-base-b])) + [base idx-a idx-b])) (defn is-shape-over-shape? [objects base-shape-id over-shape-id] (let [[base index-a index-b] (get-base objects base-shape-id over-shape-id)] (cond + ;; The base the base shape, so the other item is bellow (= base base-shape-id) - (let [object (get objects base-shape-id)] - (or (cph/frame-shape? object) - (cph/root-frame? object))) + false + ;; The base is the testing over, so it's over (= base over-shape-id) - (let [object (get objects over-shape-id)] - (or (not (cph/frame-shape? object)) - (not (cph/root-frame? object)))) + true + ;; Check which index is lower :else (< index-a index-b)))) diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index 9734c6179..6e7c6df84 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -271,16 +271,89 @@ (ptk/data-event :layout/update ids) (dwu/commit-undo-transaction undo-id)))))) +(defn fix-child-sizing + [objects parent-changes shape] + + (let [parent (-> (cph/get-parent objects (:id shape)) + (d/deep-merge parent-changes)) + + auto-width? (ctl/auto-width? parent) + auto-height? (ctl/auto-height? parent) + col? (ctl/col? parent) + row? (ctl/row? parent) + + all-children (->> parent :shapes (map (d/getf objects)))] + + (cond-> shape + ;; If the parent is hug width and the direction column + ;; change to fixed when ALL children are fill + (and col? auto-width? (every? ctl/fill-width? all-children)) + (assoc :layout-item-h-sizing :fix) + + ;; If the parent is hug height and the direction is column + ;; change to fixed when ANY children is fill + (and col? auto-height? (ctl/fill-height? shape)) + (assoc :layout-item-v-sizing :fix) + + ;; If the parent is hug width and the direction row + ;; change to fixed when ANY children is fill + (and row? auto-width? (ctl/fill-width? shape)) + (assoc :layout-item-h-sizing :fix) + + ;; If the parent is hug height and the direction row + ;; change to fixed when ALL children are fill + (and row? auto-height? (every? ctl/fill-height? all-children)) + (assoc :layout-item-v-sizing :fix)))) + +(defn fix-parent-sizing + [objects ids-set changes parent] + + (let [auto-width? (ctl/auto-width? parent) + auto-height? (ctl/auto-height? parent) + col? (ctl/col? parent) + row? (ctl/row? parent) + + all-children + (->> parent :shapes + (map (d/getf objects)) + (map (fn [shape] + (if (contains? ids-set (:id shape)) + (d/deep-merge shape changes) + shape))))] + + (cond-> parent + ;; Col layout and parent is hug-width if all children are fill-width + ;; change parent to fixed + (and col? auto-width? (every? ctl/fill-width? all-children)) + (assoc :layout-item-h-sizing :fix) + + ;; Col layout and parent is hug-height if any children is fill-height + ;; change parent to fixed + (and col? auto-height? (some ctl/fill-height? all-children)) + (assoc :layout-item-v-sizing :fix) + + ;; Row layout and parent is hug-width if any children is fill-width + ;; change parent to fixed + (and row? auto-width? (some ctl/fill-width? all-children)) + (assoc :layout-item-h-sizing :fix) + + ;; Row layout and parent is hug-height if all children are fill-height + ;; change parent to fixed + (and row? auto-height? (every? ctl/fill-height? all-children)) + (assoc :layout-item-v-sizing :fix)))) + (defn update-layout-child [ids changes] (ptk/reify ::update-layout-child ptk/WatchEvent (watch [_ state _] (let [objects (wsh/lookup-page-objects state) + children-ids (->> ids (mapcat #(cph/get-children-ids objects %))) parent-ids (->> ids (map #(cph/get-parent-id objects %))) - layout-ids (->> ids (filter (comp ctl/layout? (d/getf objects)))) undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) (dwc/update-shapes ids #(d/deep-merge (or % {}) changes)) - (ptk/data-event :layout/update (d/concat-vec layout-ids parent-ids)) + (dwc/update-shapes children-ids (partial fix-child-sizing objects changes)) + (dwc/update-shapes parent-ids (partial fix-parent-sizing objects (set ids) changes)) + (ptk/data-event :layout/update ids) (dwu/commit-undo-transaction undo-id)))))) diff --git a/frontend/src/app/main/data/workspace/thumbnails.cljs b/frontend/src/app/main/data/workspace/thumbnails.cljs index 78740ac67..d012264d0 100644 --- a/frontend/src/app/main/data/workspace/thumbnails.cljs +++ b/frontend/src/app/main/data/workspace/thumbnails.cljs @@ -77,6 +77,11 @@ ;; Delete the thumbnail first so if we interrupt we can regenerate after (->> (rp/cmd! :upsert-file-object-thumbnail params) (rx/catch #(rx/empty))) + + ;; Remove the thumbnail temporary. If the user changes pages the thumbnail is regenerated + (rx/of #(update % :workspace-thumbnails assoc object-id nil)) + + ;; Send the update to the back-end (->> blob-result (rx/merge-map (fn [blob] diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index 0900b8b75..c8a1a2c3d 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -27,23 +27,21 @@ (defn- draw-thumbnail-canvas! [canvas-node img-node] - (ts/raf - (fn [] - (try - (when (and (some? canvas-node) (some? img-node)) - (let [canvas-context (.getContext canvas-node "2d") - canvas-width (.-width canvas-node) - canvas-height (.-height canvas-node)] - (.clearRect canvas-context 0 0 canvas-width canvas-height) - (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) + (try + (when (and (some? canvas-node) (some? img-node)) + (let [canvas-context (.getContext canvas-node "2d") + canvas-width (.-width canvas-node) + canvas-height (.-height canvas-node)] + (.clearRect canvas-context 0 0 canvas-width canvas-height) + (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) - ;; Set a true on the next animation frame, we make sure the drawImage is completed - (ts/raf - #(dom/set-data! canvas-node "ready" "true")) - true)) - (catch :default err - (.error js/console err) - false))))) + ;; Set a true on the next animation frame, we make sure the drawImage is completed + (ts/raf + #(dom/set-data! canvas-node "ready" "true")) + true)) + (catch :default err + (.error js/console err) + false))) (defn- remove-image-loading "Remove the changes related to change a url for its embed value. This is necessary @@ -78,8 +76,12 @@ (gsh/selection-rect (concat [shape] all-children)) (-> shape :points gsh/points->selrect)) - fixed-width (mth/clamp width 250 2000) - fixed-height (/ (* height fixed-width) width) + [fixed-width fixed-height] + (if (> width height) + [(mth/clamp width 250 2000) + (/ (* height (mth/clamp width 250 2000)) width)] + [(/ (* width (mth/clamp height 250 2000)) height) + (mth/clamp height 250 2000)]) image-url (mf/use-state nil) observer-ref (mf/use-var nil) @@ -289,6 +291,6 @@ :height height} [:img {:ref frame-image-ref :src @image-url - :width width - :height height + :width fixed-width + :height fixed-height :on-load on-image-load}]])])]))