♻️ mainly workspace refactor

This commit is contained in:
Andrey Antukh 2019-08-02 20:18:05 +02:00
parent 4e382d456f
commit 212ae89c50
85 changed files with 18494 additions and 6609 deletions

View file

@ -2,43 +2,15 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2016-2017 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2016-2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.ui.shapes.common
(:require [lentes.core :as l]
[beicon.core :as rx]
[potok.core :as ptk]
[uxbox.main.store :as st]
[uxbox.main.refs :as refs]
[uxbox.main.streams :as streams]
[uxbox.main.geom :as geom]
[uxbox.main.user-events :as uev]
[uxbox.main.data.shapes :as uds]
[uxbox.main.ui.keyboard :as kbd]
[uxbox.util.dom :as dom]))
;; --- Movement
;; TODO: implement in the same way as drawing (move under uxbox.main.data.workspace.)
(defn start-move
[]
(letfn [(on-move [shape delta]
(st/emit! (uds/apply-temporal-displacement shape delta)))
(on-stop [shape]
(st/emit! (uds/apply-displacement shape)))
(on-start [shape]
(let [stoper (->> streams/events
(rx/filter uev/mouse-up?)
(rx/take 1))
stream (->> streams/mouse-position-deltas
(rx/take-until stoper))
on-move (partial on-move shape)
on-stop (partial on-stop shape)]
(when @refs/selected-alignment
(st/emit! (uds/initial-shape-align shape)))
(rx/subscribe stream on-move nil on-stop)))]
(run! on-start @refs/selected-shapes)))
(:require
[uxbox.main.data.workspace :as udw]
[uxbox.main.refs :as refs]
[uxbox.main.store :as st]
[uxbox.main.ui.keyboard :as kbd]
[uxbox.util.dom :as dom]))
;; --- Events
@ -48,27 +20,24 @@
drawing? @refs/selected-drawing-tool]
(when-not (:blocked shape)
(cond
(or drawing?
(and group (:locked (geom/resolve-parent shape))))
drawing?
nil
(and (not selected?) (empty? selected))
(do
(dom/stop-propagation event)
(st/emit! (uds/select-shape id))
(start-move))
(st/emit! (udw/select-shape id)
(udw/start-move-selected)))
(and (not selected?) (not (empty? selected)))
(do
(dom/stop-propagation event)
(if (kbd/shift? event)
(st/emit! (uds/select-shape id))
(do
(st/emit! (uds/deselect-all)
(uds/select-shape id))
(start-move))))
(st/emit! (udw/select-shape id))
(st/emit! (udw/deselect-all)
(udw/select-shape id)
(udw/start-move-selected))))
:else
(do
(dom/stop-propagation event)
(start-move))))))
(st/emit! (udw/start-move-selected)))))))

View file

@ -2,24 +2,25 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2016-2017 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2016-2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.ui.shapes.group
(:require [lentes.core :as l]
[uxbox.main.store :as st]
[uxbox.main.geom :as geom]
[uxbox.main.refs :as refs]
[uxbox.main.ui.shapes.common :as common]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.ui.shapes.icon :as icon]
[uxbox.main.ui.shapes.rect :as rect]
[uxbox.main.ui.shapes.circle :as circle]
[uxbox.main.ui.shapes.text :as text]
[uxbox.main.ui.shapes.path :as path]
[uxbox.main.ui.shapes.image :as image]
[uxbox.util.data :refer [classnames]]
[uxbox.util.geom.matrix :as gmt]
[rumext.core :as mx :include-macros true]))
(:require
[lentes.core :as l]
[rumext.core :as mx]
[uxbox.main.geom :as geom]
[uxbox.main.refs :as refs]
[uxbox.main.store :as st]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.ui.shapes.circle :as circle]
[uxbox.main.ui.shapes.common :as common]
[uxbox.main.ui.shapes.icon :as icon]
[uxbox.main.ui.shapes.image :as image]
[uxbox.main.ui.shapes.path :as path]
[uxbox.main.ui.shapes.rect :as rect]
[uxbox.main.ui.shapes.text :as text]
[uxbox.util.data :refer [classnames]]
[uxbox.util.geom.matrix :as gmt]))
;; --- Helpers

View file

@ -1,290 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.shapes.selection
"Multiple selection handlers component."
(:require [lentes.core :as l]
[beicon.core :as rx]
[potok.core :as ptk]
[uxbox.main.store :as st]
[uxbox.main.constants :as c]
[uxbox.main.refs :as refs]
[uxbox.main.streams :as streams]
[uxbox.main.workers :as uwrk]
[uxbox.main.user-events :as uev]
[uxbox.main.data.shapes :as uds]
[uxbox.main.ui.shapes.common :as scommon]
[uxbox.main.geom :as geom]
[rumext.core :as mx]
[rumext.alpha :as mf]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.util.dom :as dom]))
;; --- Refs & Constants
(def ^:private +circle-props+
{:r 6
:style {:fillOpacity "1"
:strokeWidth "1px"
:vectorEffect "non-scaling-stroke"}
:fill "#31e6e0"
:stroke "#28c4d4"})
(defn- focus-selected-shapes
[state]
(let [selected (get-in state [:workspace :selected])]
(mapv #(get-in state [:shapes %]) selected)))
(def ^:private selected-shapes-ref
"A customized version of `refs/selected-shapes` that
additionally resolves the shapes to the real object
instead of just return a set of ids."
(-> (l/lens focus-selected-shapes)
(l/derive st/state)))
(def ^:private selected-modifers-ref
"A customized version of `refs/selected-modifiers`
that instead of focus to one concrete id, it focuses
on the whole map."
(-> (l/key :modifiers)
(l/derive refs/workspace)))
;; --- Resize Implementation
(defn- start-resize
[vid ids shape]
(letfn [(on-resize [shape [point lock?]]
(let [result (geom/resize-shape vid shape point lock?)
scale (geom/calculate-scale-ratio shape result)
mtx (geom/generate-resize-matrix vid shape scale)
xfm (map #(uds/apply-temporal-resize % mtx))]
(apply st/emit! (sequence xfm ids))))
(on-end []
(apply st/emit! (map uds/apply-resize ids)))
;; Unifies the instantaneous proportion lock modifier
;; activated by Ctrl key and the shapes own proportion
;; lock flag that can be activated on element options.
(normalize-proportion-lock [[point ctrl?]]
(let [proportion-lock? (:proportion-lock shape)]
[point (or proportion-lock? ctrl?)]))
;; Applies alginment to point if it is currently
;; activated on the current workspace
(apply-grid-alignment [point]
(if @refs/selected-alignment
(uwrk/align-point point)
(rx/of point)))
;; Apply the current zoom factor to the point.
(apply-zoom [point]
(gpt/divide point @refs/selected-zoom))]
(let [shape (->> (geom/shape->rect-shape shape)
(geom/size))
stoper (->> streams/events
(rx/filter uev/mouse-up?)
(rx/take 1))
stream (->> streams/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/map normalize-proportion-lock))]
(rx/subscribe stream (partial on-resize shape) nil on-end))))
;; --- Controls (Component)
(def ^:private handler-size-threshold
"The size in pixels that shape width or height
should reach in order to increase the handler
control pointer radius from 4 to 6."
60)
(mx/defc control-item
[{:keys [class on-mouse-down r cy cx]}]
[:circle {:class-name class
:on-mouse-down on-mouse-down
:r r
:style {:fillOpacity "1"
:strokeWidth "1px"
:vectorEffect "non-scaling-stroke"}
:fill "#31e6e0"
:stroke "#28c4d4"
:cx cx
:cy cy}])
(mx/defc controls
[{:keys [x1 y1 width height] :as shape} zoom on-mouse-down]
(let [radius (if (> (max width height) handler-size-threshold) 6.0 4.0)]
[:g.controls
[:rect.main {:x x1 :y y1
:width width
:height height
:stroke-dasharray (str (/ 5.0 zoom) "," (/ 5 zoom))
:style {:stroke "#333" :fill "transparent"
:stroke-opacity "1"}}]
(control-item {:class "top"
:on-mouse-down #(on-mouse-down :top %)
:r (/ radius zoom)
:cx (+ x1 (/ width 2))
:cy (- y1 2)})
(control-item {:on-mouse-down #(on-mouse-down :right %)
:r (/ radius zoom)
:cy (+ y1 (/ height 2))
:cx (+ x1 width 1)
:class "right"})
(control-item {:on-mouse-down #(on-mouse-down :bottom %)
:r (/ radius zoom)
:cx (+ x1 (/ width 2))
:cy (+ y1 height 2)
:class "bottom"})
(control-item {:on-mouse-down #(on-mouse-down :left %)
:r (/ radius zoom)
:cy (+ y1 (/ height 2))
:cx (- x1 3)
:class "left"})
(control-item {:on-mouse-down #(on-mouse-down :top-left %)
:r (/ radius zoom)
:cx x1
:cy y1
:class "top-left"})
(control-item {:on-mouse-down #(on-mouse-down :top-right %)
:r (/ radius zoom)
:cx (+ x1 width)
:cy y1
:class "top-right"})
(control-item {:on-mouse-down #(on-mouse-down :bottom-left %)
:r (/ radius zoom)
:cx x1
:cy (+ y1 height)
:class "bottom-left"})
(control-item {:on-mouse-down #(on-mouse-down :bottom-right %)
:r (/ radius zoom)
:cx (+ x1 width)
:cy (+ y1 height)
:class "bottom-right"})]))
;; --- Selection Handlers (Component)
(defn get-path-edition-stoper
[stream]
(letfn [(stoper-event? [{:keys [type shift] :as event}]
(and (uev/mouse-event? event) (= type :up)))]
(rx/merge
(rx/filter stoper-event? stream)
(->> stream
(rx/filter #(= % ::uev/interrupt))
(rx/take 1)))))
(defn start-path-edition
[shape-id index]
(letfn [(on-move [delta]
(st/emit! (uds/update-path shape-id index delta)))]
(let [stoper (get-path-edition-stoper streams/events)
stream (rx/take-until stoper streams/mouse-position-deltas)]
(when @refs/selected-alignment
(st/emit! (uds/initial-path-point-align shape-id index)))
(rx/subscribe stream on-move))))
(mx/defc path-edition-selection-handlers
[{:keys [id segments modifiers] :as shape} zoom]
(letfn [(on-mouse-down [index event]
(dom/stop-propagation event)
(start-path-edition id index))]
(let [{:keys [displacement]} modifiers
segments (if displacement
(map #(gpt/transform % displacement) segments)
segments)]
[:g.controls
(for [[index {:keys [x y]}] (map-indexed vector segments)]
[:circle {:cx x :cy y
:r (/ 6.0 zoom)
:key index
:on-mouse-down (partial on-mouse-down index)
:fill "#31e6e0"
:stroke "#28c4d4"
:style {:cursor "pointer"}}])])))
(mx/defc multiple-selection-handlers
{:mixins [mx/static]}
[[shape & rest :as shapes] modifiers zoom]
(let [selection (->> shapes
(map #(assoc % :modifiers (get modifiers (:id %))))
(map #(geom/selection-rect %))
(geom/shapes->rect-shape)
(geom/selection-rect))
shape (geom/shapes->rect-shape shapes)
on-click #(do (dom/stop-propagation %2)
(start-resize %1 (map :id shapes) shape))]
(controls selection zoom on-click)))
(mx/defc single-selection-handlers
[{:keys [id] :as shape} zoom]
(let [on-click #(do (dom/stop-propagation %2)
(start-resize %1 #{id} shape))
shape (geom/selection-rect shape)]
(controls shape zoom on-click)))
(mx/defc text-edition-selection-handlers
{:mixins [mx/static]}
[{:keys [id] :as shape} zoom]
(let [{:keys [x1 y1 width height] :as shape} (geom/selection-rect shape)]
[:g.controls
[:rect.main {:x x1 :y y1
:width width
:height height
;; :stroke-dasharray (str (/ 5.0 zoom) "," (/ 5 zoom))
:style {:stroke "#333"
:stroke-width "0.5"
:stroke-opacity "0.5"
:fill "transparent"}}]]))
(mf/def selection-handlers
:mixins [mf/reactive mf/memo]
:render
(fn [own props]
(let [shapes (mf/react selected-shapes-ref)
modifiers (mf/react selected-modifers-ref)
;; Edition is a workspace global flag
;; because only one shape can be on
;; the edition mode.
edition? (mf/react refs/selected-edition)
zoom (mf/react refs/selected-zoom)
num (count shapes)
{:keys [id type] :as shape} (first shapes)]
(cond
(zero? num)
nil
(> num 1)
(multiple-selection-handlers shapes modifiers zoom)
(and (= type :text) edition?)
(-> (assoc shape :modifiers (get modifiers id))
(text-edition-selection-handlers zoom))
(= type :path)
(if (= edition? (:id shape))
(-> (assoc shape :modifiers (get modifiers id))
(path-edition-selection-handlers zoom))
(-> (assoc shape :modifiers (get modifiers id))
(single-selection-handlers zoom)))
:else
(-> (assoc shape :modifiers (get modifiers id))
(single-selection-handlers zoom))))))