From b0de23b0116c45bb69276eac530e5f370e42fb72 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 30 Jan 2017 17:08:02 +0100 Subject: [PATCH] Get rid of buggy reactive locks and refactor all related code. --- frontend/src/uxbox/main/data/pages.cljs | 10 +- frontend/src/uxbox/main/data/shapes.cljs | 24 +- frontend/src/uxbox/main/data/workspace.cljs | 21 +- .../uxbox/main/data/workspace/drawing.cljs | 365 ++++++++++++++++++ .../src/uxbox/main/data/workspace/scroll.cljs | 1 - .../uxbox/main/data/workspace/selrect.cljs | 131 +++++++ frontend/src/uxbox/main/geom.cljs | 2 +- frontend/src/uxbox/main/ui/shapes/common.cljs | 5 +- frontend/src/uxbox/main/ui/shapes/path.cljs | 1 + .../src/uxbox/main/ui/shapes/selection.cljs | 28 +- frontend/src/uxbox/main/ui/shapes/text.cljs | 8 +- .../src/uxbox/main/ui/workspace/canvas.cljs | 79 ++-- .../src/uxbox/main/ui/workspace/drawarea.cljs | 297 ++------------ .../src/uxbox/main/ui/workspace/scroll.cljs | 1 - .../src/uxbox/main/ui/workspace/selrect.cljs | 112 ------ .../main/ui/workspace/sidebar/drawtools.cljs | 4 +- frontend/src/uxbox/main/user_events.cljs | 5 + frontend/src/uxbox/util/rlocks.cljs | 33 -- 18 files changed, 611 insertions(+), 516 deletions(-) create mode 100644 frontend/src/uxbox/main/data/workspace/drawing.cljs create mode 100644 frontend/src/uxbox/main/data/workspace/selrect.cljs delete mode 100644 frontend/src/uxbox/main/ui/workspace/selrect.cljs delete mode 100644 frontend/src/uxbox/util/rlocks.cljs diff --git a/frontend/src/uxbox/main/data/pages.cljs b/frontend/src/uxbox/main/data/pages.cljs index 9b7e28d1d..2ea7224b6 100644 --- a/frontend/src/uxbox/main/data/pages.cljs +++ b/frontend/src/uxbox/main/data/pages.cljs @@ -2,7 +2,7 @@ ;; 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-2017 Andrey Antukh (ns uxbox.main.data.pages (:require [cljs.spec :as s] @@ -13,13 +13,9 @@ [uxbox.main.store :as st] [uxbox.main.repo :as rp] [uxbox.main.lenses :as ul] - [uxbox.util.rlocks :as rlocks] [uxbox.util.spec :as us] [uxbox.util.router :as r] - [uxbox.util.i18n :refer (tr)] - [uxbox.util.forms :as sc] - [uxbox.util.time :as dt] - [uxbox.util.data :refer (without-keys replace-by-id)])) + [uxbox.util.time :as dt])) ;; --- Specs @@ -66,6 +62,8 @@ ::metadata ::shapes])) +;; TODO: add interactions to spec + ;; --- Protocols (defprotocol IPageUpdate diff --git a/frontend/src/uxbox/main/data/shapes.cljs b/frontend/src/uxbox/main/data/shapes.cljs index 3e6c22147..87b434d13 100644 --- a/frontend/src/uxbox/main/data/shapes.cljs +++ b/frontend/src/uxbox/main/data/shapes.cljs @@ -16,12 +16,13 @@ [uxbox.main.workers :as uwrk] [uxbox.main.data.shapes-impl :as impl] [uxbox.main.data.pages :as udp] + [uxbox.main.user-events :as uev] + [uxbox.util.data :refer [dissoc-in]] [uxbox.util.forms :as sc] [uxbox.util.spec :as us] [uxbox.util.geom.point :as gpt] [uxbox.util.geom.matrix :as gmt] [uxbox.util.router :as r] - [uxbox.util.rlocks :as rlocks] [uxbox.util.uuid :as uuid])) ;; --- Specs @@ -568,9 +569,13 @@ (update [_ state] (assoc-in state [:workspace :edition] id)) - ptk/EffectEvent - (effect [_ state stream] - (rlocks/acquire! :shape/edition))) + ptk/WatchEvent + (watch [_ state stream] + ;; Stop edition on interrupt event + (->> stream + (rx/filter #(= % ::uev/interrupt)) + (rx/take 1) + (rx/map (fn [_] #(dissoc-in % [:workspace :edition])))))) (defn start-edition-mode [id] @@ -582,14 +587,11 @@ (deftype DeselectAll [] ptk/UpdateEvent (update [_ state] - (-> state - (assoc-in [:workspace :selected] #{}) - (assoc-in [:workspace :edition] nil) - (assoc-in [:workspace :drawing] nil))) + (assoc-in state [:workspace :selected] #{})) - ptk/EffectEvent - (effect [_ state stream] - (rlocks/release! :shape/edition))) + ptk/WatchEvent + (watch [_ state stream] + (rx/just ::uev/interrupt))) (defn deselect-all "Clear all possible state of drawing, edition diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 802bcb9fa..6d68320fe 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -21,6 +21,8 @@ [uxbox.main.data.lightbox :as udl] [uxbox.main.data.history :as udh] [uxbox.main.data.workspace.scroll :as wscroll] + [uxbox.main.data.workspace.drawing :as wdrawing] + [uxbox.main.data.workspace.selrect :as wselrect] [uxbox.util.uuid :as uuid] [uxbox.util.spec :as us] [uxbox.util.forms :as sc] @@ -33,6 +35,10 @@ (def start-viewport-positioning wscroll/start-viewport-positioning) (def stop-viewport-positioning wscroll/stop-viewport-positioning) +(def start-drawing wdrawing/start-drawing) +(def close-drawing-path wdrawing/close-drawing-path) +(def select-for-drawing wdrawing/select-for-drawing) +(def start-selrect wselrect/start-selrect) ;; --- Initialize Workspace @@ -91,21 +97,6 @@ [project page] (InitializeWorkspace. project page)) -;; --- Select for Drawing - -(deftype SelectForDrawing [shape] - ptk/UpdateEvent - (update [_ state] - (let [current (l/focus ul/selected-drawing state)] - (if (or (nil? shape) - (= shape current)) - (update state :workspace dissoc :drawing) - (assoc-in state [:workspace :drawing] shape))))) - -(defn select-for-drawing - [shape] - (SelectForDrawing. shape)) - ;; --- Workspace Flags (deftype ActivateFlag [flag] diff --git a/frontend/src/uxbox/main/data/workspace/drawing.cljs b/frontend/src/uxbox/main/data/workspace/drawing.cljs new file mode 100644 index 000000000..6674ca304 --- /dev/null +++ b/frontend/src/uxbox/main/data/workspace/drawing.cljs @@ -0,0 +1,365 @@ +;; 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-2017 Andrey Antukh + +(ns uxbox.main.data.workspace.drawing + "Workspace drawing data events and impl." + (:require [beicon.core :as rx] + [potok.core :as ptk] + [lentes.core :as l] + [uxbox.main.store :as st] + [uxbox.main.constants :as c] + [uxbox.main.refs :as refs] + [uxbox.main.streams :as streams] + [uxbox.main.data.shapes :as uds] + [uxbox.main.geom :as geom] + [uxbox.main.workers :as uwrk] + [uxbox.main.user-events :as uev] + [uxbox.main.lenses :as ul] + [uxbox.util.geom.path :as pth] + [uxbox.util.geom.point :as gpt])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Data Events +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; --- Select for Drawing + +(deftype SelectForDrawing [shape] + ptk/UpdateEvent + (update [_ state] + (let [current (l/focus ul/selected-drawing state)] + (if (or (nil? shape) + (= shape current)) + (update state :workspace dissoc :drawing) + (assoc-in state [:workspace :drawing] shape))))) + +(defn select-for-drawing + [shape] + (SelectForDrawing. shape)) + +;; -- Start Drawing + +(declare on-init-draw) + +(deftype StartDrawing [id object] + ptk/UpdateEvent + (update [_ state] + (update-in state [:workspace :drawing-lock] #(if (nil? %) id %))) + + ptk/WatchEvent + (watch [_ state stream] + (let [lock (get-in state [:workspace :drawing-lock])] + (if (= lock id) + (->> stream + (rx/filter #(= % ::uev/interrupt)) + (rx/take 1) + (rx/pr-log (str "STOPED DRAWING " id)) + (rx/map (fn [_] #(update % :workspace dissoc :drawing-lock)))) + (rx/empty)))) + + ptk/EffectEvent + (effect [_ state stream] + (let [lock (get-in state [:workspace :drawing-lock])] + (when (= lock id) + (on-init-draw object stream))))) + +(defn start-drawing + [object] + (let [id (gensym "drawing")] + (StartDrawing. id object))) + +;; --- Initialize Draw Area + +(deftype InitializeDrawing [point] + ptk/UpdateEvent + (update [_ state] + (let [shape (get-in state [:workspace :drawing]) + shape (geom/setup shape {:x1 (:x point) + :y1 (:y point) + :x2 (:x point) + :y2 (:y point)})] + (assoc-in state [:workspace :drawing] shape)))) + +(defn initialize-drawing + [point] + {:pre [(gpt/point? point)]} + (InitializeDrawing. point)) + +;; --- Update Draw Area State + +(deftype UpdateDrawing [position] + ptk/UpdateEvent + (update [_ state] + (update-in state [:workspace :drawing] geom/resize position))) + +(defn update-drawing + [position] + {:pre [(gpt/point? position)]} + (UpdateDrawing. position)) + +;; --- Finish Drawin + +(deftype FinishDrawing [] + ptk/WatchEvent + (watch [_ state stream] + (println "finish-drawing" (get-in state [:workspace :drawing])) + (if-let [shape (get-in state [:workspace :drawing])] + (rx/of #(update % :workspace dissoc :drawing) + (uds/add-shape shape) + (uds/select-first-shape) + ::uev/interrupt) + (rx/empty)))) + +(defn finish-drawing + [] + (FinishDrawing.)) + +;; --- Finish Path Drawing + +(deftype FinishPathDrawing [] + ptk/UpdateEvent + (update [_ state] + (update-in state [:workspace :drawing :points] #(vec (butlast %))))) + +(defn finish-path-drawing + [] + (FinishPathDrawing.)) + +;; --- Insert Drawing Path Point + +(deftype InsertDrawingPathPoint [point] + ptk/UpdateEvent + (update [_ state] + (update-in state [:workspace :drawing :points] (fnil conj []) point))) + +(defn insert-drawing-path-point + [point] + {:pre [(gpt/point? point)]} + (InsertDrawingPathPoint. point)) + +;; --- Update Drawing Path Point + +(deftype UpdateDrawingPathPoint [index point] + ptk/UpdateEvent + (update [_ state] + (let [points (count (get-in state [:workspace :drawing :points]))] + (cond-> state + (< -1 index points) (assoc-in [:workspace :drawing :points index] point))))) + +(defn update-drawing-path-point + [index point] + {:pre [(integer? index) (gpt/point? point)]} + (UpdateDrawingPathPoint. index point)) + +;; --- Close Drawing Path + +(deftype CloseDrawingPath [] + ptk/UpdateEvent + (update [_ state] + (assoc-in state [:workspace :drawing :close?] true)) + + ptk/WatchEvent + (watch [_ state stream] + (rx/of ::uev/interrupt))) + +(defn close-drawing-path + [] + (CloseDrawingPath.)) + +;; --- Simplify Drawing Path + +(deftype SimplifyDrawingPath [tolerance] + ptk/UpdateEvent + (update [_ state] + (update-in state [:workspace :drawing :points] pth/simplify tolerance))) + +(defn simplify-drawing-path + [tolerance] + {:pre [(number? tolerance)]} + (SimplifyDrawingPath. tolerance)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Drawing Implementation +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:private canvas-coords + (gpt/point c/canvas-start-x + c/canvas-start-y)) + +(defn- conditional-align + [point] + (if @refs/selected-alignment + (uwrk/align-point point) + (rx/of point))) + +(defn- translate-to-canvas + [point] + (let [zoom @refs/selected-zoom + ccords (gpt/multiply canvas-coords zoom)] + (-> point + (gpt/subtract ccords) + (gpt/divide zoom)))) + +(declare on-init-draw-icon) +(declare on-init-draw-path) +(declare on-init-draw-free-path) +(declare on-init-draw-generic) + +(defn- on-init-draw + "Function execution when draw shape operation is requested. + This is a entry point for the draw interaction." + [shape stream] + (let [stoper (->> stream + (rx/filter #(= % ::uev/interrupt)) + (rx/take 1))] + (case (:type shape) + :icon (on-init-draw-icon shape) + :image (on-init-draw-icon shape) + :path (if (:free shape) + (on-init-draw-free-path shape stoper) + (on-init-draw-path shape stoper)) + (on-init-draw-generic shape stoper)))) + +(defn- on-init-draw-generic + [shape stoper] + (let [stoper (rx/merge stoper (->> streams/events + (rx/filter uev/mouse-up?) + (rx/take 1))) + start? (volatile! true) + mouse (->> streams/viewport-mouse-position + (rx/mapcat conditional-align) + (rx/map translate-to-canvas) + (rx/take-until stoper) + (rx/with-latest-from vector streams/mouse-position-ctrl))] + + (letfn [(on-position [[point ctrl?]] + (if @start? + (do + (st/emit! (initialize-drawing point)) + (vreset! start? false)) + (st/emit! (update-drawing (assoc point :lock ctrl?))))) + + (on-finish [] + (st/emit! (finish-drawing)))] + (rx/subscribe mouse on-position nil on-finish)))) + +(defn- on-init-draw-icon + [{:keys [metadata] :as shape}] + (let [{:keys [x y]} (gpt/divide @refs/canvas-mouse-position + @refs/selected-zoom) + {:keys [width height]} metadata + proportion (/ width height) + props {:x1 x + :y1 y + :x2 (+ x 200) + :y2 (+ y (/ 200 proportion))} + shape (geom/setup shape props)] + (st/emit! (uds/add-shape shape) + (uds/select-first-shape) + (select-for-drawing nil) + ::uev/interrupt))) + +(def ^:private immanted-zones + (let [transform #(vector (- % 7) (+ % 7) %)] + (concat + (mapv transform (range 0 181 15)) + (mapv (comp transform -) (range 0 181 15))))) + +(defn- align-position + [angle pos] + (reduce (fn [pos [a1 a2 v]] + (if (< a1 angle a2) + (reduced (gpt/update-angle pos v)) + pos)) + pos + immanted-zones)) + +(defn- get-path-stoper-stream + ([stoper] (get-path-stoper-stream stoper false)) + ([stoper mouseup?] + (letfn [(stoper-event? [{:keys [type shift] :as event}] + (or (and (uev/mouse-event? event) + (or (and (= type :double-click) shift) + (= type :context-menu) + (and mouseup? (= type :up)))) + (and (uev/keyboard-event? event) + (= type :down) + (= 13 (:key event)))))] + (->> (rx/filter stoper-event? streams/events) + (rx/merge stoper) + (rx/take 1) + (rx/share))))) + +(defn- get-path-point-stream + [] + (->> streams/events + (rx/filter uev/mouse-click?) + (rx/filter #(false? (:shift %))))) + +(defn- on-init-draw-free-path + [shape stoper] + (let [stoper (get-path-stoper-stream stoper true) + mouse (->> (rx/sample 10 streams/viewport-mouse-position) + (rx/mapcat conditional-align) + (rx/map translate-to-canvas)) + + stream (rx/take-until stoper mouse)] + (letfn [(on-draw [point] + (st/emit! (insert-drawing-path-point point))) + (on-end [] + (st/emit! (simplify-drawing-path 0.3) + (finish-drawing)))] + (rx/subscribe stream on-draw nil on-end)))) + +(defn- on-init-draw-path + [shape stoper] + (let [last-point (volatile! @refs/canvas-mouse-position) + stoper (get-path-stoper-stream stoper) + mouse (->> (rx/sample 10 streams/viewport-mouse-position) + (rx/mapcat conditional-align) + (rx/map translate-to-canvas)) + points (->> (get-path-point-stream) + (rx/with-latest-from vector mouse) + (rx/map second) + (rx/take-until stoper)) + counter (rx/merge (rx/scan #(inc %) 1 points) (rx/of 1)) + stream (->> mouse + (rx/with-latest-from vector streams/mouse-position-ctrl) + (rx/with-latest-from vector counter) + (rx/map flatten) + (rx/take-until stoper))] + + (letfn [(on-point [point] + (vreset! last-point point) + (st/emit! (insert-drawing-path-point point))) + + (on-draw [[point ctrl? counter]] + (if ctrl? + (on-assisted-draw point counter) + (on-generic-draw point counter))) + + (on-generic-draw [point counter] + (st/emit! (update-drawing-path-point counter point))) + + (on-assisted-draw [point counter] + (let [point (as-> point $ + (gpt/subtract $ @last-point) + (align-position (gpt/angle $) $) + (gpt/add $ @last-point))] + (st/emit! (update-drawing-path-point counter point)))) + + (on-finish [] + (st/emit! (finish-path-drawing) + (finish-drawing)))] + + ;; Initialize path drawing + (st/emit! (insert-drawing-path-point @last-point) + (insert-drawing-path-point @last-point)) + + (rx/subscribe points on-point) + (rx/subscribe stream on-draw nil on-finish)))) + + diff --git a/frontend/src/uxbox/main/data/workspace/scroll.cljs b/frontend/src/uxbox/main/data/workspace/scroll.cljs index cccd18411..fcbba87c2 100644 --- a/frontend/src/uxbox/main/data/workspace/scroll.cljs +++ b/frontend/src/uxbox/main/data/workspace/scroll.cljs @@ -12,7 +12,6 @@ [uxbox.main.refs :as refs] [uxbox.main.streams :as streams] [uxbox.util.mixins :as mx :include-macros true] - [uxbox.util.rlocks :as rlocks] [uxbox.util.dom :as dom] [uxbox.util.geom.point :as gpt])) diff --git a/frontend/src/uxbox/main/data/workspace/selrect.cljs b/frontend/src/uxbox/main/data/workspace/selrect.cljs new file mode 100644 index 000000000..055eac820 --- /dev/null +++ b/frontend/src/uxbox/main/data/workspace/selrect.cljs @@ -0,0 +1,131 @@ +;; 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-2017 Andrey Antukh + +(ns uxbox.main.data.workspace.selrect + "Workspace selection rect." + (:require [beicon.core :as rx] + [potok.core :as ptk] + [uxbox.main.store :as st] + [uxbox.main.constants :as c] + [uxbox.main.refs :as refs] + [uxbox.main.streams :as streams] + [uxbox.main.data.shapes :as uds] + [uxbox.main.user-events :as uev] + [uxbox.util.geom.point :as gpt])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Data Events +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(declare stop-selrect) +(declare update-selrect) +(declare get-selection-stoper) +(declare selection->rect) +(declare translate-to-canvas) + +;; --- Start Selrect + +(deftype StartSelrect [] + ptk/UpdateEvent + (update [_ state] + (let [position @refs/viewport-mouse-position + selection {::start position + ::stop position}] + (assoc-in state [:workspace :selrect] (selection->rect selection)))) + + ptk/WatchEvent + (watch [_ state stream] + (let [stoper (get-selection-stoper stream)] + ;; NOTE: the `viewport-mouse-position` can be derived from `stream` + ;; but it used from `streams/` ns just for convenience + (rx/concat + (->> streams/viewport-mouse-position + (rx/take-until stoper) + (rx/map update-selrect)) + (rx/just (stop-selrect)))))) + +(defn start-selrect + [] + (StartSelrect.)) + +;; --- Update Selrect + +(deftype UpdateSelrect [position] + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:workspace :selrect ::stop] position) + (update-in [:workspace :selrect] selection->rect)))) + +(defn update-selrect + [position] + {:pre [(gpt/point? position)]} + (UpdateSelrect. position)) + +;; --- Clear Selrect + +(deftype ClearSelrect [] + ptk/UpdateEvent + (update [_ state] + (update state :workspace dissoc :selrect))) + +(defn clear-selrect + [] + (ClearSelrect.)) + +;; --- Stop Selrect + +(deftype StopSelrect [] + ptk/WatchEvent + (watch [_ state stream] + (let [rect (-> (get-in state [:workspace :selrect]) + (translate-to-canvas))] + (rx/of + (clear-selrect) + (uds/deselect-all) + (uds/select-shapes-by-selrect rect))))) + +(defn stop-selrect + [] + (StopSelrect.)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Selection Rect Implementation +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- selection->rect + [data] + (let [start (::start data) + stop (::stop data) + start-x (min (:x start) (:x stop)) + start-y (min (:y start) (:y stop)) + end-x (max (:x start) (:x stop)) + end-y (max (:y start) (:y stop))] + (assoc data + :x1 start-x + :y1 start-y + :x2 end-x + :y2 end-y + :type :rect))) + +(defn- get-selection-stoper + [stream] + (->> (rx/merge (rx/filter #(= % ::uev/interrupt) stream) + (rx/filter uev/mouse-up? stream)) + (rx/take 1))) + +(defn- translate-to-canvas + "Translate the given rect to the canvas coordinates system." + [rect] + (let [zoom @refs/selected-zoom + startx (* c/canvas-start-x zoom) + starty (* c/canvas-start-y zoom)] + (assoc rect + :x1 (/ (- (:x1 rect) startx) zoom) + :y1 (/ (- (:y1 rect) starty) zoom) + :x2 (/ (- (:x2 rect) startx) zoom) + :y2 (/ (- (:y2 rect) starty) zoom)))) + diff --git a/frontend/src/uxbox/main/geom.cljs b/frontend/src/uxbox/main/geom.cljs index 894bf5a11..ad16f1212 100644 --- a/frontend/src/uxbox/main/geom.cljs +++ b/frontend/src/uxbox/main/geom.cljs @@ -487,7 +487,7 @@ (defn- transform-path [{:keys [points] :as shape} xfmt] - (let [points (map #(gpt/transform % xfmt) points)] + (let [points (mapv #(gpt/transform % xfmt) points)] (assoc shape :points points))) ;; --- Outer Rect diff --git a/frontend/src/uxbox/main/ui/shapes/common.cljs b/frontend/src/uxbox/main/ui/shapes/common.cljs index 025149347..9afbebcfa 100644 --- a/frontend/src/uxbox/main/ui/shapes/common.cljs +++ b/frontend/src/uxbox/main/ui/shapes/common.cljs @@ -16,7 +16,6 @@ [uxbox.main.data.shapes :as uds] [uxbox.main.ui.keyboard :as kbd] [uxbox.util.geom.point :as gpt] - [uxbox.util.rlocks :as rlocks] [uxbox.util.dom :as dom])) ;; --- Refs @@ -37,12 +36,13 @@ ;; --- Movement +;; TODO: implement in the same way as drawing (move under uxbox.main.data.workspace.) + (defn start-move [] (letfn [(on-move [shape delta] (st/emit! (uds/apply-temporal-displacement shape delta))) (on-stop [{:keys [id] :as shape}] - (rlocks/release! :shape/move) (st/emit! (uds/apply-displacement shape))) (on-start [shape] (let [stoper (->> streams/events @@ -55,7 +55,6 @@ (when @refs/selected-alignment (st/emit! (uds/initial-align-shape shape))) (rx/subscribe stream on-move nil on-stop)))] - (rlocks/acquire! :shape/move) (run! on-start @selected-ref))) ;; --- Events diff --git a/frontend/src/uxbox/main/ui/shapes/path.cljs b/frontend/src/uxbox/main/ui/shapes/path.cljs index cb4b0be50..3fe3534e7 100644 --- a/frontend/src/uxbox/main/ui/shapes/path.cljs +++ b/frontend/src/uxbox/main/ui/shapes/path.cljs @@ -48,6 +48,7 @@ (mx/defc path-shape {:mixins [mx/static]} [{:keys [id tmp-resize-xform tmp-displacement rotation] :as shape}] + (let [shape (cond-> shape tmp-displacement (geom/transform (gmt/translate-matrix tmp-displacement)) tmp-resize-xform (geom/transform tmp-resize-xform) diff --git a/frontend/src/uxbox/main/ui/shapes/selection.cljs b/frontend/src/uxbox/main/ui/shapes/selection.cljs index 22498b7fc..632ad0e21 100644 --- a/frontend/src/uxbox/main/ui/shapes/selection.cljs +++ b/frontend/src/uxbox/main/ui/shapes/selection.cljs @@ -20,7 +20,6 @@ [uxbox.main.ui.shapes.common :as scommon] [uxbox.main.geom :as geom] [uxbox.util.mixins :as mx :include-macros true] - [uxbox.util.rlocks :as rlocks] [uxbox.util.geom.matrix :as gmt] [uxbox.util.geom.point :as gpt] [uxbox.util.dom :as dom])) @@ -209,8 +208,7 @@ (apply st/emit! (sequence xf ids)))) (on-end [] - (apply st/emit! (map uds/apply-resize-matrix ids)) - (rlocks/release! :shape/resize))] + (apply st/emit! (map uds/apply-resize-matrix ids)))] (let [shape (->> (geom/shape->rect-shape shape) (geom/size)) @@ -229,7 +227,6 @@ (rx/with-latest-from vector streams/mouse-position-ctrl) (rx/scan accumulate-width shape) (rx/map (partial calculate-ratio shape)))] - (rlocks/acquire! :shape/resize) (rx/subscribe stream (partial on-resize shape) nil @@ -316,27 +313,30 @@ ;; --- Selection Handlers (Component) +(defn get-path-edition-stoper + [stream] + (letfn [(stoper-event? [{:keys [type shift] :as event}] + (and (uev/mouse-event? event) (= type :up)))] + (rx/merge + (rx/filter stoper-event? stream) + (->> stream + (rx/filter #(= % ::uev/interrupt)) + (rx/take 1))))) + (defn start-path-edition [shape-id index] (letfn [(on-move [delta] - (st/emit! (uds/update-path shape-id index delta))) - (on-end [] - (rlocks/release! :shape/resize))] - (let [stoper (->> streams/events - (rx/map first) - (rx/filter #(= % :mouse/up)) - (rx/take 1)) + (st/emit! (uds/update-path shape-id index delta)))] + (let [stoper (get-path-edition-stoper streams/events) stream (rx/take-until stoper streams/mouse-position-deltas)] - (rlocks/acquire! :shape/resize) (when @refs/selected-alignment (st/emit! (uds/initial-path-point-align shape-id index))) - (rx/subscribe stream on-move nil on-end)))) + (rx/subscribe stream on-move)))) (mx/defc path-edition-selection-handlers [{:keys [id points] :as shape} zoom] (letfn [(on-mouse-down [index event] (dom/stop-propagation event) - (rlocks/acquire! :shape/resize) (start-path-edition id index))] [:g.controls (for [[index {:keys [x y]}] (map-indexed vector points)] diff --git a/frontend/src/uxbox/main/ui/shapes/text.cljs b/frontend/src/uxbox/main/ui/shapes/text.cljs index 9362c3a3f..e57e37f6a 100644 --- a/frontend/src/uxbox/main/ui/shapes/text.cljs +++ b/frontend/src/uxbox/main/ui/shapes/text.cljs @@ -17,7 +17,6 @@ [uxbox.util.color :as color] [uxbox.util.dom :as dom] [uxbox.util.geom.matrix :as gmt] - [uxbox.util.rlocks :as rlocks] [uxbox.util.mixins :as mx :include-macros true]) (:import goog.events.EventType)) @@ -48,6 +47,8 @@ (letfn [(on-mouse-down [event] (handle-mouse-down event shape selected)) (on-double-click [event] + ;; TODO: handle grouping event propagation + ;; TODO: handle actions locking properly (dom/stop-propagation event) (st/emit! (uds/start-edition-mode id)))] [:g.shape {:class (when selected? "selected") @@ -149,11 +150,9 @@ own)) (mx/defc text-shape-wrapper - { - :did-mount text-shape-wrapper-did-mount + {:did-mount text-shape-wrapper-did-mount :did-remount text-shape-wrapper-did-remount} [{:keys [id tmp-resize-xform tmp-displacement drawing?] :as shape}] - (println "text-shape-wrapper" shape) (let [xfmt (cond-> (gmt/matrix) tmp-displacement (gmt/translate tmp-displacement) tmp-resize-xform (gmt/multiply tmp-resize-xform)) @@ -182,7 +181,6 @@ (let [style (make-style shape)] [:div {:style style} content])) - ;; --- Text Shape Html (mx/defc text-shape diff --git a/frontend/src/uxbox/main/ui/workspace/canvas.cljs b/frontend/src/uxbox/main/ui/workspace/canvas.cljs index 13ea47395..d4ec79317 100644 --- a/frontend/src/uxbox/main/ui/workspace/canvas.cljs +++ b/frontend/src/uxbox/main/ui/workspace/canvas.cljs @@ -15,22 +15,21 @@ [uxbox.main.refs :as refs] [uxbox.main.streams :as streams] [uxbox.main.user-events :as uev] + [uxbox.main.geom :as geom] [uxbox.main.data.projects :as dp] - [uxbox.main.data.workspace :as dw] + [uxbox.main.data.workspace :as udw] [uxbox.main.data.shapes :as uds] [uxbox.main.ui.keyboard :as kbd] [uxbox.main.ui.shapes :as uus] - [uxbox.main.ui.shapes.selection :refer (selection-handlers)] + [uxbox.main.ui.shapes.selection :refer [selection-handlers]] [uxbox.main.ui.workspace.scroll :as scroll] - [uxbox.main.ui.workspace.drawarea :refer (draw-area)] - [uxbox.main.ui.workspace.ruler :refer (ruler)] - [uxbox.main.ui.workspace.selrect :refer (selrect)] - [uxbox.main.ui.workspace.grid :refer (grid)] + [uxbox.main.ui.workspace.drawarea :refer [draw-area]] + [uxbox.main.ui.workspace.ruler :refer [ruler]] + [uxbox.main.ui.workspace.grid :refer [grid]] [uxbox.util.geom.point :as gpt] [uxbox.util.dom :as dom] - [uxbox.util.data :refer (parse-int)] - [uxbox.util.mixins :as mx :include-macros true] - [uxbox.util.rlocks :as rlocks]) + [uxbox.util.data :refer [parse-int]] + [uxbox.util.mixins :as mx :include-macros true]) (:import goog.events.EventType)) ;; --- Background @@ -59,6 +58,23 @@ [:span {:alt "y"} (str "Y: " (:y coords "-"))]])) +;; --- Selection Rect + +(def selrect-ref + (-> (l/key :selrect) + (l/derive refs/workspace))) + +(mx/defc selrect + {:mixins [mx/static mx/reactive]} + [] + (when-let [rect (mx/react selrect-ref)] + (let [{:keys [x1 y1 width height]} (geom/size rect)] + [:rect.selection-rect + {:x x1 + :y y1 + :width width + :height height}]))) + ;; --- Cursor tooltip (defn- get-shape-tooltip @@ -137,12 +153,8 @@ :shift? shift? :ctrl? ctrl?}] (st/emit! (uev/keyboard-event :down key ctrl? shift?)) - - ;; TODO: remove (deprecated) - #_(rx/push! streams/events-b [:key/down opts]) (when (kbd/space? event) - (st/emit! (dw/start-viewport-positioning))))) - #_(rlocks/acquire! :workspace/scroll) + (st/emit! (udw/start-viewport-positioning))))) (on-key-up [event] (let [key (.-keyCode event) @@ -152,10 +164,8 @@ :shift? shift? :ctrl? ctrl?}] (when (kbd/space? event) - (st/emit! (dw/stop-viewport-positioning))) - (st/emit! (uev/keyboard-event :up key ctrl? shift?)) - ;; TODO: remove (deprecated) - #_(rx/push! streams/events-b [:key/up opts]))) + (st/emit! (udw/stop-viewport-positioning))) + (st/emit! (uev/keyboard-event :up key ctrl? shift?)))) (on-mousemove [event] (let [wpt (gpt/point (.-clientX event) @@ -169,9 +179,7 @@ :window-coords wpt :viewport-coords vpt :canvas-coords cpt}] - (st/emit! (uev/pointer-event wpt vpt cpt ctrl? shift?)) - ;; TODO: remove (deprecated) - #_(rx/push! streams/mouse-b event)))] + (st/emit! (uev/pointer-event wpt vpt cpt ctrl? shift?))))] (let [key1 (events/listen js/document EventType.MOUSEMOVE on-mousemove) key2 (events/listen js/document EventType.KEYDOWN on-key-down) @@ -207,15 +215,10 @@ shift? (kbd/shift? event) opts {:shift? shift? :ctrl? ctrl?}] - (st/emit! (uev/mouse-event :down ctrl? shift?)) - ;; TODO: remove (deprecated) - #_(rx/push! streams/events-b [:mouse/down opts])) - (if (:drawing workspace) - (rlocks/acquire! :ui/draw) - (do - (when (seq (:selected workspace)) - (rlocks/release! :shape/edition)) - (rlocks/acquire! :ui/selrect)))) + (st/emit! (uev/mouse-event :down ctrl? shift?))) + (if-let [object (:drawing workspace)] + (st/emit! (udw/start-drawing object)) + (st/emit! (udw/start-selrect)))) (on-context-menu [event] (dom/prevent-default event) (dom/stop-propagation event) @@ -223,36 +226,28 @@ shift? (kbd/shift? event) opts {:shift? shift? :ctrl? ctrl?}] - (st/emit! (uev/mouse-event :context-menu ctrl? shift?)) - ;; TODO: remove (deprecated) - #_(rx/push! streams/events-b [:mouse/right-click opts]))) + (st/emit! (uev/mouse-event :context-menu ctrl? shift?)))) (on-mouse-up [event] (dom/stop-propagation event) (let [ctrl? (kbd/ctrl? event) shift? (kbd/shift? event) opts {:shift? shift? :ctrl? ctrl?}] - (st/emit! (uev/mouse-event :up ctrl? shift?)) - ;; TODO: remove (deprecated) - #_(rx/push! streams/events-b [:mouse/up]))) + (st/emit! (uev/mouse-event :up ctrl? shift?)))) (on-click [event] (dom/stop-propagation event) (let [ctrl? (kbd/ctrl? event) shift? (kbd/shift? event) opts {:shift? shift? :ctrl? ctrl?}] - (st/emit! (uev/mouse-event :click ctrl? shift?)) - ;; TODO: remove (deprecated) - #_(rx/push! streams/events-b [:mouse/click opts]))) + (st/emit! (uev/mouse-event :click ctrl? shift?)))) (on-double-click [event] (dom/stop-propagation event) (let [ctrl? (kbd/ctrl? event) shift? (kbd/shift? event) opts {:shift? shift? :ctrl? ctrl?}] - (st/emit! (uev/mouse-event :double-click ctrl? shift?)) - ;; TODO: remove (deprecated) - #_(rx/push! streams/events-b [:mouse/double-click opts])))] + (st/emit! (uev/mouse-event :double-click ctrl? shift?))))] [:div (coordinates) (when tooltip diff --git a/frontend/src/uxbox/main/ui/workspace/drawarea.cljs b/frontend/src/uxbox/main/ui/workspace/drawarea.cljs index 8b54bcb7c..1fbb00df7 100644 --- a/frontend/src/uxbox/main/ui/workspace/drawarea.cljs +++ b/frontend/src/uxbox/main/ui/workspace/drawarea.cljs @@ -8,14 +8,13 @@ "Draw interaction and component." (:require [beicon.core :as rx] [potok.core :as ptk] + [lentes.core :as l] [uxbox.util.mixins :as mx :include-macros true] - [uxbox.util.rlocks :as rlocks] [uxbox.main.store :as st] [uxbox.main.constants :as c] [uxbox.main.refs :as refs] [uxbox.main.streams :as streams] [uxbox.main.workers :as uwrk] - [uxbox.main.user-events :as uev] [uxbox.main.data.workspace :as udw] [uxbox.main.data.shapes :as uds] [uxbox.main.ui.shapes :as shapes] @@ -24,67 +23,36 @@ [uxbox.util.geom.path :as path] [uxbox.util.dom :as dom])) -;; --- State +;; --- Refs -(defonce drawing-stoper (rx/subject)) -(defonce drawing-shape (atom nil)) -(defonce drawing-position (atom nil)) +(def drawing-shape + (-> (l/key :drawing) + (l/derive refs/workspace))) -(def ^:private canvas-coords - (gpt/point c/canvas-start-x - c/canvas-start-y)) +;; --- Components -;; --- Draw Area (Component) - -(declare watch-draw-actions) -(declare on-init-draw) - -(defn- watch-draw-actions - [] - (let [stream (->> (rx/map first rlocks/stream) - (rx/filter #(= % :ui/draw)))] - (rx/subscribe stream on-init-draw))) - -(defn- draw-area-will-mount - [own] - (assoc own ::sub (watch-draw-actions))) - -(defn- draw-area-will-unmount - [own] - (.close (::sub own)) - (dissoc own ::sub)) - -(declare generic-shape-draw-area) -(declare path-shape-draw-area) +(declare generic-draw-area) +(declare path-draw-area) (mx/defc draw-area - {:will-mount draw-area-will-mount - :will-unmount draw-area-will-unmount - :mixins [mx/static mx/reactive]} + {:mixins [mx/static mx/reactive]} [] - (let [shape (mx/react drawing-shape) - position (mx/react drawing-position)] - (when shape - (if (= (:type shape) :path) - (path-shape-draw-area shape) - (generic-shape-draw-area shape position))))) + (when-let [shape (mx/react drawing-shape)] + (if (= (:type shape) :path) + (path-draw-area shape) + (generic-draw-area shape)))) -(mx/defc generic-shape-draw-area - [shape position] - (if position - (-> (assoc shape :drawing? true) - (geom/resize position) - (shapes/render-component)) - (-> (assoc shape :drawing? true) - (shapes/render-component)))) +(mx/defc generic-draw-area + [shape] + (-> (assoc shape :drawing? true) + (shapes/render-component))) -(mx/defc path-shape-draw-area +(mx/defc path-draw-area [{:keys [points] :as shape}] (letfn [(on-click [event] (dom/stop-propagation event) - (swap! drawing-shape drop-last-point) - (st/emit! (udw/set-tooltip nil)) - (rx/push! drawing-stoper true)) + (st/emit! (udw/set-tooltip nil) + (udw/close-drawing-path))) (on-mouse-enter [event] (st/emit! (udw/set-tooltip "Click to close the path"))) (on-mouse-leave [event] @@ -93,227 +61,14 @@ (let [points (:points shape) points (vec (butlast points))] (assoc shape :points points :close? true)))] - (let [{:keys [x y]} (first points)] + (when-let [{:keys [x y]} (first points)] [:g (-> (assoc shape :drawing? true) (shapes/render-component)) (when-not (:free shape) [:circle.close-bezier {:cx x - :cy y - :r 5 - :on-click on-click - :on-mouse-enter on-mouse-enter - :on-mouse-leave on-mouse-leave}])]))) - -;; --- Drawing Initialization - -(declare on-init-draw-icon) -(declare on-init-draw-path) -(declare on-init-draw-free-path) -(declare on-init-draw-generic) - -(defn- on-init-draw - "Function execution when draw shape operation is requested. - This is a entry point for the draw interaction." - [] - (when-let [shape (:drawing @refs/workspace)] - (case (:type shape) - :icon (on-init-draw-icon shape) - :image (on-init-draw-icon shape) - :path (if (:free shape) - (on-init-draw-free-path shape) - (on-init-draw-path shape)) - (on-init-draw-generic shape)))) - -;; --- Icon Drawing - -(defn- on-init-draw-icon - [{:keys [metadata] :as shape}] - (let [{:keys [x y]} (gpt/divide @refs/canvas-mouse-position @refs/selected-zoom) - {:keys [width height]} metadata - proportion (/ width height) - props {:x1 x - :y1 y - :x2 (+ x 200) - :y2 (+ y (/ 200 proportion))} - shape (geom/setup shape props)] - (st/emit! (uds/add-shape shape) - (udw/select-for-drawing nil) - (uds/select-first-shape)) - (rlocks/release! :ui/draw))) - -;; --- Path Drawing - -(def ^:private immanted-zones - (let [transform #(vector (- % 7) (+ % 7) %)] - (concat - (mapv transform (range 0 181 15)) - (mapv (comp transform -) (range 0 181 15))))) - -(defn- align-position - [angle pos] - (reduce (fn [pos [a1 a2 v]] - (if (< a1 angle a2) - (reduced (gpt/update-angle pos v)) - pos)) - pos - immanted-zones)) - -(defn- translate-to-canvas - [point] - (let [zoom @refs/selected-zoom - ccords (gpt/multiply canvas-coords zoom)] - (-> point - (gpt/subtract ccords) - (gpt/divide zoom)))) - -(defn- conditional-align - [point] - (if @refs/selected-alignment - (uwrk/align-point point) - (rx/of point))) - -(defn- on-init-draw-path - [shape] - (letfn [(stoper-event? [{:keys [type shift] :as event}] - (or (and (uev/mouse-event? event) - (or (and (= type :double-click) shift) - (= type :context-menu))) - (and (uev/keyboard-event? event) - (= type :down) - (= 13 (:key event))))) - - (new-point-event? [[type opts]] - (and (= type :mouse/click) - (false? (:shift? opts))))] - - (let [mouse (->> (rx/sample 10 streams/viewport-mouse-position) - (rx/mapcat conditional-align) - (rx/map translate-to-canvas)) - stoper (->> (rx/merge - (rx/take 1 drawing-stoper) - (rx/filter stoper-event? streams/events)) - (rx/take 1)) - firstpos (rx/take 1 mouse) - stream (->> (rx/take-until stoper mouse) - (rx/skip-while #(nil? @drawing-shape)) - (rx/with-latest-from vector streams/mouse-position-ctrl)) - ptstream (->> (rx/take-until stoper streams/events) - (rx/filter new-point-event?) - (rx/with-latest-from vector mouse) - (rx/map second)) - counter (atom 0)] - (letfn [(append-point [{:keys [type] :as shape} point] - (let [point (gpt/point point)] - (update shape :points conj point))) - - (update-point [{:keys [type points] :as shape} point index] - (let [point (gpt/point point) - points (:points shape)] - (assoc-in shape [:points index] point))) - - (on-first-point [point] - (let [shape (append-point shape point)] - (swap! counter inc) - (reset! drawing-shape shape))) - - (on-click [point] - (let [shape (append-point @drawing-shape point)] - (swap! counter inc) - (reset! drawing-shape shape))) - - (on-assisted-draw [point] - (let [center (get-in @drawing-shape [:points (dec @counter)]) - point (as-> point $ - (gpt/subtract $ center) - (align-position (gpt/angle $) $) - (gpt/add $ center))] - (->> (update-point @drawing-shape point @counter) - (reset! drawing-shape)))) - - (on-free-draw [point] - (->> (update-point @drawing-shape point @counter) - (reset! drawing-shape))) - - (on-draw [[point ctrl?]] - (if ctrl? - (on-assisted-draw point) - (on-free-draw point))) - - (on-end [] - (let [shape @drawing-shape] - (st/emit! (uds/add-shape shape) - (udw/select-for-drawing nil) - (uds/select-first-shape)) - (reset! drawing-shape nil) - (reset! drawing-position nil) - (rlocks/release! :ui/draw)))] - - (rx/subscribe firstpos on-first-point) - (rx/subscribe ptstream on-click) - (rx/subscribe stream on-draw nil on-end))))) - -(defn- on-init-draw-free-path - [shape] - (let [mouse (->> (rx/sample 10 streams/viewport-mouse-position) - (rx/mapcat conditional-align) - (rx/map translate-to-canvas)) - stoper (->> streams/events - (rx/filter uev/mouse-up?) - (rx/take 1)) - stream (rx/take-until stoper mouse)] - (letfn [(simplify-shape [{:keys [points] :as shape}] - (let [prevnum (count points) - points (path/simplify points 0.2)] - (println "path simplification: previous=" prevnum - " current=" (count points)) - (assoc shape :points points))) - - (on-draw [point] - (let [point (gpt/point point) - shape (-> (or @drawing-shape shape) - (update :points conj point))] - (reset! drawing-shape shape))) - - (on-end [] - (let [shape (simplify-shape @drawing-shape)] - (st/emit! (uds/add-shape shape) - (udw/select-for-drawing nil) - (uds/select-first-shape)) - (reset! drawing-shape nil) - (reset! drawing-position nil) - (rlocks/release! :ui/draw)))] - - (rx/subscribe stream on-draw nil on-end)))) - -(defn- on-init-draw-generic - [shape] - (let [mouse (->> streams/viewport-mouse-position - (rx/mapcat conditional-align) - (rx/map translate-to-canvas)) - stoper (->> streams/events - (rx/filter uev/mouse-up?) - (rx/take 1)) - firstpos (rx/take 1 mouse) - stream (->> (rx/take-until stoper mouse) - (rx/skip-while #(nil? @drawing-shape)) - (rx/with-latest-from vector streams/mouse-position-ctrl))] - - (letfn [(on-start [{:keys [x y] :as pt}] - (let [shape (geom/setup shape {:x1 x :y1 y :x2 x :y2 y})] - (reset! drawing-shape shape))) - - (on-draw [[pt ctrl?]] - (reset! drawing-position (assoc pt :lock ctrl?))) - (on-end [] - (let [shape @drawing-shape - shpos @drawing-position - shape (geom/resize shape shpos)] - (st/emit! (uds/add-shape shape) - (udw/select-for-drawing nil) - (uds/select-first-shape)) - (reset! drawing-position nil) - (reset! drawing-shape nil) - (rlocks/release! :ui/draw)))] - (rx/subscribe firstpos on-start) - (rx/subscribe stream on-draw nil on-end)))) + :cy y + :r 5 + :on-click on-click + :on-mouse-enter on-mouse-enter + :on-mouse-leave on-mouse-leave}])]))) diff --git a/frontend/src/uxbox/main/ui/workspace/scroll.cljs b/frontend/src/uxbox/main/ui/workspace/scroll.cljs index 1516b1dac..d1378becb 100644 --- a/frontend/src/uxbox/main/ui/workspace/scroll.cljs +++ b/frontend/src/uxbox/main/ui/workspace/scroll.cljs @@ -11,7 +11,6 @@ [potok.core :as ptk] [uxbox.main.refs :as refs] [uxbox.util.mixins :as mx :include-macros true] - [uxbox.util.rlocks :as rlocks] [uxbox.util.dom :as dom] [uxbox.util.geom.point :as gpt])) diff --git a/frontend/src/uxbox/main/ui/workspace/selrect.cljs b/frontend/src/uxbox/main/ui/workspace/selrect.cljs deleted file mode 100644 index 8f740a79d..000000000 --- a/frontend/src/uxbox/main/ui/workspace/selrect.cljs +++ /dev/null @@ -1,112 +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-2017 Andrey Antukh -;; Copyright (c) 2015-2017 Juan de la Cruz - -(ns uxbox.main.ui.workspace.selrect - "Mouse selection interaction and component." - (:require [beicon.core :as rx] - [potok.core :as ptk] - [uxbox.main.store :as st] - [uxbox.main.constants :as c] - [uxbox.main.refs :as refs] - [uxbox.main.streams :as streams] - [uxbox.main.user-events :as uev] - [uxbox.main.geom :as geom] - [uxbox.main.data.workspace :as dw] - [uxbox.main.data.shapes :as uds] - [uxbox.util.mixins :as mx :include-macros true] - [uxbox.util.rlocks :as rlocks])) - -(defonce position (atom nil)) - -;; --- Selrect (Component) - -(declare selrect->rect) -(declare watch-selrect-actions) - -(defn- will-mount - [own] - (assoc own ::sub (watch-selrect-actions))) - -(defn- will-unmount - [own] - (.close (::sub own)) - (dissoc own ::sub)) - -(mx/defc selrect - {:will-mount will-mount - :will-unmount will-unmount - :mixins [mx/static mx/reactive]} - [] - (when-let [data (mx/react position)] - (let [{:keys [x1 y1 width height]} (geom/size (selrect->rect data))] - [:rect.selection-rect - {:x x1 - :y y1 - :width width - :height height}]))) - -;; --- Interaction - -(defn- selrect->rect - [{:keys [start current] :as data}] - (let [start-x (min (:x start) (:x current)) - start-y (min (:y start) (:y current)) - end-x (max (:x start) (:x current)) - end-y (max (:y start) (:y current))] - {:x1 start-x - :y1 start-y - :x2 end-x - :y2 end-y - :type :rect})) - -(defn- translate-to-canvas - "Translate the given rect to the canvas coordinates system." - [rect] - (let [zoom @refs/selected-zoom - startx (* c/canvas-start-x zoom) - starty (* c/canvas-start-y zoom)] - (assoc rect - :x1 (/ (- (:x1 rect) startx) zoom) - :y1 (/ (- (:y1 rect) starty) zoom) - :x2 (/ (- (:x2 rect) startx) zoom) - :y2 (/ (- (:y2 rect) starty) zoom)))) - -(declare on-start) - -(defn- watch-selrect-actions - [] - (let [stream (->> (rx/map first rlocks/stream) - (rx/filter #(= % :ui/selrect)))] - (rx/subscribe stream on-start))) - -(defn- on-move - "Function executed on each mouse movement while selrect - interaction is active." - [pos] - (swap! position assoc :current pos)) - -(defn- on-complete - "Function executed when the selection rect - interaction is terminated." - [] - (let [rect (-> (selrect->rect @position) - (translate-to-canvas))] - (st/emit! (uds/deselect-all) - (uds/select-shapes-by-selrect rect)) - (rlocks/release! :ui/selrect) - (reset! position nil))) - -(defn- on-start - "Function execution when selrect action is started." - [] - (let [stoper (->> streams/events - (rx/filter uev/mouse-up?) - (rx/take 1)) - stream (rx/take-until stoper streams/viewport-mouse-position) - pos @refs/viewport-mouse-position] - (reset! position {:start pos :current pos}) - (rx/subscribe stream on-move nil on-complete))) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/drawtools.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/drawtools.cljs index a31591a37..d1b65f599 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/drawtools.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/drawtools.cljs @@ -12,6 +12,7 @@ [uxbox.main.store :as st] [uxbox.main.refs :as refs] [uxbox.main.data.workspace :as dw] + [uxbox.main.user-events :as uev] [uxbox.builtins.icons :as i] [uxbox.util.i18n :refer (tr)] [uxbox.util.router :as r] @@ -82,7 +83,8 @@ (defn- select-for-draw [shape] - (st/emit! (dw/select-for-drawing shape))) + (st/emit! ::uev/interrupt + (dw/select-for-drawing shape))) (mx/defc draw-toolbox {:mixins [mx/static mx/reactive]} diff --git a/frontend/src/uxbox/main/user_events.cljs b/frontend/src/uxbox/main/user_events.cljs index cd53810da..c1bdc1907 100644 --- a/frontend/src/uxbox/main/user_events.cljs +++ b/frontend/src/uxbox/main/user_events.cljs @@ -46,6 +46,11 @@ (and (mouse-event? v) (= :up (:type v)))) +(defn mouse-click? + [v] + (and (mouse-event? v) + (= :click (:type v)))) + ;; --- Pointer Event (defrecord PointerEvent [window diff --git a/frontend/src/uxbox/util/rlocks.cljs b/frontend/src/uxbox/util/rlocks.cljs deleted file mode 100644 index 8e80af215..000000000 --- a/frontend/src/uxbox/util/rlocks.cljs +++ /dev/null @@ -1,33 +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) 2016 Andrey Antukh - -(ns uxbox.util.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/subject)) - -(defn acquire! - ([type] - (when (= @lock ::none) - (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) - (reset! lock ::none) - (rx/push! stream [::none nil]))) -