Refactor user interaction locking.

This commit is contained in:
Andrey Antukh 2016-08-10 19:15:13 +03:00
parent 5afc297e93
commit b8e5239ee3
No known key found for this signature in database
GPG key ID: 4DFEBCB8316A8B95
18 changed files with 468 additions and 545 deletions

View file

@ -1,26 +0,0 @@
(ns uxbox.main.ui.core
(:require [beicon.core :as rx]
[cuerdas.core :as str]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Actions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defonce lock (atom ""))
(defonce actions-s (rx/bus))
(defn acquire-action!
([type]
(acquire-action! type nil))
([type payload]
(when (empty? @lock)
(reset! lock type)
(rx/push! actions-s {:type type :payload payload}))))
(defn release-action!
([type]
(when (str/contains? @lock type)
(rx/push! actions-s {:type ""})
(reset! lock "")))
([type & more]
(run! release-action! (cons type more))))

View file

@ -22,12 +22,10 @@
(let [{:keys [id x y width height group]} shape (let [{:keys [id x y width height group]} shape
selected (mx/react common/selected-shapes-ref) selected (mx/react common/selected-shapes-ref)
selected? (contains? selected id) selected? (contains? selected id)
on-mouse-down #(common/on-mouse-down % shape selected) on-mouse-down #(common/on-mouse-down % shape selected)]
on-mouse-up #(common/on-mouse-up % shape)]
(html (html
[:g.shape {:class (when selected? "selected") [:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down :on-mouse-down on-mouse-down}
:on-mouse-up on-mouse-up}
(circle-shape shape identity)]))) (circle-shape shape identity)])))
(def circle-component (def circle-component

View file

@ -10,8 +10,8 @@
[uxbox.util.rstore :as rs] [uxbox.util.rstore :as rs]
[uxbox.main.state :as st] [uxbox.main.state :as st]
[uxbox.main.data.shapes :as uds] [uxbox.main.data.shapes :as uds]
[uxbox.main.ui.core :as ui]
[uxbox.main.ui.keyboard :as kbd] [uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.workspace.rlocks :as rlocks]
[uxbox.main.geom :as geom] [uxbox.main.geom :as geom]
[uxbox.util.dom :as dom])) [uxbox.util.dom :as dom]))
@ -41,7 +41,7 @@
(do (do
(dom/stop-propagation event) (dom/stop-propagation event)
(rs/emit! (uds/select-shape id)) (rs/emit! (uds/select-shape id))
(ui/acquire-action! "ui.shape.move")) (rlocks/acquire! :shape/move))
(and (not selected?) (not (empty? selected))) (and (not selected?) (not (empty? selected)))
(do (do
@ -51,21 +51,9 @@
(do (do
(rs/emit! (uds/deselect-all) (rs/emit! (uds/deselect-all)
(uds/select-shape id)) (uds/select-shape id))
(ui/acquire-action! "ui.shape.move")))) (rlocks/acquire! :shape/move))))
:else :else
(do (do
(dom/stop-propagation event) (dom/stop-propagation event)
(ui/acquire-action! "ui.shape.move")))))) (rlocks/acquire! :shape/move))))))
(defn on-mouse-up
[event {:keys [id group] :as shape}]
(cond
(and group (:locked (geom/resolve-parent shape)))
nil
:else
(do
(dom/stop-propagation event)
(ui/release-action! "ui.shape"))))

View file

@ -17,6 +17,7 @@
[uxbox.main.ui.shapes.circle :as circle] [uxbox.main.ui.shapes.circle :as circle]
[uxbox.main.ui.shapes.text :as text] [uxbox.main.ui.shapes.text :as text]
[uxbox.main.ui.shapes.line :as line] [uxbox.main.ui.shapes.line :as line]
[uxbox.main.ui.shapes.path :as path]
[uxbox.main.geom :as geom])) [uxbox.main.geom :as geom]))
;; --- Helpers ;; --- Helpers
@ -25,12 +26,14 @@
(defn render-component (defn render-component
[{:keys [type] :as shape}] [{:keys [type] :as shape}]
;; (println "render-component" shape)
(case type (case type
:group (group-component shape) :group (group-component shape)
:text (text/text-component shape) :text (text/text-component shape)
:line (line/line-component shape) :line (line/line-component shape)
:icon (icon/icon-component shape) :icon (icon/icon-component shape)
:rect (rect/rect-component shape) :rect (rect/rect-component shape)
:path (path/path-component shape)
:circle (circle/circle-component shape))) :circle (circle/circle-component shape)))
;; --- Group Component ;; --- Group Component
@ -42,13 +45,11 @@
(let [{:keys [id x y width height group]} shape (let [{:keys [id x y width height group]} shape
selected (mx/react common/selected-shapes-ref) selected (mx/react common/selected-shapes-ref)
selected? (contains? selected id) selected? (contains? selected id)
on-mouse-down #(common/on-mouse-down % shape selected) on-mouse-down #(common/on-mouse-down % shape selected)]
on-mouse-up #(common/on-mouse-up % shape)]
(html (html
[:g.shape.group-shape [:g.shape.group-shape
{:class (when selected? "selected") {:class (when selected? "selected")
:on-mouse-down on-mouse-down :on-mouse-down on-mouse-down}
:on-mouse-up on-mouse-up}
(group-shape shape render-component)]))) (group-shape shape render-component)])))
(def group-component (def group-component

View file

@ -17,16 +17,13 @@
(declare icon-shape) (declare icon-shape)
(defn- icon-component-render (defn- icon-component-render
[own shape] [own {:keys [id] :as shape}]
(let [{:keys [id x y width height group]} shape (let [selected (mx/react common/selected-shapes-ref)
selected (mx/react common/selected-shapes-ref)
selected? (contains? selected id) selected? (contains? selected id)
on-mouse-down #(common/on-mouse-down % shape selected) on-mouse-down #(common/on-mouse-down % shape selected)]
on-mouse-up #(common/on-mouse-up % shape)]
(html (html
[:g.shape {:class (when selected? "selected") [:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down :on-mouse-down on-mouse-down}
:on-mouse-up on-mouse-up}
(icon-shape shape identity)]))) (icon-shape shape identity)])))
(def icon-component (def icon-component

View file

@ -21,12 +21,10 @@
(let [{:keys [id x y width height group]} shape (let [{:keys [id x y width height group]} shape
selected (mx/react common/selected-shapes-ref) selected (mx/react common/selected-shapes-ref)
selected? (contains? selected id) selected? (contains? selected id)
on-mouse-down #(common/on-mouse-down % shape selected) on-mouse-down #(common/on-mouse-down % shape selected)]
on-mouse-up #(common/on-mouse-up % shape)]
(html (html
[:g.shape {:class (when selected? "selected") [:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down :on-mouse-down on-mouse-down}
:on-mouse-up on-mouse-up}
(line-shape shape identity)]))) (line-shape shape identity)])))
(def line-component (def line-component

View file

@ -22,12 +22,10 @@
(let [{:keys [id x y width height group]} shape (let [{:keys [id x y width height group]} shape
selected (mx/react common/selected-shapes-ref) selected (mx/react common/selected-shapes-ref)
selected? (contains? selected id) selected? (contains? selected id)
on-mouse-down #(common/on-mouse-down % shape selected) on-mouse-down #(common/on-mouse-down % shape selected)]
on-mouse-up #(common/on-mouse-up % shape)]
(html (html
[:g.shape {:class (when selected? "selected") [:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down :on-mouse-down on-mouse-down}
:on-mouse-up on-mouse-up}
(rect-shape shape identity)]))) (rect-shape shape identity)])))
(def rect-component (def rect-component

View file

@ -5,20 +5,18 @@
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz> ;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.ui.shapes.text (ns uxbox.main.ui.shapes.text
(:require [sablono.core :refer-macros [html]] (:require [cuerdas.core :as str]
[cuerdas.core :as str]
[rum.core :as rum]
[lentes.core :as l] [lentes.core :as l]
[goog.events :as events] [goog.events :as events]
[uxbox.util.rstore :as rs] [uxbox.util.rstore :as rs]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.util.color :as color]
[uxbox.util.dom :as dom]
[uxbox.main.data.shapes :as uds] [uxbox.main.data.shapes :as uds]
[uxbox.main.ui.core :as ui]
[uxbox.util.mixins :as mx]
[uxbox.main.ui.shapes.common :as common] [uxbox.main.ui.shapes.common :as common]
[uxbox.main.ui.shapes.attrs :as attrs] [uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.geom :as geom] [uxbox.main.ui.workspace.rlocks :as rlocks]
[uxbox.util.color :as color] [uxbox.main.geom :as geom])
[uxbox.util.dom :as dom])
(:import goog.events.EventType)) (:import goog.events.EventType))
;; --- Events ;; --- Events
@ -37,7 +35,8 @@
(declare text-shape) (declare text-shape)
(declare text-shape-edit) (declare text-shape-edit)
(defn- text-component-render (mx/defcs text-component
{:mixins [mx/static mx/reactive (mx/local)]}
[own {:keys [id x1 y1 content group] :as shape}] [own {:keys [id x1 y1 content group] :as shape}]
(let [selected (mx/react common/selected-shapes-ref) (let [selected (mx/react common/selected-shapes-ref)
selected? (and (contains? selected id) selected? (and (contains? selected id)
@ -45,28 +44,18 @@
local (:rum/local own)] local (:rum/local own)]
(letfn [(on-mouse-down [event] (letfn [(on-mouse-down [event]
(handle-mouse-down event local shape selected)) (handle-mouse-down event local shape selected))
(on-mouse-up [event]
(common/on-mouse-up event shape))
(on-done [_] (on-done [_]
(swap! local assoc :edition false)) (swap! local assoc :edition false))
(on-double-click [event] (on-double-click [event]
(swap! local assoc :edition true) (swap! local assoc :edition true)
(ui/acquire-action! "ui.text.edit"))] (rlocks/acquire! :ui/text-edit))]
(html [:g.shape {:class (when selected? "selected")
[:g.shape {:class (when selected? "selected") :ref "main"
:ref "main" :on-double-click on-double-click
:on-double-click on-double-click :on-mouse-down on-mouse-down}
:on-mouse-down on-mouse-down (if (:edition @local false)
:on-mouse-up on-mouse-up} (text-shape-edit shape on-done)
(if (:edition @local false) (text-shape shape))])))
(text-shape-edit shape on-done)
(text-shape shape))]))))
(def text-component
(mx/component
{:render text-component-render
:name "text-componet"
:mixins [mx/static mx/reactive (mx/local)]}))
;; --- Text Styles Helpers ;; --- Text Styles Helpers
@ -112,8 +101,10 @@
(.focus dom) (.focus dom)
own)) own))
(defn- text-shape-edit-render (mx/defc text-shape-edit
[own {:keys [id x1 y1 content] :as shape} on-done] {:did-mount text-shape-edit-did-mount
:mixins [mx/static]}
[{:keys [id x1 y1 content] :as shape} on-done]
(let [size (geom/size shape) (let [size (geom/size shape)
style (make-style shape) style (make-style shape)
rfm (geom/transformation-matrix shape) rfm (geom/transformation-matrix shape)
@ -121,33 +112,25 @@
:transform (str rfm)} :transform (str rfm)}
props (merge props size)] props (merge props size)]
(letfn [(on-blur [ev] (letfn [(on-blur [ev]
(ui/release-action! "ui.text.edit") (rlocks/release! :ui/text-edit)
(on-done)) (on-done))
(on-input [ev] (on-input [ev]
(let [content (dom/event->inner-text ev) (let [content (dom/event->inner-text ev)]
sid (:id (first (:rum/args own)))] (rs/emit! (uds/update-text id {:content content}))))]
(rs/emit! (uds/update-text sid {:content content}))))] [:g
(html [:rect (merge props +select-rect-attrs+)]
[:g [:foreignObject props
[:rect (merge props +select-rect-attrs+)] [:p {:ref "container"
[:foreignObject props :on-blur on-blur
[:p {:ref "container" :on-input on-input
:on-blur on-blur :contentEditable true
:on-input on-input :style style}]]])))
:contentEditable true
:style style}]]]))))
(def text-shape-edit
(mx/component
{:render text-shape-edit-render
:did-mount text-shape-edit-did-mount
:name "text-shape-edit"
:mixins [mx/static]}))
;; --- Text Shape ;; --- Text Shape
(defn- text-shape-render (mx/defc text-shape
[own {:keys [id x1 y1 content] :as shape}] {:mixins [mx/static]}
[{:keys [id x1 y1 content] :as shape}]
(let [key (str "shape-" id) (let [key (str "shape-" id)
rfm (geom/transformation-matrix shape) rfm (geom/transformation-matrix shape)
size (geom/size shape) size (geom/size shape)
@ -155,12 +138,5 @@
:transform (str rfm)} :transform (str rfm)}
attrs (merge props size) attrs (merge props size)
style (make-style shape)] style (make-style shape)]
(html [:foreignObject attrs
[:foreignObject attrs [:p {:style style} content]]))
[:p {:style style} content]])))
(def text-shape
(mx/component
{:render text-shape-render
:name "text-shape"
:mixins [mx/static]}))

View file

@ -77,11 +77,19 @@
(defonce scroll-a (defonce scroll-a
(rx/to-atom scroll-s)) (rx/to-atom scroll-s))
;; --- Events
(defonce mouse-events-b (rx/bus))
(defonce mouse-events-s (rx/dedupe mouse-events-b))
(defonce keyboard-events-b (rx/bus))
(defonce keyboard-events-s (rx/dedupe keyboard-events-b))
;; --- Mouse Position Stream ;; --- Mouse Position Stream
(defonce mouse-b (rx/bus)) (defonce mouse-b (rx/bus))
(defonce mouse-s (defonce mouse-s (rx/dedupe mouse-b))
(rx/dedupe mouse-b))
(defonce mouse-canvas-s (defonce mouse-canvas-s
(->> mouse-s (->> mouse-s

View file

@ -6,9 +6,7 @@
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.canvas (ns uxbox.main.ui.workspace.canvas
(:require [sablono.core :as html :refer-macros [html]] (:require [beicon.core :as rx]
[rum.core :as rum]
[beicon.core :as rx]
[lentes.core :as l] [lentes.core :as l]
[goog.events :as events] [goog.events :as events]
[uxbox.main.constants :as c] [uxbox.main.constants :as c]
@ -19,14 +17,13 @@
[uxbox.main.geom.point :as gpt] [uxbox.main.geom.point :as gpt]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.util.data :refer (parse-int)] [uxbox.util.data :refer (parse-int)]
[uxbox.main.ui.core :as uuc]
[uxbox.main.ui.keyboard :as kbd] [uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.shapes :as uus] [uxbox.main.ui.shapes :as uus]
[uxbox.main.ui.shapes.path :as spath]
[uxbox.util.mixins :as mx :include-macros true] [uxbox.util.mixins :as mx :include-macros true]
[uxbox.main.ui.workspace.base :as uuwb] [uxbox.main.ui.workspace.base :as wb]
[uxbox.main.ui.workspace.rlocks :as rlocks]
[uxbox.main.ui.workspace.drawarea :refer (draw-area)] [uxbox.main.ui.workspace.drawarea :refer (draw-area)]
[uxbox.main.ui.workspace.movement :as cmov]
[uxbox.main.ui.workspace.resize :as cres]
[uxbox.main.ui.workspace.ruler :refer (ruler)] [uxbox.main.ui.workspace.ruler :refer (ruler)]
[uxbox.main.ui.workspace.selection :refer (selection-handlers)] [uxbox.main.ui.workspace.selection :refer (selection-handlers)]
[uxbox.main.ui.workspace.selrect :refer (selrect)] [uxbox.main.ui.workspace.selrect :refer (selrect)]
@ -45,67 +42,41 @@
;; --- Canvas ;; --- Canvas
(defn- canvas-render (def ^:private test-path-shape
[own {:keys [width height id] :as page}] {:type :path
(let [workspace (mx/react uuwb/workspace-ref) :id #uuid "042951a0-804a-4cf1-b606-3e97157f55b5"
flags (:flags workspace)] :stroke-type :solid
(html :stroke "#000000"
[:svg.page-canvas {:x c/canvas-start-x :stroke-width 2
:y c/canvas-start-y :fill "transparent"
:ref (str "canvas" id) :close? true
:width width :points [(gpt/point 100 100)
:height height} (gpt/point 300 100)
(background) (gpt/point 200 300)
[:svg.page-layout {} ]})
[:g.main {}
(for [item (reverse (:shapes page))]
(-> (uus/shape item)
(rum/with-key (str item))))
(selection-handlers)
(draw-area)]]])))
(def canvas (mx/defc canvas
(mx/component {:mixins [mx/reactive]}
{:render canvas-render [{:keys [width height id] :as page}]
:name "canvas" (let [workspace (mx/react wb/workspace-ref)
:mixins [mx/static mx/reactive]})) flags (:flags workspace)]
[:svg.page-canvas {:x c/canvas-start-x
:y c/canvas-start-y
:ref (str "canvas" id)
:width width
:height height}
(background)
[:svg.page-layout {}
[:g.main {}
(for [item (reverse (:shapes page))]
(-> (uus/shape item)
(mx/with-key (str item))))
(spath/path-component test-path-shape)
(selection-handlers)
(draw-area)]]]))
;; --- Viewport ;; --- Viewport
(defn viewport-render
[own]
(let [workspace (mx/react uuwb/workspace-ref)
page (mx/react uuwb/page-ref)
flags (:flags workspace)
drawing? (:drawing workspace)
zoom (or (:zoom workspace) 1)]
(letfn [(on-mouse-down [event]
(dom/stop-propagation event)
(if-let [shape (:drawing workspace)]
(uuc/acquire-action! "ui.shape.draw")
(do
(when-not (empty? (:selected workspace))
(rs/emit! (uds/deselect-all)))
(uuc/acquire-action! "ui.selrect"))))
(on-mouse-up [event]
(dom/stop-propagation event)
(uuc/release-action! "ui.shape"
"ui.selrect"))]
(html
[:svg.viewport {:width (* c/viewport-width zoom)
:height (* c/viewport-height zoom)
:ref "viewport"
:class (when drawing? "drawing")
:on-mouse-down on-mouse-down
:on-mouse-up on-mouse-up}
[:g.zoom {:transform (str "scale(" zoom ", " zoom ")")}
(if page
(canvas page))
(if (contains? flags :grid)
(grid))]
(ruler)
(selrect)]))))
(defn- viewport-did-mount (defn- viewport-did-mount
[own] [own]
(letfn [(translate-point-to-viewport [pt] (letfn [(translate-point-to-viewport [pt]
@ -127,12 +98,18 @@
(gpt/subtract brect)))))) (gpt/subtract brect))))))
(on-key-down [event] (on-key-down [event]
(rx/push! wb/keyboard-events-b {:type :keyboard/down
:key (.-keyCode event)
:shift? (kbd/shift? event)
:ctrl? (kbd/ctrl? event)})
(when (kbd/space? event) (when (kbd/space? event)
(uuc/acquire-action! "ui.workspace.scroll"))) (rlocks/acquire! :workspace/scroll)))
(on-key-up [event] (on-key-up [event]
(when (kbd/space? event) (rx/push! wb/keyboard-events-b {:type :keyboard/up
(uuc/release-action! "ui.workspace.scroll"))) :key (.-keyCode event)
:shift? (kbd/shift? event)
:ctrl? (kbd/ctrl? event)}))
(on-mousemove [event] (on-mousemove [event]
(let [wpt (gpt/point (.-clientX event) (let [wpt (gpt/point (.-clientX event)
@ -144,16 +121,12 @@
:window-coords wpt :window-coords wpt
:viewport-coords vppt :viewport-coords vppt
:canvas-coords cvpt}] :canvas-coords cvpt}]
(rx/push! uuwb/mouse-b event)))] (rx/push! wb/mouse-b event)))]
(let [key1 (events/listen js/document EventType.MOUSEMOVE on-mousemove) (let [key1 (events/listen js/document EventType.MOUSEMOVE on-mousemove)
key2 (events/listen js/document EventType.KEYDOWN on-key-down) key2 (events/listen js/document EventType.KEYDOWN on-key-down)
key3 (events/listen js/document EventType.KEYUP on-key-up) key3 (events/listen js/document EventType.KEYUP on-key-up)]
sub1 (cmov/watch-move-actions)
sub2 (cres/watch-resize-actions)]
(assoc own (assoc own
::sub1 sub1
::sub2 sub2
::key1 key1 ::key1 key1
::key2 key2 ::key2 key2
::key3 key3)))) ::key3 key3))))
@ -163,14 +136,39 @@
(events/unlistenByKey (::key1 own)) (events/unlistenByKey (::key1 own))
(events/unlistenByKey (::key2 own)) (events/unlistenByKey (::key2 own))
(events/unlistenByKey (::key3 own)) (events/unlistenByKey (::key3 own))
(.close (::sub1 own)) (dissoc own ::key1 ::key2 ::key3))
(.close (::sub2 own))
(dissoc own ::key1 ::key2 ::key3 ::sub1 ::sub2)) (mx/defc viewport
{:did-mount viewport-did-mount
:will-unmount viewport-will-unmount
:mixins [mx/reactive]}
[]
(let [workspace (mx/react wb/workspace-ref)
page (mx/react wb/page-ref)
flags (:flags workspace)
drawing? (:drawing workspace)
zoom (or (:zoom workspace) 1)]
(letfn [(on-mouse-down [event]
(dom/stop-propagation event)
(rx/push! wb/mouse-events-b :mouse/down)
(if (:drawing workspace)
(rlocks/acquire! :ui/draw)
(rlocks/acquire! :ui/selrect)))
(on-mouse-up [event]
(rx/push! wb/mouse-events-b :mouse/up)
(dom/stop-propagation event))]
[:svg.viewport {:width (* c/viewport-width zoom)
:height (* c/viewport-height zoom)
:ref "viewport"
:class (when drawing? "drawing")
:on-mouse-down on-mouse-down
:on-mouse-up on-mouse-up}
[:g.zoom {:transform (str "scale(" zoom ", " zoom ")")}
(if page
(canvas page))
(if (contains? flags :grid)
(grid))]
(ruler)
(selrect)])))
(def viewport
(mx/component
{:render viewport-render
:name "viewport"
:did-mount viewport-did-mount
:will-unmount viewport-will-unmount
:mixins [mx/reactive]}))

View file

@ -6,18 +6,16 @@
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.drawarea (ns uxbox.main.ui.workspace.drawarea
(:require [sablono.core :as html :refer-macros [html]] "Draw interaction and component."
[beicon.core :as rx] (:require [beicon.core :as rx]
[lentes.core :as l]
[uxbox.util.rstore :as rs] [uxbox.util.rstore :as rs]
[uxbox.util.mixins :as mx :include-macros true] [uxbox.util.mixins :as mx :include-macros true]
[uxbox.main.constants :as c] [uxbox.main.constants :as c]
[uxbox.main.state :as st]
[uxbox.main.data.workspace :as udw] [uxbox.main.data.workspace :as udw]
[uxbox.main.data.shapes :as uds] [uxbox.main.data.shapes :as uds]
[uxbox.main.ui.core :as uuc]
[uxbox.main.ui.shapes :as shapes] [uxbox.main.ui.shapes :as shapes]
[uxbox.main.ui.workspace.base :as wb] [uxbox.main.ui.workspace.base :as wb]
[uxbox.main.ui.workspace.rlocks :as rlocks]
[uxbox.main.geom :as geom] [uxbox.main.geom :as geom]
[uxbox.main.geom.point :as gpt] [uxbox.main.geom.point :as gpt]
[uxbox.util.dom :as dom])) [uxbox.util.dom :as dom]))
@ -27,6 +25,10 @@
(defonce drawing-shape (atom nil)) (defonce drawing-shape (atom nil))
(defonce drawing-position (atom nil)) (defonce drawing-position (atom nil))
(def ^:private canvas-coords
(gpt/point c/canvas-start-x
c/canvas-start-y))
;; --- Draw Area (Component) ;; --- Draw Area (Component)
(declare watch-draw-actions) (declare watch-draw-actions)
@ -52,48 +54,41 @@
(geom/resize position) (geom/resize position)
(shapes/render-component))))) (shapes/render-component)))))
;; --- Drawing Logic ;; --- Drawing Initialization
(declare initialize) (declare on-init)
(declare on-init-draw-icon)
(declare on-init-draw-generic)
(declare on-draw-start)
(declare on-draw)
(declare on-draw-complete)
(defn- watch-draw-actions (defn- watch-draw-actions
[] []
(let [stream (->> uuc/actions-s (let [stream (->> (rx/map first rlocks/stream)
(rx/map :type) (rx/filter #(= % :ui/draw)))]
(rx/dedupe) (rx/subscribe stream on-init)))
(rx/filter #(= "ui.shape.draw" %))
(rx/map #(:drawing @wb/workspace-ref))
(rx/filter identity))]
(rx/subscribe stream initialize)))
(declare initialize-icon-drawing) (defn- on-init
(declare initialize-shape-drawing) "Function execution when draw shape operation is requested.
This is a entry point for the draw interaction."
[]
(when-let [shape (:drawing @wb/workspace-ref)]
(case (:type shape)
:icon (on-init-draw-icon shape)
(on-init-draw-generic shape))))
(defn- initialize (defn- on-init-draw-icon
[shape]
(if (= (:type shape) :icon)
(initialize-icon-drawing shape)
(initialize-shape-drawing shape)))
(defn- initialize-icon-drawing
"A drawing handler for icons."
[shape] [shape]
(let [{:keys [x y]} (gpt/divide @wb/mouse-canvas-a @wb/zoom-ref) (let [{:keys [x y]} (gpt/divide @wb/mouse-canvas-a @wb/zoom-ref)
props {:x1 x :y1 y :x2 (+ x 100) :y2 (+ y 100)} props {:x1 x :y1 y :x2 (+ x 100) :y2 (+ y 100)}
shape (geom/setup shape props)] shape (geom/setup shape props)]
(rs/emit! (uds/add-shape shape) (rs/emit! (uds/add-shape shape)
(udw/select-for-drawing nil) (udw/select-for-drawing nil)
(uds/select-first-shape)))) (uds/select-first-shape))
(rlocks/release! :ui/draw)))
(def ^:private canvas-coords (defn- on-init-draw-generic
(gpt/point c/canvas-start-x
c/canvas-start-y))
(declare on-draw)
(declare on-draw-complete)
(declare on-first-draw)
(defn- initialize-shape-drawing
[shape] [shape]
(let [mouse (->> (rx/sample 10 wb/mouse-viewport-s) (let [mouse (->> (rx/sample 10 wb/mouse-viewport-s)
(rx/mapcat (fn [point] (rx/mapcat (fn [point]
@ -101,20 +96,21 @@
(uds/align-point point) (uds/align-point point)
(rx/of point)))) (rx/of point))))
(rx/map #(gpt/subtract % canvas-coords))) (rx/map #(gpt/subtract % canvas-coords)))
stoper (->> uuc/actions-s
(rx/map :type) stoper (->> wb/mouse-events-s
(rx/filter #(empty? %)) (rx/filter #(= % :mouse/up))
(rx/pr-log "mouse-events-s")
(rx/take 1)) (rx/take 1))
firstpos (rx/take 1 mouse) firstpos (rx/take 1 mouse)
stream (->> mouse stream (->> (rx/take-until stoper mouse)
(rx/take-until stoper)
(rx/skip-while #(nil? @drawing-shape)) (rx/skip-while #(nil? @drawing-shape))
(rx/with-latest-from vector wb/mouse-ctrl-s))] (rx/with-latest-from vector wb/mouse-ctrl-s))]
(rx/subscribe firstpos #(on-first-draw shape %)) (rx/subscribe firstpos #(on-draw-start shape %))
(rx/subscribe stream on-draw nil on-draw-complete))) (rx/subscribe stream on-draw nil on-draw-complete)))
(defn- on-first-draw (defn- on-draw-start
[shape {:keys [x y] :as pt}] [shape {:keys [x y] :as pt}]
(let [shape (geom/setup shape {:x1 x :y1 y :x2 x :y2 y})] (let [shape (geom/setup shape {:x1 x :y1 y :x2 x :y2 y})]
(reset! drawing-shape shape))) (reset! drawing-shape shape)))
@ -133,4 +129,5 @@
(udw/select-for-drawing nil) (udw/select-for-drawing nil)
(uds/select-first-shape)) (uds/select-first-shape))
(reset! drawing-position nil) (reset! drawing-position nil)
(reset! drawing-shape nil))) (reset! drawing-shape nil)
(rlocks/release! :ui/draw)))

View file

@ -1,43 +0,0 @@
;; 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) 2015-2016 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.movement
"Shape movement in workspace logic."
(:require [beicon.core :as rx]
[lentes.core :as l]
[uxbox.main.constants :as c]
[uxbox.util.rstore :as rs]
[uxbox.main.state :as st]
[uxbox.main.ui.core :as uuc]
[uxbox.main.ui.workspace.base :as wb]
[uxbox.main.data.shapes :as uds]
[uxbox.main.geom :as geom]
[uxbox.main.geom.point :as gpt]))
;; --- Public Api
(declare watch-movement)
(defn watch-move-actions
[]
(let [initialize #(run! watch-movement @wb/selected-shapes-ref)
stream (rx/filter #(= "ui.shape.move" (:type %)) uuc/actions-s)]
(rx/subscribe stream initialize)))
;; --- Implementation
(defn- watch-movement
[shape]
(let [stoper (->> uuc/actions-s
(rx/map :type)
(rx/filter empty?)
(rx/take 1))
stream (->> wb/mouse-delta-s
(rx/take-until stoper))]
(when @wb/alignment-ref
(rs/emit! (uds/initial-align-shape shape)))
(rx/subscribe stream #(rs/emit! (uds/move-shape shape %)))))

View file

@ -1,48 +0,0 @@
;; 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) 2015-2016 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.resize
(:require [beicon.core :as rx]
[uxbox.util.rstore :as rs]
[uxbox.main.data.shapes :as uds]
[uxbox.main.ui.core :as uuc]
[uxbox.main.ui.workspace.base :as wb]
[uxbox.main.geom.point :as gpt]))
(declare initialize)
;; --- Public Api
(defn watch-resize-actions
[]
(as-> uuc/actions-s $
(rx/dedupe $)
(rx/filter #(= (:type %) "ui.shape.resize") $)
(rx/on-value $ initialize)))
;; --- Implementation
(declare handle-resize)
(defn- initialize
[event]
(let [{:keys [vid shape] :as payload} (:payload event)
stoper (->> uuc/actions-s
(rx/map :type)
(rx/filter #(empty? %))
(rx/take 1))
stream (->> wb/mouse-delta-s
(rx/take-until stoper)
(rx/with-latest-from vector wb/mouse-ctrl-s))]
(when @wb/alignment-ref
(rs/emit! (uds/initial-vertext-align shape vid)))
(rx/subscribe stream #(handle-resize shape vid %))))
(defn- handle-resize
[shape vid [delta ctrl?]]
(let [params {:vid vid :delta (assoc delta :lock ctrl?)}]
(rs/emit! (uds/update-vertex-position shape params))))

View file

@ -0,0 +1,35 @@
;; 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) 2016 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.ui.workspace.rlocks
"Reactive locks abstraction.
Mainly used for lock the interface to do one concrete user action
such can be draw new shape, scroll, move shape, etc, and avoid
that other posible actions interfere in the locked one."
(:require [beicon.core :as rx]))
(defonce lock (atom ::none))
(defonce stream (rx/bus))
(defn acquire!
([type]
(when (= @lock ::none)
(println "acquire!" type)
(reset! lock type)
(rx/push! stream [type nil])))
([type payload]
(when (= @lock ::none)
(reset! lock type)
(rx/push! stream [type payload]))))
(defn release!
[type]
(when (= @lock type)
(println "release!" type)
(reset! lock ::none)
(rx/push! stream [::none nil])))

View file

@ -6,32 +6,30 @@
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.scroll (ns uxbox.main.ui.workspace.scroll
"Workspace scroll events handling."
(:require [beicon.core :as rx] (:require [beicon.core :as rx]
[lentes.core :as l]
[uxbox.main.constants :as c]
[uxbox.util.rstore :as rs]
[uxbox.main.state :as ust]
[uxbox.main.data.shapes :as uds]
[uxbox.main.ui.core :as uuc]
[uxbox.util.mixins :as mx] [uxbox.util.mixins :as mx]
[uxbox.main.ui.workspace.base :as uuwb] [uxbox.main.ui.workspace.base :as wb]
[uxbox.main.ui.workspace.rlocks :as rlocks]
[uxbox.main.geom.point :as gpt])) [uxbox.main.geom.point :as gpt]))
(defn watch-scroll-interactions (defn watch-scroll-interactions
[own] [own]
(letfn [(handle-scroll-interaction [] (letfn [(is-space-up? [{:keys [key type]}]
(let [stoper (->> uuc/actions-s (and (= 32 key) (= :keyboard/up type)))
(rx/map :type)
(rx/filter #(empty? %)) (on-start []
(let [stoper (->> wb/keyboard-events-s
(rx/filter is-space-up?)
(rx/take 1)) (rx/take 1))
local (:rum/local own) local (:rum/local own)
initial @uuwb/mouse-viewport-a] initial @wb/mouse-viewport-a
stream (rx/take-until stoper wb/mouse-viewport-s)]
(swap! local assoc :scrolling true) (swap! local assoc :scrolling true)
(as-> uuwb/mouse-viewport-s $ (rx/subscribe stream #(on-scroll % initial) nil on-scroll-end)))
(rx/take-until stoper $)
(rx/subscribe $ #(on-scroll % initial) nil on-scroll-end))))
(on-scroll-end [] (on-scroll-end []
(rlocks/release! :workspace/scroll)
(let [local (:rum/local own)] (let [local (:rum/local own)]
(swap! local assoc :scrolling false))) (swap! local assoc :scrolling false)))
@ -42,8 +40,7 @@
cy (.-scrollTop el)] cy (.-scrollTop el)]
(set! (.-scrollLeft el) (- cx x)) (set! (.-scrollLeft el) (- cx x))
(set! (.-scrollTop el) (- cy y))))] (set! (.-scrollTop el) (- cy y))))]
(as-> uuc/actions-s $
(rx/map :type $) (let [stream (->> (rx/map first rlocks/stream)
(rx/dedupe $) (rx/filter #(= % :workspace/scroll)))]
(rx/filter #(= "ui.workspace.scroll" %) $) (rx/subscribe stream on-start))))
(rx/on-value $ handle-scroll-interaction))))

View file

@ -7,18 +7,21 @@
(ns uxbox.main.ui.workspace.selection (ns uxbox.main.ui.workspace.selection
"Multiple selection handlers component." "Multiple selection handlers component."
(:require [sablono.core :as html :refer-macros [html]] (:require [lentes.core :as l]
[rum.core :as rum] [beicon.core :as rx]
[lentes.core :as l]
[uxbox.main.state :as st] [uxbox.main.state :as st]
[uxbox.util.mixins :as mx] [uxbox.util.mixins :as mx :include-macros true]
[uxbox.main.ui.core :as uuc] [uxbox.util.rstore :as rs]
[uxbox.main.data.shapes :as uds]
[uxbox.main.ui.workspace.base :as wb]
[uxbox.main.ui.workspace.rlocks :as rlocks]
[uxbox.main.geom :as geom] [uxbox.main.geom :as geom]
[uxbox.main.geom.point :as gpt]
[uxbox.util.dom :as dom])) [uxbox.util.dom :as dom]))
;; --- Constants ;; --- Refs & Constants
(def +circle-props+ (def ^:private +circle-props+
{:r 6 {:r 6
:style {:fillOpacity "1" :style {:fillOpacity "1"
:strokeWidth "1px" :strokeWidth "1px"
@ -26,100 +29,150 @@
:fill "#31e6e0" :fill "#31e6e0"
:stroke "#28c4d4"}) :stroke "#28c4d4"})
;; --- Lenses (defn- focus-selected-shapes
[state]
(let [selected (get-in state [:workspace :selected])]
(mapv #(get-in state [:shapes-by-id %]) selected)))
(def selected-shapes-ref (def ^:private selected-shapes-ref
(letfn [(getter [state] (-> (l/lens focus-selected-shapes)
(let [selected (get-in state [:workspace :selected])] (l/derive st/state)))
(mapv #(get-in state [:shapes-by-id %]) selected)))]
(-> (l/lens getter) ;; --- Resize
(l/derive st/state))))
(declare on-resize-start)
(defn watch-resize-actions
[]
(let [stream (->> rlocks/stream
(rx/filter #(= (first %) :shape/resize))
(rx/map second))]
(rx/subscribe stream on-resize-start)))
(defn- on-resize
[shape vid [delta ctrl?]]
(let [params {:vid vid :delta (assoc delta :lock ctrl?)}]
(rs/emit! (uds/update-vertex-position shape params))))
(defn- on-resize-stop
[]
(rlocks/release! :shape/resize))
(defn- on-resize-start
[[vid shape]]
(let [stoper (->> wb/mouse-events-s
(rx/filter #(= % :mouse/up))
(rx/take 1))
stream (->> wb/mouse-delta-s
(rx/take-until stoper)
(rx/with-latest-from vector wb/mouse-ctrl-s))]
(when @wb/alignment-ref
(rs/emit! (uds/initial-vertext-align shape vid)))
(rx/subscribe stream (partial on-resize shape vid) nil on-resize-stop)))
;; --- Movement
(declare on-move-start)
(defn watch-move-actions
[]
(-> (rx/filter #(= (first %) :shape/move) rlocks/stream)
(rx/subscribe #(run! on-move-start @wb/selected-shapes-ref))))
(defn- on-move-start
[shape]
(let [stoper (->> wb/mouse-events-s
(rx/filter #(= % :mouse/up))
(rx/take 1))
stream (rx/take-until stoper wb/mouse-delta-s)
on-move #(rs/emit! (uds/move-shape shape %))
on-stop #(rlocks/release! :shape/move)]
(when @wb/alignment-ref
(rs/emit! (uds/initial-align-shape shape)))
(rx/subscribe stream on-move nil on-stop)))
;; --- Selection Handlers (Component) ;; --- Selection Handlers (Component)
(defn- multiple-selection-handlers-render (mx/defc multiple-selection-handlers
[shapes] [shapes]
(let [{:keys [width height x y]} (geom/outer-rect-coll shapes)] (let [{:keys [width height x y]} (geom/outer-rect-coll shapes)]
(html [:g.controls
[:g.controls [:rect.main {:x x :y y
[:rect.main {:x x :y y :width width :height height :stroke-dasharray "5,5" :width width
:style {:stroke "#333" :fill "transparent" :stroke-opacity "1"}}]]))) :height height
:stroke-dasharray "5,5"
:style {:stroke "#333" :fill "transparent"
:stroke-opacity "1"}}]]))
(defn- single-selection-handlers-render (mx/defc single-selection-handlers
[shape] [{:keys [id] :as shape}]
(letfn [ (letfn [(on-mouse-down [vid event]
(on-mouse-down [vid event]
(dom/stop-propagation event) (dom/stop-propagation event)
(uuc/acquire-action! "ui.shape.resize" (rlocks/acquire! :shape/resize [vid id]))]
{:vid vid :shape (:id shape)}))
(on-mouse-up [vid event]
(dom/stop-propagation event)
(uuc/release-action! "ui.shape.resize"))]
(let [{:keys [x y width height]} (geom/outer-rect shape)] (let [{:keys [x y width height]} (geom/outer-rect shape)]
(html [:g.controls
[:g.controls [:rect.main {:x x :y y :width width :height height :stroke-dasharray "5,5"
[:rect.main {:x x :y y :width width :height height :stroke-dasharray "5,5" :style {:stroke "#333" :fill "transparent" :stroke-opacity "1"}}]
:style {:stroke "#333" :fill "transparent" :stroke-opacity "1"}}] [:circle.top
[:circle.top (merge +circle-props+
(merge +circle-props+ {:on-mouse-down #(on-mouse-down :top %)
{:on-mouse-up #(on-mouse-up :top %) :cx (+ x (/ width 2))
:on-mouse-down #(on-mouse-down :top %) :cy (- y 2)})]
:cx (+ x (/ width 2)) [:circle.right
:cy (- y 2)})] (merge +circle-props+
[:circle.right {:on-mouse-down #(on-mouse-down :right %)
(merge +circle-props+ :cy (+ y (/ height 2))
{:on-mouse-up #(on-mouse-up :right %) :cx (+ x width 1)})]
:on-mouse-down #(on-mouse-down :right %) [:circle.bottom
:cy (+ y (/ height 2)) (merge +circle-props+
:cx (+ x width 1)})] {:on-mouse-down #(on-mouse-down :bottom %)
[:circle.bottom :cx (+ x (/ width 2))
(merge +circle-props+ :cy (+ y height 2)})]
{:on-mouse-up #(on-mouse-up :bottom %) [:circle.left
:on-mouse-down #(on-mouse-down :bottom %) (merge +circle-props+
:cx (+ x (/ width 2)) {:on-mouse-down #(on-mouse-down :left %)
:cy (+ y height 2)})] :cy (+ y (/ height 2))
[:circle.left :cx (- x 3)})]
(merge +circle-props+ [:circle.top-left
{:on-mouse-up #(on-mouse-up :left %) (merge +circle-props+
:on-mouse-down #(on-mouse-down :left %) {:on-mouse-down #(on-mouse-down :top-left %)
:cy (+ y (/ height 2)) :cx x
:cx (- x 3)})] :cy y})]
[:circle.top-left [:circle.top-right
(merge +circle-props+ (merge +circle-props+
{:on-mouse-up #(on-mouse-up :top-left %) {:on-mouse-down #(on-mouse-down :top-right %)
:on-mouse-down #(on-mouse-down :top-left %) :cx (+ x width)
:cx x :cy y})]
:cy y})] [:circle.bottom-left
[:circle.top-right (merge +circle-props+
(merge +circle-props+ {:on-mouse-down #(on-mouse-down :bottom-left %)
{:on-mouse-up #(on-mouse-up :top-right %) :cx x
:on-mouse-down #(on-mouse-down :top-right %) :cy (+ y height)})]
:cx (+ x width) [:circle.bottom-right
:cy y})] (merge +circle-props+
[:circle.bottom-left {:on-mouse-down #(on-mouse-down :bottom-right %)
(merge +circle-props+ :cx (+ x width)
{:on-mouse-up #(on-mouse-up :bottom-left %) :cy (+ y height)})]])))
:on-mouse-down #(on-mouse-down :bottom-left %)
:cx x
:cy (+ y height)})]
[:circle.bottom-right
(merge +circle-props+
{:on-mouse-up #(on-mouse-up :bottom-right %)
:on-mouse-down #(on-mouse-down :bottom-right %)
:cx (+ x width)
:cy (+ y height)})]]))))
(defn selection-handlers-render (defn- selection-handlers-will-mount
[own] [own]
(assoc own
::sub1 (watch-resize-actions)
::sub2 (watch-move-actions)))
(defn- selection-handlers-will-unmount
[own]
(.close (::sub1 own))
(.close (::sub2 own))
(dissoc own ::sub1 ::sub2))
(mx/defc selection-handlers
{:mixins [mx/reactive mx/static]
:will-mount selection-handlers-will-mount
:will-unmount selection-handlers-will-unmount}
[]
(let [shapes (mx/react selected-shapes-ref) (let [shapes (mx/react selected-shapes-ref)
shapes-num (count shapes)] shapes-num (count shapes)]
(cond (cond
(> shapes-num 1) (multiple-selection-handlers-render shapes) (> shapes-num 1) (multiple-selection-handlers shapes)
(= shapes-num 1) (single-selection-handlers-render (first shapes))))) (= shapes-num 1) (single-selection-handlers (first shapes)))))
(def selection-handlers
(mx/component
{:render selection-handlers-render
:name "selection-handlers"
:mixins [mx/reactive mx/static]}))

View file

@ -6,17 +6,15 @@
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.selrect (ns uxbox.main.ui.workspace.selrect
"Components for indicate the user selection and selected shapes group." "Mouse selection interaction and component."
(:require [sablono.core :as html :refer-macros [html]] (:require [beicon.core :as rx]
[rum.core :as rum]
[beicon.core :as rx]
[uxbox.main.constants :as c]
[uxbox.util.rstore :as rs] [uxbox.util.rstore :as rs]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.main.constants :as c]
[uxbox.main.data.workspace :as dw] [uxbox.main.data.workspace :as dw]
[uxbox.main.data.shapes :as uds] [uxbox.main.data.shapes :as uds]
[uxbox.main.ui.core :as uuc] [uxbox.main.ui.workspace.base :as wb]
[uxbox.util.mixins :as mx] [uxbox.main.ui.workspace.rlocks :as rlocks]))
[uxbox.main.ui.workspace.base :as wb]))
(defonce position (atom nil)) (defonce position (atom nil))
@ -25,35 +23,29 @@
(declare selrect->rect) (declare selrect->rect)
(declare watch-selrect-actions) (declare watch-selrect-actions)
(defn- selrect-render (defn- will-mount
[own]
(when-let [data (mx/react position)]
(let [{:keys [x y width height]} (selrect->rect data)]
(html
[:rect.selection-rect
{:x x
:y y
:width width
:height height}]))))
(defn- selrect-will-mount
[own] [own]
(assoc own ::sub (watch-selrect-actions))) (assoc own ::sub (watch-selrect-actions)))
(defn- selrect-will-unmount (defn- will-unmount
[own] [own]
(.close (::sub own)) (.close (::sub own))
(dissoc own ::sub)) (dissoc own ::sub))
(def selrect (mx/defc selrect
(mx/component {:will-mount will-mount
{:render selrect-render :will-unmount will-unmount
:name "selrect" :mixins [mx/static mx/reactive]}
:will-mount selrect-will-mount []
:will-unmount selrect-will-unmount (when-let [data (mx/react position)]
:mixins [mx/static mx/reactive]})) (let [{:keys [x y width height]} (selrect->rect data)]
[:rect.selection-rect
{:x x
:y y
:width width
:height height}])))
;; --- Implementation ;; --- Interaction
(defn- selrect->rect (defn- selrect->rect
[data] [data]
@ -82,31 +74,37 @@
:width (/ (:width rect) zoom) :width (/ (:width rect) zoom)
:height (/ (:height rect) zoom)))) :height (/ (:height rect) zoom))))
(declare on-start)
(defn- watch-selrect-actions (defn- watch-selrect-actions
[] []
(letfn [(on-value [pos] (let [stream (->> (rx/map first rlocks/stream)
(swap! position assoc :current pos)) (rx/filter #(= % :ui/selrect)))]
(rx/subscribe stream on-start)))
(on-complete [] (defn- on-move
(rs/emit! (-> (selrect->rect @position) "Function executed on each mouse movement while selrect
(translate-to-canvas) interaction is active."
(uds/select-shapes))) [pos]
(reset! position nil)) (swap! position assoc :current pos))
(init [] (defn- on-complete
(let [stoper (->> uuc/actions-s "Function executed when the selection rect
(rx/map :type) interaction is terminated."
(rx/filter #(empty? %)) []
(rx/take 1)) (rs/emit! (-> (selrect->rect @position)
pos @wb/mouse-viewport-a] (translate-to-canvas)
(reset! position {:start pos :current pos}) (uds/select-shapes)))
(rlocks/release! :ui/selrect)
(reset! position nil))
(as-> wb/mouse-viewport-s $ (defn- on-start
(rx/take-until stoper $) "Function execution when selrect action is started."
(rx/subscribe $ on-value nil on-complete))))] []
(let [stoper (->> wb/mouse-events-s
(as-> uuc/actions-s $ (rx/filter #(= % :mouse/up))
(rx/map :type $) (rx/take 1))
(rx/dedupe $) stream (rx/take-until stoper wb/mouse-viewport-s)
(rx/filter #(= "ui.selrect" %) $) pos @wb/mouse-viewport-a]
(rx/on-value $ init)))) (reset! position {:start pos :current pos})
(rx/subscribe stream on-move nil on-complete)))

View file

@ -7,35 +7,30 @@
(ns uxbox.main.ui.workspace.sidebar.drawtools (ns uxbox.main.ui.workspace.sidebar.drawtools
(:require [sablono.core :as html :refer-macros [html]] (:require [sablono.core :as html :refer-macros [html]]
[rum.core :as rum]
[lentes.core :as l] [lentes.core :as l]
[uxbox.util.i18n :refer (tr)] [uxbox.util.i18n :refer (tr)]
[uxbox.util.router :as r] [uxbox.util.router :as r]
[uxbox.util.rstore :as rs] [uxbox.util.rstore :as rs]
[uxbox.util.data :refer (read-string)]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.util.dom :as dom]
[uxbox.main.state :as st] [uxbox.main.state :as st]
[uxbox.main.library :as library] [uxbox.main.library :as library]
[uxbox.util.data :refer (read-string)]
[uxbox.main.data.workspace :as dw] [uxbox.main.data.workspace :as dw]
[uxbox.main.ui.workspace.base :as wb] [uxbox.main.ui.workspace.base :as wb]
[uxbox.main.ui.icons :as i] [uxbox.main.ui.icons :as i]))
[uxbox.util.mixins :as mx]
[uxbox.util.dom :as dom]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; --- Refs
;; Lenses
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:private drawing-shape (def ^:private drawing-shape
"A focused vision of the drawing property "A focused vision of the drawing property
of the workspace status. This avoids of the workspace status. This avoids
rerender the whole toolbox on each workspace rerender the whole toolbox on each workspace
change." change."
(as-> (l/in [:workspace :drawing]) $ (-> (l/in [:workspace :drawing])
(l/derive $ st/state))) (l/derive st/state)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; --- Constants
;; Draw Tools
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def +draw-tool-rect+ (def +draw-tool-rect+
{:type :rect {:type :rect
@ -52,6 +47,12 @@
:stroke-type :solid :stroke-type :solid
:stroke "#000000"}) :stroke "#000000"})
(def +draw-tool-path+
{:type :path
:name "Path"
:stroke-type :solid
:stroke "#000000"})
(def +draw-tool-text+ (def +draw-tool-text+
{:type :text {:type :text
:name "Text" :name "Text"
@ -77,41 +78,38 @@
{:icon i/text {:icon i/text
:help (tr "ds.help.text") :help (tr "ds.help.text")
:shape +draw-tool-text+ :shape +draw-tool-text+
:priority 4}}) :priority 4}
:path
{:icon i/curve
:help (tr "ds.help.path")
:shape +draw-tool-path+
:priority 5}})
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; --- Draw Toolbox (Component)
;; Draw Tool Box
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- select-for-draw (defn- select-for-draw
[shape] [shape]
(rs/emit! (dw/select-for-drawing shape))) (rs/emit! (dw/select-for-drawing shape)))
(defn draw-tools-render (mx/defc draw-toolbox
[open-toolboxes] {:mixins [mx/static mx/reactive]}
[own]
(let [workspace (mx/react wb/workspace-ref) (let [workspace (mx/react wb/workspace-ref)
drawing (mx/react drawing-shape) drawing (mx/react drawing-shape)
close #(rs/emit! (dw/toggle-flag :drawtools)) close #(rs/emit! (dw/toggle-flag :drawtools))
tools (->> (into [] +draw-tools+) tools (->> (into [] +draw-tools+)
(sort-by (comp :priority second)))] (sort-by (comp :priority second)))]
(html [:div#form-tools.tool-window.drawing-tools
[:div#form-tools.tool-window.drawing-tools [:div.tool-window-bar
[:div.tool-window-bar [:div.tool-window-icon i/window]
[:div.tool-window-icon i/window] [:span (tr "ds.draw-tools")]
[:span (tr "ds.draw-tools")] [:div.tool-window-close {:on-click close} i/close]]
[:div.tool-window-close {:on-click close} i/close]] [:div.tool-window-content
[:div.tool-window-content (for [[key props] tools
(for [[key props] tools :let [selected? (= drawing (:shape props))]]
:let [selected? (= drawing (:shape props))]] [:div.tool-btn.tooltip.tooltip-hover
[:div.tool-btn.tooltip.tooltip-hover {:alt (:help props)
{:alt (:help props) :class (when selected? "selected")
:class (when selected? "selected") :key (name key)
:key (name key) :on-click (partial select-for-draw (:shape props))}
:on-click (partial select-for-draw (:shape props))} (:icon props)])]]))
(:icon props)])]])))
(def draw-toolbox
(mx/component
{:render draw-tools-render
:name "draw-tools"
:mixins [mx/static mx/reactive]}))