diff --git a/src/uxbox/data/shapes.cljs b/src/uxbox/data/shapes.cljs index 42c86ca7c..893bdc40e 100644 --- a/src/uxbox/data/shapes.cljs +++ b/src/uxbox/data/shapes.cljs @@ -125,6 +125,13 @@ size (merge (sh/size shape) opts)] (update-in state [:shapes-by-id sid] sh/resize' size))))) +(defn update-vertex-position + [id {:keys [vid delta]}] + (reify + rs/UpdateEvent + (-apply-update [_ state] + (update-in state [:shapes-by-id id] sh/move-vertex vid delta)))) + (defn update-position "Update the start position coordenate of the shape." [sid {:keys [x y] :as opts}] diff --git a/src/uxbox/shapes.cljs b/src/uxbox/shapes.cljs index bdf5cbefc..471262147 100644 --- a/src/uxbox/shapes.cljs +++ b/src/uxbox/shapes.cljs @@ -39,6 +39,10 @@ dispatch-by-type :hierarchy #'+hierarchy+) +(defmulti move-vertex + dispatch-by-type + :hierarchy #'+hierarchy+) + (defmulti resize dispatch-by-type :hierarchy #'+hierarchy+) @@ -112,6 +116,38 @@ :rx (mth/abs (- x2 x1)) :ry (mth/abs (- y2 y1)))) +(defmethod move-vertex ::rect + [shape vid {dx :x dy :y}] + (case vid + 1 (assoc shape + :x1 (+ (:x1 shape) dx) + :y1 (+ (:y1 shape) dy)) + 2 (assoc shape + :x2 (+ (:x2 shape) dx) + :y1 (+ (:y1 shape) dy)) + 3 (assoc shape + :x1 (+ (:x1 shape) dx) + :y2 (+ (:y2 shape) dy)) + 4 (assoc shape + :x2 (+ (:x2 shape) dx) + :y2 (+ (:y2 shape) dy)))) + +(defmethod move-vertex :builtin/circle + [shape vid {dx :x dy :y}] + (case vid + 1 (assoc shape + :rx (- (:rx shape) dx) + :ry (- (:ry shape) dy)) + 2 (assoc shape + :rx (+ (:rx shape) dx) + :ry (- (:ry shape) dy)) + 3 (assoc shape + :rx (- (:rx shape) dx) + :ry (+ (:ry shape) dy)) + 4 (assoc shape + :rx (+ (:rx shape) dx) + :ry (+ (:ry shape) dy)))) + ;; FIXME: lock mode (defmethod resize :builtin/line diff --git a/src/uxbox/ui/shapes/circle.cljs b/src/uxbox/ui/shapes/circle.cljs index 3e5bc94ec..2252a7417 100644 --- a/src/uxbox/ui/shapes/circle.cljs +++ b/src/uxbox/ui/shapes/circle.cljs @@ -8,10 +8,89 @@ [uxbox.shapes :as ush] [uxbox.data.workspace :as dw] [uxbox.ui.core :as uuc] + [uxbox.ui.mixins :as mx] [uxbox.ui.keyboard :as kbd] [uxbox.ui.shapes.core :as uusc] + [uxbox.ui.shapes.icon :as uusi] [uxbox.util.dom :as dom])) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Circle Component +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(declare handlers) + +(defmethod uusc/render-component :default ;; :builtin/icon + [own shape] + (let [{:keys [id x y width height group]} shape + selected (rum/react uusc/selected-shapes-l) + selected? (contains? selected id) + on-mouse-down #(uusi/on-mouse-down % shape selected) + on-mouse-up #(uusi/on-mouse-up % shape)] + (html + [:g.shape {:class (when selected? "selected") + :on-mouse-down on-mouse-down + :on-mouse-up on-mouse-up} + (uusc/render-shape shape #(uusc/shape %)) + (when (and selected? (= (count selected) 1)) + (handlers shape))]))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Circle Handlers +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- handlers-render + [own shape] + (letfn [(on-mouse-down [vid event] + (println "on-mouse-down" vid) + (dom/stop-propagation event) + (uuc/acquire-action! :resize/shape {:vid vid :shape (:id shape)})) + + (on-mouse-up [vid event] + (println "on-mouse-up" vid) + (dom/stop-propagation event) + (uuc/release-action! :resize/shape))] + (let [{:keys [x y width height]} (ush/outer-rect' shape)] + (html + [:g.controls + [:rect {:x x :y y :width width :height height :stroke-dasharray "5,5" + :style {:stroke "#333" :fill "transparent" + :stroke-opacity "1"}}] + [:circle.top-left + (merge uusc/+circle-props+ + {:on-mouse-up #(on-mouse-up 1 %) + :on-mouse-down #(on-mouse-down 1 %) + :cx x + :cy y})] + [:circle.top-right + (merge uusc/+circle-props+ + {:on-mouse-up #(on-mouse-up 2 %) + :on-mouse-down #(on-mouse-down 2 %) + :cx (+ x width) + :cy y})] + [:circle.bottom-left + (merge uusc/+circle-props+ + {:on-mouse-up #(on-mouse-up 3 %) + :on-mouse-down #(on-mouse-down 3 %) + :cx x + :cy (+ y height)})] + [:circle.bottom-right + (merge uusc/+circle-props+ + {:on-mouse-up #(on-mouse-up 4 %) + :on-mouse-down #(on-mouse-down 4 %) + :cx (+ x width) + :cy (+ y height)})]])))) + +(def ^:const handlers + (mx/component + {:render handlers-render + :name "handlers" + :mixins [mx/static]})) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Shape +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (defmethod uusc/render-shape :builtin/circle [{:keys [id] :as shape}] (let [key (str id) diff --git a/src/uxbox/ui/shapes/core.cljs b/src/uxbox/ui/shapes/core.cljs index 3f54c40ba..e84b2c8d7 100644 --- a/src/uxbox/ui/shapes/core.cljs +++ b/src/uxbox/ui/shapes/core.cljs @@ -7,6 +7,18 @@ [uxbox.shapes :as sh] [uxbox.ui.mixins :as mx])) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Common constants +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:const +circle-props+ + {:r 5 + :style {:fillOpacity "0.5" + :strokeWidth "1px" + :vectorEffect "non-scaling-stroke"} + :fill "#333" + :stroke "#333"}) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Implementation Api ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/src/uxbox/ui/shapes/icon.cljs b/src/uxbox/ui/shapes/icon.cljs index 0be425e5c..23723ba3f 100644 --- a/src/uxbox/ui/shapes/icon.cljs +++ b/src/uxbox/ui/shapes/icon.cljs @@ -5,7 +5,7 @@ [lentes.core :as l] [uxbox.rstore :as rs] [uxbox.state :as st] - [uxbox.shapes :as sh] + [uxbox.shapes :as ush] [uxbox.data.workspace :as dw] [uxbox.ui.core :as uuc] [uxbox.ui.mixins :as mx] @@ -13,16 +13,6 @@ [uxbox.ui.shapes.core :as uusc] [uxbox.util.dom :as dom])) -(def ^:private ^:const selection-circle-style - {:fillOpacity "0.5" - :strokeWidth "1px" - :vectorEffect "non-scaling-stroke"}) - -(def ^:private ^:const default-selection-props - {:r 5 :style selection-circle-style - :fill "#333" - :stroke "#333"}) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Icon Component ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -32,7 +22,7 @@ (let [selected? (contains? selected id)] (when-not (:blocked shape) (cond - (and group (:locked (sh/resolve-parent shape))) + (and group (:locked (ush/resolve-parent shape))) nil (and (not selected?) (empty? selected)) @@ -57,7 +47,7 @@ (defn on-mouse-up [event {:keys [id group] :as shape}] (cond - (and group (:locked (sh/resolve-parent shape))) + (and group (:locked (ush/resolve-parent shape))) nil :else @@ -88,20 +78,45 @@ (defn- handlers-render [own shape] - (let [{:keys [x y width height]} (sh/outer-rect' shape)] - (html - [:g.controls - [:rect {:x x :y y :width width :height height :stroke-dasharray "5,5" - :style {:stroke "#333" :fill "transparent" - :stroke-opacity "1"}}] - [:circle.top-left (merge default-selection-props - {:cx x :cy y})] - [:circle.top-right (merge default-selection-props - {:cx (+ x width) :cy y})] - [:circle.bottom-left (merge default-selection-props - {:cx x :cy (+ y height)})] - [:circle.bottom-right (merge default-selection-props - {:cx (+ x width) :cy (+ y height)})]]))) + (letfn [(on-mouse-down [vid event] + (println "on-mouse-down" vid) + (dom/stop-propagation event) + (uuc/acquire-action! :resize/shape {:vid vid :shape (:id shape)})) + + (on-mouse-up [vid event] + (println "on-mouse-up" vid) + (dom/stop-propagation event) + (uuc/release-action! :resize/shape))] + (let [{:keys [x y width height]} (ush/outer-rect' shape)] + (html + [:g.controls + [:rect {:x x :y y :width width :height height :stroke-dasharray "5,5" + :style {:stroke "#333" :fill "transparent" + :stroke-opacity "1"}}] + [:circle.top-left + (merge uusc/+circle-props+ + {:on-mouse-up #(on-mouse-up 1 %) + :on-mouse-down #(on-mouse-down 1 %) + :cx x + :cy y})] + [:circle.top-right + (merge uusc/+circle-props+ + {:on-mouse-up #(on-mouse-up 2 %) + :on-mouse-down #(on-mouse-down 2 %) + :cx (+ x width) + :cy y})] + [:circle.bottom-left + (merge uusc/+circle-props+ + {:on-mouse-up #(on-mouse-up 3 %) + :on-mouse-down #(on-mouse-down 3 %) + :cx x + :cy (+ y height)})] + [:circle.bottom-right + (merge uusc/+circle-props+ + {:on-mouse-up #(on-mouse-up 4 %) + :on-mouse-down #(on-mouse-down 4 %) + :cx (+ x width) + :cy (+ y height)})]])))) (def ^:const handlers (mx/component @@ -116,7 +131,7 @@ (defmethod uusc/render-shape :builtin/icon [{:keys [data id] :as shape} _] (let [key (str id) - rfm (sh/transformation shape) + rfm (ush/transformation shape) attrs (merge {:id key :key key :transform (str rfm)} (uusc/extract-style-attrs shape) (uusc/make-debug-attrs shape))] diff --git a/src/uxbox/ui/workspace/canvas.cljs b/src/uxbox/ui/workspace/canvas.cljs index 1d5606645..77fbdb70a 100644 --- a/src/uxbox/ui/workspace/canvas.cljs +++ b/src/uxbox/ui/workspace/canvas.cljs @@ -17,6 +17,7 @@ [uxbox.ui.mixins :as mx] [uxbox.ui.workspace.base :as uuwb] [uxbox.ui.workspace.canvas.movement] + [uxbox.ui.workspace.canvas.resize] [uxbox.ui.workspace.canvas.draw :refer (draw-area)] [uxbox.ui.workspace.canvas.ruler :refer (ruler)] [uxbox.ui.workspace.canvas.selection :refer (shapes-selection)] diff --git a/src/uxbox/ui/workspace/canvas/resize.cljs b/src/uxbox/ui/workspace/canvas/resize.cljs new file mode 100644 index 000000000..eb359de79 --- /dev/null +++ b/src/uxbox/ui/workspace/canvas/resize.cljs @@ -0,0 +1,41 @@ +(ns uxbox.ui.workspace.canvas.resize + (:require-macros [uxbox.util.syntax :refer [define-once]]) + (:require [sablono.core :as html :refer-macros [html]] + [rum.core :as rum] + [beicon.core :as rx] + [lentes.core :as l] + [uxbox.rstore :as rs] + [uxbox.shapes :as ush] + [uxbox.data.workspace :as udw] + [uxbox.data.shapes :as uds] + [uxbox.ui.core :as uuc] + [uxbox.ui.shapes.core :as uusc] + [uxbox.ui.workspace.base :as uuwb] + [uxbox.ui.mixins :as mx] + [uxbox.util.geom.point :as gpt] + [uxbox.util.dom :as dom])) + +(define-once :resize-subscriptions + (letfn [(init [{:keys [payload]}] + (println payload) + (let [stoper (->> uuc/actions-s + (rx/map :type) + (rx/pr-log "kaka:") + (rx/filter #(= :nothing %)) + (rx/take 1))] + (as-> uuwb/mouse-delta-s $ + (rx/take-until stoper $) + (rx/subscribe + $ #(on-value payload %) nil on-complete)))) + + (on-complete [] + (println "on-complete")) + + (on-value [{:keys [vid shape]} delta] + (let [params {:vid vid :delta delta}] + (rs/emit! (uds/update-vertex-position shape params))))] + + (as-> uuc/actions-s $ + (rx/dedupe $) + (rx/filter #(= (:type %) :resize/shape) $) + (rx/on-value $ init))))