mirror of
https://github.com/penpot/penpot.git
synced 2025-04-06 12:01:19 -05: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
|
||||
;; 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
|
||||
(: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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
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.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]))
|
||||
|
||||
|
|
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
|
||||
[{: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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}])])))
|
||||
|
|
|
@ -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]))
|
||||
|
||||
|
|
|
@ -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.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]}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Reference in a new issue