mirror of
https://github.com/penpot/penpot.git
synced 2025-06-11 10:01:38 +02:00
♻️ mainly workspace refactor
This commit is contained in:
parent
4e382d456f
commit
212ae89c50
85 changed files with 18494 additions and 6609 deletions
|
@ -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)))))))
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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))))))
|
Loading…
Add table
Add a link
Reference in a new issue