mirror of
https://github.com/penpot/penpot.git
synced 2025-06-03 08:21:39 +02:00
Get rid of buggy reactive locks and refactor all related code.
This commit is contained in:
parent
bce58df413
commit
b0de23b011
18 changed files with 611 additions and 516 deletions
|
@ -2,7 +2,7 @@
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
;; 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/.
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.data.pages
|
(ns uxbox.main.data.pages
|
||||||
(:require [cljs.spec :as s]
|
(:require [cljs.spec :as s]
|
||||||
|
@ -13,13 +13,9 @@
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.repo :as rp]
|
[uxbox.main.repo :as rp]
|
||||||
[uxbox.main.lenses :as ul]
|
[uxbox.main.lenses :as ul]
|
||||||
[uxbox.util.rlocks :as rlocks]
|
|
||||||
[uxbox.util.spec :as us]
|
[uxbox.util.spec :as us]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.util.router :as r]
|
||||||
[uxbox.util.i18n :refer (tr)]
|
[uxbox.util.time :as dt]))
|
||||||
[uxbox.util.forms :as sc]
|
|
||||||
[uxbox.util.time :as dt]
|
|
||||||
[uxbox.util.data :refer (without-keys replace-by-id)]))
|
|
||||||
|
|
||||||
;; --- Specs
|
;; --- Specs
|
||||||
|
|
||||||
|
@ -66,6 +62,8 @@
|
||||||
::metadata
|
::metadata
|
||||||
::shapes]))
|
::shapes]))
|
||||||
|
|
||||||
|
;; TODO: add interactions to spec
|
||||||
|
|
||||||
;; --- Protocols
|
;; --- Protocols
|
||||||
|
|
||||||
(defprotocol IPageUpdate
|
(defprotocol IPageUpdate
|
||||||
|
|
|
@ -16,12 +16,13 @@
|
||||||
[uxbox.main.workers :as uwrk]
|
[uxbox.main.workers :as uwrk]
|
||||||
[uxbox.main.data.shapes-impl :as impl]
|
[uxbox.main.data.shapes-impl :as impl]
|
||||||
[uxbox.main.data.pages :as udp]
|
[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.forms :as sc]
|
||||||
[uxbox.util.spec :as us]
|
[uxbox.util.spec :as us]
|
||||||
[uxbox.util.geom.point :as gpt]
|
[uxbox.util.geom.point :as gpt]
|
||||||
[uxbox.util.geom.matrix :as gmt]
|
[uxbox.util.geom.matrix :as gmt]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.util.router :as r]
|
||||||
[uxbox.util.rlocks :as rlocks]
|
|
||||||
[uxbox.util.uuid :as uuid]))
|
[uxbox.util.uuid :as uuid]))
|
||||||
|
|
||||||
;; --- Specs
|
;; --- Specs
|
||||||
|
@ -568,9 +569,13 @@
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(assoc-in state [:workspace :edition] id))
|
(assoc-in state [:workspace :edition] id))
|
||||||
|
|
||||||
ptk/EffectEvent
|
ptk/WatchEvent
|
||||||
(effect [_ state stream]
|
(watch [_ state stream]
|
||||||
(rlocks/acquire! :shape/edition)))
|
;; Stop edition on interrupt event
|
||||||
|
(->> stream
|
||||||
|
(rx/filter #(= % ::uev/interrupt))
|
||||||
|
(rx/take 1)
|
||||||
|
(rx/map (fn [_] #(dissoc-in % [:workspace :edition]))))))
|
||||||
|
|
||||||
(defn start-edition-mode
|
(defn start-edition-mode
|
||||||
[id]
|
[id]
|
||||||
|
@ -582,14 +587,11 @@
|
||||||
(deftype DeselectAll []
|
(deftype DeselectAll []
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(-> state
|
(assoc-in state [:workspace :selected] #{}))
|
||||||
(assoc-in [:workspace :selected] #{})
|
|
||||||
(assoc-in [:workspace :edition] nil)
|
|
||||||
(assoc-in [:workspace :drawing] nil)))
|
|
||||||
|
|
||||||
ptk/EffectEvent
|
ptk/WatchEvent
|
||||||
(effect [_ state stream]
|
(watch [_ state stream]
|
||||||
(rlocks/release! :shape/edition)))
|
(rx/just ::uev/interrupt)))
|
||||||
|
|
||||||
(defn deselect-all
|
(defn deselect-all
|
||||||
"Clear all possible state of drawing, edition
|
"Clear all possible state of drawing, edition
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
[uxbox.main.data.lightbox :as udl]
|
[uxbox.main.data.lightbox :as udl]
|
||||||
[uxbox.main.data.history :as udh]
|
[uxbox.main.data.history :as udh]
|
||||||
[uxbox.main.data.workspace.scroll :as wscroll]
|
[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.uuid :as uuid]
|
||||||
[uxbox.util.spec :as us]
|
[uxbox.util.spec :as us]
|
||||||
[uxbox.util.forms :as sc]
|
[uxbox.util.forms :as sc]
|
||||||
|
@ -33,6 +35,10 @@
|
||||||
|
|
||||||
(def start-viewport-positioning wscroll/start-viewport-positioning)
|
(def start-viewport-positioning wscroll/start-viewport-positioning)
|
||||||
(def stop-viewport-positioning wscroll/stop-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
|
;; --- Initialize Workspace
|
||||||
|
|
||||||
|
@ -91,21 +97,6 @@
|
||||||
[project page]
|
[project page]
|
||||||
(InitializeWorkspace. 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
|
;; --- Workspace Flags
|
||||||
|
|
||||||
(deftype ActivateFlag [flag]
|
(deftype ActivateFlag [flag]
|
||||||
|
|
365
frontend/src/uxbox/main/data/workspace/drawing.cljs
Normal file
365
frontend/src/uxbox/main/data/workspace/drawing.cljs
Normal file
|
@ -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 <niwi@niwi.nz>
|
||||||
|
|
||||||
|
(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))))
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.main.streams :as streams]
|
[uxbox.main.streams :as streams]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.util.rlocks :as rlocks]
|
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.geom.point :as gpt]))
|
[uxbox.util.geom.point :as gpt]))
|
||||||
|
|
||||||
|
|
131
frontend/src/uxbox/main/data/workspace/selrect.cljs
Normal file
131
frontend/src/uxbox/main/data/workspace/selrect.cljs
Normal file
|
@ -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 <niwi@niwi.nz>
|
||||||
|
|
||||||
|
(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))))
|
||||||
|
|
|
@ -487,7 +487,7 @@
|
||||||
|
|
||||||
(defn- transform-path
|
(defn- transform-path
|
||||||
[{:keys [points] :as shape} xfmt]
|
[{:keys [points] :as shape} xfmt]
|
||||||
(let [points (map #(gpt/transform % xfmt) points)]
|
(let [points (mapv #(gpt/transform % xfmt) points)]
|
||||||
(assoc shape :points points)))
|
(assoc shape :points points)))
|
||||||
|
|
||||||
;; --- Outer Rect
|
;; --- Outer Rect
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
[uxbox.main.data.shapes :as uds]
|
[uxbox.main.data.shapes :as uds]
|
||||||
[uxbox.main.ui.keyboard :as kbd]
|
[uxbox.main.ui.keyboard :as kbd]
|
||||||
[uxbox.util.geom.point :as gpt]
|
[uxbox.util.geom.point :as gpt]
|
||||||
[uxbox.util.rlocks :as rlocks]
|
|
||||||
[uxbox.util.dom :as dom]))
|
[uxbox.util.dom :as dom]))
|
||||||
|
|
||||||
;; --- Refs
|
;; --- Refs
|
||||||
|
@ -37,12 +36,13 @@
|
||||||
|
|
||||||
;; --- Movement
|
;; --- Movement
|
||||||
|
|
||||||
|
;; TODO: implement in the same way as drawing (move under uxbox.main.data.workspace.)
|
||||||
|
|
||||||
(defn start-move
|
(defn start-move
|
||||||
[]
|
[]
|
||||||
(letfn [(on-move [shape delta]
|
(letfn [(on-move [shape delta]
|
||||||
(st/emit! (uds/apply-temporal-displacement shape delta)))
|
(st/emit! (uds/apply-temporal-displacement shape delta)))
|
||||||
(on-stop [{:keys [id] :as shape}]
|
(on-stop [{:keys [id] :as shape}]
|
||||||
(rlocks/release! :shape/move)
|
|
||||||
(st/emit! (uds/apply-displacement shape)))
|
(st/emit! (uds/apply-displacement shape)))
|
||||||
(on-start [shape]
|
(on-start [shape]
|
||||||
(let [stoper (->> streams/events
|
(let [stoper (->> streams/events
|
||||||
|
@ -55,7 +55,6 @@
|
||||||
(when @refs/selected-alignment
|
(when @refs/selected-alignment
|
||||||
(st/emit! (uds/initial-align-shape shape)))
|
(st/emit! (uds/initial-align-shape shape)))
|
||||||
(rx/subscribe stream on-move nil on-stop)))]
|
(rx/subscribe stream on-move nil on-stop)))]
|
||||||
(rlocks/acquire! :shape/move)
|
|
||||||
(run! on-start @selected-ref)))
|
(run! on-start @selected-ref)))
|
||||||
|
|
||||||
;; --- Events
|
;; --- Events
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
(mx/defc path-shape
|
(mx/defc path-shape
|
||||||
{:mixins [mx/static]}
|
{:mixins [mx/static]}
|
||||||
[{:keys [id tmp-resize-xform tmp-displacement rotation] :as shape}]
|
[{:keys [id tmp-resize-xform tmp-displacement rotation] :as shape}]
|
||||||
|
|
||||||
(let [shape (cond-> shape
|
(let [shape (cond-> shape
|
||||||
tmp-displacement (geom/transform (gmt/translate-matrix tmp-displacement))
|
tmp-displacement (geom/transform (gmt/translate-matrix tmp-displacement))
|
||||||
tmp-resize-xform (geom/transform tmp-resize-xform)
|
tmp-resize-xform (geom/transform tmp-resize-xform)
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
[uxbox.main.ui.shapes.common :as scommon]
|
[uxbox.main.ui.shapes.common :as scommon]
|
||||||
[uxbox.main.geom :as geom]
|
[uxbox.main.geom :as geom]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.util.rlocks :as rlocks]
|
|
||||||
[uxbox.util.geom.matrix :as gmt]
|
[uxbox.util.geom.matrix :as gmt]
|
||||||
[uxbox.util.geom.point :as gpt]
|
[uxbox.util.geom.point :as gpt]
|
||||||
[uxbox.util.dom :as dom]))
|
[uxbox.util.dom :as dom]))
|
||||||
|
@ -209,8 +208,7 @@
|
||||||
(apply st/emit! (sequence xf ids))))
|
(apply st/emit! (sequence xf ids))))
|
||||||
|
|
||||||
(on-end []
|
(on-end []
|
||||||
(apply st/emit! (map uds/apply-resize-matrix ids))
|
(apply st/emit! (map uds/apply-resize-matrix ids)))]
|
||||||
(rlocks/release! :shape/resize))]
|
|
||||||
|
|
||||||
(let [shape (->> (geom/shape->rect-shape shape)
|
(let [shape (->> (geom/shape->rect-shape shape)
|
||||||
(geom/size))
|
(geom/size))
|
||||||
|
@ -229,7 +227,6 @@
|
||||||
(rx/with-latest-from vector streams/mouse-position-ctrl)
|
(rx/with-latest-from vector streams/mouse-position-ctrl)
|
||||||
(rx/scan accumulate-width shape)
|
(rx/scan accumulate-width shape)
|
||||||
(rx/map (partial calculate-ratio shape)))]
|
(rx/map (partial calculate-ratio shape)))]
|
||||||
(rlocks/acquire! :shape/resize)
|
|
||||||
(rx/subscribe stream
|
(rx/subscribe stream
|
||||||
(partial on-resize shape)
|
(partial on-resize shape)
|
||||||
nil
|
nil
|
||||||
|
@ -316,27 +313,30 @@
|
||||||
|
|
||||||
;; --- Selection Handlers (Component)
|
;; --- 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
|
(defn start-path-edition
|
||||||
[shape-id index]
|
[shape-id index]
|
||||||
(letfn [(on-move [delta]
|
(letfn [(on-move [delta]
|
||||||
(st/emit! (uds/update-path shape-id index delta)))
|
(st/emit! (uds/update-path shape-id index delta)))]
|
||||||
(on-end []
|
(let [stoper (get-path-edition-stoper streams/events)
|
||||||
(rlocks/release! :shape/resize))]
|
|
||||||
(let [stoper (->> streams/events
|
|
||||||
(rx/map first)
|
|
||||||
(rx/filter #(= % :mouse/up))
|
|
||||||
(rx/take 1))
|
|
||||||
stream (rx/take-until stoper streams/mouse-position-deltas)]
|
stream (rx/take-until stoper streams/mouse-position-deltas)]
|
||||||
(rlocks/acquire! :shape/resize)
|
|
||||||
(when @refs/selected-alignment
|
(when @refs/selected-alignment
|
||||||
(st/emit! (uds/initial-path-point-align shape-id index)))
|
(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
|
(mx/defc path-edition-selection-handlers
|
||||||
[{:keys [id points] :as shape} zoom]
|
[{:keys [id points] :as shape} zoom]
|
||||||
(letfn [(on-mouse-down [index event]
|
(letfn [(on-mouse-down [index event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(rlocks/acquire! :shape/resize)
|
|
||||||
(start-path-edition id index))]
|
(start-path-edition id index))]
|
||||||
[:g.controls
|
[:g.controls
|
||||||
(for [[index {:keys [x y]}] (map-indexed vector points)]
|
(for [[index {:keys [x y]}] (map-indexed vector points)]
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
[uxbox.util.color :as color]
|
[uxbox.util.color :as color]
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.geom.matrix :as gmt]
|
[uxbox.util.geom.matrix :as gmt]
|
||||||
[uxbox.util.rlocks :as rlocks]
|
|
||||||
[uxbox.util.mixins :as mx :include-macros true])
|
[uxbox.util.mixins :as mx :include-macros true])
|
||||||
(:import goog.events.EventType))
|
(:import goog.events.EventType))
|
||||||
|
|
||||||
|
@ -48,6 +47,8 @@
|
||||||
(letfn [(on-mouse-down [event]
|
(letfn [(on-mouse-down [event]
|
||||||
(handle-mouse-down event shape selected))
|
(handle-mouse-down event shape selected))
|
||||||
(on-double-click [event]
|
(on-double-click [event]
|
||||||
|
;; TODO: handle grouping event propagation
|
||||||
|
;; TODO: handle actions locking properly
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(st/emit! (uds/start-edition-mode id)))]
|
(st/emit! (uds/start-edition-mode id)))]
|
||||||
[:g.shape {:class (when selected? "selected")
|
[:g.shape {:class (when selected? "selected")
|
||||||
|
@ -149,11 +150,9 @@
|
||||||
own))
|
own))
|
||||||
|
|
||||||
(mx/defc text-shape-wrapper
|
(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}
|
:did-remount text-shape-wrapper-did-remount}
|
||||||
[{:keys [id tmp-resize-xform tmp-displacement drawing?] :as shape}]
|
[{:keys [id tmp-resize-xform tmp-displacement drawing?] :as shape}]
|
||||||
(println "text-shape-wrapper" shape)
|
|
||||||
(let [xfmt (cond-> (gmt/matrix)
|
(let [xfmt (cond-> (gmt/matrix)
|
||||||
tmp-displacement (gmt/translate tmp-displacement)
|
tmp-displacement (gmt/translate tmp-displacement)
|
||||||
tmp-resize-xform (gmt/multiply tmp-resize-xform))
|
tmp-resize-xform (gmt/multiply tmp-resize-xform))
|
||||||
|
@ -182,7 +181,6 @@
|
||||||
(let [style (make-style shape)]
|
(let [style (make-style shape)]
|
||||||
[:div {:style style} content]))
|
[:div {:style style} content]))
|
||||||
|
|
||||||
|
|
||||||
;; --- Text Shape Html
|
;; --- Text Shape Html
|
||||||
|
|
||||||
(mx/defc text-shape
|
(mx/defc text-shape
|
||||||
|
|
|
@ -15,22 +15,21 @@
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.main.streams :as streams]
|
[uxbox.main.streams :as streams]
|
||||||
[uxbox.main.user-events :as uev]
|
[uxbox.main.user-events :as uev]
|
||||||
|
[uxbox.main.geom :as geom]
|
||||||
[uxbox.main.data.projects :as dp]
|
[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.data.shapes :as uds]
|
||||||
[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.selection :refer (selection-handlers)]
|
[uxbox.main.ui.shapes.selection :refer [selection-handlers]]
|
||||||
[uxbox.main.ui.workspace.scroll :as scroll]
|
[uxbox.main.ui.workspace.scroll :as scroll]
|
||||||
[uxbox.main.ui.workspace.drawarea :refer (draw-area)]
|
[uxbox.main.ui.workspace.drawarea :refer [draw-area]]
|
||||||
[uxbox.main.ui.workspace.ruler :refer (ruler)]
|
[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.grid :refer (grid)]
|
|
||||||
[uxbox.util.geom.point :as gpt]
|
[uxbox.util.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.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true])
|
||||||
[uxbox.util.rlocks :as rlocks])
|
|
||||||
(:import goog.events.EventType))
|
(:import goog.events.EventType))
|
||||||
|
|
||||||
;; --- Background
|
;; --- Background
|
||||||
|
@ -59,6 +58,23 @@
|
||||||
[:span {:alt "y"}
|
[:span {:alt "y"}
|
||||||
(str "Y: " (:y coords "-"))]]))
|
(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
|
;; --- Cursor tooltip
|
||||||
|
|
||||||
(defn- get-shape-tooltip
|
(defn- get-shape-tooltip
|
||||||
|
@ -137,12 +153,8 @@
|
||||||
:shift? shift?
|
:shift? shift?
|
||||||
:ctrl? ctrl?}]
|
:ctrl? ctrl?}]
|
||||||
(st/emit! (uev/keyboard-event :down key ctrl? shift?))
|
(st/emit! (uev/keyboard-event :down key ctrl? shift?))
|
||||||
|
|
||||||
;; TODO: remove (deprecated)
|
|
||||||
#_(rx/push! streams/events-b [:key/down opts])
|
|
||||||
(when (kbd/space? event)
|
(when (kbd/space? event)
|
||||||
(st/emit! (dw/start-viewport-positioning)))))
|
(st/emit! (udw/start-viewport-positioning)))))
|
||||||
#_(rlocks/acquire! :workspace/scroll)
|
|
||||||
|
|
||||||
(on-key-up [event]
|
(on-key-up [event]
|
||||||
(let [key (.-keyCode event)
|
(let [key (.-keyCode event)
|
||||||
|
@ -152,10 +164,8 @@
|
||||||
:shift? shift?
|
:shift? shift?
|
||||||
:ctrl? ctrl?}]
|
:ctrl? ctrl?}]
|
||||||
(when (kbd/space? event)
|
(when (kbd/space? event)
|
||||||
(st/emit! (dw/stop-viewport-positioning)))
|
(st/emit! (udw/stop-viewport-positioning)))
|
||||||
(st/emit! (uev/keyboard-event :up key ctrl? shift?))
|
(st/emit! (uev/keyboard-event :up key ctrl? shift?))))
|
||||||
;; TODO: remove (deprecated)
|
|
||||||
#_(rx/push! streams/events-b [:key/up opts])))
|
|
||||||
|
|
||||||
(on-mousemove [event]
|
(on-mousemove [event]
|
||||||
(let [wpt (gpt/point (.-clientX event)
|
(let [wpt (gpt/point (.-clientX event)
|
||||||
|
@ -169,9 +179,7 @@
|
||||||
:window-coords wpt
|
:window-coords wpt
|
||||||
:viewport-coords vpt
|
:viewport-coords vpt
|
||||||
:canvas-coords cpt}]
|
:canvas-coords cpt}]
|
||||||
(st/emit! (uev/pointer-event wpt vpt cpt ctrl? shift?))
|
(st/emit! (uev/pointer-event wpt vpt cpt ctrl? shift?))))]
|
||||||
;; TODO: remove (deprecated)
|
|
||||||
#_(rx/push! streams/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)
|
||||||
|
@ -207,15 +215,10 @@
|
||||||
shift? (kbd/shift? event)
|
shift? (kbd/shift? event)
|
||||||
opts {:shift? shift?
|
opts {:shift? shift?
|
||||||
:ctrl? ctrl?}]
|
:ctrl? ctrl?}]
|
||||||
(st/emit! (uev/mouse-event :down ctrl? shift?))
|
(st/emit! (uev/mouse-event :down ctrl? shift?)))
|
||||||
;; TODO: remove (deprecated)
|
(if-let [object (:drawing workspace)]
|
||||||
#_(rx/push! streams/events-b [:mouse/down opts]))
|
(st/emit! (udw/start-drawing object))
|
||||||
(if (:drawing workspace)
|
(st/emit! (udw/start-selrect))))
|
||||||
(rlocks/acquire! :ui/draw)
|
|
||||||
(do
|
|
||||||
(when (seq (:selected workspace))
|
|
||||||
(rlocks/release! :shape/edition))
|
|
||||||
(rlocks/acquire! :ui/selrect))))
|
|
||||||
(on-context-menu [event]
|
(on-context-menu [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
|
@ -223,36 +226,28 @@
|
||||||
shift? (kbd/shift? event)
|
shift? (kbd/shift? event)
|
||||||
opts {:shift? shift?
|
opts {:shift? shift?
|
||||||
:ctrl? ctrl?}]
|
:ctrl? ctrl?}]
|
||||||
(st/emit! (uev/mouse-event :context-menu ctrl? shift?))
|
(st/emit! (uev/mouse-event :context-menu ctrl? shift?))))
|
||||||
;; TODO: remove (deprecated)
|
|
||||||
#_(rx/push! streams/events-b [:mouse/right-click opts])))
|
|
||||||
(on-mouse-up [event]
|
(on-mouse-up [event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(let [ctrl? (kbd/ctrl? event)
|
(let [ctrl? (kbd/ctrl? event)
|
||||||
shift? (kbd/shift? event)
|
shift? (kbd/shift? event)
|
||||||
opts {:shift? shift?
|
opts {:shift? shift?
|
||||||
:ctrl? ctrl?}]
|
:ctrl? ctrl?}]
|
||||||
(st/emit! (uev/mouse-event :up ctrl? shift?))
|
(st/emit! (uev/mouse-event :up ctrl? shift?))))
|
||||||
;; TODO: remove (deprecated)
|
|
||||||
#_(rx/push! streams/events-b [:mouse/up])))
|
|
||||||
(on-click [event]
|
(on-click [event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(let [ctrl? (kbd/ctrl? event)
|
(let [ctrl? (kbd/ctrl? event)
|
||||||
shift? (kbd/shift? event)
|
shift? (kbd/shift? event)
|
||||||
opts {:shift? shift?
|
opts {:shift? shift?
|
||||||
:ctrl? ctrl?}]
|
:ctrl? ctrl?}]
|
||||||
(st/emit! (uev/mouse-event :click ctrl? shift?))
|
(st/emit! (uev/mouse-event :click ctrl? shift?))))
|
||||||
;; TODO: remove (deprecated)
|
|
||||||
#_(rx/push! streams/events-b [:mouse/click opts])))
|
|
||||||
(on-double-click [event]
|
(on-double-click [event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(let [ctrl? (kbd/ctrl? event)
|
(let [ctrl? (kbd/ctrl? event)
|
||||||
shift? (kbd/shift? event)
|
shift? (kbd/shift? event)
|
||||||
opts {:shift? shift?
|
opts {:shift? shift?
|
||||||
:ctrl? ctrl?}]
|
:ctrl? ctrl?}]
|
||||||
(st/emit! (uev/mouse-event :double-click ctrl? shift?))
|
(st/emit! (uev/mouse-event :double-click ctrl? shift?))))]
|
||||||
;; TODO: remove (deprecated)
|
|
||||||
#_(rx/push! streams/events-b [:mouse/double-click opts])))]
|
|
||||||
[:div
|
[:div
|
||||||
(coordinates)
|
(coordinates)
|
||||||
(when tooltip
|
(when tooltip
|
||||||
|
|
|
@ -8,14 +8,13 @@
|
||||||
"Draw interaction and component."
|
"Draw interaction and component."
|
||||||
(:require [beicon.core :as rx]
|
(:require [beicon.core :as rx]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
|
[lentes.core :as l]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.util.rlocks :as rlocks]
|
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.constants :as c]
|
[uxbox.main.constants :as c]
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.main.streams :as streams]
|
[uxbox.main.streams :as streams]
|
||||||
[uxbox.main.workers :as uwrk]
|
[uxbox.main.workers :as uwrk]
|
||||||
[uxbox.main.user-events :as uev]
|
|
||||||
[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.shapes :as shapes]
|
[uxbox.main.ui.shapes :as shapes]
|
||||||
|
@ -24,67 +23,36 @@
|
||||||
[uxbox.util.geom.path :as path]
|
[uxbox.util.geom.path :as path]
|
||||||
[uxbox.util.dom :as dom]))
|
[uxbox.util.dom :as dom]))
|
||||||
|
|
||||||
;; --- State
|
;; --- Refs
|
||||||
|
|
||||||
(defonce drawing-stoper (rx/subject))
|
(def drawing-shape
|
||||||
(defonce drawing-shape (atom nil))
|
(-> (l/key :drawing)
|
||||||
(defonce drawing-position (atom nil))
|
(l/derive refs/workspace)))
|
||||||
|
|
||||||
(def ^:private canvas-coords
|
;; --- Components
|
||||||
(gpt/point c/canvas-start-x
|
|
||||||
c/canvas-start-y))
|
|
||||||
|
|
||||||
;; --- Draw Area (Component)
|
(declare generic-draw-area)
|
||||||
|
(declare path-draw-area)
|
||||||
(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)
|
|
||||||
|
|
||||||
(mx/defc draw-area
|
(mx/defc draw-area
|
||||||
{:will-mount draw-area-will-mount
|
{:mixins [mx/static mx/reactive]}
|
||||||
:will-unmount draw-area-will-unmount
|
|
||||||
:mixins [mx/static mx/reactive]}
|
|
||||||
[]
|
[]
|
||||||
(let [shape (mx/react drawing-shape)
|
(when-let [shape (mx/react drawing-shape)]
|
||||||
position (mx/react drawing-position)]
|
(if (= (:type shape) :path)
|
||||||
(when shape
|
(path-draw-area shape)
|
||||||
(if (= (:type shape) :path)
|
(generic-draw-area shape))))
|
||||||
(path-shape-draw-area shape)
|
|
||||||
(generic-shape-draw-area shape position)))))
|
|
||||||
|
|
||||||
(mx/defc generic-shape-draw-area
|
(mx/defc generic-draw-area
|
||||||
[shape position]
|
[shape]
|
||||||
(if position
|
(-> (assoc shape :drawing? true)
|
||||||
(-> (assoc shape :drawing? true)
|
(shapes/render-component)))
|
||||||
(geom/resize position)
|
|
||||||
(shapes/render-component))
|
|
||||||
(-> (assoc shape :drawing? true)
|
|
||||||
(shapes/render-component))))
|
|
||||||
|
|
||||||
(mx/defc path-shape-draw-area
|
(mx/defc path-draw-area
|
||||||
[{:keys [points] :as shape}]
|
[{:keys [points] :as shape}]
|
||||||
(letfn [(on-click [event]
|
(letfn [(on-click [event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(swap! drawing-shape drop-last-point)
|
(st/emit! (udw/set-tooltip nil)
|
||||||
(st/emit! (udw/set-tooltip nil))
|
(udw/close-drawing-path)))
|
||||||
(rx/push! drawing-stoper true))
|
|
||||||
(on-mouse-enter [event]
|
(on-mouse-enter [event]
|
||||||
(st/emit! (udw/set-tooltip "Click to close the path")))
|
(st/emit! (udw/set-tooltip "Click to close the path")))
|
||||||
(on-mouse-leave [event]
|
(on-mouse-leave [event]
|
||||||
|
@ -93,227 +61,14 @@
|
||||||
(let [points (:points shape)
|
(let [points (:points shape)
|
||||||
points (vec (butlast points))]
|
points (vec (butlast points))]
|
||||||
(assoc shape :points points :close? true)))]
|
(assoc shape :points points :close? true)))]
|
||||||
(let [{:keys [x y]} (first points)]
|
(when-let [{:keys [x y]} (first points)]
|
||||||
[:g
|
[:g
|
||||||
(-> (assoc shape :drawing? true)
|
(-> (assoc shape :drawing? true)
|
||||||
(shapes/render-component))
|
(shapes/render-component))
|
||||||
(when-not (:free shape)
|
(when-not (:free shape)
|
||||||
[:circle.close-bezier {:cx x
|
[:circle.close-bezier {:cx x
|
||||||
:cy y
|
:cy y
|
||||||
:r 5
|
:r 5
|
||||||
:on-click on-click
|
:on-click on-click
|
||||||
:on-mouse-enter on-mouse-enter
|
:on-mouse-enter on-mouse-enter
|
||||||
:on-mouse-leave on-mouse-leave}])])))
|
: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))))
|
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.util.rlocks :as rlocks]
|
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.geom.point :as gpt]))
|
[uxbox.util.geom.point :as gpt]))
|
||||||
|
|
||||||
|
|
|
@ -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 <niwi@niwi.nz>
|
|
||||||
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
|
||||||
|
|
||||||
(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)))
|
|
|
@ -12,6 +12,7 @@
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.main.data.workspace :as dw]
|
[uxbox.main.data.workspace :as dw]
|
||||||
|
[uxbox.main.user-events :as uev]
|
||||||
[uxbox.builtins.icons :as i]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.util.i18n :refer (tr)]
|
[uxbox.util.i18n :refer (tr)]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.util.router :as r]
|
||||||
|
@ -82,7 +83,8 @@
|
||||||
|
|
||||||
(defn- select-for-draw
|
(defn- select-for-draw
|
||||||
[shape]
|
[shape]
|
||||||
(st/emit! (dw/select-for-drawing shape)))
|
(st/emit! ::uev/interrupt
|
||||||
|
(dw/select-for-drawing shape)))
|
||||||
|
|
||||||
(mx/defc draw-toolbox
|
(mx/defc draw-toolbox
|
||||||
{:mixins [mx/static mx/reactive]}
|
{:mixins [mx/static mx/reactive]}
|
||||||
|
|
|
@ -46,6 +46,11 @@
|
||||||
(and (mouse-event? v)
|
(and (mouse-event? v)
|
||||||
(= :up (:type v))))
|
(= :up (:type v))))
|
||||||
|
|
||||||
|
(defn mouse-click?
|
||||||
|
[v]
|
||||||
|
(and (mouse-event? v)
|
||||||
|
(= :click (:type v))))
|
||||||
|
|
||||||
;; --- Pointer Event
|
;; --- Pointer Event
|
||||||
|
|
||||||
(defrecord PointerEvent [window
|
(defrecord PointerEvent [window
|
||||||
|
|
|
@ -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 <niwi@niwi.nz>
|
|
||||||
|
|
||||||
(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])))
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue