Complete refactor of shapes rendering.

This commit is contained in:
Andrey Antukh 2016-02-26 19:42:53 +02:00
parent 2fed551cf1
commit 1572c4cdd3
18 changed files with 403 additions and 261 deletions

View file

@ -31,14 +31,6 @@
[shape & params] [shape & params]
(:type shape)) (:type shape))
(defmulti -render
dispatch-by-type
:hierarchy #'+hierarchy+)
(defmulti -render-svg
dispatch-by-type
:hierarchy #'+hierarchy+)
(defmulti -move (defmulti -move
dispatch-by-type dispatch-by-type
:hierarchy #'+hierarchy+) :hierarchy #'+hierarchy+)

13
src/uxbox/ui/core.cljs Normal file
View file

@ -0,0 +1,13 @@
(ns uxbox.ui.core
(:require [beicon.core :as rx]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Actions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defonce actions-s (rx/bus))
(defn emit-action!
[type]
(rx/push! actions-s type))

View file

@ -7,11 +7,11 @@
[uxbox.rstore :as rs] [uxbox.rstore :as rs]
[uxbox.schema :as sc] [uxbox.schema :as sc]
[uxbox.library :as library] [uxbox.library :as library]
[uxbox.shapes :as shapes]
[uxbox.data.dashboard :as dd] [uxbox.data.dashboard :as dd]
[uxbox.util.lens :as ul] [uxbox.util.lens :as ul]
[uxbox.ui.icons :as i] [uxbox.ui.icons :as i]
[uxbox.ui.form :as form] [uxbox.ui.form :as form]
[uxbox.ui.shapes.core :as uusc]
[uxbox.ui.lightbox :as lightbox] [uxbox.ui.lightbox :as lightbox]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.ui.mixins :as mx])) [uxbox.ui.mixins :as mx]))
@ -111,7 +111,7 @@
[:div.dashboard-grid-content [:div.dashboard-grid-content
(for [icon (:icons coll)] (for [icon (:icons coll)]
[:div.grid-item.small-item.project-th {} [:div.grid-item.small-item.project-th {}
[:span.grid-item-image #_i/toggle (shapes/-render-svg icon nil)] [:span.grid-item-image #_i/toggle (uusc/render-shape-svg icon nil)]
[:h3 (:name icon)] [:h3 (:name icon)]
#_[:div.project-th-actions #_[:div.project-th-actions
[:div.project-th-icon.edit i/pencil] [:div.project-th-icon.edit i/pencil]

View file

@ -1,115 +1,10 @@
(ns uxbox.ui.shapes (ns uxbox.ui.shapes
"A ui related implementation for uxbox.shapes ns." "A ui related implementation for uxbox.shapes ns."
(:require [sablono.core :refer-macros [html]] (:require [uxbox.ui.shapes.core :as usc]
[cuerdas.core :as str] [uxbox.ui.shapes.icon]
[rum.core :as rum] [uxbox.ui.shapes.rect]
[uxbox.state :as st] [uxbox.ui.shapes.group]
[uxbox.shapes :as sh] [uxbox.ui.shapes.line]
[uxbox.ui.icons :as i] [uxbox.ui.shapes.circle]))
[uxbox.util.math :as mth]
[uxbox.util.data :refer (remove-nil-vals)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (def ^:const shape usc/shape)
;; Attribute transformations
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:static ^:private +style-attrs+
#{:fill :opacity :stroke :stroke-opacity :stroke-width :stroke-type :rx :ry})
(defn- transform-stroke-type
[attrs]
(if-let [type (:stroke-type attrs)]
(let [value (case type
:dotted "1,1"
:dashed "10,10")]
(-> attrs
(assoc! :stroke-dasharray value)
(dissoc! :stroke-type)))
attrs))
(defn- extract-style-attrs
"Extract predefinet attrs from shapes."
[shape]
(let [attrs (select-keys shape +style-attrs+)]
(-> (transient attrs)
(transform-stroke-type)
(persistent!))))
(defn- make-debug-attrs
[shape]
(let [attrs (select-keys shape [:rotation :width :height :x :y])
xf (map (fn [[x v]]
[(keyword (str "data-" (name x))) v]))]
(into {} xf attrs)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Implementation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod sh/-render :builtin/icon
[{:keys [data id] :as shape} _]
(let [key (str id)
rfm (sh/-transformation shape)
attrs (merge {:id key :key key :transform (str rfm)}
(extract-style-attrs shape)
(make-debug-attrs shape))]
(html
[:g attrs data])))
(defmethod sh/-render :builtin/line
[{:keys [id x1 y1 x2 y2] :as shape}]
(let [key (str id)
props (select-keys shape [:x1 :x2 :y2 :y1])
attrs (-> (extract-style-attrs shape)
(merge {:id key :key key})
(merge props))]
(html
[:line attrs])))
(defmethod sh/-render :builtin/circle
[{:keys [id] :as shape}]
(let [key (str id)
rfm (sh/-transformation shape)
props (select-keys shape [:cx :cy :rx :ry])
attrs (-> (extract-style-attrs shape)
(merge {:id key :key key :transform (str rfm)})
(merge props))]
(html
[:ellipse attrs])))
(defmethod sh/-render :builtin/rect
[{:keys [id x1 y1 x2 y2] :as shape}]
(let [key (str id)
rfm (sh/-transformation shape)
size (sh/-size shape)
props {:x x1 :y y1 :id key :key key :transform (str rfm)}
attrs (-> (extract-style-attrs shape)
(merge props size))]
(html
[:rect attrs])))
(defmethod sh/-render :builtin/group
[{:keys [items id dx dy rotation] :as shape} factory]
(let [key (str "group-" id)
rfm (sh/-transformation shape)
attrs (merge {:id key :key key :transform (str rfm)}
(extract-style-attrs shape)
(make-debug-attrs shape))
shapes-by-id (get @st/state :shapes-by-id)]
(html
[:g attrs
(for [item (->> items
(map #(get shapes-by-id %))
(remove :hidden)
(reverse))]
(-> (factory item)
(rum/with-key (str (:id item)))))])))
(defmethod sh/-render-svg :builtin/icon
[{:keys [data id view-box] :as shape}]
(let [key (str "icon-svg-" id)
view-box (apply str (interpose " " view-box))
props {:view-box view-box :id key :key key}]
(html
[:svg props data])))

View file

@ -0,0 +1,24 @@
(ns uxbox.ui.shapes.circle
(:require [sablono.core :refer-macros [html]]
[cuerdas.core :as str]
[rum.core :as rum]
[cats.labs.lens :as l]
[uxbox.rstore :as rs]
[uxbox.state :as st]
[uxbox.shapes :as ush]
[uxbox.data.workspace :as dw]
[uxbox.ui.core :as uuc]
[uxbox.ui.keyboard :as kbd]
[uxbox.ui.shapes.core :as uusc]
[uxbox.util.dom :as dom]))
(defmethod uusc/render-shape :builtin/circle
[{:keys [id] :as shape}]
(let [key (str id)
rfm (ush/-transformation shape)
props (select-keys shape [:cx :cy :rx :ry])
attrs (-> (uusc/extract-style-attrs shape)
(merge {:id key :key key :transform (str rfm)})
(merge props))]
(html
[:ellipse attrs])))

View file

@ -0,0 +1,86 @@
(ns uxbox.ui.shapes.core
(:require [cats.labs.lens :as l]
[cuerdas.core :as str]
[rum.core :as rum]
[beicon.core :as rx]
[uxbox.state :as st]
[uxbox.shapes :as sh]
[uxbox.ui.mixins :as mx]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Implementation Api
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmulti render-component
(fn [own shape] (:type shape))
:hierarchy #'sh/+hierarchy+)
(defmulti render-shape
sh/dispatch-by-type
:hierarchy #'sh/+hierarchy+)
(defmulti render-shape-svg
sh/dispatch-by-type
:hierarchy #'sh/+hierarchy+)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Lenses
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:const selected-shapes-l
(as-> (l/in [:workspace :selected]) $
(l/focus-atom $ st/state)))
(defn- focus-shape
[id]
(as-> (l/in [:shapes-by-id id]) $
(l/focus-atom $ st/state)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Component Api
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- shape-render
[own id]
(let [shape-l (focus-shape id)
shape (rum/react shape-l)]
(render-component own shape)))
(def ^:const shape
(mx/component
{:render shape-render
:name "shape"
:mixins [(mx/local) mx/static rum/reactive]}))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Attribute transformations
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:static ^:private +style-attrs+
#{:fill :opacity :stroke :stroke-opacity :stroke-width :stroke-type :rx :ry})
(defn- transform-stroke-type
[attrs]
(if-let [type (:stroke-type attrs)]
(let [value (case type
:dotted "1,1"
:dashed "10,10")]
(-> attrs
(assoc! :stroke-dasharray value)
(dissoc! :stroke-type)))
attrs))
(defn- extract-style-attrs
"Extract predefinet attrs from shapes."
[shape]
(let [attrs (select-keys shape +style-attrs+)]
(-> (transient attrs)
(transform-stroke-type)
(persistent!))))
(defn- make-debug-attrs
[shape]
(let [attrs (select-keys shape [:rotation :width :height :x :y])
xf (map (fn [[x v]]
[(keyword (str "data-" (name x))) v]))]
(into {} xf attrs)))

View file

@ -0,0 +1,31 @@
(ns uxbox.ui.shapes.group
(:require [sablono.core :refer-macros [html]]
[cuerdas.core :as str]
[rum.core :as rum]
[cats.labs.lens :as l]
[uxbox.rstore :as rs]
[uxbox.state :as st]
[uxbox.shapes :as ush]
[uxbox.data.workspace :as dw]
[uxbox.ui.core :as uuc]
[uxbox.ui.keyboard :as kbd]
[uxbox.ui.shapes.core :as uusc]
[uxbox.util.dom :as dom]))
(defmethod uusc/render-shape :builtin/group
[{:keys [items id dx dy rotation] :as shape} factory]
(let [key (str "group-" id)
rfm (ush/-transformation shape)
attrs (merge {:id key :key key :transform (str rfm)}
(uusc/extract-style-attrs shape)
(uusc/make-debug-attrs shape))
shapes-by-id (get @st/state :shapes-by-id)]
(html
[:g attrs
(for [item (->> items
(map #(get shapes-by-id %))
(remove :hidden)
(reverse))]
(-> (factory (:id item))
(rum/with-key (str (:id item)))))])))

View file

@ -0,0 +1,132 @@
(ns uxbox.ui.shapes.icon
(:require [sablono.core :refer-macros [html]]
[cuerdas.core :as str]
[rum.core :as rum]
[cats.labs.lens :as l]
[uxbox.rstore :as rs]
[uxbox.state :as st]
[uxbox.shapes :as sh]
[uxbox.data.workspace :as dw]
[uxbox.ui.core :as uuc]
[uxbox.ui.mixins :as mx]
[uxbox.ui.keyboard :as kbd]
[uxbox.ui.shapes.core :as uusc]
[uxbox.util.dom :as dom]))
(def ^:private ^:const selection-circle-style
{:fillOpacity "0.5"
:strokeWidth "1px"
:vectorEffect "non-scaling-stroke"})
(def ^:private ^:const default-selection-props
{:r 5 :style selection-circle-style
:fill "#333"
:stroke "#333"})
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Icon Component
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn on-mouse-down
[event {:keys [id group] :as shape} selected]
(let [selected? (contains? selected id)]
(when-not (:blocked shape)
(cond
(and group (:locked (sh/resolve-parent shape)))
nil
(and (not selected?) (empty? selected))
(do
(dom/stop-propagation event)
(uuc/emit-action! :shape/movement)
(rs/emit! (dw/select-shape id)))
(and (not selected?) (not (empty? selected)))
(do
(dom/stop-propagation event)
(if (kbd/shift? event)
(rs/emit! (dw/select-shape id))
(rs/emit! (dw/deselect-all)
(dw/select-shape id))))
:else
(do
(dom/stop-propagation event)
(uuc/emit-action! :shape/movement))))))
(defn on-mouse-up
[event {:keys [id group] :as shape}]
(cond
(and group (:locked (sh/resolve-parent shape)))
nil
:else
(do
(dom/stop-propagation event)
(uuc/emit-action! :nothing))))
(declare handlers)
(defmethod uusc/render-component :default ;; :builtin/icon
[own shape]
(let [{:keys [id x y width height group]} shape
selected (rum/react uusc/selected-shapes-l)
selected? (contains? selected id)
on-mouse-down #(on-mouse-down % shape selected)
on-mouse-up #(on-mouse-up % shape)]
(html
[:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down
:on-mouse-up on-mouse-up}
(uusc/render-shape shape #(uusc/shape %))
(when selected?
(handlers shape))])))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Icon Handlers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- handlers-render
[own shape]
(let [{:keys [x y width height]} (sh/-outer-rect shape)]
(html
[:g.controls
[:rect {:x x :y y :width width :height height :stroke-dasharray "5,5"
:style {:stroke "#333" :fill "transparent"
:stroke-opacity "1"}}]
[:circle.top-left (merge default-selection-props
{:cx x :cy y})]
[:circle.top-right (merge default-selection-props
{:cx (+ x width) :cy y})]
[:circle.bottom-left (merge default-selection-props
{:cx x :cy (+ y height)})]
[:circle.bottom-right (merge default-selection-props
{:cx (+ x width) :cy (+ y height)})]])))
(def ^:const handlers
(mx/component
{:render handlers-render
:name "handlers"
:mixins [mx/static]}))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Shape & Shape Svg
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod uusc/render-shape :builtin/icon
[{:keys [data id] :as shape} _]
(let [key (str id)
rfm (sh/-transformation shape)
attrs (merge {:id key :key key :transform (str rfm)}
(uusc/extract-style-attrs shape)
(uusc/make-debug-attrs shape))]
(html
[:g attrs data])))
(defmethod uusc/render-shape-svg :builtin/icon
[{:keys [data id view-box] :as shape}]
(let [key (str "icon-svg-" id)
view-box (apply str (interpose " " view-box))
props {:view-box view-box :id key :key key}]
(html
[:svg props data])))

View file

@ -0,0 +1,23 @@
(ns uxbox.ui.shapes.line
(:require [sablono.core :refer-macros [html]]
[cuerdas.core :as str]
[rum.core :as rum]
[cats.labs.lens :as l]
[uxbox.rstore :as rs]
[uxbox.state :as st]
[uxbox.shapes :as ush]
[uxbox.data.workspace :as dw]
[uxbox.ui.core :as uuc]
[uxbox.ui.keyboard :as kbd]
[uxbox.ui.shapes.core :as uusc]
[uxbox.util.dom :as dom]))
(defmethod uusc/render-shape :builtin/line
[{:keys [id x1 y1 x2 y2] :as shape}]
(let [key (str id)
props (select-keys shape [:x1 :x2 :y2 :y1])
attrs (-> (uusc/extract-style-attrs shape)
(merge {:id key :key key})
(merge props))]
(html
[:line attrs])))

View file

@ -0,0 +1,25 @@
(ns uxbox.ui.shapes.rect
(:require [sablono.core :refer-macros [html]]
[cuerdas.core :as str]
[rum.core :as rum]
[cats.labs.lens :as l]
[uxbox.rstore :as rs]
[uxbox.state :as st]
[uxbox.shapes :as ush]
[uxbox.data.workspace :as dw]
[uxbox.ui.core :as uuc]
[uxbox.ui.keyboard :as kbd]
[uxbox.ui.shapes.core :as uusc]
[uxbox.util.dom :as dom]))
(defmethod uusc/render-shape :builtin/rect
[{:keys [id x1 y1 x2 y2] :as shape}]
(let [key (str id)
rfm (ush/-transformation shape)
size (ush/-size shape)
props {:x x1 :y y1 :id key :key key :transform (str rfm)}
attrs (-> (uusc/extract-style-attrs shape)
(merge props size))]
(html
[:rect attrs])))

View file

@ -8,9 +8,10 @@
[uxbox.data.workspace :as dw] [uxbox.data.workspace :as dw]
[uxbox.util.geom.point :as gpt] [uxbox.util.geom.point :as gpt]
[uxbox.util.data :refer (classnames)] [uxbox.util.data :refer (classnames)]
[uxbox.ui.core :as uuc]
[uxbox.ui.icons :as i] [uxbox.ui.icons :as i]
[uxbox.ui.mixins :as mx] [uxbox.ui.mixins :as mx]
[uxbox.ui.workspace.base :as wb] [uxbox.ui.workspace.base :as uuwb]
[uxbox.ui.workspace.shortcuts :as wshortcuts] [uxbox.ui.workspace.shortcuts :as wshortcuts]
[uxbox.ui.workspace.header :refer (header)] [uxbox.ui.workspace.header :refer (header)]
[uxbox.ui.workspace.rules :refer (horizontal-rule vertical-rule)] [uxbox.ui.workspace.rules :refer (horizontal-rule vertical-rule)]
@ -27,7 +28,7 @@
(let [target (.-target event) (let [target (.-target event)
top (.-scrollTop target) top (.-scrollTop target)
left (.-scrollLeft target)] left (.-scrollLeft target)]
(rx/push! wb/scroll-b (gpt/point left top)))) (rx/push! uuwb/scroll-b (gpt/point left top))))
(defn- on-key-down (defn- on-key-down
[event] [event]
@ -39,7 +40,7 @@
(defn- workspace-render (defn- workspace-render
[own projectid] [own projectid]
(let [{:keys [flags] :as workspace} (rum/react wb/workspace-l) (let [{:keys [flags] :as workspace} (rum/react uuwb/workspace-l)
left-sidebar? (not (empty? (keep flags [:layers :sitemap :document-history]))) left-sidebar? (not (empty? (keep flags [:layers :sitemap :document-history])))
right-sidebar? (not (empty? (keep flags [:icons :drawtools right-sidebar? (not (empty? (keep flags [:icons :drawtools
:element-options]))) :element-options])))
@ -85,13 +86,13 @@
(defn- workspace-did-mount (defn- workspace-did-mount
[own] [own]
(letfn [(handle-scroll-interaction [] (letfn [(handle-scroll-interaction []
(let [stoper (->> wb/interactions-b (let [stoper (->> uuc/actions-s
(rx/filter #(not= % :scroll/viewport)) (rx/filter #(not= % :scroll/viewport))
(rx/take 1)) (rx/take 1))
local (:rum/local own) local (:rum/local own)
initial @wb/mouse-viewport-a] initial @uuwb/mouse-viewport-a]
(swap! local assoc :scrolling true) (swap! local assoc :scrolling true)
(as-> wb/mouse-viewport-s $ (as-> uuwb/mouse-viewport-s $
(rx/take-until stoper $) (rx/take-until stoper $)
(rx/subscribe $ #(on-scroll % initial) nil on-scroll-end)))) (rx/subscribe $ #(on-scroll % initial) nil on-scroll-end))))
@ -108,12 +109,12 @@
(set! (.-scrollTop el) (- cy y))))] (set! (.-scrollTop el) (- cy y))))]
(let [el (mx/get-ref-dom own "workspace-canvas") (let [el (mx/get-ref-dom own "workspace-canvas")
sub (as-> wb/interactions-b $ sub (as-> uuc/actions-s $
(rx/dedupe $) (rx/dedupe $)
(rx/filter #(= :scroll/viewport %) $) (rx/filter #(= :scroll/viewport %) $)
(rx/on-value $ handle-scroll-interaction))] (rx/on-value $ handle-scroll-interaction))]
(set! (.-scrollLeft el) wb/canvas-start-scroll-x) (set! (.-scrollLeft el) uuwb/canvas-start-scroll-x)
(set! (.-scrollTop el) wb/canvas-start-scroll-y) (set! (.-scrollTop el) uuwb/canvas-start-scroll-y)
(assoc own ::sub sub)))) (assoc own ::sub sub))))
(defn- workspace-will-unmount (defn- workspace-will-unmount

View file

@ -71,16 +71,6 @@
(defonce scroll-a (defonce scroll-a
(rx/to-atom scroll-s)) (rx/to-atom scroll-s))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interactions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defonce interactions-b (rx/bus))
(defn emit-interaction!
[type]
(rx/push! interactions-b type))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Mouse Position Stream ;; Mouse Position Stream
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -4,21 +4,18 @@
[beicon.core :as rx] [beicon.core :as rx]
[cats.labs.lens :as l] [cats.labs.lens :as l]
[goog.events :as events] [goog.events :as events]
[uxbox.router :as r]
[uxbox.rstore :as rs] [uxbox.rstore :as rs]
[uxbox.state :as st]
[uxbox.xforms :as xf]
[uxbox.shapes :as sh] [uxbox.shapes :as sh]
[uxbox.util.lens :as ul]
[uxbox.library.icons :as _icons]
[uxbox.data.projects :as dp] [uxbox.data.projects :as dp]
[uxbox.data.workspace :as dw] [uxbox.data.workspace :as dw]
[uxbox.ui.mixins :as mx]
[uxbox.util.geom.point :as gpt] [uxbox.util.geom.point :as gpt]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.util.data :refer (parse-int)] [uxbox.util.data :refer (parse-int)]
[uxbox.ui.core :as uuc]
[uxbox.ui.keyboard :as kbd] [uxbox.ui.keyboard :as kbd]
[uxbox.ui.workspace.base :as wb] [uxbox.ui.shapes :as uus]
[uxbox.ui.mixins :as mx]
[uxbox.ui.workspace.base :as uuwb]
[uxbox.ui.workspace.canvas.movement] [uxbox.ui.workspace.canvas.movement]
[uxbox.ui.workspace.canvas.draw :refer (draw-area)] [uxbox.ui.workspace.canvas.draw :refer (draw-area)]
[uxbox.ui.workspace.canvas.ruler :refer (ruler)] [uxbox.ui.workspace.canvas.ruler :refer (ruler)]
@ -43,97 +40,27 @@
:name "background" :name "background"
:mixins [mx/static]})) :mixins [mx/static]}))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Shape
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare shape)
(defn shape-render
[own item selected]
(let [{:keys [id x y width height group] :as item} item
selected? (contains? selected id)
local (:rum/local own)]
(letfn [(on-mouse-down [event]
(when-not (:blocked item)
(cond
(and group (:locked (sh/resolve-parent item)))
nil
(and (not selected?) (empty? selected))
(do
(dom/stop-propagation event)
(swap! local assoc :init-coords [x y])
(wb/emit-interaction! :shape/movement)
(rs/emit! (dw/select-shape id)))
(and (not selected?) (not (empty? selected)))
(do
(dom/stop-propagation event)
(swap! local assoc :init-coords [x y])
(if (kbd/shift? event)
(rs/emit! (dw/select-shape id))
(rs/emit! (dw/deselect-all)
(dw/select-shape id))))
:else
(do
(dom/stop-propagation event)
(swap! local assoc :init-coords [x y])
(wb/emit-interaction! :shape/movement)))))
(on-mouse-up [event]
(cond
(and group (:locked (sh/resolve-parent item)))
nil
:else
(do
(dom/stop-propagation event)
(wb/emit-interaction! :nothing)
)))]
(html
[:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down
:on-mouse-up on-mouse-up}
(sh/-render item #(shape % selected))]))))
(def ^:static shape
(mx/component
{:render shape-render
:name "shape"
:mixins [(mx/local {}) rum/reactive]}))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Canvas ;; Canvas
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- canvas-render (defn- canvas-render
[own {:keys [width height id] :as page}] [own {:keys [width height id] :as page}]
(let [workspace (rum/react wb/workspace-l) (let [workspace (rum/react uuwb/workspace-l)]
shapes-by-id (rum/react wb/shapes-by-id-l)
workspace-selected (:selected workspace)
xf (comp
(map #(get shapes-by-id %))
(remove :hidden))
shapes (->> (vals shapes-by-id)
(filter #(= (:page %) id)))
shapes-selected (filter (comp workspace-selected :id) shapes)
shapes-notselected (filter (comp not workspace-selected :id) shapes)]
(html (html
[:svg.page-canvas {:x wb/canvas-start-x [:svg.page-canvas {:x uuwb/canvas-start-x
:y wb/canvas-start-y :y uuwb/canvas-start-y
:ref (str "canvas" id) :ref (str "canvas" id)
:width width :width width
:height height} :height height}
(background) (background)
(grid 1) (grid 1)
[:svg.page-layout {} [:svg.page-layout {}
(shapes-selection shapes-selected) #_(shapes-selection shapes-selected)
[:g.main {} [:g.main {}
(for [item (reverse (sequence xf (:shapes page)))] (for [item (:shapes page)]
(-> (shape item workspace-selected) (-> (uus/shape item)
(rum/with-key (str (:id item))))) (rum/with-key (str item))))
(draw-area)]]]))) (draw-area)]]])))
(def canvas (def canvas
@ -148,8 +75,8 @@
(defn viewport-render (defn viewport-render
[own] [own]
(let [workspace (rum/react wb/workspace-l) (let [workspace (rum/react uuwb/workspace-l)
page (rum/react wb/page-l) page (rum/react uuwb/page-l)
drawing? (:drawing workspace) drawing? (:drawing workspace)
zoom 1] zoom 1]
(letfn [(on-mouse-down [event] (letfn [(on-mouse-down [event]
@ -157,14 +84,14 @@
(when-not (empty? (:selected workspace)) (when-not (empty? (:selected workspace))
(rs/emit! (dw/deselect-all))) (rs/emit! (dw/deselect-all)))
(if-let [shape (:drawing workspace)] (if-let [shape (:drawing workspace)]
(wb/emit-interaction! :draw/shape) (uuc/emit-action! :draw/shape)
(wb/emit-interaction! :draw/selrect))) (uuc/emit-action! :draw/selrect)))
(on-mouse-up [event] (on-mouse-up [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(wb/emit-interaction! :nothing))] (uuc/emit-action! :nothing))]
(html (html
[:svg.viewport {:width wb/viewport-width [:svg.viewport {:width uuwb/viewport-width
:height wb/viewport-height :height uuwb/viewport-height
:ref "viewport" :ref "viewport"
:class (when drawing? "drawing") :class (when drawing? "drawing")
:on-mouse-down on-mouse-down :on-mouse-down on-mouse-down
@ -197,11 +124,11 @@
(on-key-down [event] (on-key-down [event]
(when (kbd/space? event) (when (kbd/space? event)
(wb/emit-interaction! :scroll/viewport))) (uuc/emit-action! :scroll/viewport)))
(on-key-up [event] (on-key-up [event]
(when (kbd/space? event) (when (kbd/space? event)
(wb/emit-interaction! :nothing))) (uuc/emit-action! :nothing)))
(on-mousemove [event] (on-mousemove [event]
(let [wpt (gpt/point (.-clientX event) (let [wpt (gpt/point (.-clientX event)
@ -213,7 +140,7 @@
:window-coords wpt :window-coords wpt
:viewport-coords vppt :viewport-coords vppt
:canvas-coords cvpt}] :canvas-coords cvpt}]
(rx/push! wb/mouse-b event)))] (rx/push! uuwb/mouse-b event)))]
(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)

View file

@ -5,9 +5,10 @@
[beicon.core :as rx] [beicon.core :as rx]
[cats.labs.lens :as l] [cats.labs.lens :as l]
[uxbox.rstore :as rs] [uxbox.rstore :as rs]
[uxbox.state :as st] [uxbox.shapes :as ush]
[uxbox.shapes :as sh]
[uxbox.data.workspace :as dw] [uxbox.data.workspace :as dw]
[uxbox.ui.core :as uuc]
[uxbox.ui.shapes.core :as uusc]
[uxbox.ui.workspace.base :as wb] [uxbox.ui.workspace.base :as wb]
[uxbox.ui.mixins :as mx] [uxbox.ui.mixins :as mx]
[uxbox.util.geom.point :as gpt] [uxbox.util.geom.point :as gpt]
@ -25,8 +26,8 @@
(let [shape (rum/react +drawing-shape+) (let [shape (rum/react +drawing-shape+)
position (rum/react +drawing-position+)] position (rum/react +drawing-position+)]
(when shape (when shape
(-> (sh/-resize shape position) (-> (ush/-resize shape position)
(sh/-render identity))))) (uusc/render-shape identity)))))
(def ^:static draw-area (def ^:static draw-area
(mx/component (mx/component
@ -44,11 +45,11 @@
(define-once :drawing-subscriptions (define-once :drawing-subscriptions
(letfn [(init-shape [shape] (letfn [(init-shape [shape]
(let [{:keys [x y] :as point} @wb/mouse-canvas-a (let [{:keys [x y] :as point} @wb/mouse-canvas-a
shape (sh/-initialize shape {:x1 x :y1 y :x2 x :y2 y})] shape (ush/-initialize shape {:x1 x :y1 y :x2 x :y2 y})]
(reset! +drawing-shape+ shape) (reset! +drawing-shape+ shape)
(reset! +drawing-position+ (assoc point :lock false)) (reset! +drawing-position+ (assoc point :lock false))
(let [stoper (->> wb/interactions-b (let [stoper (->> uuc/actions-s
(rx/filter #(not= % :shape/movement)) (rx/filter #(not= % :shape/movement))
(rx/take 1))] (rx/take 1))]
(as-> wb/mouse-canvas-s $ (as-> wb/mouse-canvas-s $
@ -62,7 +63,7 @@
(on-complete [] (on-complete []
(let [shape @+drawing-shape+ (let [shape @+drawing-shape+
shpos @+drawing-position+ shpos @+drawing-position+
shape (sh/-resize shape shpos)] shape (ush/-resize shape shpos)]
(rs/emit! (dw/add-shape shape) (rs/emit! (dw/add-shape shape)
(dw/select-for-drawing nil)) (dw/select-for-drawing nil))
(reset! +drawing-position+ nil) (reset! +drawing-position+ nil)
@ -71,7 +72,7 @@
(init-icon [shape] (init-icon [shape]
(let [{:keys [x y]} @wb/mouse-canvas-a (let [{:keys [x y]} @wb/mouse-canvas-a
props {:x1 x :y1 y :x2 (+ x 100) :y2 (+ y 100)} props {:x1 x :y1 y :x2 (+ x 100) :y2 (+ y 100)}
shape (sh/-initialize shape props)] shape (ush/-initialize shape props)]
(rs/emit! (dw/add-shape shape) (rs/emit! (dw/add-shape shape)
(dw/select-for-drawing nil)))) (dw/select-for-drawing nil))))
(init [] (init []
@ -82,7 +83,7 @@
:builtin/circle (init-shape shape) :builtin/circle (init-shape shape)
:builtin/line (init-shape shape))))] :builtin/line (init-shape shape))))]
(as-> wb/interactions-b $ (as-> uuc/actions-s $
(rx/dedupe $) (rx/dedupe $)
(rx/filter #(= :draw/shape %) $) (rx/filter #(= :draw/shape %) $)
(rx/on-value $ init)))) (rx/on-value $ init))))

View file

@ -3,28 +3,29 @@
(:require [beicon.core :as rx] (:require [beicon.core :as rx]
[cats.labs.lens :as l] [cats.labs.lens :as l]
[uxbox.rstore :as rs] [uxbox.rstore :as rs]
[uxbox.state :as st] [uxbox.state :as ust]
[uxbox.ui.workspace.base :as wb] [uxbox.ui.core :as uuc]
[uxbox.ui.workspace.base :as uuwb]
[uxbox.data.workspace :as dw])) [uxbox.data.workspace :as dw]))
(define-once :movement-subscription (define-once :movement-subscription
(letfn [(on-value [delta] (letfn [(on-value [delta]
(let [pageid (get-in @st/state [:workspace :page]) (let [pageid (get-in @ust/state [:workspace :page])
selected (get-in @st/state [:workspace :selected]) selected (get-in @ust/state [:workspace :selected])
shapes (->> (vals @wb/shapes-by-id-l) shapes (->> (vals @uuwb/shapes-by-id-l)
(filter #(= (:page %) pageid)) (filter #(= (:page %) pageid))
(filter (comp selected :id)))] (filter (comp selected :id)))]
(doseq [{:keys [id group]} shapes] (doseq [{:keys [id group]} shapes]
(rs/emit! (dw/move-shape id delta))))) (rs/emit! (dw/move-shape id delta)))))
(init [] (init []
(as-> wb/interactions-b $ (as-> uuc/actions-s $
(rx/filter #(not= % :shape/movement) $) (rx/filter #(not= % :shape/movement) $)
(rx/take 1 $) (rx/take 1 $)
(rx/take-until $ wb/mouse-delta-s) (rx/take-until $ uuwb/mouse-delta-s)
(rx/on-value $ on-value)))] (rx/on-value $ on-value)))]
(as-> wb/interactions-b $ (as-> uuc/actions-s $
(rx/dedupe $) (rx/dedupe $)
(rx/filter #(= :shape/movement %) $) (rx/filter #(= :shape/movement %) $)
(rx/on-value $ init)))) (rx/on-value $ init))))

View file

@ -9,6 +9,7 @@
[uxbox.state :as st] [uxbox.state :as st]
[uxbox.shapes :as sh] [uxbox.shapes :as sh]
[uxbox.data.workspace :as dw] [uxbox.data.workspace :as dw]
[uxbox.ui.core :as uuc]
[uxbox.ui.workspace.base :as wb] [uxbox.ui.workspace.base :as wb]
[uxbox.ui.mixins :as mx] [uxbox.ui.mixins :as mx]
[uxbox.util.geom.point :as gpt] [uxbox.util.geom.point :as gpt]
@ -74,7 +75,7 @@
(reset! selrect-pos nil))) (reset! selrect-pos nil)))
(init [] (init []
(let [stoper (->> wb/interactions-b (let [stoper (->> uuc/actions-s
(rx/filter #(not= % :draw/selrect)) (rx/filter #(not= % :draw/selrect))
(rx/take 1)) (rx/take 1))
pos @wb/mouse-viewport-a] pos @wb/mouse-viewport-a]
@ -84,7 +85,7 @@
(rx/take-until stoper $) (rx/take-until stoper $)
(rx/subscribe $ on-value nil on-complete))))] (rx/subscribe $ on-value nil on-complete))))]
(as-> wb/interactions-b $ (as-> uuc/actions-s $
(rx/dedupe $) (rx/dedupe $)
(rx/filter #(= :draw/selrect %) $) (rx/filter #(= :draw/selrect %) $)
(rx/on-value $ init)))) (rx/on-value $ init))))

View file

@ -5,20 +5,20 @@
[uxbox.router :as r] [uxbox.router :as r]
[uxbox.rstore :as rs] [uxbox.rstore :as rs]
[uxbox.state :as st] [uxbox.state :as st]
[uxbox.shapes :as shapes]
[uxbox.library :as library] [uxbox.library :as library]
[uxbox.util.data :refer (read-string)]
[uxbox.data.workspace :as dw] [uxbox.data.workspace :as dw]
[uxbox.ui.shapes.core :as uusc]
[uxbox.ui.workspace.base :as wb] [uxbox.ui.workspace.base :as wb]
[uxbox.ui.icons :as i] [uxbox.ui.icons :as i]
[uxbox.ui.mixins :as mx] [uxbox.ui.mixins :as mx]
[uxbox.util.dom :as dom])) [uxbox.util.dom :as dom]
[uxbox.util.data :refer (read-string)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Lenses ;; Lenses
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:private ^:static drawing-shape (def ^:private ^:const drawing-shape
"A focused vision of the drawing property "A focused vision of the drawing property
of the workspace status. This avoids of the workspace status. This avoids
rerender the whole toolbox on each workspace rerender the whole toolbox on each workspace
@ -45,7 +45,7 @@
(defn- icon-wrapper-render (defn- icon-wrapper-render
[own icon] [own icon]
(shapes/-render-svg icon nil)) (uusc/render-shape-svg icon nil))
(def ^:static ^:private icon-wrapper (def ^:static ^:private icon-wrapper
(mx/component (mx/component

View file

@ -7,10 +7,10 @@
[uxbox.router :as r] [uxbox.router :as r]
[uxbox.rstore :as rs] [uxbox.rstore :as rs]
[uxbox.state :as st] [uxbox.state :as st]
[uxbox.shapes :as shapes]
[uxbox.library :as library] [uxbox.library :as library]
[uxbox.util.data :refer (read-string classnames)] [uxbox.util.data :refer (read-string classnames)]
[uxbox.data.workspace :as dw] [uxbox.data.workspace :as dw]
[uxbox.ui.shapes.core :as uusc]
[uxbox.ui.workspace.base :as wb] [uxbox.ui.workspace.base :as wb]
[uxbox.ui.icons :as i] [uxbox.ui.icons :as i]
[uxbox.ui.mixins :as mx] [uxbox.ui.mixins :as mx]
@ -86,7 +86,7 @@
(defn- element-icon (defn- element-icon
[item] [item]
(case (:type item) (case (:type item)
:builtin/icon (shapes/-render-svg item) :builtin/icon (uusc/render-shape-svg item)
:builtin/line i/line :builtin/line i/line
:builtin/circle i/circle :builtin/circle i/circle
:builtin/rect i/box :builtin/rect i/box