0
Fork 0
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:
Andrey Antukh 2017-01-30 17:08:02 +01:00
parent bce58df413
commit b0de23b011
No known key found for this signature in database
GPG key ID: 4DFEBCB8316A8B95
18 changed files with 611 additions and 516 deletions

View file

@ -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

View file

@ -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

View file

@ -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]

View 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))))

View file

@ -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]))

View 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))))

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)]

View file

@ -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

View file

@ -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

View file

@ -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}])])))

View file

@ -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]))

View file

@ -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)))

View file

@ -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]}

View file

@ -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

View file

@ -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])))