diff --git a/common/uxbox/common/pages.cljc b/common/uxbox/common/pages.cljc index 89cac2cef..e02dfbffe 100644 --- a/common/uxbox/common/pages.cljc +++ b/common/uxbox/common/pages.cljc @@ -5,7 +5,7 @@ ;; This Source Code Form is "Incompatible With Secondary Licenses", as ;; defined by the Mozilla Public License, v. 2.0. ;; -;; Copyright (c) 2019-2020 Andrey Antukh +;; Copyright (c) 2020 UXBOX Labs SL (ns uxbox.common.pages "A common (clj/cljs) functions and specs for pages." @@ -80,22 +80,25 @@ (remove p? after)))) (defn select-toplevel-shapes - [objects] - (let [lookup #(get objects %) - root (lookup uuid/zero) - childs (:shapes root)] - (loop [id (first childs) - ids (rest childs) - res []] - (if (nil? id) - res - (let [obj (lookup id) - typ (:type obj)] - (recur (first ids) - (rest ids) - (if (= :frame typ) - (into res (map lookup) (:shapes obj)) - (conj res obj)))))))) + ([objects] (select-toplevel-shapes objects nil)) + ([objects {:keys [include-frames?] :or {include-frames? false}}] + (let [lookup #(get objects %) + root (lookup uuid/zero) + childs (:shapes root)] + (loop [id (first childs) + ids (rest childs) + res []] + (if (nil? id) + res + (let [obj (lookup id) + typ (:type obj)] + (recur (first ids) + (rest ids) + (if (= :frame typ) + (if include-frames? + (d/concat res [obj] (map lookup (:shapes obj))) + (d/concat res (map lookup (:shapes obj)))) + (conj res obj))))))))) (defn select-frames [objects] diff --git a/frontend/resources/styles/main/layouts/main-layout.scss b/frontend/resources/styles/main/layouts/main-layout.scss index 738f6c0ab..b15a9bd4e 100644 --- a/frontend/resources/styles/main/layouts/main-layout.scss +++ b/frontend/resources/styles/main/layouts/main-layout.scss @@ -30,6 +30,4 @@ .dashboard-content { background-color: lighten($color-gray-10, 5%); - display: flex; - flex-direction: column; } diff --git a/frontend/resources/styles/main/partials/workspace.scss b/frontend/resources/styles/main/partials/workspace.scss index 0d59b6392..b3f12c849 100644 --- a/frontend/resources/styles/main/partials/workspace.scss +++ b/frontend/resources/styles/main/partials/workspace.scss @@ -119,7 +119,7 @@ } .workspace-viewport { - height: 100%; + height: calc(100% - 40px); overflow: hidden; transition: none; width: 100%; @@ -129,8 +129,9 @@ grid-template-columns: 20px 1fr; .viewport { - grid-column: 2 / span 1; - grid-row: 2 / span 1; + grid-column: 1 / span 2; + grid-row: 1 / span 2; + overflow: hidden; &.drawing { cursor: cell; @@ -156,7 +157,7 @@ } } - .viewport, .page-canvas, .page-layout { + .page-canvas, .page-layout { overflow: visible; } } diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 7d2ad4f54..e07bf8c0b 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -66,18 +66,13 @@ :sitemap-pages :layers :element-options - :rules}) + :rules + }) (s/def ::options-mode #{:design :prototype}) (def workspace-default {:zoom 1 - :size {:x 0 - :y 0 - :width c/viewport-width - :height c/viewport-width - :viewport-width c/viewport-width - :viewport-height c/viewport-height} :flags #{} :selected #{} :drawing nil @@ -196,51 +191,59 @@ ;; --- Viewport Sizing +(declare zoom-to-fit-all) + +;; TODO: add-spec + (defn initialize-viewport [{:keys [width height] :as size}] + (js/console.log "initialize-viewport" size) (ptk/reify ::initialize-viewport ptk/UpdateEvent (update [_ state] (update state :workspace-local (fn [local] (-> local - (assoc :zoom 1) - (update :size assoc - :x 0 - :y 0 - :width width - :height height - :viewport-width width - :viewport-height height))))))) - + (assoc :vport size) + (update :vbox (fn [vbox] + (if (nil? vbox) + (assoc size :x 0 :y 0) + vbox))))))) + ptk/WatchEvent + (watch [_ state stream] + #_(rx/of zoom-to-fit-all)))) (defn update-viewport-position [{:keys [x y] :or {x identity y identity}}] + (us/assert fn? x) + (us/assert fn? y) (ptk/reify ::update-viewport-position ptk/UpdateEvent (update [_ state] - (update-in state [:workspace-local :size] - (fn [size] - (-> size - (update :x x) - (update :y y))))))) + (update-in state [:workspace-local :vbox] + (fn [vbox] + (-> vbox + (update :x (comp mth/round x)) + (update :y (comp mth/round y)))))))) + +;; TODO: add spec (defn update-viewport-size - [{:keys [width height]}] + [{:keys [width height] :as size}] (ptk/reify ::update-viewport-size ptk/UpdateEvent (update [_ state] (update state :workspace-local - (fn [{:keys [size] :as local}] - (let [wprop (/ (:viewport-width size) width) - hprop (/ (:viewport-height size) height) - size' (-> size - (assoc :viewport-width width) - (assoc :viewport-height height) - (update :width #(/ % wprop)) - (update :height #(/ % hprop)))] - (assoc local :size size'))))))) + (fn [{:keys [vbox vport] :as local}] + (let [wprop (/ (:width vport) width) + hprop (/ (:height vport) height)] + (-> local + (assoc :vport size) + (update :vbox (fn [vbox] + (-> vbox + (update :width #(/ % wprop)) + (update :height #(/ % hprop)))))))))))) ;; --- @@ -325,19 +328,15 @@ ;; --- Zoom Management (defn- impl-update-zoom - [local zoom] - (let [base-width (get-in local [:size :viewport-width]) - base-height (get-in local [:size :viewport-height]) - - zoom (if (fn? zoom) + [{:keys [vbox vport] :as local} zoom] + (let [zoom (if (fn? zoom) (zoom (:zoom local)) zoom) - - width (/ base-width zoom) - height (/ base-height zoom)] + width (/ (:width vport) zoom) + height (/ (:height vport) zoom)] (-> local (assoc :zoom zoom) - (update :size assoc :width width :height height)))) + (update :vbox assoc :width width :height height)))) (defn increase-zoom [center] @@ -376,51 +375,45 @@ (update state :workspace-local #(impl-update-zoom % 2))))) -;; (defn impl-expand-rect -;; [{:keys [x y width height] :as rect} padding] -;; (assoc rect -;; :x (- x padding) -;; :y (- y padding) -;; :width (+ width padding) -;; :height (+ height padding))) +(def zoom-to-fit-all + (ptk/reify ::zoom-to-fit-all + ptk/UpdateEvent + (update [_ state] + (let [page-id (get-in state [:workspace-page :id]) + objects (get-in state [:workspace-data page-id :objects]) + shapes (cp/select-toplevel-shapes objects {:include-frames? true}) + srect (geom/shapes->rect-shape shapes)] -;; (def zoom-to-selected-shape -;; (ptk/reify ::zoom-to-selected-shape -;; ptk/UpdateEvent -;; (update [_ state] -;; (let [page-id (get-in state [:workspace-page :id]) -;; objects (get-in state [:workspace-data page-id :objects]) -;; selected (get-in state [:workspace-local :selected]) -;; shapes (map #(get objects %) selected) -;; rect (geom/selection-rect shapes) -;; rect (impl-expand-rect rect 20) -;; ] -;; (update state :workspace-local -;; (fn [{:keys [size] :as local}] -;; (let [proportion (/ (:viewport-width size) (:viewport-height size)) -;; width (:width rect) -;; height (/ (:width rect) proportion) - -;; [width height] (if (> (:height rect) height) -;; [(* width proportion) (:height rect)] -;; [width height]) - -;; zoom (/ (:viewport-width size) width)] - -;; ;; (js/console.log "proportion=" proportion -;; ;; "width=" width -;; ;; "height=" height -;; ;; "viewport-width=" (:viewport-width size) -;; ;; "viewport-height=" (:viewport-height size)) -;; (-> local -;; (assoc :zoom zoom) -;; (update :size assoc -;; :x (:x rect) -;; :y (:y rect) -;; :width width -;; :height height -;; ))))))))) + (if (or (mth/nan? (:width srect)) + (mth/nan? (:height srect))) + state + (update state :workspace-local + (fn [{:keys [vbox vport] :as local}] + (let [srect (geom/adjust-to-viewport vport srect {:padding 40}) + zoom (/ (:width vport) (:width srect))] + (-> local + (assoc :zoom zoom) + (update :vbox merge srect)))))))))) +(def zoom-to-selected-shape + (ptk/reify ::zoom-to-selected-shape + ptk/UpdateEvent + (update [_ state] + (let [selected (get-in state [:workspace-local :selected])] + (if (empty? selected) + state + (let [page-id (get-in state [:workspace-page :id]) + objects (get-in state [:workspace-data page-id :objects]) + srect (->> selected + (map #(get objects %)) + (geom/selection-rect))] + (update state :workspace-local + (fn [{:keys [vbox vport] :as local}] + (let [srect (geom/adjust-to-viewport vport srect {:padding 40}) + zoom (/ (:width vport) (:width srect))] + (-> local + (assoc :zoom zoom) + (update :vbox merge srect))))))))))) ;; --- Selection Rect @@ -1464,9 +1457,10 @@ "ctrl+g" #(st/emit! create-group) "ctrl+shift+g" #(st/emit! remove-group) "shift+0" #(st/emit! zoom-to-50) - "shift+1" #(st/emit! reset-zoom) - "shift+2" #(st/emit! zoom-to-200) - ;; "shift+2" #(st/emit! zoom-to-selected-shape) + ;; "shift+1" #(st/emit! reset-zoom) + "shift+1" #(st/emit! zoom-to-fit-all) + ;; "shift+2" #(st/emit! zoom-to-200) + "shift+2" #(st/emit! zoom-to-selected-shape) "ctrl+d" #(st/emit! duplicate-selected) "ctrl+z" #(st/emit! dwc/undo) "ctrl+shift+z" #(st/emit! dwc/redo) diff --git a/frontend/src/uxbox/main/data/workspace/transforms.cljs b/frontend/src/uxbox/main/data/workspace/transforms.cljs index 80fd3799a..2c4adb2be 100644 --- a/frontend/src/uxbox/main/data/workspace/transforms.cljs +++ b/frontend/src/uxbox/main/data/workspace/transforms.cljs @@ -1,3 +1,12 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + (ns uxbox.main.data.workspace.transforms "Events related with shapes transformations" (:require diff --git a/frontend/src/uxbox/main/exports.cljs b/frontend/src/uxbox/main/exports.cljs index 4c6ace7b7..806c0dbbe 100644 --- a/frontend/src/uxbox/main/exports.cljs +++ b/frontend/src/uxbox/main/exports.cljs @@ -2,7 +2,10 @@ ;; 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) 2016 Andrey Antukh +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL (ns uxbox.main.exports "The main logic for SVG export functionality." @@ -34,13 +37,11 @@ :fill background-color}]) (defn- calculate-dimensions - [data] - (let [shapes (vals (:objects data)) - shape (geom/shapes->rect-shape shapes) - width (+ (:x shape) (:width shape) 100) - height (+ (:y shape) (:height shape) 100)] - {:width (if (mth/nan? width) 100 width) - :height (if (mth/nan? height) 100 height)})) + [{:keys [objects] :as data} vport] + (let [shapes (cp/select-toplevel-shapes objects {:include-frames? true})] + (->> (geom/shapes->rect-shape shapes) + (geom/adjust-to-viewport vport) + (geom/fix-invalid-rect-values)))) (declare shape-wrapper-factory) @@ -86,14 +87,19 @@ (mf/defc page-svg {::mf/wrap [mf/memo]} - [{:keys [data] :as props}] + [{:keys [data width height] :as props}] (let [objects (:objects data) - dim (calculate-dimensions data) + vport {:width width :height height} + + dim (calculate-dimensions data vport) root (get objects uuid/zero) shapes (->> (:shapes root) (map #(get objects %))) - vbox (str "0 0 " (:width dim 0) " " (:height dim 0)) + vbox (str (:x dim 0) " " + (:y dim 0) " " + (:width dim 100) " " + (:height dim 100)) frame-wrapper (mf/use-memo @@ -104,7 +110,6 @@ (mf/use-memo (mf/deps objects) #(shape-wrapper-factory objects))] - [:svg {:view-box vbox :version "1.1" :xmlnsXlink "http://www.w3.org/1999/xlink" diff --git a/frontend/src/uxbox/main/ui/workspace.cljs b/frontend/src/uxbox/main/ui/workspace.cljs index 2bbc1a035..e5306bf38 100644 --- a/frontend/src/uxbox/main/ui/workspace.cljs +++ b/frontend/src/uxbox/main/ui/workspace.cljs @@ -61,11 +61,14 @@ [:* [:div.empty-rule-square] [:& horizontal-rule {:zoom (:zoom local) - :size (:size local)}] + :vbox (:vbox local) + :vport (:vport local)}] [:& vertical-rule {:zoom (:zoom local 1) - :size (:size local)}]]) + :vbox (:vbox local) + :vport (:vport local)}]]) [:& viewport {:page page + :key (:id page) :file file :local local}]]] diff --git a/frontend/src/uxbox/main/ui/workspace/rules.cljs b/frontend/src/uxbox/main/ui/workspace/rules.cljs index 3b0d83fba..ce9160b88 100644 --- a/frontend/src/uxbox/main/ui/workspace/rules.cljs +++ b/frontend/src/uxbox/main/ui/workspace/rules.cljs @@ -10,121 +10,100 @@ (ns uxbox.main.ui.workspace.rules (:require [rumext.alpha :as mf] + [uxbox.util.math :as mth] [uxbox.util.object :as obj])) -(def STEP-PADDING 20) +(defn draw-rule! + [dctx {:keys [zoom size start count type] :or {count 200}}] + (let [txfm (- (* (- 0 start) zoom) 20) + minv (mth/round start) + maxv (mth/round (+ start (/ size zoom))) + + step (mth/round (/ (mth/abs (- maxv minv)) count)) + step (max (* 1 (* 10 step)) 1)] + + (obj/set! dctx "fillStyle" "#E8E9EA") + (if (= type :horizontal) + (do + (.fillRect dctx 0 0 size 20) + (.translate dctx txfm 0)) + (do + (.fillRect dctx 0 0 20 size) + (.translate dctx 0 txfm))) + + (obj/set! dctx "font" "12px serif") + (obj/set! dctx "fillStyle" "#7B7D85") + (obj/set! dctx "strokeStyle" "#7B7D85") + (obj/set! dctx "textAlign" "center") + + (loop [i minv] + (when (< i maxv) + (let [pos (+ (* i zoom) 0)] + (when (= (mod i step) 0) + (.save dctx) + (if (= type :horizontal) + (do + (.fillText dctx (str i) pos 13)) + (do + (.translate dctx 12 pos) + (.rotate dctx (/ (* 270 js/Math.PI) 180)) + (.fillText dctx (str i) 0 0))) + (.restore dctx)) + (recur (inc i))))) + + (let [path (js/Path2D.)] + (loop [i minv] + (if (> i maxv) + (.stroke dctx path) + (let [pos (+ (* i zoom) 0)] + (when (= (mod i step) 0) + (if (= type :horizontal) + (do + (.moveTo path pos 17) + (.lineTo path pos 20)) + (do + (.moveTo path 17 pos) + (.lineTo path 20 pos)))) + (recur (inc i)))))))) + (mf/defc horizontal-rule - [{:keys [zoom size]}] + [{:keys [zoom vbox vport] :as props}] (let [canvas (mf/use-ref) - {:keys [x viewport-width width]} size] - + width (- (:width vport) 20)] (mf/use-layout-effect - (mf/deps viewport-width width x zoom) + (mf/deps zoom width (:x vbox)) (fn [] (let [node (mf/ref-val canvas) - dctx (.getContext node "2d") + dctx (.getContext ^js node "2d")] + (obj/set! node "width" width) + (draw-rule! dctx {:zoom zoom + :type :horizontal + :size width + :start (:x vbox)})))) - btm 1 - trx (- (* (- 0 x) zoom) 50) - - min-val (js/Math.round x) - max-val (js/Math.round (+ x (/ viewport-width zoom))) - - tmp0 (js/Math.abs (- max-val min-val)) - tmp1 (js/Math.round (/ tmp0 200)) - btm (max (* btm (* 10 tmp1)) 1)] - - (obj/set! node "width" viewport-width) - - (obj/set! dctx "fillStyle" "#E8E9EA") - (.fillRect dctx 0 0 viewport-width 20) - - (.save dctx) - (.translate dctx trx 0) - - (obj/set! dctx "font" "12px serif") - (obj/set! dctx "fillStyle" "#7B7D85") - (obj/set! dctx "strokeStyle" "#7B7D85") - (obj/set! dctx "textAlign" "center") - - (loop [i min-val] - (when (< i max-val) - (let [pos (+ (* i zoom) 50)] - (when (= (mod i btm) 0) - (.fillText dctx (str i) (- pos 0) 13)) - (recur (+ i 1))))) - - (let [path (js/Path2D.)] - (loop [i min-val] - (if (> i max-val) - (.stroke dctx path) - (let [pos (+ (* i zoom) 50)] - (when (= (mod i btm) 0) - (.moveTo path pos 17) - (.lineTo path pos STEP-PADDING)) - (recur (inc i)))))) - - (.restore dctx)))) - - [:canvas.horizontal-rule {:ref canvas :width (:viewport-width size) :height 20}])) - - -;; --- Vertical Rule (Component) + [:canvas.horizontal-rule + {:ref canvas + :width width + :height 20}])) (mf/defc vertical-rule - {::mf/wrap [mf/memo #(mf/throttle % 60)]} - [{:keys [zoom size]}] + [{:keys [zoom vbox vport] :as props}] (let [canvas (mf/use-ref) - {:keys [y height viewport-height]} size] + height (- (:height vport) 20)] (mf/use-layout-effect - (mf/deps height y zoom) + (mf/deps zoom height (:y vbox)) (fn [] (let [node (mf/ref-val canvas) - dctx (.getContext node "2d") - - btm 1 - try (- (* (- 0 y) zoom) 50) - - min-val (js/Math.round y) - max-val (js/Math.round (+ y (/ viewport-height zoom))) - - tmp0 (js/Math.abs (- max-val min-val)) - tmp1 (js/Math.round (/ tmp0 100)) - btm (max (* btm (* 10 tmp1)) 1)] - - (obj/set! node "height" viewport-height) - - (obj/set! dctx "fillStyle" "#E8E9EA") - (.fillRect dctx 0 0 20 viewport-height) - - (obj/set! dctx "font" "11px serif") - (obj/set! dctx "fillStyle" "#7B7D85") - (obj/set! dctx "strokeStyle" "#7B7D85") - (obj/set! dctx "textAlign" "center") - - (.translate dctx 0 try) - - (loop [i min-val] - (when (< i max-val) - (let [pos (+ (* i zoom) 50)] - (when (= (mod i btm) 0) - (.save dctx) - (.translate dctx 12 pos) - (.rotate dctx (/ (* 270 js/Math.PI) 180)) - (.fillText dctx (str i) 0 0) - (.restore dctx)) - (recur (inc i))))) - - (let [path (js/Path2D.)] - (loop [i min-val] - (if (> i max-val) - (.stroke dctx path) - (let [pos (+ (* i zoom) 50)] - (when (= (mod i btm) 0) - (.moveTo path 17 pos) - (.lineTo path STEP-PADDING pos)) - (recur (inc i))))))))) - - [:canvas.vertical-rule {:ref canvas :width 20 :height height}])) + dctx (.getContext ^js node "2d")] + (obj/set! node "height" height) + (draw-rule! dctx {:zoom zoom + :type :vertical + :size height + :count 100 + :start (:y vbox)})))) + [:canvas.vertical-rule + {:ref canvas + :width 20 + :height height}])) diff --git a/frontend/src/uxbox/main/ui/workspace/selection.cljs b/frontend/src/uxbox/main/ui/workspace/selection.cljs index 073695d65..495ee38bd 100644 --- a/frontend/src/uxbox/main/ui/workspace/selection.cljs +++ b/frontend/src/uxbox/main/ui/workspace/selection.cljs @@ -118,7 +118,7 @@ :width (+ width 2) :height (+ height 2) :style {:stroke "#1FDEA7" - :stroke-width "1" + :stroke-width (/ 1 zoom) :fill "transparent"}}]) (when (not (#{:move :rotate} current-transform)) diff --git a/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs b/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs index 44aa4e0d9..c4471b231 100644 --- a/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs @@ -98,7 +98,8 @@ :transform (str "scale(" inv-zoom ", " inv-zoom ") " "translate(" (* zoom (:x label-pos)) ", " - (* zoom (:y label-pos)) ")") + (* zoom (:y label-pos)) + ")") ;; User may also select the frame with single click in the label :on-click on-double-click} (:name shape)] diff --git a/frontend/src/uxbox/main/ui/workspace/viewport.cljs b/frontend/src/uxbox/main/ui/workspace/viewport.cljs index 56094fc2e..79bbb068b 100644 --- a/frontend/src/uxbox/main/ui/workspace/viewport.cljs +++ b/frontend/src/uxbox/main/ui/workspace/viewport.cljs @@ -130,7 +130,8 @@ {:keys [drawing-tool zoom flags - size + vport + vbox edition tooltip selected]} local @@ -276,7 +277,8 @@ (st/emit! (dw/decrease-zoom pos)) (st/emit! (dw/increase-zoom pos)))) (let [event (.getBrowserEvent event) - delta (.-deltaY ^js event)] + delta (.-deltaY ^js event) + delta (/ delta @refs/selected-zoom)] (if (kbd/shift? event) (st/emit! (dw/update-viewport-position {:x #(+ % delta)})) (st/emit! (dw/update-viewport-position {:y #(+ % delta)}))))))) @@ -299,28 +301,23 @@ on-resize (fn [event] (let [node (mf/ref-val viewport-ref) - parent (.-parentElement ^js node)] - (st/emit! (dw/update-viewport-size - {:width (.-clientWidth ^js parent) - :height (.-clientHeight ^js parent)})))) + prnt (dom/get-parent node)] + (st/emit! (dw/update-viewport-size (dom/get-client-size prnt))))) on-mount (fn [] (let [node (mf/ref-val viewport-ref) - prnt (.-parentElement ^js node) + prnt (dom/get-parent node) key1 (events/listen js/document EventType.KEYDOWN on-key-down) key2 (events/listen js/document EventType.KEYUP on-key-up) key3 (events/listen node EventType.MOUSEMOVE on-mouse-move) ;; bind with passive=false to allow the event to be cancelled ;; https://stackoverflow.com/a/57582286/3219895 - key4 (events/listen js/window EventType.WHEEL on-mouse-wheel - #js {"passive" false}) + key4 (events/listen js/window EventType.WHEEL on-mouse-wheel #js {:passive false}) key5 (events/listen js/window EventType.RESIZE on-resize)] - (st/emit! (dw/initialize-viewport - {:width (.-clientWidth ^js prnt) - :height (.-clientHeight ^js prnt)})) + (st/emit! (dw/initialize-viewport (dom/get-client-size prnt))) (fn [] (events/unlistenByKey key1) @@ -335,22 +332,23 @@ (mf/use-effect on-mount) [:* [:& coordinates {:zoom zoom}] - [:svg.viewport { - :width (:viewport-width size) - :height (:viewport-height size) - :view-box (str/join " " [(:x size) - (:y size) - (:width size) - (:height size)]) - :ref viewport-ref - :class (when drawing-tool "drawing") - :on-context-menu on-context-menu - :on-click on-click - :on-double-click on-double-click - :on-mouse-down on-mouse-down - :on-mouse-up on-mouse-up - :on-drag-over on-drag-over - :on-drop on-drop} + [:svg.viewport + {:preserveAspectRatio "xMidYMid meet" + :width (:width vport 0) + :height (:height vport 0) + :view-box (str/join " " [(:x vbox 0) + (:y vbox 0) + (:width vbox 0 ) + (:height vbox 0)]) + :ref viewport-ref + :class (when drawing-tool "drawing") + :on-context-menu on-context-menu + :on-click on-click + :on-double-click on-double-click + :on-mouse-down on-mouse-down + :on-mouse-up on-mouse-up + :on-drag-over on-drag-over + :on-drop on-drop} [:g [:& frames {:key (:id page)}] diff --git a/frontend/src/uxbox/util/dom.cljs b/frontend/src/uxbox/util/dom.cljs index 00583aea7..1f65132c9 100644 --- a/frontend/src/uxbox/util/dom.cljs +++ b/frontend/src/uxbox/util/dom.cljs @@ -5,8 +5,7 @@ ;; This Source Code Form is "Incompatible With Secondary Licenses", as ;; defined by the Mozilla Public License, v. 2.0. ;; -;; Copyright (c) 2015-2016 Juan de la Cruz -;; Copyright (c) 2015-2020 Andrey Antukh +;; Copyright (c) 2020 UXBOX Labs SL (ns uxbox.util.dom (:require @@ -69,6 +68,10 @@ [event] (.-target event)) +(defn get-parent + [dom] + (.-parentElement ^js dom)) + (defn get-value "Extract the value from dom node." [node] @@ -150,6 +153,11 @@ y (.-clientY event)] (gpt/point x y))) +(defn get-client-size + [node] + {:width (.-clientWidth ^js node) + :height (.-clientHeight ^js node)}) + (defn focus! [node] (.focus node)) diff --git a/frontend/src/uxbox/util/geom/shapes.cljs b/frontend/src/uxbox/util/geom/shapes.cljs index dbc5190e6..a8236876a 100644 --- a/frontend/src/uxbox/util/geom/shapes.cljs +++ b/frontend/src/uxbox/util/geom/shapes.cljs @@ -2,6 +2,9 @@ ;; 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/. ;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; ;; Copyright (c) 2020 UXBOX Labs SL (ns uxbox.util.geom.shapes @@ -80,52 +83,6 @@ dy (if y (- (_chk y) (_chk (:y shape))) 0)] (move shape (gpt/point dx dy)))) -;; --- Rotation - -;; TODO: maybe we can consider apply the rotation -;; directly to the shape coordinates? -;; FIXME: deprecated, should be removed - -(defn rotate - "Apply the rotation to the shape." - [shape rotation] - (assoc shape :rotation rotation)) - -;; --- Corner points - -(defn corner-points [points] - (let [minx (apply min (map :x points)) - miny (apply min (map :y points)) - maxx (apply max (map :x points)) - maxy (apply max (map :y points))] - {:x1 minx :y1 miny :x2 maxx :y2 maxy})) - -;; --- Size - -(declare size-path) - -(defn size - "Calculate the size of the shape." - [shape] - (case (:type shape) - :curve (size-path shape) - :path (size-path shape) - shape)) - -(defn- size-path - [{:keys [segments x1 y1 x2 y2] :as shape}] - (if (and x1 y1 x2 y2) - (assoc shape - :width (- x2 x1) - :height (- y2 y1)) - (let [minx (apply min (map :x segments)) - miny (apply min (map :y segments)) - maxx (apply max (map :x segments)) - maxy (apply max (map :y segments))] - (assoc shape - :width (- maxx minx) - :height (- maxy miny))))) - ;; --- Center (declare center-rect) @@ -174,8 +131,6 @@ [{:keys [width height] :as shape}] (assoc shape :proportion (/ width height))) -;; TODO: implement the rest of shapes - ;; --- Paths (defn update-path-point @@ -231,49 +186,6 @@ (assoc :height value) (assoc :width (* value proportion))))))) -;; --- Resize - -(defn calculate-scale-ratio - "Calculate the scale factor from one shape to an other. - - The shapes should be of rect-like type because width - and height are used for calculate the ratio." - [origin final] - [(/ (:width final) (:width origin)) - (/ (:height final) (:height origin))]) - -(defn- get-vid-coords [vid] - (case vid - :top-left [:x2 :y2] - :top-right [:x1 :y2] - :top [:x1 :y2] - :bottom-left [:x2 :y1] - :bottom-right [:x :y ] - :bottom [:x1 :y1] - :right [:x1 :y1] - :left [:x2 :y1])) - -(defn resize-shape - "Apply a resize transformation to a rect-like shape. The shape - should have the `width` and `height` attrs, because these attrs - are used for the resize transformation. - - Mainly used in drawarea and interactive resize on workspace - with the main objective that on the end of resize have a way - a calculte the resize ratio with `calculate-scale-ratio`." - [vid shape initial target lock?] - - (let [{:keys [x y]} (gpt/subtract target initial) - [cor-x cor-y] (get-vid-coords vid)] - (let [final-x (if (#{:top :bottom} vid) (:x2 shape) x) - final-y (if (#{:right :left} vid) (:y2 shape) y) - width (Math/abs (- final-x (cor-x shape))) - height (Math/abs (- final-y (cor-y shape))) - proportion (:proportion shape 1)] - (assoc shape - :width width - :height (if lock? (/ width proportion) height))))) - ;; --- Setup (Initialize) (declare setup-rect) @@ -454,15 +366,6 @@ ;; --- Outer Rect -(defn rotation-matrix - "Generate a rotation matrix from shape." - [{:keys [x y width height rotation] :as shape}] - (let [cx (+ x (/ width 2)) - cy (+ y (/ height 2))] - (cond-> (gmt/matrix) - (and rotation (pos? rotation)) - (gmt/rotate rotation (gpt/point cx cy))))) - (declare transform-apply-modifiers) (defn selection-rect-shape @@ -730,7 +633,8 @@ (gpt/divide (gpt/point (:width shape-path-temp-rec) (:height shape-path-temp-rec)) (gpt/point (:width shape-path-temp-dim) (:height shape-path-temp-dim))))) -(defn- fix-invalid-rect-values [rect-shape] +(defn fix-invalid-rect-values + [rect-shape] (letfn [(check [num] (if (or (nil? num) (mth/nan? num)) 0 num)) (to-positive [num] (if (< num 1) 1 num))] (-> rect-shape @@ -831,3 +735,33 @@ (gmt/translate shape-center) (gmt/multiply (:transform shape (gmt/matrix))) (gmt/translate (gpt/negate shape-center)))))) + +(defn adjust-to-viewport + ([viewport srect] (adjust-to-viewport viewport srect nil)) + ([viewport srect {:keys [padding] :or {padding 0}}] + (let [gprop (/ (:width viewport) (:height viewport)) + srect (-> srect + (update :x #(- % padding)) + (update :y #(- % padding)) + (update :width #(+ % padding padding)) + (update :height #(+ % padding padding))) + width (:width srect) + height (:height srect) + lprop (/ width height)] + (cond + (> gprop lprop) + (let [width' (* (/ width lprop) gprop) + padding (/ (- width' width) 2)] + (-> srect + (update :x #(- % padding)) + (assoc :width width'))) + + (< gprop lprop) + (let [height' (/ (* height lprop) gprop) + padding (/ (- height' height) 2)] + (-> srect + (update :y #(- % padding)) + (assoc :height height'))) + + :else srect)))) + diff --git a/frontend/src/uxbox/util/math.cljs b/frontend/src/uxbox/util/math.cljs index 0810d4683..57df24608 100644 --- a/frontend/src/uxbox/util/math.cljs +++ b/frontend/src/uxbox/util/math.cljs @@ -2,8 +2,10 @@ ;; 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) 2015-2016 Andrey Antukh -;; Copyright (c) 2015-2016 Juan de la Cruz +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL (ns uxbox.util.math "A collection of math utils."