♻️ Completelly rewrite drawing mechanism.

This commit is contained in:
Andrey Antukh 2019-08-10 19:09:56 +00:00
parent 4cacb9f92c
commit f8ff79e23c
15 changed files with 935 additions and 623 deletions

View file

@ -7,7 +7,7 @@
environ/environ {:mvn/version "1.1.0"} environ/environ {:mvn/version "1.1.0"}
metosin/reitit-core {:mvn/version "0.3.9"} metosin/reitit-core {:mvn/version "0.3.9"}
funcool/beicon {:mvn/version "5.0.0"} funcool/beicon {:mvn/version "5.1.0-SNAPSHOT"}
funcool/cuerdas {:mvn/version "2.2.0"} funcool/cuerdas {:mvn/version "2.2.0"}
funcool/lentes {:mvn/version "1.3.0-SNAPSHOT"} funcool/lentes {:mvn/version "1.3.0-SNAPSHOT"}
funcool/potok {:mvn/version "2.3.0"} funcool/potok {:mvn/version "2.3.0"}
@ -19,7 +19,7 @@
{:dev {:extra-deps {com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"} {:dev {:extra-deps {com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}
com.bhauman/rebel-readline {:mvn/version "0.1.4"} com.bhauman/rebel-readline {:mvn/version "0.1.4"}
com.bhauman/figwheel-main {:mvn/version "0.2.3"} com.bhauman/figwheel-main {:mvn/version "0.2.3"}
org.clojure/tools.namespace {:mvn/version "0.3.0"}} org.clojure/tools.namespace {:mvn/version "0.3.1"}}
:extra-paths ["test"]} :extra-paths ["test"]}
:repl {:main-opts ["-m" "rebel-readline.main"]} :repl {:main-opts ["-m" "rebel-readline.main"]}

View file

@ -6,7 +6,6 @@
(ns uxbox.main.data.workspace (ns uxbox.main.data.workspace
(:require (:require
;; [uxbox.main.data.workspace.drawing :as wdrawing]
[beicon.core :as rx] [beicon.core :as rx]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[potok.core :as ptk] [potok.core :as ptk]
@ -25,8 +24,6 @@
[uxbox.main.lenses :as ul] [uxbox.main.lenses :as ul]
[uxbox.main.refs :as refs] [uxbox.main.refs :as refs]
[uxbox.main.store :as st] [uxbox.main.store :as st]
[uxbox.main.streams :as streams]
[uxbox.main.user-events :as uev]
[uxbox.main.workers :as uwrk] [uxbox.main.workers :as uwrk]
[uxbox.util.data :refer [dissoc-in]] [uxbox.util.data :refer [dissoc-in]]
[uxbox.util.data :refer [index-of]] [uxbox.util.data :refer [index-of]]
@ -368,7 +365,7 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(rx/just ::uev/interrupt))) (rx/just :interrupt)))
(defn deselect-all (defn deselect-all
"Clear all possible state of drawing, edition "Clear all possible state of drawing, edition
@ -626,40 +623,6 @@
{:pre [(uuid? id)]} {:pre [(uuid? id)]}
(ApplyResize. id)) (ApplyResize. id))
;; --- Shape Movement (by mouse)
(defrecord StartMove [id]
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
wst (get-in state [:workspace pid])
stoper (->> streams/events
(rx/filter uev/mouse-up?)
(rx/take 1))
stream (->> streams/mouse-position-deltas
(rx/take-until stoper))]
(rx/concat
(when (refs/alignment-activated? (:flags wst))
(rx/of (initial-shape-align id)))
(rx/map #(apply-temporal-displacement id %) stream)
(rx/of (apply-displacement id))))))
(defn start-move
[id]
{:pre [(uuid? id)]}
(StartMove. id))
(defrecord StartMoveSelected []
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
selected (get-in state [:workspace pid :selected])]
(rx/from-coll (map start-move selected)))))
(defn start-move-selected
[]
(StartMoveSelected.))
;; --- Start shape "edition mode" ;; --- Start shape "edition mode"
(defn start-edition-mode (defn start-edition-mode
@ -675,96 +638,36 @@
(watch [_ state stream] (watch [_ state stream]
(let [pid (get-in state [:workspace :current])] (let [pid (get-in state [:workspace :current])]
(->> stream (->> stream
(rx/filter #(= % ::uev/interrupt)) (rx/filter #(= % :interrupt))
(rx/take 1) (rx/take 1)
(rx/map (fn [_] #(dissoc-in % [:workspace pid :edition])))))))) (rx/map (fn [_] #(dissoc-in % [:workspace pid :edition]))))))))
;; --- Select for Drawing
(defn select-for-drawing
[shape]
(reify
ptk/UpdateEvent
(update [_ state]
(let [pid (get-in state [:workspace :current])
current (get-in state [:workspace pid :drawing-tool])]
(if (or (nil? shape)
(= shape current))
(update-in state [:workspace pid] dissoc :drawing :drawing-tool)
(update-in state [:workspace pid] assoc
:drawing shape
:drawing-tool shape))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Selection Rect Events ;; Selection Rect IMPL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare stop-selrect) ;; TODO: move to shapes impl maybe...
(declare update-selrect)
(declare get-selection-stoper)
(declare selection->rect)
(declare translate-to-canvas)
;; --- Start Selrect (defn selection->rect
(defrecord StartSelrect []
ptk/UpdateEvent
(update [_ state]
(let [id (get-in state [:workspace :current])
position (get-in state [:workspace :pointer :viewport])
selection {::start position ::stop position}]
(assoc-in state [:workspace id :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
(defrecord UpdateSelrect [position]
ptk/UpdateEvent
(update [_ state]
(let [id (get-in state [:workspace :current])]
(-> state
(assoc-in [:workspace id :selrect ::stop] position)
(update-in [:workspace id :selrect] selection->rect)))))
(defn update-selrect
[position]
{:pre [(gpt/point? position)]}
(UpdateSelrect. position))
;; --- Clear Selrect
(defrecord ClearSelrect []
ptk/UpdateEvent
(update [_ state]
(let [id (get-in state [:workspace :current])]
(update-in state [:workspace id] dissoc :selrect))))
(defn clear-selrect
[]
(ClearSelrect.))
;; --- Stop Selrect
(defrecord StopSelrect []
ptk/WatchEvent
(watch [_ state stream]
(let [id (get-in state [:workspace :current])
zoom (get-in state [:workspace id :zoom])
rect (-> (get-in state [:workspace id :selrect])
(translate-to-canvas zoom))]
(rx/of
(clear-selrect)
(deselect-all)
(select-shapes-by-selrect rect)))))
(defn stop-selrect
[]
(StopSelrect.))
;; --- Impl
(defn- selection->rect
[data] [data]
(let [start (::start data) (let [start (:start data)
stop (::stop data) stop (:stop data)
start-x (min (:x start) (:x stop)) start-x (min (:x start) (:x stop))
start-y (min (:y start) (:y stop)) start-y (min (:y start) (:y stop))
end-x (max (:x start) (:x stop)) end-x (max (:x start) (:x stop))
@ -776,13 +679,7 @@
:y2 end-y :y2 end-y
:type :rect))) :type :rect)))
(defn- get-selection-stoper (defn translate-to-canvas
[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." "Translate the given rect to the canvas coordinates system."
[rect zoom] [rect zoom]
(let [startx (* c/canvas-start-x zoom) (let [startx (* c/canvas-start-x zoom)

View file

@ -4,6 +4,8 @@
;; ;;
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz> ;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
;; TODO: DEPRECTATED, maintained just for temporal documentation, delete on near future
(ns uxbox.main.data.workspace-drawing (ns uxbox.main.data.workspace-drawing
"Workspace drawing data events and impl." "Workspace drawing data events and impl."
(:require [beicon.core :as rx] (:require [beicon.core :as rx]
@ -26,390 +28,359 @@
;; Data Events ;; Data Events
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; --- Select for Drawing
(deftype SelectForDrawing [shape]
ptk/UpdateEvent
(update [_ state]
(let [pid (get-in state [:workspace :current])
current (l/focus ul/selected-drawing state)]
(if (or (nil? shape)
(= shape current))
(update-in state [:workspace pid] dissoc :drawing :drawing-tool)
(update-in state [:workspace pid] assoc
:drawing shape
:drawing-tool shape)))))
(defn select-for-drawing
[shape]
(SelectForDrawing. shape))
;; --- Clear Drawing State
(deftype ClearDrawingState []
ptk/UpdateEvent
(update [_ state]
(let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid] dissoc :drawing-tool :drawing))))
(defn clear-drawing-state
[]
(ClearDrawingState.))
;; -- Start Drawing ;; -- Start Drawing
(declare on-init-draw) ;; (declare on-init-draw)
(deftype StartDrawing [id object] ;; (deftype StartDrawing [id object]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(update-in state [:workspace :drawing-lock] #(if (nil? %) id %))) ;; (update-in state [:workspace :drawing-lock] #(if (nil? %) id %)))
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state stream] ;; (watch [_ state stream]
(let [lock (get-in state [:workspace :drawing-lock])] ;; (let [lock (get-in state [:workspace :drawing-lock])]
(if (= lock id) ;; (if (= lock id)
(->> stream ;; (->> stream
(rx/filter #(= % ::uev/interrupt)) ;; (rx/filter #(= % :interrupt))
(rx/take 1) ;; (rx/take 1)
(rx/map (fn [_] #(update % :workspace dissoc :drawing-lock)))) ;; (rx/map (fn [_] #(update % :workspace dissoc :drawing-lock))))
(rx/empty)))) ;; (rx/empty))))
ptk/EffectEvent ;; ptk/EffectEvent
(effect [_ state stream] ;; (effect [_ state stream]
(let [lock (get-in state [:workspace :drawing-lock])] ;; (let [lock (get-in state [:workspace :drawing-lock])]
(when (= lock id) ;; (when (= lock id)
(on-init-draw object stream))))) ;; (on-init-draw object stream)))))
(defn start-drawing ;; (defn start-drawing
[object] ;; [object]
(let [id (gensym "drawing")] ;; (let [id (gensym "drawing")]
(StartDrawing. id object))) ;; (StartDrawing. id object)))
;; --- Initialize Draw Area ;; ;; --- Initialize Draw Area
(deftype InitializeDrawing [point] ;; (deftype InitializeDrawing [point]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [pid (get-in state [:workspace :current]) ;; (let [pid (get-in state [:workspace :current])
shape (get-in state [:workspace pid :drawing]) ;; shape (get-in state [:workspace pid :drawing])
shape (geom/setup shape {:x1 (:x point) ;; shape (geom/setup shape {:x1 (:x point)
:y1 (:y point) ;; :y1 (:y point)
:x2 (+ (:x point) 2) ;; :x2 (+ (:x point) 2)
:y2 (+ (:y point) 2)})] ;; :y2 (+ (:y point) 2)})]
(assoc-in state [:workspace pid :drawing] shape)))) ;; (assoc-in state [:workspace pid :drawing] shape))))
(defn initialize-drawing ;; (defn initialize-drawing
[point] ;; [point]
{:pre [(gpt/point? point)]} ;; {:pre [(gpt/point? point)]}
(InitializeDrawing. point)) ;; (InitializeDrawing. point))
;; --- Update Draw Area State ;; ;; --- Update Draw Area State
(deftype UpdateDrawing [position lock?] ;; (deftype UpdateDrawing [position lock?]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [pid (get-in state [:workspace :current]) ;; (let [pid (get-in state [:workspace :current])
{:keys [id] :as shape} (-> (get-in state [:workspace pid :drawing]) ;; {:keys [id] :as shape} (-> (get-in state [:workspace pid :drawing])
(geom/shape->rect-shape) ;; (geom/shape->rect-shape)
(geom/size)) ;; (geom/size))
result (geom/resize-shape :bottom-right shape position lock?) ;; result (geom/resize-shape :bottom-right shape position lock?)
scale (geom/calculate-scale-ratio shape result) ;; scale (geom/calculate-scale-ratio shape result)
resize-mtx (geom/generate-resize-matrix :bottom-right shape scale)] ;; resize-mtx (geom/generate-resize-matrix :bottom-right shape scale)]
(assoc-in state [:workspace pid :modifiers id] {:resize resize-mtx})))) ;; (assoc-in state [:workspace pid :modifiers id] {:resize resize-mtx}))))
(defn update-drawing ;; (defn update-drawing
[position lock?] ;; [position lock?]
{:pre [(gpt/point? position) (boolean? lock?)]} ;; {:pre [(gpt/point? position) (boolean? lock?)]}
(UpdateDrawing. position lock?)) ;; (UpdateDrawing. position lock?))
;; --- Finish Drawin ;; ;; --- Finish Drawin
(deftype FinishDrawing [] ;; (deftype FinishDrawing []
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state stream] ;; (watch [_ state stream]
(let [pid (get-in state [:workspace :current]) ;; (let [pid (get-in state [:workspace :current])
{:keys [id] :as shape} (get-in state [:workspace pid :drawing]) ;; {:keys [id] :as shape} (get-in state [:workspace pid :drawing])
resize-mtx (get-in state [:workspace pid :modifiers id :resize]) ;; resize-mtx (get-in state [:workspace pid :modifiers id :resize])
shape (cond-> shape ;; shape (cond-> shape
resize-mtx (geom/transform resize-mtx))] ;; resize-mtx (geom/transform resize-mtx))]
(prn "finish-drawing" shape) ;; (prn "finish-drawing" shape)
(if-not shape ;; (if-not shape
(rx/empty) ;; (rx/empty)
(rx/of (clear-drawing-state) ;; (rx/of (clear-drawing-state)
(uds/add-shape shape) ;; (uds/add-shape shape)
(udw/select-first-shape) ;; (udw/select-first-shape)
::uev/interrupt))))) ;; :interrupt)))))
(defn finish-drawing ;; (defn finish-drawing
[] ;; []
(FinishDrawing.)) ;; (FinishDrawing.))
;; --- Finish Path Drawing ;; ;; --- Finish Path Drawing
(deftype FinishPathDrawing [] ;; (deftype FinishPathDrawing []
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [pid (get-in state [:workspace :current])] ;; (let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing :segments] #(vec (butlast %)))))) ;; (update-in state [:workspace pid :drawing :segments] #(vec (butlast %))))))
(defn finish-path-drawing ;; (defn finish-path-drawing
[] ;; []
(FinishPathDrawing.)) ;; (FinishPathDrawing.))
;; --- Insert Drawing Path Point ;; ;; --- Insert Drawing Path Point
(deftype InsertDrawingPathPoint [point] ;; (deftype InsertDrawingPathPoint [point]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [pid (get-in state [:workspace :current])] ;; (let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing :segments] (fnil conj []) point)))) ;; (update-in state [:workspace pid :drawing :segments] (fnil conj []) point))))
(defn insert-drawing-path-point ;; (defn insert-drawing-path-point
[point] ;; [point]
{:pre [(gpt/point? point)]} ;; {:pre [(gpt/point? point)]}
(InsertDrawingPathPoint. point)) ;; (InsertDrawingPathPoint. point))
;; --- Update Drawing Path Point ;; ;; --- Update Drawing Path Point
(deftype UpdateDrawingPathPoint [index point] ;; (deftype UpdateDrawingPathPoint [index point]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [pid (get-in state [:workspace :current]) ;; (let [pid (get-in state [:workspace :current])
segments (count (get-in state [:workspace pid :drawing :segments])) ;; segments (count (get-in state [:workspace pid :drawing :segments]))
exists? (< -1 index segments)] ;; exists? (< -1 index segments)]
(cond-> state ;; (cond-> state
exists? (assoc-in [:workspace pid :drawing :segments index] point))))) ;; exists? (assoc-in [:workspace pid :drawing :segments index] point)))))
(defn update-drawing-path-point ;; (defn update-drawing-path-point
[index point] ;; [index point]
{:pre [(integer? index) (gpt/point? point)]} ;; {:pre [(integer? index) (gpt/point? point)]}
(UpdateDrawingPathPoint. index point)) ;; (UpdateDrawingPathPoint. index point))
;; --- Close Drawing Path ;; ;; --- Close Drawing Path
(deftype CloseDrawingPath [] ;; (deftype CloseDrawingPath []
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [pid (get-in state [:workspace :current])] ;; (let [pid (get-in state [:workspace :current])]
(assoc-in state [:workspace pid :drawing :close?] true))) ;; (assoc-in state [:workspace pid :drawing :close?] true)))
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state stream] ;; (watch [_ state stream]
(rx/of ::uev/interrupt))) ;; (rx/of :interrupt)))
(defn close-drawing-path ;; (defn close-drawing-path
[] ;; []
(CloseDrawingPath.)) ;; (CloseDrawingPath.))
;; --- Simplify Drawing Path ;; ;; --- Simplify Drawing Path
(deftype SimplifyDrawingPath [tolerance] ;; (deftype SimplifyDrawingPath [tolerance]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [pid (get-in state [:workspace :current])] ;; (let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing :segments] pth/simplify tolerance)))) ;; (update-in state [:workspace pid :drawing :segments] pth/simplify tolerance))))
(defn simplify-drawing-path ;; (defn simplify-drawing-path
[tolerance] ;; [tolerance]
{:pre [(number? tolerance)]} ;; {:pre [(number? tolerance)]}
(SimplifyDrawingPath. tolerance)) ;; (SimplifyDrawingPath. tolerance))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Drawing Implementation ;; ;; Drawing Implementation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:private canvas-coords ;; (def ^:private canvas-coords
(gpt/point c/canvas-start-x ;; (gpt/point c/canvas-start-x
c/canvas-start-y)) ;; c/canvas-start-y))
(defn- conditional-align ;; (defn- conditional-align
[point] ;; [point]
(if @refs/selected-alignment ;; (if @refs/selected-alignment
(uwrk/align-point point) ;; (uwrk/align-point point)
(rx/of point))) ;; (rx/of point)))
(defn- translate-to-canvas ;; (defn- translate-to-canvas
[point] ;; [point]
(-> point ;; (-> point
(gpt/subtract (gpt/multiply canvas-coords @refs/selected-zoom)) ;; (gpt/subtract (gpt/multiply canvas-coords @refs/selected-zoom))
(gpt/divide @refs/selected-zoom))) ;; (gpt/divide @refs/selected-zoom)))
(declare on-init-draw-icon) ;; (declare on-init-draw-icon)
(declare on-init-draw-image) ;; (declare on-init-draw-image)
(declare on-init-draw-path) ;; (declare on-init-draw-path)
(declare on-init-draw-free-path) ;; (declare on-init-draw-free-path)
(declare on-init-draw-generic) ;; (declare on-init-draw-generic)
(defn- on-init-draw ;; (defn- on-init-draw
"Function execution when draw shape operation is requested. ;; "Function execution when draw shape operation is requested.
This is a entry point for the draw interaction." ;; This is a entry point for the draw interaction."
[shape stream] ;; [shape stream]
(let [stoper (->> stream ;; (let [stoper (->> stream
(rx/filter #(= % ::uev/interrupt)) ;; (rx/filter #(= % :interrupt))
(rx/take 1))] ;; (rx/take 1))]
(case (:type shape) ;; (case (:type shape)
:icon (on-init-draw-icon shape) ;; :icon (on-init-draw-icon shape)
:image (on-init-draw-image shape) ;; :image (on-init-draw-image shape)
:path (if (:free shape) ;; :path (if (:free shape)
(on-init-draw-free-path shape stoper) ;; (on-init-draw-free-path shape stoper)
(on-init-draw-path shape stoper)) ;; (on-init-draw-path shape stoper))
(on-init-draw-generic shape stoper)))) ;; (on-init-draw-generic shape stoper))))
(defn- on-init-draw-generic ;; (defn- on-init-draw-generic
[shape stoper] ;; [shape stoper]
(let [stoper (rx/merge stoper (->> streams/events ;; (let [stoper (rx/merge stoper (->> streams/events
(rx/filter uev/mouse-up?) ;; (rx/filter uev/mouse-up?)
(rx/take 1))) ;; (rx/take 1)))
start? (volatile! true) ;; start? (volatile! true)
mouse (->> streams/viewport-mouse-position ;; mouse (->> streams/viewport-mouse-position
(rx/take-until stoper) ;; (rx/take-until stoper)
(rx/mapcat conditional-align) ;; (rx/mapcat conditional-align)
(rx/map translate-to-canvas) ;; (rx/map translate-to-canvas)
(rx/with-latest vector streams/mouse-position-ctrl))] ;; (rx/with-latest vector streams/mouse-position-ctrl))]
(letfn [(on-position [[point ctrl?]] ;; (letfn [(on-position [[point ctrl?]]
(if @start? ;; (if @start?
(do ;; (do
(st/emit! (initialize-drawing point)) ;; (st/emit! (initialize-drawing point))
(vreset! start? false)) ;; (vreset! start? false))
(st/emit! (update-drawing point ctrl?)))) ;; (st/emit! (update-drawing point ctrl?))))
(on-finish [] ;; (on-finish []
(if @start? ;; (if @start?
(st/emit! ::uev/interrupt) ;; (st/emit! ::uev/interrupt)
(st/emit! (finish-drawing))))] ;; (st/emit! (finish-drawing))))]
(rx/subscribe mouse on-position nil on-finish)))) ;; (rx/subscribe mouse on-position nil on-finish))))
(defn- on-init-draw-icon ;; (defn- on-init-draw-icon
[{:keys [metadata] :as shape}] ;; [{:keys [metadata] :as shape}]
(let [{:keys [x y]} (gpt/divide @refs/canvas-mouse-position ;; (let [{:keys [x y]} (gpt/divide @refs/canvas-mouse-position
@refs/selected-zoom) ;; @refs/selected-zoom)
{:keys [width height]} metadata ;; {:keys [width height]} metadata
proportion (/ width height) ;; proportion (/ width height)
props {:x1 x ;; props {:x1 x
:y1 y ;; :y1 y
:x2 (+ x 200) ;; :x2 (+ x 200)
:y2 (+ y (/ 200 proportion))} ;; :y2 (+ y (/ 200 proportion))}
shape (geom/setup shape props)] ;; shape (geom/setup shape props)]
(st/emit! (uds/add-shape shape) ;; (st/emit! (uds/add-shape shape)
(udw/select-first-shape) ;; (udw/select-first-shape)
(select-for-drawing nil) ;; (select-for-drawing nil)
::uev/interrupt))) ;; ::uev/interrupt)))
(defn- on-init-draw-image ;; (defn- on-init-draw-image
[{:keys [metadata] :as shape}] ;; [{:keys [metadata] :as shape}]
(let [{:keys [x y]} (gpt/divide @refs/canvas-mouse-position ;; (let [{:keys [x y]} (gpt/divide @refs/canvas-mouse-position
@refs/selected-zoom) ;; @refs/selected-zoom)
{:keys [width height]} metadata ;; {:keys [width height]} metadata
proportion (/ width height) ;; proportion (/ width height)
props {:x1 x ;; props {:x1 x
:y1 y ;; :y1 y
:x2 (+ x width) ;; :x2 (+ x width)
:y2 (+ y height)} ;; :y2 (+ y height)}
shape (geom/setup shape props)] ;; shape (geom/setup shape props)]
(st/emit! (uds/add-shape shape) ;; (st/emit! (uds/add-shape shape)
(udw/select-first-shape) ;; (udw/select-first-shape)
(select-for-drawing nil) ;; (select-for-drawing nil)
::uev/interrupt))) ;; ::uev/interrupt)))
(def ^:private immanted-zones ;; (def ^:private immanted-zones
(let [transform #(vector (- % 7) (+ % 7) %)] ;; (let [transform #(vector (- % 7) (+ % 7) %)]
(concat ;; (concat
(mapv transform (range 0 181 15)) ;; (mapv transform (range 0 181 15))
(mapv (comp transform -) (range 0 181 15))))) ;; (mapv (comp transform -) (range 0 181 15)))))
(defn- align-position ;; (defn- align-position
[angle pos] ;; [angle pos]
(reduce (fn [pos [a1 a2 v]] ;; (reduce (fn [pos [a1 a2 v]]
(if (< a1 angle a2) ;; (if (< a1 angle a2)
(reduced (gpt/update-angle pos v)) ;; (reduced (gpt/update-angle pos v))
pos)) ;; pos))
pos ;; pos
immanted-zones)) ;; immanted-zones))
(defn- get-path-stoper-stream ;; (defn- get-path-stoper-stream
([stoper] (get-path-stoper-stream stoper false)) ;; ([stoper] (get-path-stoper-stream stoper false))
([stoper mouseup?] ;; ([stoper mouseup?]
(letfn [(stoper-event? [{:keys [type shift] :as event}] ;; (letfn [(stoper-event? [{:keys [type shift] :as event}]
(or (and (uev/mouse-event? event) ;; (or (and (uev/mouse-event? event)
(or (and (= type :double-click) shift) ;; (or (and (= type :double-click) shift)
(= type :context-menu) ;; (= type :context-menu)
(and mouseup? (= type :up)))) ;; (and mouseup? (= type :up))))
(and (uev/keyboard-event? event) ;; (and (uev/keyboard-event? event)
(= type :down) ;; (= type :down)
(= 13 (:key event)))))] ;; (= 13 (:key event)))))]
(->> (rx/filter stoper-event? streams/events) ;; (->> (rx/filter stoper-event? streams/events)
(rx/merge stoper) ;; (rx/merge stoper)
(rx/take 1) ;; (rx/take 1)
(rx/share))))) ;; (rx/share)))))
(defn- get-path-point-stream ;; (defn- get-path-point-stream
[] ;; []
(->> streams/events ;; (->> streams/events
(rx/filter uev/mouse-click?) ;; (rx/filter uev/mouse-click?)
(rx/filter #(false? (:shift %))))) ;; (rx/filter #(false? (:shift %)))))
(defn- on-init-draw-free-path ;; (defn- on-init-draw-free-path
[shape stoper] ;; [shape stoper]
(let [stoper (get-path-stoper-stream stoper true) ;; (let [stoper (get-path-stoper-stream stoper true)
mouse (->> streams/viewport-mouse-position ;; mouse (->> streams/viewport-mouse-position
(rx/mapcat conditional-align) ;; (rx/mapcat conditional-align)
(rx/map translate-to-canvas)) ;; (rx/map translate-to-canvas))
stream (rx/take-until stoper mouse)] ;; stream (rx/take-until stoper mouse)]
(letfn [(on-draw [point] ;; (letfn [(on-draw [point]
(st/emit! (insert-drawing-path-point point))) ;; (st/emit! (insert-drawing-path-point point)))
(on-end [] ;; (on-end []
(st/emit! (simplify-drawing-path 0.3) ;; (st/emit! (simplify-drawing-path 0.3)
(finish-drawing)))] ;; (finish-drawing)))]
(rx/subscribe stream on-draw nil on-end)))) ;; (rx/subscribe stream on-draw nil on-end))))
(defn- on-init-draw-path ;; (defn- on-init-draw-path
[shape stoper] ;; [shape stoper]
(let [last-point (volatile! @refs/canvas-mouse-position) ;; (let [last-point (volatile! @refs/canvas-mouse-position)
stoper (get-path-stoper-stream stoper) ;; stoper (get-path-stoper-stream stoper)
mouse (->> (rx/sample 10 streams/viewport-mouse-position) ;; mouse (->> (rx/sample 10 streams/viewport-mouse-position)
(rx/mapcat conditional-align) ;; (rx/mapcat conditional-align)
(rx/map translate-to-canvas)) ;; (rx/map translate-to-canvas))
points (->> (get-path-point-stream) ;; points (->> (get-path-point-stream)
(rx/with-latest vector mouse) ;; (rx/with-latest vector mouse)
(rx/map second) ;; (rx/map second)
(rx/take-until stoper)) ;; (rx/take-until stoper))
counter (rx/merge (rx/scan #(inc %) 1 points) (rx/of 1)) ;; counter (rx/merge (rx/scan #(inc %) 1 points) (rx/of 1))
stream (->> mouse ;; stream (->> mouse
(rx/with-latest vector streams/mouse-position-ctrl) ;; (rx/with-latest vector streams/mouse-position-ctrl)
(rx/with-latest vector counter) ;; (rx/with-latest vector counter)
(rx/map flatten) ;; (rx/map flatten)
(rx/take-until stoper))] ;; (rx/take-until stoper))]
(letfn [(on-point [point] ;; (letfn [(on-point [point]
(vreset! last-point point) ;; (vreset! last-point point)
(st/emit! (insert-drawing-path-point point))) ;; (st/emit! (insert-drawing-path-point point)))
(on-generic-draw [point counter] ;; (on-generic-draw [point counter]
(st/emit! (update-drawing-path-point counter point))) ;; (st/emit! (update-drawing-path-point counter point)))
(on-assisted-draw [point counter] ;; (on-assisted-draw [point counter]
(let [point (as-> point $ ;; (let [point (as-> point $
(gpt/subtract $ @last-point) ;; (gpt/subtract $ @last-point)
(align-position (gpt/angle $) $) ;; (align-position (gpt/angle $) $)
(gpt/add $ @last-point))] ;; (gpt/add $ @last-point))]
(st/emit! (update-drawing-path-point counter point)))) ;; (st/emit! (update-drawing-path-point counter point))))
(on-draw [[point ctrl? counter]] ;; (on-draw [[point ctrl? counter]]
(if ctrl? ;; (if ctrl?
(on-assisted-draw point counter) ;; (on-assisted-draw point counter)
(on-generic-draw point counter))) ;; (on-generic-draw point counter)))
(on-finish [] ;; (on-finish []
(st/emit! (finish-path-drawing) ;; (st/emit! (finish-path-drawing)
(finish-drawing)))] ;; (finish-drawing)))]
;; Initialize path drawing ;; ;; Initialize path drawing
(st/emit! (insert-drawing-path-point @last-point) ;; (st/emit! (insert-drawing-path-point @last-point)
(insert-drawing-path-point @last-point)) ;; (insert-drawing-path-point @last-point))
(rx/subscribe points on-point) ;; (rx/subscribe points on-point)
(rx/subscribe stream on-draw nil on-finish)))) ;; (rx/subscribe stream on-draw nil on-finish))))

View file

@ -409,19 +409,15 @@
(declare setup-rect) (declare setup-rect)
(declare setup-image) (declare setup-image)
(declare setup-circle) (declare setup-circle)
(declare setup-group)
(defn setup (defn setup
"A function that initializes the first coordinates for "A function that initializes the first coordinates for
the shape. Used mainly for draw operations." the shape. Used mainly for draw operations."
[shape props] [shape props]
(case (:type shape) (case (:type shape)
:rect (setup-rect shape props)
:icon (setup-rect shape props)
:image (setup-image shape props) :image (setup-image shape props)
:text (setup-rect shape props)
:circle (setup-circle shape props) :circle (setup-circle shape props)
:group (setup-group shape props))) (setup-rect shape props)))
(defn- setup-rect (defn- setup-rect
"A specialized function for setup rect-like shapes." "A specialized function for setup rect-like shapes."
@ -432,11 +428,6 @@
:x2 x2 :x2 x2
:y2 y2)) :y2 y2))
(defn- setup-group
"A specialized function for setup group shapes."
[shape {:keys [x1 y1 x2 y2] :as props}]
(assoc shape :initial props))
(defn- setup-circle (defn- setup-circle
"A specialized function for setup circle shapes." "A specialized function for setup circle shapes."
[shape {:keys [x1 y1 x2 y2]}] [shape {:keys [x1 y1 x2 y2]}]
@ -447,8 +438,8 @@
:ry (mth/abs (- y2 y1)))) :ry (mth/abs (- y2 y1))))
(defn- setup-image (defn- setup-image
[{:keys [view-box] :as shape} {:keys [x1 y1 x2 y2] :as props}] [{:keys [metadata] :as shape} {:keys [x1 y1 x2 y2] :as props}]
(let [[_ _ width height] view-box] (let [{:keys [width height]} metadata]
(assoc shape (assoc shape
:x1 x1 :x1 x1
:y1 y1 :y1 y1
@ -470,7 +461,6 @@
(case type (case type
:circle (circle->rect-shape state shape) :circle (circle->rect-shape state shape)
:path (path->rect-shape state shape) :path (path->rect-shape state shape)
:group (group->rect-shape state shape)
shape))) shape)))
(defn shapes->rect-shape (defn shapes->rect-shape
@ -488,13 +478,6 @@
:y2 maxy :y2 maxy
:type :rect}))) :type :rect})))
(defn- group->rect-shape
[state {:keys [id items rotation] :as group}]
(let [shapes (map #(get-in state [:shapes %]) items)]
(-> (shapes->rect-shape state shapes)
(assoc :rotation rotation)
(assoc :id id))))
(defn- path->rect-shape (defn- path->rect-shape
[state {:keys [segments] :as shape}] [state {:keys [segments] :as shape}]
(let [minx (apply min (map :x segments)) (let [minx (apply min (map :x segments))
@ -606,32 +589,13 @@
([shape] ([shape]
(selection-rect @st/state shape)) (selection-rect @st/state shape))
([state shape] ([state shape]
(case (:type shape) (let [{:keys [displacement resize]} (:modifiers shape)]
:group (selection-rect-group state shape) (-> (shape->rect-shape shape)
(selection-rect-generic state shape)))) (assoc :type :rect :id (:id shape))
(transform (or resize (gmt/matrix)))
(defn- selection-rect-generic (transform (or displacement (gmt/matrix)))
[state {:keys [id modifiers] :as shape}] (rotate-shape)
(let [{:keys [displacement resize]} modifiers] (size)))))
(-> (shape->rect-shape shape)
(assoc :type :rect :id id)
(transform (or resize (gmt/matrix)))
(transform (or displacement (gmt/matrix)))
(rotate-shape)
(size))))
(defn- selection-rect-group
[state {:keys [id group items modifiers] :as shape}]
(let [{:keys [displacement resize]} modifiers
shapes (->> items
(map #(get-in state [:shapes %]))
(map #(selection-rect state %)))]
(-> (shapes->rect-shape shapes)
(assoc :id id)
(transform (or resize (gmt/matrix)))
(transform (or displacement (gmt/matrix)))
(rotate-shape)
(size))))
;; --- Helpers ;; --- Helpers

View file

@ -6,13 +6,45 @@
(ns uxbox.main.ui.shapes.common (ns uxbox.main.ui.shapes.common
(:require (:require
[uxbox.main.data.workspace :as udw] [potok.core :as ptk]
[beicon.core :as rx]
[uxbox.main.data.workspace :as dw]
[uxbox.main.refs :as refs] [uxbox.main.refs :as refs]
[uxbox.main.store :as st] [uxbox.main.store :as st]
[uxbox.main.ui.keyboard :as kbd] [uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.workspace.streams :as ws]
[uxbox.util.dom :as dom])) [uxbox.util.dom :as dom]))
;; --- Events ;; --- Shape Movement (by mouse)
(defn start-move
[id]
{:pre [(uuid? id)]}
(reify
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
wst (get-in state [:workspace pid])
stoper (->> ws/interaction-events
(rx/filter ws/mouse-up?)
(rx/take 1))
stream (->> ws/mouse-position-deltas
(rx/take-until stoper))]
(rx/concat
(when (refs/alignment-activated? (:flags wst))
(rx/of (dw/initial-shape-align id)))
(rx/map #(dw/apply-temporal-displacement id %) stream)
(rx/of (dw/apply-displacement id)))))))
(defn start-move-selected
[]
(reify
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
selected (get-in state [:workspace pid :selected])]
(rx/from-coll (map start-move selected))))))
(defn on-mouse-down (defn on-mouse-down
[event {:keys [id group] :as shape} selected] [event {:keys [id group] :as shape} selected]
@ -26,18 +58,18 @@
(and (not selected?) (empty? selected)) (and (not selected?) (empty? selected))
(do (do
(dom/stop-propagation event) (dom/stop-propagation event)
(st/emit! (udw/select-shape id) (st/emit! (dw/select-shape id)
(udw/start-move-selected))) (start-move-selected)))
(and (not selected?) (not (empty? selected))) (and (not selected?) (not (empty? selected)))
(do (do
(dom/stop-propagation event) (dom/stop-propagation event)
(if (kbd/shift? event) (if (kbd/shift? event)
(st/emit! (udw/select-shape id)) (st/emit! (dw/select-shape id))
(st/emit! (udw/deselect-all) (st/emit! (dw/deselect-all)
(udw/select-shape id) (dw/select-shape id)
(udw/start-move-selected)))) (start-move-selected))))
:else :else
(do (do
(dom/stop-propagation event) (dom/stop-propagation event)
(st/emit! (udw/start-move-selected))))))) (st/emit! (start-move-selected)))))))

View file

@ -18,7 +18,6 @@
[uxbox.main.data.workspace :as dw] [uxbox.main.data.workspace :as dw]
[uxbox.main.refs :as refs] [uxbox.main.refs :as refs]
[uxbox.main.store :as st] [uxbox.main.store :as st]
[uxbox.main.streams :as streams]
[uxbox.main.ui.confirm] [uxbox.main.ui.confirm]
[uxbox.main.ui.keyboard :as kbd] [uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.messages :refer [messages-widget]] [uxbox.main.ui.messages :refer [messages-widget]]
@ -32,7 +31,7 @@
[uxbox.main.ui.workspace.shortcuts :as shortcuts] [uxbox.main.ui.workspace.shortcuts :as shortcuts]
[uxbox.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]] [uxbox.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]]
[uxbox.main.ui.workspace.sidebar.history :refer [history-dialog]] [uxbox.main.ui.workspace.sidebar.history :refer [history-dialog]]
[uxbox.main.user-events :as uev] [uxbox.main.ui.workspace.streams :as ws]
[uxbox.util.data :refer [classnames]] [uxbox.util.data :refer [classnames]]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt] [uxbox.util.geom.point :as gpt]
@ -42,10 +41,11 @@
(defn- on-scroll (defn- on-scroll
[event] [event]
(let [target (.-target event) ;; TODO: refactor
#_(let [target (.-target event)
top (.-scrollTop target) top (.-scrollTop target)
left (.-scrollLeft target)] left (.-scrollLeft target)]
(st/emit! (uev/scroll-event (gpt/point left top))))) (ws/emit! (ws/scroll-event (gpt/point left top)))))
(defn- on-wheel (defn- on-wheel
[event canvas] [event canvas]

View file

@ -7,17 +7,298 @@
(ns uxbox.main.ui.workspace.drawarea (ns uxbox.main.ui.workspace.drawarea
"Draw interaction and component." "Draw interaction and component."
(:require (:require
[beicon.core :as rx]
[potok.core :as ptk]
[rumext.alpha :as mf] [rumext.alpha :as mf]
[uxbox.main.data.workspace :as udw] [uxbox.main.constants :as c]
[uxbox.main.data.workspace-drawing :as udwd] [uxbox.main.data.workspace :as dw]
[uxbox.main.data.shapes :as ds]
[uxbox.main.geom :as geom] [uxbox.main.geom :as geom]
[uxbox.main.refs :as refs]
[uxbox.main.store :as st] [uxbox.main.store :as st]
[uxbox.main.streams :as streams]
[uxbox.main.ui.shapes :as shapes] [uxbox.main.ui.shapes :as shapes]
[uxbox.main.ui.workspace.streams :as uws]
[uxbox.main.workers :as uwrk]
[uxbox.util.math :as mth]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.util.geom.path :as path] [uxbox.util.geom.path :as path]
[uxbox.util.geom.point :as gpt])) [uxbox.util.geom.point :as gpt]))
(def ^:private canvas-coords
(gpt/point c/canvas-start-x
c/canvas-start-y))
;; --- Events
(declare handle-drawing)
(declare handle-drawing-generic)
(declare handle-drawing-path)
(declare handle-drawing-free-path)
(declare handle-finish-drawing)
(defn start-drawing
[object]
(let [id (gensym "drawing")]
(reify
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)
(rx/merge (->> stream
(rx/filter #(= % handle-finish-drawing))
(rx/take 1)
(rx/map (fn [_] #(update % :workspace dissoc :drawing-lock))))
(rx/of (handle-drawing object)))
(rx/empty)))))))
(defn- translate-to-canvas [point zoom]
(-> point
(gpt/subtract (gpt/multiply canvas-coords zoom))
(gpt/divide zoom)))
(defn- conditional-align [point align?]
(if align?
(uwrk/align-point point)
(rx/of point)))
;; TODO: maybe this should be a simple function
(defn handle-drawing
[shape]
(reify
ptk/WatchEvent
(watch [_ state stream]
(rx/of
(if (= :path (:type shape))
(if (:free shape)
(handle-drawing-free-path shape)
(handle-drawing-path shape))
(handle-drawing-generic shape))))))
(defn- handle-drawing-generic
[shape]
(letfn [(initialize-drawing [state point]
(let [pid (get-in state [:workspace :current])
shape (get-in state [:workspace pid :drawing])
shape (geom/setup shape {:x1 (:x point)
:y1 (:y point)
:x2 (+ (:x point) 2)
:y2 (+ (:y point) 2)})]
(assoc-in state [:workspace pid :drawing] (assoc shape ::initialized? true))))
;; TODO: this is a new approach for resizing, when all the
;; subsystem are migrated to the new resize approach, this
;; function should be moved into uxbox.main.geom ns.
(resize-shape [shape point lock?]
(if (= (:type shape) :circle)
(let [rx (mth/abs (- (:x point) (:cx shape)))
ry (mth/abs (- (:y point) (:cy shape)))]
(if lock?
(assoc shape :rx rx :ry ry)
(assoc shape :rx rx :ry rx)))
(let [width (- (:x point) (:x1 shape))
height (- (:y point) (:y1 shape))
proportion (:proportion shape 1)]
(assoc shape
:x2 (+ (:x1 shape) width)
:y2 (if lock?
(+ (:y1 shape) (/ width proportion))
(+ (:y1 shape) height))))))
(update-drawing [state point lock?]
(let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing] resize-shape point lock?)))]
(reify
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
zoom (get-in state [:workspace pid :zoom])
flags (get-in state [:workspace pid :flags])
align? (refs/alignment-activated? flags)
stoper (->> (rx/filter #(or (uws/mouse-up? %) (= % :interrupt)) stream)
(rx/take 1))
mouse (->> uws/viewport-mouse-position
(rx/mapcat #(conditional-align % align?))
(rx/map #(translate-to-canvas % zoom))
(rx/with-latest vector uws/mouse-position-ctrl))]
(rx/concat
(rx/of #(initialize-drawing % @uws/canvas-mouse-position))
(->> mouse
(rx/map (fn [[pt ctrl?]] #(update-drawing % pt ctrl?)))
(rx/take-until stoper))
(rx/of handle-finish-drawing)))))))
(defn handle-drawing-path
[shape]
(letfn [(stoper-event? [{:keys [type shift] :as event}]
(or (= event :interrupt)
(and (uws/mouse-event? event)
(or (and (= type :double-click) shift)
(= type :context-menu)))
(and (uws/keyboard-event? event)
(= type :down)
(= 13 (:key event)))))
(initialize-drawing [state point]
(let [pid (get-in state [:workspace :current])]
(-> state
(assoc-in [:workspace pid :drawing :segments] [point point])
(assoc-in [:workspace pid :drawing ::initialized?] true))))
(insert-point-segment [state point]
(let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing :segments] (fnil conj []) point)))
(update-point-segment [state index point]
(let [pid (get-in state [:workspace :current])
segments (count (get-in state [:workspace pid :drawing :segments]))
exists? (< -1 index segments)]
(cond-> state
exists? (assoc-in [:workspace pid :drawing :segments index] point))))
(remove-dangling-segmnet [state]
(let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing :segments] #(vec (butlast %)))))]
(reify
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
zoom (get-in state [:workspace pid :zoom])
flags (get-in state [:workspace pid :flags])
align? (refs/alignment-activated? flags)
last-point (volatile! @uws/canvas-mouse-position)
stoper (->> (rx/filter stoper-event? stream)
(rx/take 1))
mouse (->> (rx/sample 10 uws/viewport-mouse-position)
(rx/mapcat #(conditional-align % align?))
(rx/map #(translate-to-canvas % zoom)))
points (->> stream
(rx/filter uws/mouse-click?)
(rx/filter #(false? (:shift %)))
(rx/with-latest vector mouse)
(rx/map second))
counter (rx/merge (rx/scan #(inc %) 1 points) (rx/of 1))
stream' (->> mouse
(rx/with-latest vector uws/mouse-position-ctrl)
(rx/with-latest vector counter)
(rx/map flatten))
imm-transform #(vector (- % 7) (+ % 7) %)
immanted-zones (vec (concat
(map imm-transform (range 0 181 15))
(map (comp imm-transform -) (range 0 181 15))))
align-position (fn [angle pos]
(reduce (fn [pos [a1 a2 v]]
(if (< a1 angle a2)
(reduced (gpt/update-angle pos v))
pos))
pos
immanted-zones))]
(rx/merge
(rx/of #(initialize-drawing % @last-point))
(->> points
(rx/take-until stoper)
(rx/map (fn [pt]
#(insert-point-segment % pt))))
(rx/concat
(->> stream'
(rx/map (fn [[point ctrl? index :as xxx]]
(let [point (if ctrl?
(as-> point $
(gpt/subtract $ @last-point)
(align-position (gpt/angle $) $)
(gpt/add $ @last-point))
point)]
#(update-point-segment % index point))))
(rx/take-until stoper))
(rx/of remove-dangling-segmnet
handle-finish-drawing))))))))
(defn- handle-drawing-free-path
[shape]
(letfn [(stoper-event? [{:keys [type shift] :as event}]
(or (= event :interrupt)
(and (uws/mouse-event? event) (= type :up))))
(initialize-drawing [state]
(let [pid (get-in state [:workspace :current])]
(assoc-in state [:workspace pid :drawing ::initialized?] true)))
(insert-point-segment [state point]
(let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing :segments] (fnil conj []) point)))
(simplify-drawing-path [state tolerance]
(let [pid (get-in state [:workspace :current])]
(update-in state [:workspace pid :drawing :segments] path/simplify tolerance)))]
(reify
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
zoom (get-in state [:workspace pid :zoom])
flags (get-in state [:workspace pid :flags])
align? (refs/alignment-activated? flags)
stoper (->> (rx/filter stoper-event? stream)
(rx/take 1))
mouse (->> (rx/sample 10 uws/viewport-mouse-position)
(rx/mapcat #(conditional-align % align?))
(rx/map #(translate-to-canvas % zoom)))]
(rx/concat
(rx/of initialize-drawing)
(->> mouse
(rx/map (fn [pt] #(insert-point-segment % pt)))
(rx/take-until stoper))
(rx/of #(simplify-drawing-path % 0.3)
handle-finish-drawing)))))))
(def handle-finish-drawing
(reify
ptk/WatchEvent
(watch [_ state stream]
(let [pid (get-in state [:workspace :current])
shape (get-in state [:workspace pid :drawing])]
(if (::initialized? shape)
(let [resize-mtx (get-in state [:workspace pid :modifiers (:id shape) :resize])
shape (cond-> shape
resize-mtx (geom/transform resize-mtx))]
(rx/of
;; Remove the stalled modifiers
;; TODO: maybe a specific event for "clear modifiers"
#(update-in % [:workspace pid :modifiers] dissoc (:id shape))
;; Unselect the drawing tool
#(update-in % [:workspace pid] dissoc :drawing :drawing-tool)
;; Add & select the cred shape to the workspace
(ds/add-shape shape)
(dw/select-first-shape)))
(rx/of #(update-in % [:workspace pid] dissoc :drawing :drawing-tool)))))))
(def close-drawing-path
(reify
ptk/UpdateEvent
(update [_ state]
(let [pid (get-in state [:workspace :current])]
(assoc-in state [:workspace pid :drawing :close?] true)))))
;; --- Components ;; --- Components
(declare generic-draw-area) (declare generic-draw-area)
@ -47,12 +328,13 @@
[{:keys [shape] :as props}] [{:keys [shape] :as props}]
(letfn [(on-click [event] (letfn [(on-click [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(st/emit! (udw/set-tooltip nil) (st/emit! (dw/set-tooltip nil)
(udwd/close-drawing-path))) close-drawing-path
:interrupt))
(on-mouse-enter [event] (on-mouse-enter [event]
(st/emit! (udw/set-tooltip "Click to close the path"))) (st/emit! (dw/set-tooltip "Click to close the path")))
(on-mouse-leave [event] (on-mouse-leave [event]
(st/emit! (udw/set-tooltip nil)))] (st/emit! (dw/set-tooltip nil)))]
(when-let [{:keys [x y] :as segment} (first (:segments shape))] (when-let [{:keys [x y] :as segment} (first (:segments shape))]
[:g [:g
(shapes/render-shape shape) (shapes/render-shape shape)

View file

@ -12,7 +12,7 @@
[rumext.core :as mx] [rumext.core :as mx]
[uxbox.builtins.icons :as i] [uxbox.builtins.icons :as i]
[uxbox.main.data.images :as udi] [uxbox.main.data.images :as udi]
[uxbox.main.data.workspace-drawing :as udwd] [uxbox.main.data.workspace :as dw]
[uxbox.main.store :as st] [uxbox.main.store :as st]
[uxbox.main.ui.modal :as modal] [uxbox.main.ui.modal :as modal]
[uxbox.util.data :refer [read-string jscoll->vec]] [uxbox.util.data :refer [read-string jscoll->vec]]
@ -54,7 +54,7 @@
:metadata {:width width :metadata {:width width
:height height} :height height}
:image id}] :image id}]
(st/emit! (udwd/select-for-drawing shape)) (st/emit! (dw/select-for-drawing shape))
(modal/hide!))) (modal/hide!)))
(on-files-selected [event] (on-files-selected [event]
@ -99,7 +99,7 @@
:metadata {:width (:width image) :metadata {:width (:width image)
:height (:height image)} :height (:height image)}
:image (:id image)}] :image (:id image)}]
(st/emit! (udwd/select-for-drawing shape)) (st/emit! (dw/select-for-drawing shape))
(modal/hide!)))] (modal/hide!)))]
[:div.library-item {:on-click on-click} [:div.library-item {:on-click on-click}
[:div.library-item-th [:div.library-item-th

View file

@ -11,7 +11,7 @@
[uxbox.main.constants :as c] [uxbox.main.constants :as c]
[uxbox.main.data.workspace :as udw] [uxbox.main.data.workspace :as udw]
[uxbox.main.store :as st] [uxbox.main.store :as st]
[uxbox.main.user-events :as uev] [uxbox.main.ui.workspace.streams :as ws]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt] [uxbox.util.geom.point :as gpt]
[uxbox.util.math :as mth])) [uxbox.util.math :as mth]))
@ -56,14 +56,14 @@
[{:keys [ruler zoom] :as props}] [{:keys [ruler zoom] :as props}]
(letfn [(on-mouse-down [event] (letfn [(on-mouse-down [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(st/emit! ::uev/interrupt (st/emit! :interrupt
(udw/set-tooltip nil) (udw/set-tooltip nil)
(udw/start-ruler))) (udw/start-ruler)))
(on-mouse-up [event] (on-mouse-up [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(st/emit! ::uev/interrupt)) (st/emit! :interrupt))
(on-unmount [] (on-unmount []
(st/emit! ::uev/interrupt (st/emit! :interrupt
(udw/clear-ruler)))] (udw/clear-ruler)))]
(mf/use-effect {:end on-unmount}) (mf/use-effect {:end on-unmount})
[:svg {:on-mouse-down on-mouse-down [:svg {:on-mouse-down on-mouse-down

View file

@ -17,8 +17,7 @@
[uxbox.main.geom :as geom] [uxbox.main.geom :as geom]
[uxbox.main.refs :as refs] [uxbox.main.refs :as refs]
[uxbox.main.store :as st] [uxbox.main.store :as st]
[uxbox.main.streams :as streams] [uxbox.main.ui.workspace.streams :as ws]
[uxbox.main.user-events :as uev]
[uxbox.main.workers :as uwrk] [uxbox.main.workers :as uwrk]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt])) [uxbox.util.geom.point :as gpt]))
@ -69,14 +68,14 @@
(let [shape (->> (geom/shape->rect-shape shape) (let [shape (->> (geom/shape->rect-shape shape)
(geom/size)) (geom/size))
stoper (->> streams/events stoper (->> ws/interaction-events
(rx/filter uev/mouse-up?) (rx/filter ws/mouse-up?)
(rx/take 1)) (rx/take 1))
stream (->> streams/canvas-mouse-position stream (->> ws/canvas-mouse-position
(rx/take-until stoper) (rx/take-until stoper)
(rx/map apply-zoom) (rx/map apply-zoom)
(rx/mapcat apply-grid-alignment) (rx/mapcat apply-grid-alignment)
(rx/with-latest vector streams/mouse-position-ctrl) (rx/with-latest vector ws/mouse-position-ctrl)
(rx/map normalize-proportion-lock))] (rx/map normalize-proportion-lock))]
(rx/subscribe stream (partial on-resize shape) nil on-end)))) (rx/subscribe stream (partial on-resize shape) nil on-end))))
@ -161,18 +160,18 @@
(letfn [(on-mouse-down [event index] (letfn [(on-mouse-down [event index]
(dom/stop-propagation event) (dom/stop-propagation event)
(let [stoper (get-edition-stream-stoper streams/events) (let [stoper (get-edition-stream-stoper ws/interaction-events)
stream (rx/take-until stoper streams/mouse-position-deltas)] stream (rx/take-until stoper ws/mouse-position-deltas)]
(when @refs/selected-alignment (when @refs/selected-alignment
(st/emit! (uds/initial-path-point-align (:id shape) index))) (st/emit! (uds/initial-path-point-align (:id shape) index)))
(rx/subscribe stream #(on-handler-move % index)))) (rx/subscribe stream #(on-handler-move % index))))
(get-edition-stream-stoper [stream] (get-edition-stream-stoper [stream]
(let [stoper? #(and (uev/mouse-event? %) (= (:type %) :up))] (let [stoper? #(and (ws/mouse-event? %) (= (:type %) :up))]
(rx/merge (rx/merge
(rx/filter stoper? stream) (rx/filter stoper? stream)
(->> stream (->> stream
(rx/filter #(= % ::uev/interrupt)) (rx/filter #(= % :interrupt))
(rx/take 1))))) (rx/take 1)))))
(on-handler-move [delta index] (on-handler-move [delta index]

View file

@ -10,9 +10,8 @@
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk] [potok.core :as ptk]
[uxbox.main.store :as st] [uxbox.main.store :as st]
[uxbox.main.data.lightbox :as udl] [uxbox.main.data.lightbox :as dl]
[uxbox.main.data.workspace :as udw] [uxbox.main.data.workspace :as dw]
[uxbox.main.data.workspace-drawing :as udwd]
[uxbox.main.data.shapes :as uds] [uxbox.main.data.shapes :as uds]
[uxbox.main.data.undo :as udu] [uxbox.main.data.undo :as udu]
[uxbox.main.data.history :as udh] [uxbox.main.data.history :as udh]
@ -27,39 +26,39 @@
;; --- Shortcuts ;; --- Shortcuts
(defonce +shortcuts+ (defonce +shortcuts+
{:shift+g #(st/emit! (udw/toggle-flag :grid)) {:shift+g #(st/emit! (dw/toggle-flag :grid))
:ctrl+g #(st/emit! (uds/group-selected)) :ctrl+g #(st/emit! (uds/group-selected))
:ctrl+shift+g #(st/emit! (uds/ungroup-selected)) :ctrl+shift+g #(st/emit! (uds/ungroup-selected))
:ctrl+shift+m #(st/emit! (udw/toggle-flag :sitemap)) :ctrl+shift+m #(st/emit! (dw/toggle-flag :sitemap))
:ctrl+shift+f #(st/emit! (udw/toggle-flag :drawtools)) :ctrl+shift+f #(st/emit! (dw/toggle-flag :drawtools))
:ctrl+shift+i #(st/emit! (udw/toggle-flag :icons)) :ctrl+shift+i #(st/emit! (dw/toggle-flag :icons))
:ctrl+shift+l #(st/emit! (udw/toggle-flag :layers)) :ctrl+shift+l #(st/emit! (dw/toggle-flag :layers))
:ctrl+0 #(st/emit! (udw/reset-zoom)) :ctrl+0 #(st/emit! (dw/reset-zoom))
:ctrl+r #(st/emit! (udw/toggle-flag :ruler)) :ctrl+r #(st/emit! (dw/toggle-flag :ruler))
:ctrl+d #(st/emit! (uds/duplicate-selected)) :ctrl+d #(st/emit! (uds/duplicate-selected))
:ctrl+c #(st/emit! (udw/copy-to-clipboard)) :ctrl+c #(st/emit! (dw/copy-to-clipboard))
:ctrl+v #(st/emit! (udw/paste-from-clipboard)) :ctrl+v #(st/emit! (dw/paste-from-clipboard))
:ctrl+shift+v #(udl/open! :clipboard) :ctrl+shift+v #(dl/open! :clipboard)
:ctrl+z #(st/emit! (udu/undo)) :ctrl+z #(st/emit! (udu/undo))
:ctrl+shift+z #(st/emit! (udu/redo)) :ctrl+shift+z #(st/emit! (udu/redo))
:ctrl+y #(st/emit! (udu/redo)) :ctrl+y #(st/emit! (udu/redo))
:ctrl+b #(st/emit! (udwd/select-for-drawing wsd/+draw-tool-rect+)) :ctrl+b #(st/emit! (dw/select-for-drawing wsd/+draw-tool-rect+))
:ctrl+e #(st/emit! (udwd/select-for-drawing wsd/+draw-tool-circle+)) :ctrl+e #(st/emit! (dw/select-for-drawing wsd/+draw-tool-circle+))
:ctrl+t #(st/emit! (udwd/select-for-drawing wsd/+draw-tool-text+)) :ctrl+t #(st/emit! (dw/select-for-drawing wsd/+draw-tool-text+))
:esc #(st/emit! (udw/deselect-all)) :esc #(st/emit! (dw/deselect-all))
:delete #(st/emit! (udw/delete-selected)) :delete #(st/emit! (dw/delete-selected))
:ctrl+up #(st/emit! (udw/move-selected-layer :up)) :ctrl+up #(st/emit! (dw/move-selected-layer :up))
:ctrl+down #(st/emit! (udw/move-selected-layer :down)) :ctrl+down #(st/emit! (dw/move-selected-layer :down))
:ctrl+shift+up #(st/emit! (udw/move-selected-layer :top)) :ctrl+shift+up #(st/emit! (dw/move-selected-layer :top))
:ctrl+shift+down #(st/emit! (udw/move-selected-layer :bottom)) :ctrl+shift+down #(st/emit! (dw/move-selected-layer :bottom))
:shift+up #(st/emit! (udw/move-selected :up :fast)) :shift+up #(st/emit! (dw/move-selected :up :fast))
:shift+down #(st/emit! (udw/move-selected :down :fast)) :shift+down #(st/emit! (dw/move-selected :down :fast))
:shift+right #(st/emit! (udw/move-selected :right :fast)) :shift+right #(st/emit! (dw/move-selected :right :fast))
:shift+left #(st/emit! (udw/move-selected :left :fast)) :shift+left #(st/emit! (dw/move-selected :left :fast))
:up #(st/emit! (udw/move-selected :up :std)) :up #(st/emit! (dw/move-selected :up :std))
:down #(st/emit! (udw/move-selected :down :std)) :down #(st/emit! (dw/move-selected :down :std))
:right #(st/emit! (udw/move-selected :right :std)) :right #(st/emit! (dw/move-selected :right :std))
:left #(st/emit! (udw/move-selected :left :std)) :left #(st/emit! (dw/move-selected :left :std))
}) })
;; --- Shortcuts Setup Functions ;; --- Shortcuts Setup Functions

View file

@ -9,13 +9,10 @@
(:require (:require
[rumext.alpha :as mf] [rumext.alpha :as mf]
[uxbox.builtins.icons :as i] [uxbox.builtins.icons :as i]
[uxbox.main.data.shapes :as uds] [uxbox.main.data.workspace :as dw]
[uxbox.main.data.workspace :as udw]
[uxbox.main.data.workspace-drawing :as udwd]
[uxbox.main.refs :as refs] [uxbox.main.refs :as refs]
[uxbox.main.store :as st] [uxbox.main.store :as st]
[uxbox.main.user-events :as uev] [uxbox.util.i18n :refer [tr]]
[uxbox.util.i18n :refer (tr)]
[uxbox.util.uuid :as uuid])) [uxbox.util.uuid :as uuid]))
;; --- Constants ;; --- Constants
@ -81,17 +78,17 @@
(mf/defc draw-toolbox (mf/defc draw-toolbox
{:wrap [mf/wrap-memo]} {:wrap [mf/wrap-memo]}
[{:keys [flags] :as props}] [{:keys [flags] :as props}]
(let [close #(st/emit! (udw/toggle-flag :drawtools)) (let [close #(st/emit! (dw/toggle-flag :drawtools))
dtool (mf/deref refs/selected-drawing-tool) dtool (mf/deref refs/selected-drawing-tool)
tools (->> (into [] +draw-tools+) tools (->> (into [] +draw-tools+)
(sort-by (comp :priority second))) (sort-by (comp :priority second)))
select-drawtool #(st/emit! ::uev/interrupt select-drawtool #(st/emit! :interrupt
(udw/deactivate-ruler) (dw/deactivate-ruler)
(udwd/select-for-drawing %)) (dw/select-for-drawing %))
toggle-ruler #(st/emit! (udwd/select-for-drawing nil) toggle-ruler #(st/emit! (dw/select-for-drawing nil)
(uds/deselect-all) (dw/deselect-all)
(udw/toggle-ruler))] (dw/toggle-ruler))]
[:div#form-tools.tool-window.drawing-tools [:div#form-tools.tool-window.drawing-tools
[:div.tool-window-bar [:div.tool-window-bar

View file

@ -6,20 +6,19 @@
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.sidebar.icons (ns uxbox.main.ui.workspace.sidebar.icons
(:require [lentes.core :as l] (:require
[uxbox.util.router :as r] [lentes.core :as l]
[potok.core :as ptk] [rumext.core :as mx]
[uxbox.main.store :as st] [uxbox.builtins.icons :as i]
[uxbox.main.lenses :as ul] [uxbox.main.data.icons :as udi]
[uxbox.main.data.workspace :as udw] [uxbox.main.data.workspace :as dw]
[uxbox.main.data.workspace-drawing :as udwd] [uxbox.main.lenses :as ul]
[uxbox.main.data.icons :as udi] [uxbox.main.store :as st]
[uxbox.main.ui.shapes.icon :as icon] [uxbox.main.ui.dashboard.icons :as icons]
[uxbox.main.ui.dashboard.icons :as icons] [uxbox.main.ui.shapes.icon :as icon]
[uxbox.builtins.icons :as i] [uxbox.util.data :refer (read-string)]
[rumext.core :as mx :include-macros true] [uxbox.util.dom :as dom]
[uxbox.util.dom :as dom] [uxbox.util.router :as r]))
[uxbox.util.data :refer (read-string)]))
;; --- Refs ;; --- Refs
@ -43,7 +42,7 @@
(defn- icons-toolbox-init (defn- icons-toolbox-init
[own] [own]
(st/emit! (udw/initialize-icons-toolbox)) (st/emit! (dw/initialize-icons-toolbox))
own) own)
(mx/defc icons-toolbox (mx/defc icons-toolbox
@ -60,13 +59,13 @@
icons (->> (vals (mx/react icons/icons-ref)) icons (->> (vals (mx/react icons/icons-ref))
(filter #(= (:id selected-coll) (:collection %))))] (filter #(= (:id selected-coll) (:collection %))))]
(letfn [(on-close [event] (letfn [(on-close [event]
(st/emit! (udw/toggle-flag :icons))) (st/emit! (dw/toggle-flag :icons)))
(on-select [icon event] (on-select [icon event]
(st/emit! (udwd/select-for-drawing icon))) (st/emit! (dw/select-for-drawing icon)))
(on-change [event] (on-change [event]
(let [value (read-string (dom/event->value event))] (let [value (read-string (dom/event->value event))]
(st/emit! (udwd/select-for-drawing nil) (st/emit! (dw/select-for-drawing nil)
(udw/select-icons-toolbox-collection value))))] (dw/select-icons-toolbox-collection value))))]
[:div#form-figures.tool-window [:div#form-figures.tool-window
[:div.tool-window-bar [:div.tool-window-bar
[:div.tool-window-icon i/icon-set] [:div.tool-window-icon i/icon-set]

View file

@ -0,0 +1,128 @@
;; 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) 2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.ui.workspace.streams
"User interaction events and streams."
(:require
[beicon.core :as rx]
[uxbox.main.store :as st]
[uxbox.main.refs :as refs]
[uxbox.main.workers :as uwrk]
[uxbox.util.geom.point :as gpt]))
;; --- User Events
(defrecord KeyboardEvent [type key shift ctrl])
(defn keyboard-event
[type key ctrl shift]
{:pre [(keyword? type)
(integer? key)
(boolean? ctrl)
(boolean? shift)]}
(KeyboardEvent. type key ctrl shift))
(defn keyboard-event?
[v]
(instance? KeyboardEvent v))
(defrecord MouseEvent [type ctrl shift])
(defn mouse-event
[type ctrl shift]
{:pre [(keyword? type)
(boolean? ctrl)
(boolean? shift)]}
(MouseEvent. type ctrl shift))
(defn mouse-event?
[v]
(instance? MouseEvent v))
(defn mouse-up?
[v]
(and (mouse-event? v)
(= :up (:type v))))
(defn mouse-click?
[v]
(and (mouse-event? v)
(= :click (:type v))))
(defrecord PointerEvent [window
viewport
canvas
ctrl
shift])
(defn pointer-event
[window viewport canvas ctrl shift]
{:pre [(gpt/point? window)
(gpt/point? viewport)
(or (gpt/point? canvas)
(nil? canvas))
(boolean? ctrl)
(boolean? shift)]}
(PointerEvent. window
viewport
canvas
ctrl
shift))
(defn pointer-event?
[v]
(instance? PointerEvent v))
;; --- Derived streams
(defn interaction-event?
[event]
(or (keyboard-event? event)
(mouse-event? event)))
;; TODO: this shoul be DEPRECATED
(defonce interaction-events
(rx/filter interaction-event? st/stream))
(defonce mouse-position
(rx/filter pointer-event? st/stream))
(defonce canvas-mouse-position
(let [sub (rx/behavior-subject nil)]
(-> (rx/map :canvas mouse-position)
(rx/subscribe-with sub))
sub))
(defonce viewport-mouse-position
(let [sub (rx/behavior-subject nil)]
(-> (rx/map :viewport mouse-position)
(rx/subscribe-with sub))
sub))
(defonce window-mouse-position
(let [sub (rx/behavior-subject nil)]
(-> (rx/map :window mouse-position)
(rx/subscribe-with sub))
sub))
(defonce mouse-position-ctrl
(let [sub (rx/behavior-subject nil)]
(-> (rx/map :ctrl mouse-position)
(rx/subscribe-with sub))
sub))
(defonce mouse-position-deltas
(->> viewport-mouse-position
(rx/sample 10)
(rx/map #(gpt/divide % @refs/selected-zoom))
(rx/mapcat (fn [point]
(if @refs/selected-alignment
(uwrk/align-point point)
(rx/of point))))
(rx/buffer 2 1)
(rx/map (fn [[old new]]
(gpt/subtract new old)))
(rx/share)))

View file

@ -7,11 +7,12 @@
(ns uxbox.main.ui.workspace.viewport (ns uxbox.main.ui.workspace.viewport
(:require (:require
[beicon.core :as rx]
[goog.events :as events] [goog.events :as events]
[potok.core :as ptk]
[rumext.alpha :as mf] [rumext.alpha :as mf]
[uxbox.main.constants :as c] [uxbox.main.constants :as c]
[uxbox.main.data.workspace :as udw] [uxbox.main.data.workspace :as dw]
[uxbox.main.data.workspace-drawing :as udwd]
[uxbox.main.geom :as geom] [uxbox.main.geom :as geom]
[uxbox.main.refs :as refs] [uxbox.main.refs :as refs]
[uxbox.main.store :as st] [uxbox.main.store :as st]
@ -19,7 +20,8 @@
[uxbox.main.ui.workspace.canvas :refer [canvas]] [uxbox.main.ui.workspace.canvas :refer [canvas]]
[uxbox.main.ui.workspace.grid :refer [grid]] [uxbox.main.ui.workspace.grid :refer [grid]]
[uxbox.main.ui.workspace.ruler :refer [ruler]] [uxbox.main.ui.workspace.ruler :refer [ruler]]
[uxbox.main.user-events :as uev] [uxbox.main.ui.workspace.streams :as uws]
[uxbox.main.ui.workspace.drawarea :refer [start-drawing]]
[uxbox.util.data :refer [parse-int]] [uxbox.util.data :refer [parse-int]]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt]) [uxbox.util.geom.point :as gpt])
@ -67,11 +69,53 @@
;; --- Selection Rect ;; --- Selection Rect
(defn stop-selrect
[]
(letfn [(clear-state [state]
(prn "clear-state")
(let [id (get-in state [:workspace :current])]
(update-in state [:workspace id] dissoc :selrect)))]
(reify
ptk/WatchEvent
(watch [_ state stream]
(let [id (get-in state [:workspace :current])
zoom (get-in state [:workspace id :zoom])
rect (some-> (get-in state [:workspace id :selrect])
(dw/translate-to-canvas zoom))]
(if rect
(rx/of clear-state
(dw/deselect-all)
(dw/select-shapes-by-selrect rect))
(rx/of (dw/deselect-all))))))))
(defn start-selrect
[]
(letfn [(update-state [state position]
(let [id (get-in state [:workspace :current])
selrect (get-in state [:workspace id :selrect])]
(if selrect
(assoc-in state [:workspace id :selrect] (dw/selection->rect (assoc selrect :stop position)))
(assoc-in state [:workspace id :selrect] (dw/selection->rect {:start position :stop position})))))
(selection-stoper [stream]
(->> (rx/merge (rx/filter #(= % :interrupt) stream)
(rx/filter uws/mouse-up? stream))
(rx/take 1)))]
(reify
ptk/WatchEvent
(watch [_ state stream]
(rx/concat
(->> uws/viewport-mouse-position
(rx/take-until (selection-stoper stream))
(rx/map (fn [pos] #(update-state % pos))))
(rx/of (stop-selrect)))))))
(mf/defc selrect (mf/defc selrect
{:wrap [mf/wrap-memo]} {:wrap [mf/wrap-memo]}
[{rect :value}] [{:keys [data] :as props}]
(when rect (when data
(let [{:keys [x1 y1 width height]} (geom/size rect)] (let [{:keys [x1 y1 width height]} (geom/size data)]
[:rect.selection-rect [:rect.selection-rect
{:x x1 {:x x1
:y y1 :y y1
@ -112,9 +156,9 @@
opts {:key key opts {:key key
:shift? shift? :shift? shift?
:ctrl? ctrl?}] :ctrl? ctrl?}]
(st/emit! (uev/keyboard-event :down key ctrl? shift?)) (st/emit! (uws/keyboard-event :down key ctrl? shift?))
(when (kbd/space? event) (when (kbd/space? event)
(st/emit! (udw/start-viewport-positioning))))) (st/emit! (dw/start-viewport-positioning)))))
(on-key-up [event] (on-key-up [event]
(let [key (.-keyCode event) (let [key (.-keyCode event)
@ -124,8 +168,8 @@
:shift? shift? :shift? shift?
:ctrl? ctrl?}] :ctrl? ctrl?}]
(when (kbd/space? event) (when (kbd/space? event)
(st/emit! (udw/stop-viewport-positioning))) (st/emit! (dw/stop-viewport-positioning)))
(st/emit! (uev/keyboard-event :up key ctrl? shift?)))) (st/emit! (uws/keyboard-event :up key ctrl? shift?))))
(on-mousemove [event] (on-mousemove [event]
(let [wpt (gpt/point (.-clientX event) (let [wpt (gpt/point (.-clientX event)
@ -139,7 +183,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! (uws/pointer-event wpt vpt cpt ctrl? shift?))))]
(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)
@ -169,11 +213,11 @@
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! (uws/mouse-event :down ctrl? shift?)))
(when (not edition) (when (not edition)
(if drawing-tool (if drawing-tool
(st/emit! (udwd/start-drawing drawing-tool)) (st/emit! (start-drawing drawing-tool))
(st/emit! ::uev/interrupt (udw/start-selrect))))) (st/emit! :interrupt (start-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)
@ -181,28 +225,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! (uws/mouse-event :context-menu ctrl? shift?))))
(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! (uws/mouse-event :up ctrl? shift?))))
(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! (uws/mouse-event :click ctrl? shift?))))
(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! (uws/mouse-event :double-click ctrl? shift?))))]
[:* [:*
[:& coordinates {:zoom zoom}] [:& coordinates {:zoom zoom}]
[:div.tooltip-container [:div.tooltip-container
@ -224,4 +268,4 @@
[:& grid {:page page}])] [:& grid {:page page}])]
(when (contains? flags :ruler) (when (contains? flags :ruler)
[:& ruler {:zoom zoom :ruler (:ruler wst)}]) [:& ruler {:zoom zoom :ruler (:ruler wst)}])
[:& selrect {:value (:selrect wst)}]]])))) [:& selrect {:data (:selrect wst)}]]]))))