♻️ 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

@ -6,13 +6,45 @@
(ns uxbox.main.ui.shapes.common
(: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.store :as st]
[uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.workspace.streams :as ws]
[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
[event {:keys [id group] :as shape} selected]
@ -26,18 +58,18 @@
(and (not selected?) (empty? selected))
(do
(dom/stop-propagation event)
(st/emit! (udw/select-shape id)
(udw/start-move-selected)))
(st/emit! (dw/select-shape id)
(start-move-selected)))
(and (not selected?) (not (empty? selected)))
(do
(dom/stop-propagation event)
(if (kbd/shift? event)
(st/emit! (udw/select-shape id))
(st/emit! (udw/deselect-all)
(udw/select-shape id)
(udw/start-move-selected))))
(st/emit! (dw/select-shape id))
(st/emit! (dw/deselect-all)
(dw/select-shape id)
(start-move-selected))))
:else
(do
(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.refs :as refs]
[uxbox.main.store :as st]
[uxbox.main.streams :as streams]
[uxbox.main.ui.confirm]
[uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.messages :refer [messages-widget]]
@ -32,7 +31,7 @@
[uxbox.main.ui.workspace.shortcuts :as shortcuts]
[uxbox.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]]
[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.dom :as dom]
[uxbox.util.geom.point :as gpt]
@ -42,10 +41,11 @@
(defn- on-scroll
[event]
(let [target (.-target event)
;; TODO: refactor
#_(let [target (.-target event)
top (.-scrollTop 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
[event canvas]

View file

@ -7,17 +7,298 @@
(ns uxbox.main.ui.workspace.drawarea
"Draw interaction and component."
(:require
[beicon.core :as rx]
[potok.core :as ptk]
[rumext.alpha :as mf]
[uxbox.main.data.workspace :as udw]
[uxbox.main.data.workspace-drawing :as udwd]
[uxbox.main.constants :as c]
[uxbox.main.data.workspace :as dw]
[uxbox.main.data.shapes :as ds]
[uxbox.main.geom :as geom]
[uxbox.main.refs :as refs]
[uxbox.main.store :as st]
[uxbox.main.streams :as streams]
[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.geom.path :as path]
[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
(declare generic-draw-area)
@ -47,12 +328,13 @@
[{:keys [shape] :as props}]
(letfn [(on-click [event]
(dom/stop-propagation event)
(st/emit! (udw/set-tooltip nil)
(udwd/close-drawing-path)))
(st/emit! (dw/set-tooltip nil)
close-drawing-path
:interrupt))
(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]
(st/emit! (udw/set-tooltip nil)))]
(st/emit! (dw/set-tooltip nil)))]
(when-let [{:keys [x y] :as segment} (first (:segments shape))]
[:g
(shapes/render-shape shape)

View file

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

View file

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

View file

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

View file

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

View file

@ -9,13 +9,10 @@
(:require
[rumext.alpha :as mf]
[uxbox.builtins.icons :as i]
[uxbox.main.data.shapes :as uds]
[uxbox.main.data.workspace :as udw]
[uxbox.main.data.workspace-drawing :as udwd]
[uxbox.main.data.workspace :as dw]
[uxbox.main.refs :as refs]
[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]))
;; --- Constants
@ -81,17 +78,17 @@
(mf/defc draw-toolbox
{:wrap [mf/wrap-memo]}
[{: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)
tools (->> (into [] +draw-tools+)
(sort-by (comp :priority second)))
select-drawtool #(st/emit! ::uev/interrupt
(udw/deactivate-ruler)
(udwd/select-for-drawing %))
toggle-ruler #(st/emit! (udwd/select-for-drawing nil)
(uds/deselect-all)
(udw/toggle-ruler))]
select-drawtool #(st/emit! :interrupt
(dw/deactivate-ruler)
(dw/select-for-drawing %))
toggle-ruler #(st/emit! (dw/select-for-drawing nil)
(dw/deselect-all)
(dw/toggle-ruler))]
[:div#form-tools.tool-window.drawing-tools
[:div.tool-window-bar

View file

@ -6,20 +6,19 @@
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.sidebar.icons
(:require [lentes.core :as l]
[uxbox.util.router :as r]
[potok.core :as ptk]
[uxbox.main.store :as st]
[uxbox.main.lenses :as ul]
[uxbox.main.data.workspace :as udw]
[uxbox.main.data.workspace-drawing :as udwd]
[uxbox.main.data.icons :as udi]
[uxbox.main.ui.shapes.icon :as icon]
[uxbox.main.ui.dashboard.icons :as icons]
[uxbox.builtins.icons :as i]
[rumext.core :as mx :include-macros true]
[uxbox.util.dom :as dom]
[uxbox.util.data :refer (read-string)]))
(:require
[lentes.core :as l]
[rumext.core :as mx]
[uxbox.builtins.icons :as i]
[uxbox.main.data.icons :as udi]
[uxbox.main.data.workspace :as dw]
[uxbox.main.lenses :as ul]
[uxbox.main.store :as st]
[uxbox.main.ui.dashboard.icons :as icons]
[uxbox.main.ui.shapes.icon :as icon]
[uxbox.util.data :refer (read-string)]
[uxbox.util.dom :as dom]
[uxbox.util.router :as r]))
;; --- Refs
@ -43,7 +42,7 @@
(defn- icons-toolbox-init
[own]
(st/emit! (udw/initialize-icons-toolbox))
(st/emit! (dw/initialize-icons-toolbox))
own)
(mx/defc icons-toolbox
@ -60,13 +59,13 @@
icons (->> (vals (mx/react icons/icons-ref))
(filter #(= (:id selected-coll) (:collection %))))]
(letfn [(on-close [event]
(st/emit! (udw/toggle-flag :icons)))
(st/emit! (dw/toggle-flag :icons)))
(on-select [icon event]
(st/emit! (udwd/select-for-drawing icon)))
(st/emit! (dw/select-for-drawing icon)))
(on-change [event]
(let [value (read-string (dom/event->value event))]
(st/emit! (udwd/select-for-drawing nil)
(udw/select-icons-toolbox-collection value))))]
(st/emit! (dw/select-for-drawing nil)
(dw/select-icons-toolbox-collection value))))]
[:div#form-figures.tool-window
[:div.tool-window-bar
[: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
(:require
[beicon.core :as rx]
[goog.events :as events]
[potok.core :as ptk]
[rumext.alpha :as mf]
[uxbox.main.constants :as c]
[uxbox.main.data.workspace :as udw]
[uxbox.main.data.workspace-drawing :as udwd]
[uxbox.main.data.workspace :as dw]
[uxbox.main.geom :as geom]
[uxbox.main.refs :as refs]
[uxbox.main.store :as st]
@ -19,7 +20,8 @@
[uxbox.main.ui.workspace.canvas :refer [canvas]]
[uxbox.main.ui.workspace.grid :refer [grid]]
[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.dom :as dom]
[uxbox.util.geom.point :as gpt])
@ -67,11 +69,53 @@
;; --- 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
{:wrap [mf/wrap-memo]}
[{rect :value}]
(when rect
(let [{:keys [x1 y1 width height]} (geom/size rect)]
[{:keys [data] :as props}]
(when data
(let [{:keys [x1 y1 width height]} (geom/size data)]
[:rect.selection-rect
{:x x1
:y y1
@ -112,9 +156,9 @@
opts {:key key
:shift? shift?
:ctrl? ctrl?}]
(st/emit! (uev/keyboard-event :down key ctrl? shift?))
(st/emit! (uws/keyboard-event :down key ctrl? shift?))
(when (kbd/space? event)
(st/emit! (udw/start-viewport-positioning)))))
(st/emit! (dw/start-viewport-positioning)))))
(on-key-up [event]
(let [key (.-keyCode event)
@ -124,8 +168,8 @@
:shift? shift?
:ctrl? ctrl?}]
(when (kbd/space? event)
(st/emit! (udw/stop-viewport-positioning)))
(st/emit! (uev/keyboard-event :up key ctrl? shift?))))
(st/emit! (dw/stop-viewport-positioning)))
(st/emit! (uws/keyboard-event :up key ctrl? shift?))))
(on-mousemove [event]
(let [wpt (gpt/point (.-clientX event)
@ -139,7 +183,7 @@
:window-coords wpt
:viewport-coords vpt
: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)
key2 (events/listen js/document EventType.KEYDOWN on-key-down)
@ -169,11 +213,11 @@
shift? (kbd/shift? event)
opts {:shift? shift?
:ctrl? ctrl?}]
(st/emit! (uev/mouse-event :down ctrl? shift?)))
(st/emit! (uws/mouse-event :down ctrl? shift?)))
(when (not edition)
(if drawing-tool
(st/emit! (udwd/start-drawing drawing-tool))
(st/emit! ::uev/interrupt (udw/start-selrect)))))
(st/emit! (start-drawing drawing-tool))
(st/emit! :interrupt (start-selrect)))))
(on-context-menu [event]
(dom/prevent-default event)
(dom/stop-propagation event)
@ -181,28 +225,28 @@
shift? (kbd/shift? event)
opts {:shift? shift?
:ctrl? ctrl?}]
(st/emit! (uev/mouse-event :context-menu ctrl? shift?))))
(st/emit! (uws/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?))))
(st/emit! (uws/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?))))
(st/emit! (uws/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?))))]
(st/emit! (uws/mouse-event :double-click ctrl? shift?))))]
[:*
[:& coordinates {:zoom zoom}]
[:div.tooltip-container
@ -224,4 +268,4 @@
[:& grid {:page page}])]
(when (contains? flags :ruler)
[:& ruler {:zoom zoom :ruler (:ruler wst)}])
[:& selrect {:value (:selrect wst)}]]]))))
[:& selrect {:data (:selrect wst)}]]]))))