mirror of
https://github.com/penpot/penpot.git
synced 2025-03-17 10:11:22 -05:00
Merge branch 'alignment'
This commit is contained in:
commit
574d78871a
31 changed files with 666 additions and 318 deletions
|
@ -25,5 +25,8 @@ var TopLevel = {
|
|||
"value": function() {},
|
||||
"identifier": function() {},
|
||||
"getBoundingClientRect": function() {},
|
||||
"getBBox": function() {}
|
||||
"getBBox": function() {},
|
||||
"addEventListener": function() {},
|
||||
"postMessage": function() {},
|
||||
"data": function() {}
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
(ra/start-figwheel!
|
||||
{:figwheel-options {:css-dirs ["resources/public/css"]}
|
||||
:build-ids ["dev"]
|
||||
:build-ids ["dev" "worker"]
|
||||
:all-builds
|
||||
[{:id "dev"
|
||||
:figwheel {:on-jsload "uxbox.ui/init"}
|
||||
|
@ -20,10 +20,24 @@
|
|||
"https://test.uxbox.io/api"}
|
||||
:warnings {:ns-var-clash false}
|
||||
:pretty-print true
|
||||
:language-in :ecmascript5
|
||||
:language-in :ecmascript6
|
||||
:language-out :ecmascript5
|
||||
:output-to "resources/public/js/main.js"
|
||||
:output-dir "resources/public/js"
|
||||
:verbose true}}
|
||||
|
||||
{:id "worker"
|
||||
:source-paths ["src" "vendor"]
|
||||
:compiler {:main 'uxbox.worker
|
||||
:asset-path "js"
|
||||
:parallel-build false
|
||||
:optimizations :simple
|
||||
:warnings {:ns-var-clash false}
|
||||
:pretty-print true
|
||||
:static-fns true
|
||||
:language-in :ecmascript6
|
||||
:language-out :ecmascript5
|
||||
:output-to "resources/public/js/worker.js"
|
||||
:verbose true}}]})
|
||||
|
||||
(ra/cljs-repl)
|
||||
(ra/cljs-repl "dev")
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#!/bin/sh
|
||||
lein trampoline run -m clojure.main scripts/watch.clj
|
||||
lein trampoline run -m clojure.main scripts/watch-tests.clj
|
||||
|
|
22
src/uxbox/constants.cljs
Normal file
22
src/uxbox/constants.cljs
Normal file
|
@ -0,0 +1,22 @@
|
|||
;; 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-2016 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.constants)
|
||||
|
||||
(def ^:const grid-x-axis 10)
|
||||
(def ^:const grid-y-axis 10)
|
||||
|
||||
(def ^:const viewport-width 4000)
|
||||
(def ^:const viewport-height 4000)
|
||||
|
||||
(def ^:const canvas-start-x 1200)
|
||||
(def ^:const canvas-start-y 1200)
|
||||
(def ^:const canvas-scroll-padding 50)
|
||||
(def ^:const canvas-start-scroll-x (- canvas-start-x canvas-scroll-padding))
|
||||
(def ^:const canvas-start-scroll-y (- canvas-start-y canvas-scroll-padding))
|
||||
|
||||
|
|
@ -94,10 +94,17 @@
|
|||
(let [shape (get-in state [:shapes-by-id id])]
|
||||
(stsh/dissoc-shape state shape)))))
|
||||
|
||||
(defn update-shape
|
||||
"Just updates in place the shape."
|
||||
[{:keys [id] :as shape}]
|
||||
(reify
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(update-in state [:shapes-by-id id] merge shape))))
|
||||
|
||||
(defn move-shape
|
||||
"Mark a shape selected for drawing in the canvas."
|
||||
"Move shape using relative position (delta)."
|
||||
[sid delta]
|
||||
{:pre [(gpt/point? delta)]}
|
||||
(reify
|
||||
udp/IPageUpdate
|
||||
rs/UpdateEvent
|
||||
|
@ -338,3 +345,144 @@
|
|||
(-apply-update [_ state]
|
||||
(stsh/drop-shape state sid tid loc))))
|
||||
|
||||
(defn select-shape
|
||||
"Mark a shape selected for drawing in the canvas."
|
||||
[id]
|
||||
(reify
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(let [selected (get-in state [:workspace :selected])]
|
||||
(if (contains? selected id)
|
||||
(update-in state [:workspace :selected] disj id)
|
||||
(update-in state [:workspace :selected] conj id))))))
|
||||
|
||||
(defn select-shapes
|
||||
"Select shapes that matches the select rect."
|
||||
[selrect]
|
||||
(reify
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(let [pageid (get-in state [:workspace :page])
|
||||
xf (comp
|
||||
(filter #(= (:page %) pageid))
|
||||
(remove :hidden)
|
||||
(remove :blocked)
|
||||
(map sh/outer-rect')
|
||||
(filter #(sh/contained-in? % selrect))
|
||||
(map :id))]
|
||||
(->> (into #{} xf (vals (:shapes-by-id state)))
|
||||
(assoc-in state [:workspace :selected]))))))
|
||||
|
||||
;; --- Events (implicit) (for selected)
|
||||
|
||||
(defn deselect-all
|
||||
"Mark a shape selected for drawing in the canvas."
|
||||
[]
|
||||
(reify
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(assoc-in state [:workspace :selected] #{}))))
|
||||
|
||||
(defn group-selected
|
||||
[]
|
||||
(letfn [(update-shapes-on-page [state pid selected group]
|
||||
(as-> (get-in state [:pages-by-id pid :shapes]) $
|
||||
(remove selected $)
|
||||
(into [group] $)
|
||||
(assoc-in state [:pages-by-id pid :shapes] $)))
|
||||
|
||||
(update-shapes-on-index [state shapes group]
|
||||
(reduce (fn [state {:keys [id] :as shape}]
|
||||
(as-> shape $
|
||||
(assoc $ :group group)
|
||||
(assoc-in state [:shapes-by-id id] $)))
|
||||
state
|
||||
shapes))
|
||||
(valid-selection? [shapes]
|
||||
(let [groups (into #{} (map :group shapes))]
|
||||
(= 1 (count groups))))]
|
||||
(reify
|
||||
udp/IPageUpdate
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(let [shapes-by-id (get state :shapes-by-id)
|
||||
sid (random-uuid)
|
||||
pid (get-in state [:workspace :page])
|
||||
selected (get-in state [:workspace :selected])
|
||||
selected' (map #(get shapes-by-id %) selected)
|
||||
group {:type :builtin/group
|
||||
:name (str "Group " (rand-int 1000))
|
||||
:items (into [] selected)
|
||||
:id sid
|
||||
:page pid}]
|
||||
(if (valid-selection? selected')
|
||||
(as-> state $
|
||||
(update-shapes-on-index $ selected' sid)
|
||||
(update-shapes-on-page $ pid selected sid)
|
||||
(update $ :shapes-by-id assoc sid group)
|
||||
(update $ :workspace assoc :selected #{}))
|
||||
state))))))
|
||||
|
||||
;; TODO: maybe split in two separate events
|
||||
(defn duplicate-selected
|
||||
[]
|
||||
(reify
|
||||
udp/IPageUpdate
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(let [selected (get-in state [:workspace :selected])]
|
||||
(stsh/duplicate-shapes state selected)))))
|
||||
|
||||
(defn delete-selected
|
||||
"Deselect all and remove all selected shapes."
|
||||
[]
|
||||
(reify
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(let [selected (get-in state [:workspace :selected])]
|
||||
(rx/from-coll
|
||||
(into [(deselect-all)] (map #(delete-shape %) selected)))))))
|
||||
|
||||
(defn move-selected
|
||||
"Move a minimal position unit the selected shapes."
|
||||
([dir] (move-selected dir 1))
|
||||
([dir n]
|
||||
{:pre [(contains? #{:up :down :right :left} dir)]}
|
||||
(reify
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(let [selected (get-in state [:workspace :selected])
|
||||
delta (case dir
|
||||
:up (gpt/point 0 (- n))
|
||||
:down (gpt/point 0 n)
|
||||
:right (gpt/point n 0)
|
||||
:left (gpt/point (- n) 0))]
|
||||
(rx/from-coll
|
||||
(map #(move-shape % delta) selected)))))))
|
||||
|
||||
(defn update-selected-shapes-fill
|
||||
"Update the fill related attributed on
|
||||
selected shapes."
|
||||
[opts]
|
||||
(sc/validate! +shape-fill-attrs-schema+ opts)
|
||||
(reify
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(rx/from-coll
|
||||
(->> (get-in state [:workspace :selected])
|
||||
(map #(update-fill-attrs % opts)))))))
|
||||
|
||||
|
||||
(defn update-selected-shapes-stroke
|
||||
"Update the fill related attributed on
|
||||
selected shapes."
|
||||
[opts]
|
||||
(sc/validate! +shape-stroke-attrs-schema+ opts)
|
||||
(reify
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(rx/from-coll
|
||||
(->> (get-in state [:workspace :selected])
|
||||
(map #(update-stroke-attrs % opts)))))))
|
||||
|
||||
|
||||
|
|
32
src/uxbox/data/worker.cljs
Normal file
32
src/uxbox/data/worker.cljs
Normal file
|
@ -0,0 +1,32 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.data.worker
|
||||
"Worker related api and initialization events."
|
||||
(:require [beicon.core :as rx]
|
||||
[uxbox.rstore :as rs]
|
||||
[uxbox.constants :as c]
|
||||
[uxbox.util.workers :as uw]))
|
||||
|
||||
(defonce worker (uw/init "/js/worker.js"))
|
||||
|
||||
;; --- Worker Initialization
|
||||
|
||||
(defrecord InitializeWorker [id]
|
||||
rs/EffectEvent
|
||||
(-apply-effect [_ state]
|
||||
(let [page (get-in state [:pages-by-id id])
|
||||
opts (:options page)
|
||||
message {:cmd :grid/init
|
||||
:width c/viewport-width
|
||||
:height c/viewport-height
|
||||
:x-axis (:grid/x-axis opts c/grid-x-axis)
|
||||
:y-axis (:grid/y-axis opts c/grid-y-axis)}]
|
||||
(uw/send! worker message))))
|
||||
|
||||
(defn initialize
|
||||
[id]
|
||||
(InitializeWorker. id))
|
|
@ -10,39 +10,45 @@
|
|||
[beicon.core :as rx]
|
||||
[uxbox.shapes :as sh]
|
||||
[uxbox.rstore :as rs]
|
||||
[uxbox.router :as r]
|
||||
[uxbox.state :as st]
|
||||
[uxbox.state.shapes :as stsh]
|
||||
[uxbox.schema :as sc]
|
||||
[uxbox.xforms :as xf]
|
||||
[uxbox.shapes :as sh]
|
||||
[uxbox.data.pages :as udp]
|
||||
[uxbox.data.shapes :as uds]
|
||||
[uxbox.data.worker :as wrk]
|
||||
[uxbox.util.datetime :as dt]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.data :refer (index-of)]))
|
||||
[uxbox.util.geom.point :as gpt]))
|
||||
|
||||
;; --- Events (concrete)
|
||||
;; --- Workspace Initialization
|
||||
|
||||
(defrecord InitializeWorkspace [project page]
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(if (:workspace state)
|
||||
(update state :workspace merge
|
||||
{:project project
|
||||
:page page
|
||||
:selected #{}
|
||||
:drawing nil})
|
||||
(assoc state :workspace
|
||||
{:project project
|
||||
:zoom 1
|
||||
:page page
|
||||
:flags #{:layers :element-options}
|
||||
:selected #{}
|
||||
:drawing nil})))
|
||||
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(if (get-in state [:pages-by-id page])
|
||||
(rx/of (wrk/initialize page))
|
||||
(->> (rx/filter udp/pages-fetched? s)
|
||||
(rx/take 1)
|
||||
(rx/map #(wrk/initialize page))))))
|
||||
|
||||
(defn initialize
|
||||
"Initialize the workspace state."
|
||||
[project page]
|
||||
(reify
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(if (:workspace state)
|
||||
(update state :workspace merge
|
||||
{:project project
|
||||
:page page
|
||||
:selected #{}
|
||||
:drawing nil})
|
||||
(assoc state :workspace
|
||||
{:project project
|
||||
:zoom 1
|
||||
:page page
|
||||
:flags #{:layers :element-options}
|
||||
:selected #{}
|
||||
:drawing nil})))))
|
||||
(InitializeWorkspace. project page))
|
||||
|
||||
(defn toggle-flag
|
||||
"Toggle the enabled flag of the specified tool."
|
||||
|
@ -65,146 +71,6 @@
|
|||
(assoc-in state [:workspace :drawing] shape)
|
||||
(update-in state [:workspace] dissoc :drawing)))))
|
||||
|
||||
(defn select-shape
|
||||
"Mark a shape selected for drawing in the canvas."
|
||||
[id]
|
||||
(reify
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(let [selected (get-in state [:workspace :selected])]
|
||||
(if (contains? selected id)
|
||||
(update-in state [:workspace :selected] disj id)
|
||||
(update-in state [:workspace :selected] conj id))))))
|
||||
|
||||
(defn select-shapes
|
||||
"Select shapes that matches the select rect."
|
||||
[selrect]
|
||||
(reify
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(let [pageid (get-in state [:workspace :page])
|
||||
xf (comp
|
||||
(filter #(= (:page %) pageid))
|
||||
(remove :hidden)
|
||||
(remove :blocked)
|
||||
(map sh/outer-rect')
|
||||
(filter #(sh/contained-in? % selrect))
|
||||
(map :id))]
|
||||
(->> (into #{} xf (vals (:shapes-by-id state)))
|
||||
(assoc-in state [:workspace :selected]))))))
|
||||
|
||||
;; --- Events (implicit) (for selected)
|
||||
|
||||
(defn deselect-all
|
||||
"Mark a shape selected for drawing in the canvas."
|
||||
[]
|
||||
(reify
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(assoc-in state [:workspace :selected] #{}))))
|
||||
|
||||
(defn group-selected
|
||||
[]
|
||||
(letfn [(update-shapes-on-page [state pid selected group]
|
||||
(as-> (get-in state [:pages-by-id pid :shapes]) $
|
||||
(remove selected $)
|
||||
(into [group] $)
|
||||
(assoc-in state [:pages-by-id pid :shapes] $)))
|
||||
|
||||
(update-shapes-on-index [state shapes group]
|
||||
(reduce (fn [state {:keys [id] :as shape}]
|
||||
(as-> shape $
|
||||
(assoc $ :group group)
|
||||
(assoc-in state [:shapes-by-id id] $)))
|
||||
state
|
||||
shapes))
|
||||
(valid-selection? [shapes]
|
||||
(let [groups (into #{} (map :group shapes))]
|
||||
(= 1 (count groups))))]
|
||||
(reify
|
||||
udp/IPageUpdate
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(let [shapes-by-id (get state :shapes-by-id)
|
||||
sid (random-uuid)
|
||||
pid (get-in state [:workspace :page])
|
||||
selected (get-in state [:workspace :selected])
|
||||
selected' (map #(get shapes-by-id %) selected)
|
||||
group {:type :builtin/group
|
||||
:name (str "Group " (rand-int 1000))
|
||||
:items (into [] selected)
|
||||
:id sid
|
||||
:page pid}]
|
||||
(if (valid-selection? selected')
|
||||
(as-> state $
|
||||
(update-shapes-on-index $ selected' sid)
|
||||
(update-shapes-on-page $ pid selected sid)
|
||||
(update $ :shapes-by-id assoc sid group)
|
||||
(update $ :workspace assoc :selected #{}))
|
||||
state))))))
|
||||
|
||||
;; TODO: maybe split in two separate events
|
||||
(defn duplicate-selected
|
||||
[]
|
||||
(reify
|
||||
udp/IPageUpdate
|
||||
rs/UpdateEvent
|
||||
(-apply-update [_ state]
|
||||
(let [selected (get-in state [:workspace :selected])]
|
||||
(stsh/duplicate-shapes state selected)))))
|
||||
|
||||
(defn delete-selected
|
||||
"Deselect all and remove all selected shapes."
|
||||
[]
|
||||
(reify
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(let [selected (get-in state [:workspace :selected])]
|
||||
(rx/from-coll
|
||||
(into [(deselect-all)] (map #(uds/delete-shape %) selected)))))))
|
||||
|
||||
(defn move-selected
|
||||
"Move a minimal position unit the selected shapes."
|
||||
([dir] (move-selected dir 1))
|
||||
([dir n]
|
||||
{:pre [(contains? #{:up :down :right :left} dir)]}
|
||||
(reify
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(let [selected (get-in state [:workspace :selected])
|
||||
delta (case dir
|
||||
:up (gpt/point 0 (- n))
|
||||
:down (gpt/point 0 n)
|
||||
:right (gpt/point n 0)
|
||||
:left (gpt/point (- n) 0))]
|
||||
(rx/from-coll
|
||||
(map #(uds/move-shape % delta) selected)))))))
|
||||
|
||||
(defn update-selected-shapes-fill
|
||||
"Update the fill related attributed on
|
||||
selected shapes."
|
||||
[opts]
|
||||
(sc/validate! uds/+shape-fill-attrs-schema+ opts)
|
||||
(reify
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(rx/from-coll
|
||||
(->> (get-in state [:workspace :selected])
|
||||
(map #(uds/update-fill-attrs % opts)))))))
|
||||
|
||||
|
||||
(defn update-selected-shapes-stroke
|
||||
"Update the fill related attributed on
|
||||
selected shapes."
|
||||
[opts]
|
||||
(sc/validate! uds/+shape-stroke-attrs-schema+ opts)
|
||||
(reify
|
||||
rs/WatchEvent
|
||||
(-apply-watch [_ state s]
|
||||
(rx/from-coll
|
||||
(->> (get-in state [:workspace :selected])
|
||||
(map #(uds/update-stroke-attrs % opts)))))))
|
||||
|
||||
;; --- Copy to Clipboard
|
||||
|
||||
(defrecord CopyToClipboard []
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
[hodgepodge.core :refer (local-storage)]
|
||||
[promesa.core :as p :include-macros true]
|
||||
[beicon.core :as rx]
|
||||
[uxbox.transit :as t]
|
||||
[uxbox.util.transit :as t]
|
||||
[uxbox.state :as ust])
|
||||
(:import [goog.Uri QueryData]))
|
||||
|
||||
|
|
|
@ -211,7 +211,7 @@
|
|||
[shape _]
|
||||
(throw (ex-info "Not implemented (size)" (select-keys shape [:type]))))
|
||||
|
||||
;; Move
|
||||
;; Move (with deltas)
|
||||
|
||||
(defmethod move ::rect
|
||||
[shape {dx :x dy :y}]
|
||||
|
|
|
@ -20,20 +20,20 @@
|
|||
|
||||
(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 #(uusi/on-mouse-down % shape selected)
|
||||
on-mouse-up #(uusi/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 (and selected? (= (count selected) 1))
|
||||
(handlers shape))])))
|
||||
;; (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 #(uusi/on-mouse-down % shape selected)
|
||||
;; on-mouse-up #(uusi/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 (and selected? (= (count selected) 1))
|
||||
;; (handlers shape))])))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Circle Handlers
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
[uxbox.state :as st]
|
||||
[uxbox.shapes :as ush]
|
||||
[uxbox.data.workspace :as dw]
|
||||
[uxbox.data.shapes :as uds]
|
||||
[uxbox.ui.core :as uuc]
|
||||
[uxbox.ui.mixins :as mx]
|
||||
[uxbox.ui.keyboard :as kbd]
|
||||
|
@ -28,16 +29,16 @@
|
|||
(and (not selected?) (empty? selected))
|
||||
(do
|
||||
(dom/stop-propagation event)
|
||||
(uuc/acquire-action! "ui.shape.move")
|
||||
(rs/emit! (dw/select-shape id)))
|
||||
(rs/emit! (uds/select-shape id))
|
||||
(uuc/acquire-action! "ui.shape.move"))
|
||||
|
||||
(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))))
|
||||
(rs/emit! (uds/select-shape id))
|
||||
(rs/emit! (uds/deselect-all)
|
||||
(uds/select-shape id))))
|
||||
|
||||
:else
|
||||
(do
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
[uxbox.rstore :as rs]
|
||||
[uxbox.state :as st]
|
||||
[uxbox.shapes :as ush]
|
||||
[uxbox.data.shapes :as ds]
|
||||
[uxbox.data.shapes :as uds]
|
||||
[uxbox.data.workspace :as dw]
|
||||
[uxbox.ui.core :as uuc]
|
||||
[uxbox.ui.mixins :as mx]
|
||||
|
@ -34,15 +34,15 @@
|
|||
(do
|
||||
(dom/stop-propagation event)
|
||||
(uuc/acquire-action! "ui.shape.move")
|
||||
(rs/emit! (dw/select-shape id)))
|
||||
(rs/emit! (uds/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))))
|
||||
(rs/emit! (uds/select-shape id))
|
||||
(rs/emit! (uds/deselect-all)
|
||||
(uds/select-shape id))))
|
||||
|
||||
:else
|
||||
(do
|
||||
|
@ -80,7 +80,7 @@
|
|||
(on-input [ev]
|
||||
(let [content (dom/event->inner-text ev)
|
||||
sid (:id (first (:rum/props own)))]
|
||||
(rs/emit! (ds/update-text sid {:content content}))))]
|
||||
(rs/emit! (uds/update-text sid {:content content}))))]
|
||||
(let [dom (mx/get-ref-dom own "main")
|
||||
dom2 (mx/get-ref-dom own "container")
|
||||
key1 (events/listen dom EventType.DBLCLICK on-double-click)
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
;; 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-2016 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.ui.workspace
|
||||
(:require [sablono.core :as html :refer-macros [html]]
|
||||
[rum.core :as rum]
|
||||
[beicon.core :as rx]
|
||||
[uxbox.constants :as c]
|
||||
[uxbox.rstore :as rs]
|
||||
[uxbox.data.workspace :as dw]
|
||||
[uxbox.data.pages :as udp]
|
||||
|
@ -42,8 +50,8 @@
|
|||
dom (mx/get-ref-dom own "workspace-canvas")]
|
||||
|
||||
;; Set initial scroll position
|
||||
(set! (.-scrollLeft dom) (* wb/canvas-start-scroll-x @wb/zoom-l))
|
||||
(set! (.-scrollTop dom) (* wb/canvas-start-scroll-y @wb/zoom-l))
|
||||
(set! (.-scrollLeft dom) (* c/canvas-start-scroll-x @wb/zoom-l))
|
||||
(set! (.-scrollTop dom) (* c/canvas-start-scroll-y @wb/zoom-l))
|
||||
|
||||
(assoc own ::sub1 sub1 ::sub2 sub2)))
|
||||
|
||||
|
@ -89,8 +97,8 @@
|
|||
(rs/emit! (dw/decrease-zoom)))
|
||||
|
||||
(let [dom (mx/get-ref-dom own "workspace-canvas")]
|
||||
(set! (.-scrollLeft dom) (* wb/canvas-start-scroll-x @wb/zoom-l))
|
||||
(set! (.-scrollTop dom) (* wb/canvas-start-scroll-y @wb/zoom-l)))))
|
||||
(set! (.-scrollLeft dom) (* c/canvas-start-scroll-x @wb/zoom-l))
|
||||
(set! (.-scrollTop dom) (* c/canvas-start-scroll-y @wb/zoom-l)))))
|
||||
|
||||
(defn- workspace-render
|
||||
[own projectid]
|
||||
|
|
37
src/uxbox/ui/workspace/align.cljs
Normal file
37
src/uxbox/ui/workspace/align.cljs
Normal file
|
@ -0,0 +1,37 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.ui.workspace.align
|
||||
"Shape alignmen impl."
|
||||
(:require [beicon.core :as rx]
|
||||
[lentes.core :as l]
|
||||
[uxbox.state :as st]
|
||||
[uxbox.shapes :as sh]
|
||||
[uxbox.data.worker :refer (worker)]
|
||||
[uxbox.ui.workspace.base :as wb]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.workers :as uw]))
|
||||
|
||||
(defn- move
|
||||
[shape p1]
|
||||
(let [dx (- (:x2 shape) (:x1 shape))
|
||||
dy (- (:y2 shape) (:y1 shape))
|
||||
p2 (gpt/add p1 [dx dy])]
|
||||
(assoc shape
|
||||
:x1 (:x p1)
|
||||
:y1 (:y p1)
|
||||
:x2 (:x p2)
|
||||
:y2 (:y p2))))
|
||||
|
||||
(defn translate
|
||||
[{:keys [x1 y1] :as shape}]
|
||||
(let [message {:cmd :grid/align
|
||||
:point (gpt/point x1 y1)}]
|
||||
(->> (uw/ask! worker message)
|
||||
(rx/map (fn [{:keys [point]}]
|
||||
(if point
|
||||
(move shape point)
|
||||
shape))))))
|
|
@ -18,9 +18,7 @@
|
|||
[goog.events :as events])
|
||||
(:import goog.events.EventType))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Lenses
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; --- Lenses
|
||||
|
||||
(def ^:const workspace-l
|
||||
(as-> (l/in [:workspace]) $
|
||||
|
@ -60,9 +58,7 @@
|
|||
(-> (l/in [:workspace :zoom])
|
||||
(l/focus-atom st/state)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Scroll Stream
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; --- Scroll Stream
|
||||
|
||||
(defonce scroll-b (rx/bus))
|
||||
|
||||
|
@ -75,9 +71,7 @@
|
|||
(defonce scroll-a
|
||||
(rx/to-atom scroll-s))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Mouse Position Stream
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; --- Mouse Position Stream
|
||||
|
||||
(defonce mouse-b (rx/bus))
|
||||
(defonce mouse-s
|
||||
|
@ -119,16 +113,3 @@
|
|||
(rx/buffer 2 1)
|
||||
(rx/map coords-delta)
|
||||
(rx/share)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Constants
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def ^:const viewport-width 4000)
|
||||
(def ^:const viewport-height 4000)
|
||||
|
||||
(def ^:const canvas-start-x 1200)
|
||||
(def ^:const canvas-start-y 1200)
|
||||
(def ^:const canvas-scroll-padding 50)
|
||||
(def ^:const canvas-start-scroll-x (- canvas-start-x canvas-scroll-padding))
|
||||
(def ^:const canvas-start-scroll-y (- canvas-start-y canvas-scroll-padding))
|
||||
|
|
|
@ -11,10 +11,12 @@
|
|||
[beicon.core :as rx]
|
||||
[lentes.core :as l]
|
||||
[goog.events :as events]
|
||||
[uxbox.constants :as c]
|
||||
[uxbox.rstore :as rs]
|
||||
[uxbox.shapes :as sh]
|
||||
[uxbox.data.projects :as dp]
|
||||
[uxbox.data.workspace :as dw]
|
||||
[uxbox.data.shapes :as uds]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.data :refer (parse-int)]
|
||||
|
@ -57,8 +59,8 @@
|
|||
(let [workspace (rum/react uuwb/workspace-l)
|
||||
flags (:flags workspace)]
|
||||
(html
|
||||
[:svg.page-canvas {:x uuwb/canvas-start-x
|
||||
:y uuwb/canvas-start-y
|
||||
[:svg.page-canvas {:x c/canvas-start-x
|
||||
:y c/canvas-start-y
|
||||
:ref (str "canvas" id)
|
||||
:width width
|
||||
:height height}
|
||||
|
@ -69,9 +71,7 @@
|
|||
(for [item (reverse (:shapes page))]
|
||||
(-> (uus/shape item)
|
||||
(rum/with-key (str item))))
|
||||
(draw-area)]]
|
||||
(when (contains? flags :grid)
|
||||
(grid))])))
|
||||
(draw-area)]]])))
|
||||
|
||||
(def canvas
|
||||
(mx/component
|
||||
|
@ -87,12 +87,13 @@
|
|||
[own]
|
||||
(let [workspace (rum/react uuwb/workspace-l)
|
||||
page (rum/react uuwb/page-l)
|
||||
flags (:flags workspace)
|
||||
drawing? (:drawing workspace)
|
||||
zoom (or (:zoom workspace) 1)]
|
||||
(letfn [(on-mouse-down [event]
|
||||
(dom/stop-propagation event)
|
||||
(when-not (empty? (:selected workspace))
|
||||
(rs/emit! (dw/deselect-all)))
|
||||
(rs/emit! (uds/deselect-all)))
|
||||
(if-let [shape (:drawing workspace)]
|
||||
(uuc/acquire-action! "ui.shape.draw")
|
||||
(uuc/acquire-action! "ui.selrect")))
|
||||
|
@ -101,17 +102,19 @@
|
|||
(uuc/release-action! "ui.shape"
|
||||
"ui.selrect"))]
|
||||
(html
|
||||
[:svg.viewport {:width (* uuwb/viewport-width zoom)
|
||||
:height (* uuwb/viewport-height zoom)
|
||||
[:svg.viewport {:width (* c/viewport-width zoom)
|
||||
:height (* c/viewport-height zoom)
|
||||
:ref "viewport"
|
||||
:class (when drawing? "drawing")
|
||||
:on-mouse-down on-mouse-down
|
||||
:on-mouse-up on-mouse-up}
|
||||
[:g.zoom {:transform (str "scale(" zoom ", " zoom ")")}
|
||||
(if page
|
||||
(canvas page))]
|
||||
(ruler)
|
||||
(selrect)]))))
|
||||
(canvas page))
|
||||
(if (contains? flags :grid)
|
||||
(grid))]
|
||||
(ruler)
|
||||
(selrect)]))))
|
||||
|
||||
(defn- viewport-did-mount
|
||||
[own]
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[uxbox.state :as st]
|
||||
[uxbox.library :as library]
|
||||
[uxbox.data.workspace :as dw]
|
||||
[uxbox.data.shapes :as uds]
|
||||
[uxbox.util.lens :as ul]
|
||||
[uxbox.util.data :refer (read-string)]
|
||||
[uxbox.util.color :refer (hex->rgb)]
|
||||
|
@ -48,8 +49,8 @@
|
|||
[color event]
|
||||
(dom/prevent-default event)
|
||||
(if (kbd/shift? event)
|
||||
(rs/emit! (dw/update-selected-shapes-stroke {:color color}))
|
||||
(rs/emit! (dw/update-selected-shapes-fill {:color color}))))
|
||||
(rs/emit! (uds/update-selected-shapes-stroke {:color color}))
|
||||
(rs/emit! (uds/update-selected-shapes-fill {:color color}))))
|
||||
|
||||
(defn- colorpalette-render
|
||||
[own]
|
||||
|
|
|
@ -9,21 +9,29 @@
|
|||
(:require [sablono.core :as html :refer-macros [html]]
|
||||
[rum.core :as rum]
|
||||
[cuerdas.core :as str]
|
||||
[uxbox.constants :as c]
|
||||
[uxbox.ui.mixins :as mx]
|
||||
[uxbox.ui.workspace.base :as wb]))
|
||||
|
||||
;; --- Grid (Component)
|
||||
|
||||
(declare ticks-range)
|
||||
(declare vertical-line)
|
||||
(declare horizontal-line)
|
||||
|
||||
(defn- grid-render
|
||||
[own]
|
||||
(let [{:keys [width height options]} (deref wb/page-l)
|
||||
(let [options (:options @wb/page-l)
|
||||
color (:grid/color options "#cccccc")
|
||||
x-ticks (ticks-range width (:grid/x-axis options 10))
|
||||
y-ticks (ticks-range height (:grid/y-axis options 10))
|
||||
width c/viewport-width
|
||||
height c/viewport-height
|
||||
x-ticks (range (- 0 c/canvas-start-x)
|
||||
(- width c/canvas-start-x)
|
||||
(:grid/x-axis options 10))
|
||||
|
||||
y-ticks (range (- 0 c/canvas-start-x)
|
||||
(- height c/canvas-start-x)
|
||||
(:grid/y-axis options 10))
|
||||
|
||||
path (as-> [] $
|
||||
(reduce (partial vertical-line height) $ x-ticks)
|
||||
(reduce (partial horizontal-line width) $ y-ticks))]
|
||||
|
@ -41,16 +49,10 @@
|
|||
|
||||
(defn- horizontal-line
|
||||
[width acc value]
|
||||
(let [pos (+ value wb/canvas-start-y)]
|
||||
(let [pos (+ value c/canvas-start-y)]
|
||||
(conj acc (str/format "M %s %s L %s %s" 0 pos width pos))))
|
||||
|
||||
(defn- vertical-line
|
||||
[height acc value]
|
||||
(let [pos (+ value wb/canvas-start-y)]
|
||||
(let [pos (+ value c/canvas-start-y)]
|
||||
(conj acc (str/format "M %s %s L %s %s" pos 0 pos height))))
|
||||
|
||||
(defn- ticks-range
|
||||
[size step]
|
||||
(range (- 0 wb/canvas-start-y)
|
||||
(- size wb/canvas-start-y)
|
||||
step))
|
||||
|
|
|
@ -8,16 +8,40 @@
|
|||
(ns uxbox.ui.workspace.movement
|
||||
"Shape movement in workspace logic."
|
||||
(:require [beicon.core :as rx]
|
||||
[lentes.core :as l]
|
||||
[uxbox.constants :as c]
|
||||
[uxbox.rstore :as rs]
|
||||
[uxbox.state :as st]
|
||||
[uxbox.shapes :as sh]
|
||||
[uxbox.ui.core :as uuc]
|
||||
[uxbox.ui.workspace.base :as wb]
|
||||
[uxbox.ui.workspace.align :as align]
|
||||
[uxbox.data.shapes :as uds]
|
||||
[uxbox.util.geom.point :as gpt]))
|
||||
|
||||
(declare initialize)
|
||||
(declare handle-movement)
|
||||
|
||||
;; --- Lenses
|
||||
|
||||
(declare translate-to-viewport)
|
||||
|
||||
(defn- resolve-selected
|
||||
[state]
|
||||
(let [selected (get-in state [:workspace :selected])
|
||||
xf (comp
|
||||
(map #(get-in state [:shapes-by-id %]))
|
||||
(map translate-to-viewport))]
|
||||
(into #{} xf selected)))
|
||||
|
||||
(def ^:const ^:private selected-shapes-l
|
||||
(-> (l/getter resolve-selected)
|
||||
(l/focus-atom st/state)))
|
||||
|
||||
(def ^:const ^:privae page-options-l
|
||||
(-> (l/key :options)
|
||||
(l/focus-atom wb/page-l)))
|
||||
|
||||
;; --- Public Api
|
||||
|
||||
(defn watch-move-actions
|
||||
|
@ -28,24 +52,63 @@
|
|||
|
||||
;; --- Implementation
|
||||
|
||||
(def coords
|
||||
(gpt/point c/canvas-start-x
|
||||
c/canvas-start-y))
|
||||
|
||||
(defn- translate-to-viewport
|
||||
[shape]
|
||||
(let [dx (- (:x2 shape) (:x1 shape))
|
||||
dy (- (:y2 shape) (:y1 shape))
|
||||
p1 (gpt/point (:x1 shape) (:y1 shape))
|
||||
p2 (gpt/add p1 coords)
|
||||
p3 (gpt/add p2 [dx dy])]
|
||||
(assoc shape
|
||||
:x1 (:x p2)
|
||||
:y1 (:y p2)
|
||||
:x2 (:x p3)
|
||||
:y2 (:y p3))))
|
||||
|
||||
(defn- translate-to-canvas
|
||||
[shape]
|
||||
(let [dx (- (:x2 shape) (:x1 shape))
|
||||
dy (- (:y2 shape) (:y1 shape))
|
||||
p1 (gpt/point (:x1 shape) (:y1 shape))
|
||||
p2 (gpt/subtract p1 coords)
|
||||
p3 (gpt/add p2 [dx dy])]
|
||||
(assoc shape
|
||||
:x1 (:x p2)
|
||||
:y1 (:y p2)
|
||||
:x2 (:x p3)
|
||||
:y2 (:y p3))))
|
||||
|
||||
(defn- initialize
|
||||
[]
|
||||
(let [stoper (->> uuc/actions-s
|
||||
(let [shapes @selected-shapes-l
|
||||
options @page-options-l
|
||||
stoper (->> uuc/actions-s
|
||||
(rx/map :type)
|
||||
(rx/filter empty?)
|
||||
(rx/take 1))]
|
||||
(as-> wb/mouse-delta-s $
|
||||
(rx/take-until stoper $)
|
||||
(rx/on-value $ handle-movement))))
|
||||
(rx/scan (fn [acc delta]
|
||||
(let [xf (map #(sh/move % delta))]
|
||||
(into [] xf acc))) shapes $)
|
||||
(rx/mapcat (fn [items]
|
||||
(if (:grid/align options)
|
||||
(->> (apply rx/of items)
|
||||
(rx/mapcat align/translate)
|
||||
(rx/reduce conj []))
|
||||
(rx/of items))) $)
|
||||
(rx/map (fn [items]
|
||||
(mapv translate-to-canvas items)) $)
|
||||
|
||||
(rx/subscribe $ handle-movement))))
|
||||
|
||||
(defn- handle-movement
|
||||
[delta]
|
||||
(let [pageid (get-in @st/state [:workspace :page])
|
||||
selected (get-in @st/state [:workspace :selected])
|
||||
shapes (->> (vals @wb/shapes-by-id-l)
|
||||
(filter #(= (:page %) pageid))
|
||||
(filter (comp selected :id)))
|
||||
delta (gpt/divide delta @wb/zoom-l)]
|
||||
(doseq [{:keys [id group]} shapes]
|
||||
(rs/emit! (uds/move-shape id delta)))))
|
||||
(doseq [shape delta]
|
||||
(rs/emit! (uds/update-shape shape))))
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
(:require [sablono.core :as html :refer-macros [html]]
|
||||
[rum.core :as rum]
|
||||
[beicon.core :as rx]
|
||||
[uxbox.constants :as c]
|
||||
[uxbox.rstore :as rs]
|
||||
[uxbox.util.math :as mth]
|
||||
[uxbox.ui.workspace.base :as wb]
|
||||
|
@ -104,8 +105,8 @@
|
|||
[:rect {:style {:fill "transparent"
|
||||
:stroke "transparent"
|
||||
:cursor "cell"}
|
||||
:width wb/viewport-width
|
||||
:height wb/viewport-height}]
|
||||
:width c/viewport-width
|
||||
:height c/viewport-height}]
|
||||
(if (and p1 p2)
|
||||
(overlay-line-render own p1 p2))])))
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[rum.core :as rum]
|
||||
[cuerdas.core :as str]
|
||||
[beicon.core :as rx]
|
||||
[uxbox.constants :as c]
|
||||
[uxbox.state :as s]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.ui.workspace.base :as wb]
|
||||
|
@ -24,8 +25,8 @@
|
|||
(defn mid-ticks-mod [zoom] (/ 50 zoom))
|
||||
|
||||
(def ^:const +ticks+
|
||||
(concat (range (- (/ wb/viewport-width 1)) 0 step-size)
|
||||
(range 0 (/ wb/viewport-width 1) step-size)))
|
||||
(concat (range (- (/ c/viewport-width 1)) 0 step-size)
|
||||
(range 0 (/ c/viewport-width 1) step-size)))
|
||||
|
||||
(def ^:const rule-padding 20)
|
||||
|
||||
|
@ -35,8 +36,8 @@
|
|||
mid-ticks-mod (mid-ticks-mod zoom)
|
||||
pos (+ (* value zoom)
|
||||
rule-padding
|
||||
(* wb/canvas-start-x zoom)
|
||||
wb/canvas-scroll-padding)]
|
||||
(* c/canvas-start-x zoom)
|
||||
c/canvas-scroll-padding)]
|
||||
(cond
|
||||
(< (mod value big-ticks-mod) step-size)
|
||||
(conj acc (str/format "M %s %s L %s %s" pos 5 pos step-padding))
|
||||
|
@ -52,8 +53,8 @@
|
|||
(let [big-ticks-mod (big-ticks-mod zoom)
|
||||
mid-ticks-mod (mid-ticks-mod zoom)
|
||||
pos (+ (* value zoom)
|
||||
(* wb/canvas-start-x zoom)
|
||||
wb/canvas-scroll-padding)]
|
||||
(* c/canvas-start-x zoom)
|
||||
c/canvas-scroll-padding)]
|
||||
(cond
|
||||
(< (mod value big-ticks-mod) step-size)
|
||||
(conj acc (str/format "M %s %s L %s %s" 5 pos step-padding pos))
|
||||
|
@ -71,8 +72,8 @@
|
|||
(let [big-ticks-mod (big-ticks-mod zoom)
|
||||
pos (+ (* value zoom)
|
||||
rule-padding
|
||||
(* wb/canvas-start-x zoom)
|
||||
wb/canvas-scroll-padding)]
|
||||
(* c/canvas-start-x zoom)
|
||||
c/canvas-scroll-padding)]
|
||||
(when (< (mod value big-ticks-mod) step-size)
|
||||
(html
|
||||
[:text {:x (+ pos 2)
|
||||
|
@ -88,9 +89,9 @@
|
|||
[zoom value]
|
||||
(let [big-ticks-mod (big-ticks-mod zoom)
|
||||
pos (+ (* value zoom)
|
||||
(* wb/canvas-start-x zoom)
|
||||
;; wb/canvas-start-x
|
||||
wb/canvas-scroll-padding)]
|
||||
(* c/canvas-start-x zoom)
|
||||
;; c/canvas-start-x
|
||||
c/canvas-scroll-padding)]
|
||||
(when (< (mod value big-ticks-mod) step-size)
|
||||
(html
|
||||
[:text {:y (- pos 3)
|
||||
|
@ -145,10 +146,10 @@
|
|||
[own zoom]
|
||||
(let [scroll (rum/react wb/scroll-a)
|
||||
scroll-x (:x scroll)
|
||||
translate-x (- (- wb/canvas-scroll-padding) (:x scroll))]
|
||||
translate-x (- (- c/canvas-scroll-padding) (:x scroll))]
|
||||
(html
|
||||
[:svg.horizontal-rule
|
||||
{:width wb/viewport-width
|
||||
{:width c/viewport-width
|
||||
:height 20}
|
||||
[:g {:transform (str "translate(" translate-x ", 0)")}
|
||||
(horizontal-rule-ticks zoom)]])))
|
||||
|
@ -165,11 +166,11 @@
|
|||
[own zoom]
|
||||
(let [scroll (rum/react wb/scroll-a)
|
||||
scroll-y (:y scroll)
|
||||
translate-y (- (- wb/canvas-scroll-padding) (:y scroll))]
|
||||
translate-y (- (- c/canvas-scroll-padding) (:y scroll))]
|
||||
(html
|
||||
[:svg.vertical-rule
|
||||
{:width 20
|
||||
:height wb/viewport-height}
|
||||
:height c/viewport-height}
|
||||
|
||||
[:g {:transform (str "translate(0, " translate-y ")")}
|
||||
(vertical-rule-ticks zoom)]
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
(:require-macros [uxbox.util.syntax :refer [define-once]])
|
||||
(:require [beicon.core :as rx]
|
||||
[lentes.core :as l]
|
||||
[uxbox.constants :as c]
|
||||
[uxbox.rstore :as rs]
|
||||
[uxbox.state :as ust]
|
||||
[uxbox.data.shapes :as uds]
|
||||
|
|
|
@ -10,9 +10,11 @@
|
|||
(:require [sablono.core :as html :refer-macros [html]]
|
||||
[rum.core :as rum]
|
||||
[beicon.core :as rx]
|
||||
[uxbox.constants :as c]
|
||||
[uxbox.rstore :as rs]
|
||||
[uxbox.shapes :as sh]
|
||||
[uxbox.data.workspace :as dw]
|
||||
[uxbox.data.shapes :as uds]
|
||||
[uxbox.ui.core :as uuc]
|
||||
[uxbox.ui.mixins :as mx]
|
||||
[uxbox.ui.workspace.base :as wb]))
|
||||
|
@ -78,8 +80,8 @@
|
|||
"Translate the given rect to the canvas coordinates system."
|
||||
[rect]
|
||||
(let [zoom @wb/zoom-l
|
||||
startx (* wb/canvas-start-x zoom)
|
||||
starty (* wb/canvas-start-y zoom)]
|
||||
startx (* c/canvas-start-x zoom)
|
||||
starty (* c/canvas-start-y zoom)]
|
||||
(assoc rect
|
||||
:x (- (:x rect) startx)
|
||||
:y (- (:y rect) starty)
|
||||
|
@ -94,7 +96,7 @@
|
|||
(on-complete []
|
||||
(rs/emit! (-> (selrect->rect @position)
|
||||
(translate-to-canvas)
|
||||
(dw/select-shapes)))
|
||||
(uds/select-shapes)))
|
||||
(reset! position nil))
|
||||
|
||||
(init []
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
(:require [sablono.core :as html :refer-macros [html]]
|
||||
[lentes.core :as l]
|
||||
[rum.core :as rum]
|
||||
[uxbox.constants :as c]
|
||||
[uxbox.rstore :as rs]
|
||||
[uxbox.data.pages :as udp]
|
||||
[uxbox.ui.icons :as i]
|
||||
|
@ -16,7 +17,8 @@
|
|||
[uxbox.ui.lightbox :as lightbox]
|
||||
[uxbox.ui.colorpicker :as uucp]
|
||||
[uxbox.ui.workspace.base :as wb]
|
||||
[uxbox.util.dom :as dom]))
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.data :refer (parse-int)]))
|
||||
|
||||
;; --- Lentes
|
||||
|
||||
|
@ -33,7 +35,8 @@
|
|||
opts (merge (:options page)
|
||||
(deref local))]
|
||||
(letfn [(on-field-change [field event]
|
||||
(let [value (dom/event->value event)]
|
||||
(let [value (dom/event->value event)
|
||||
value (parse-int value)]
|
||||
(swap! local assoc field value)))
|
||||
(on-color-change [color]
|
||||
(swap! local assoc :grid/color color))
|
||||
|
@ -42,6 +45,7 @@
|
|||
(dom/checked?))]
|
||||
(swap! local assoc :grid/align checked?)))
|
||||
(on-submit [event]
|
||||
(dom/prevent-default event)
|
||||
(let [page (assoc page :options opts)]
|
||||
(rs/emit! (udp/update-page-metadata page))
|
||||
(lightbox/close!)))]
|
||||
|
@ -52,14 +56,14 @@
|
|||
[:input#grid-x.input-text
|
||||
{:placeholder "X px"
|
||||
:type "number"
|
||||
:value (:grid/x-axis opts "2")
|
||||
:value (:grid/x-axis opts c/grid-x-axis)
|
||||
:on-change (partial on-field-change :grid/x-axis)
|
||||
:min 1 ;;TODO check this value
|
||||
:min 1
|
||||
:max 100}]
|
||||
[:input#grid-y.input-text
|
||||
{:placeholder "Y px"
|
||||
:type "number"
|
||||
:value (:grid/y-axis opts "2")
|
||||
:value (:grid/y-axis opts c/grid-y-axis)
|
||||
:on-change (partial on-field-change :grid/y-axis)
|
||||
:min 1
|
||||
:max 100}]]
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
[uxbox.rstore :as rs]
|
||||
[uxbox.ui.lightbox :as lightbox]
|
||||
[uxbox.data.workspace :as dw]
|
||||
[uxbox.data.shapes :as uds]
|
||||
[uxbox.data.history :as udh])
|
||||
(:import goog.events.EventType
|
||||
goog.events.KeyCodes
|
||||
|
@ -28,15 +29,15 @@
|
|||
:ctrl+shift+l #(rs/emit! (dw/toggle-flag :layers))
|
||||
:ctrl+0 #(rs/emit! (dw/reset-zoom))
|
||||
:ctrl+r #(rs/emit! (dw/toggle-flag :ruler))
|
||||
:ctrl+d #(rs/emit! (dw/duplicate-selected))
|
||||
:ctrl+d #(rs/emit! (uds/duplicate-selected))
|
||||
:ctrl+c #(rs/emit! (dw/copy-to-clipboard))
|
||||
:ctrl+v #(rs/emit! (dw/paste-from-clipboard))
|
||||
:ctrl+z #(rs/emit! (udh/backwards-to-previous-version))
|
||||
:ctrl+shift+z #(rs/emit! (udh/forward-to-next-version))
|
||||
:ctrl+shift+v #(lightbox/open! :clipboard)
|
||||
:esc #(rs/emit! (dw/deselect-all))
|
||||
:backspace #(rs/emit! (dw/delete-selected))
|
||||
:delete #(rs/emit! (dw/delete-selected))
|
||||
:esc #(rs/emit! (uds/deselect-all))
|
||||
:backspace #(rs/emit! (uds/delete-selected))
|
||||
:delete #(rs/emit! (uds/delete-selected))
|
||||
:shift+up #(move-selected :up :fast)
|
||||
:shift+down #(move-selected :down :fast)
|
||||
:shift+right #(move-selected :right :fast)
|
||||
|
@ -78,8 +79,8 @@
|
|||
(defn- move-selected
|
||||
[dir speed]
|
||||
(case speed
|
||||
:std (rs/emit! (dw/move-selected dir 1))
|
||||
:fast (rs/emit! (dw/move-selected dir 20))))
|
||||
:std (rs/emit! (uds/move-selected dir 1))
|
||||
:fast (rs/emit! (uds/move-selected dir 20))))
|
||||
|
||||
;; --- Mixin
|
||||
|
||||
|
|
|
@ -49,18 +49,18 @@
|
|||
nil
|
||||
|
||||
(.-ctrlKey event)
|
||||
(rs/emit! (udw/select-shape id))
|
||||
(rs/emit! (uds/select-shape id))
|
||||
|
||||
(> (count selected) 1)
|
||||
(rs/emit! (udw/deselect-all)
|
||||
(udw/select-shape id))
|
||||
(rs/emit! (uds/deselect-all)
|
||||
(uds/select-shape id))
|
||||
|
||||
(contains? selected id)
|
||||
(rs/emit! (udw/select-shape id))
|
||||
(rs/emit! (uds/select-shape id))
|
||||
|
||||
:else
|
||||
(rs/emit! (udw/deselect-all)
|
||||
(udw/select-shape id)))))
|
||||
(rs/emit! (uds/deselect-all)
|
||||
(uds/select-shape id)))))
|
||||
|
||||
(defn- toggle-visibility
|
||||
[selected item event]
|
||||
|
@ -71,7 +71,7 @@
|
|||
(rs/emit! (uds/show-shape id))
|
||||
(rs/emit! (uds/hide-shape id)))
|
||||
(when (contains? selected id)
|
||||
(rs/emit! (udw/select-shape id)))))
|
||||
(rs/emit! (uds/select-shape id)))))
|
||||
|
||||
(defn- toggle-blocking
|
||||
[item event]
|
||||
|
@ -289,9 +289,9 @@
|
|||
shapes-by-id (rum/react wb/shapes-by-id-l)
|
||||
page (rum/react (focus-page (:page workspace)))
|
||||
close #(rs/emit! (udw/toggle-flag :layers))
|
||||
duplicate #(rs/emit! (udw/duplicate-selected))
|
||||
group #(rs/emit! (udw/group-selected))
|
||||
delete #(rs/emit! (udw/delete-selected))
|
||||
duplicate #(rs/emit! (uds/duplicate-selected))
|
||||
group #(rs/emit! (uds/group-selected))
|
||||
delete #(rs/emit! (uds/delete-selected))
|
||||
dragel (volatile! nil)]
|
||||
(html
|
||||
[:div#layers.tool-window
|
||||
|
|
|
@ -4,38 +4,47 @@
|
|||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.transit
|
||||
(ns uxbox.util.transit
|
||||
"A lightweight abstraction for transit serialization."
|
||||
(:refer-clojure :exclude [do])
|
||||
(:require [cognitect.transit :as t]
|
||||
[com.cognitect.transit :as tr]
|
||||
[uxbox.util.data :refer (parse-int)]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.datetime :as dt]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Read/Write Transit handlers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; --- Transit Handlers
|
||||
|
||||
(def ^:private datetime-write-handler
|
||||
(reify
|
||||
Object
|
||||
(tag [_ v] "m")
|
||||
(rep [_ v] (dt/format v :offset))
|
||||
(stringRep [this v] (str (dt/format v :offset)))))
|
||||
(def datetime-write-handler
|
||||
(t/write-handler (constantly "m")
|
||||
#(str (dt/format % :offset))))
|
||||
|
||||
(defn- datetime-read-handler
|
||||
[v]
|
||||
(dt/datetime (parse-int v)))
|
||||
(def datetime-read-handler
|
||||
(t/read-handler
|
||||
#(dt/datetime (parse-int %))))
|
||||
|
||||
(def point-write-handler
|
||||
(t/write-handler
|
||||
(constantly "point")
|
||||
(fn [v]
|
||||
(let [ret #js []]
|
||||
(.push ret (:x v))
|
||||
(.push ret (:y v))
|
||||
ret))))
|
||||
|
||||
(def point-read-handler
|
||||
(t/read-handler
|
||||
#(gpt/point (js->clj %))))
|
||||
|
||||
(def ^:privare +read-handlers+
|
||||
{"u" uuid
|
||||
"m" datetime-read-handler})
|
||||
"m" datetime-read-handler
|
||||
"point" point-read-handler})
|
||||
|
||||
(def ^:privare +write-handlers+
|
||||
{dt/DateTime datetime-write-handler})
|
||||
{dt/DateTime datetime-write-handler
|
||||
gpt/Point point-write-handler})
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Public Api
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; --- Public Api
|
||||
|
||||
(defn decode
|
||||
[data]
|
62
src/uxbox/util/workers.cljs
Normal file
62
src/uxbox/util/workers.cljs
Normal file
|
@ -0,0 +1,62 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.util.workers
|
||||
"A lightweight layer on top of webworkers api."
|
||||
(:require [beicon.core :as rx]
|
||||
[uxbox.util.transit :as t]))
|
||||
|
||||
;; --- Implementation
|
||||
|
||||
(defprotocol IWorker
|
||||
(-ask [_ msg] "Send and receive message as rx stream.")
|
||||
(-send [_ msg] "Send message and forget."))
|
||||
|
||||
(deftype WebWorker [stream wrk]
|
||||
IWorker
|
||||
(-ask [this message]
|
||||
(let [sender (random-uuid)
|
||||
data (assoc message :sender sender)
|
||||
data (t/encode data)]
|
||||
(.postMessage wrk data)
|
||||
(->> stream
|
||||
(rx/filter #(= (:reply-to %) sender))
|
||||
(rx/take 1))))
|
||||
|
||||
(-send [this message]
|
||||
(let [sender (random-uuid)
|
||||
data (assoc message :sender sender)
|
||||
data (t/encode data)]
|
||||
(.postMessage wrk data)
|
||||
(->> stream
|
||||
(rx/filter #(= (:reply-to %) sender))))))
|
||||
|
||||
;; --- Public Api
|
||||
|
||||
(defn init
|
||||
"Return a initialized webworker instance."
|
||||
[path]
|
||||
(let [wrk (js/Worker. path)
|
||||
bus (rx/bus)]
|
||||
(.addEventListener wrk "message"
|
||||
(fn [event]
|
||||
(let [data (.-data event)
|
||||
data (t/decode data)]
|
||||
(rx/push! bus data))))
|
||||
(.addEventListener wrk "error"
|
||||
(fn [event]
|
||||
(rx/error! bus event)))
|
||||
|
||||
(WebWorker. (rx/map identity bus) wrk)))
|
||||
|
||||
(defn ask!
|
||||
[wrk message]
|
||||
(-ask wrk message))
|
||||
|
||||
(defn send!
|
||||
[wrk message]
|
||||
(-send wrk message))
|
||||
|
21
src/uxbox/worker.cljs
Normal file
21
src/uxbox/worker.cljs
Normal file
|
@ -0,0 +1,21 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.worker
|
||||
(:require [beicon.core :as rx]
|
||||
[uxbox.util.transit :as t]
|
||||
[uxbox.worker.core :as wrk]
|
||||
[uxbox.worker.align]))
|
||||
|
||||
(enable-console-print!)
|
||||
|
||||
(defn- on-message
|
||||
[event]
|
||||
(let [message (t/decode (.-data event))]
|
||||
(wrk/handler message)))
|
||||
|
||||
(defonce _
|
||||
(.addEventListener js/self "message" on-message))
|
34
src/uxbox/worker/align.cljs
Normal file
34
src/uxbox/worker/align.cljs
Normal file
|
@ -0,0 +1,34 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.worker.align
|
||||
"Workspace aligment indexes worker."
|
||||
(:require [beicon.core :as rx]
|
||||
[kdtree :as kd]
|
||||
[uxbox.worker.core :as wrk]
|
||||
[uxbox.util.geom.point :as gpt]))
|
||||
|
||||
(defonce state (volatile! nil))
|
||||
|
||||
(defmethod wrk/handler :grid/init
|
||||
[{:keys [width height x-axis y-axis] :as opts}]
|
||||
(println ":grid/init" opts)
|
||||
(let [points (into-array
|
||||
(for [x (range 0 width (or x-axis 10))
|
||||
y (range 0 height (or y-axis 10))]
|
||||
#js [x y]))
|
||||
tree (kd/create2d points)]
|
||||
(vreset! state tree)))
|
||||
|
||||
(defmethod wrk/handler :grid/align
|
||||
[{:keys [sender point] :as message}]
|
||||
(println "request" point)
|
||||
(let [point #js [(:x point) (:y point)]
|
||||
results (js->clj (.nearest @state point 1))
|
||||
[[x y] d] (first results)
|
||||
result (gpt/point x y)]
|
||||
(println "result:" result)
|
||||
(wrk/reply! sender {:point (gpt/point x y)})))
|
31
src/uxbox/worker/core.cljs
Normal file
31
src/uxbox/worker/core.cljs
Normal file
|
@ -0,0 +1,31 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.worker.core
|
||||
(:require [uxbox.util.transit :as t]))
|
||||
|
||||
(enable-console-print!)
|
||||
|
||||
;; --- Handler
|
||||
|
||||
(defmulti handler :cmd)
|
||||
|
||||
(defmethod handler :default
|
||||
[message]
|
||||
(println "Unexpected message:" message))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defn worker?
|
||||
"Check if the code is executed in webworker context."
|
||||
[]
|
||||
(undefined? (.-document js/self)))
|
||||
|
||||
(defn reply!
|
||||
[sender message]
|
||||
(let [message (assoc message :reply-to sender)]
|
||||
(println "replying " message)
|
||||
(.postMessage js/self (t/encode message))))
|
Loading…
Add table
Reference in a new issue