0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-13 00:01:51 -05:00
penpot/frontend/src/uxbox/main/ui/workspace/viewport.cljs
2019-08-24 16:01:17 +02:00

267 lines
9.7 KiB
Clojure

;; 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-2019 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2019 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.viewport
(:require
[beicon.core :as rx]
[goog.events :as events]
[potok.core :as ptk]
[rumext.alpha :as mf]
[uxbox.main.constants :as c]
[uxbox.main.data.workspace :as dw]
[uxbox.main.geom :as geom]
[uxbox.main.refs :as refs]
[uxbox.main.store :as st]
[uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.workspace.grid :refer [grid]]
[uxbox.main.ui.workspace.ruler :refer [ruler]]
[uxbox.main.ui.workspace.streams :as uws]
[uxbox.main.ui.workspace.drawarea :refer [start-drawing]]
[uxbox.main.ui.shapes :as uus]
[uxbox.main.ui.workspace.drawarea :refer [draw-area]]
[uxbox.main.ui.workspace.selection :refer [selection-handlers]]
[uxbox.util.data :refer [parse-int]]
[uxbox.util.components :refer [use-rxsub]]
[uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt])
(:import goog.events.EventType))
;; --- Coordinates Widget
(mf/defc coordinates
[{:keys [zoom] :as props}]
(let [coords (some-> (use-rxsub uws/mouse-position)
(gpt/divide zoom)
(gpt/round 0))]
[:ul.coordinates
[:span {:alt "x"}
(str "X: " (:x coords "-"))]
[:span {:alt "y"}
(str "Y: " (:y coords "-"))]]))
;; --- Cursor tooltip
(defn- get-shape-tooltip
"Return the shape tooltip text"
[shape]
(case (:type shape)
:icon "Click to place the Icon"
:image "Click to place the Image"
:rect "Drag to draw a Box"
:text "Drag to draw a Text Box"
:path "Click to draw a Path"
:circle "Drag to draw a Circle"
nil))
;; (mf/defc cursor-tooltip
;; {:wrap [mf/wrap-memo]}
;; [{:keys [tooltip]}]
;; (let [coords (mf/deref refs/window-mouse-position)]
;; [:span.cursor-tooltip
;; {:style
;; {:position "fixed"
;; :left (str (+ (:x coords) 5) "px")
;; :top (str (- (:y coords) 25) "px")}}
;; tooltip]))
;; --- Selection Rect
(def ^:private handle-selrect
(letfn [(update-state [state position]
(let [id (get-in state [:workspace :current])
selrect (get-in state [:workspace id :selrect])]
(if selrect
(assoc-in state [:workspace id :selrect]
(dw/selection->rect (assoc selrect :stop position)))
(assoc-in state [:workspace id :selrect]
(dw/selection->rect {:start position :stop position})))))
(clear-state [state]
(let [id (get-in state [:workspace :current])]
(update-in state [:workspace id] dissoc :selrect)))]
(reify
ptk/WatchEvent
(watch [_ state stream]
(let [stoper (rx/filter #(or (dw/interrupt? %) (uws/mouse-up? %)) stream)]
(rx/concat
(rx/of (dw/deselect-all))
(->> uws/mouse-position
(rx/map (fn [pos] #(update-state % pos)))
(rx/take-until stoper))
(rx/of dw/select-shapes-by-current-selrect
clear-state)))))))
(mf/defc selrect
{:wrap [mf/wrap-memo]}
[{:keys [data] :as props}]
(when data
(let [{:keys [x1 y1 width height]} (geom/size data)]
[:rect.selection-rect
{:x x1
:y y1
:width width
:height height}])))
;; --- Viewport Positioning
(def handle-viewport-positioning
(letfn [(on-point [dom reference point]
(let [{:keys [x y]} (gpt/subtract point reference)
cx (.-scrollLeft dom)
cy (.-scrollTop dom)]
(set! (.-scrollLeft dom) (- cx x))
(set! (.-scrollTop dom) (- cy y))))]
(reify
ptk/EffectEvent
(effect [_ state stream]
(let [stoper (rx/filter #(= ::finish-positioning %) stream)
reference @uws/mouse-position
dom (dom/get-element "workspace-viewport")]
(-> (rx/take-until stoper uws/mouse-position)
(rx/subscribe #(on-point dom reference %))))))))
;; --- Viewport
(mf/defc viewport
[{:keys [page] :as props}]
(let [{:keys [drawing-tool tooltip zoom flags edition] :as wst} (mf/deref refs/workspace)
viewport-ref (mf/use-ref nil)
tooltip (or tooltip (get-shape-tooltip drawing-tool))
zoom (or zoom 1)]
(letfn [(on-mouse-down [event]
(dom/stop-propagation event)
(let [ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
opts {:shift? shift?
:ctrl? ctrl?}]
(st/emit! (uws/mouse-event :down ctrl? shift?)))
(when (not edition)
(if drawing-tool
(st/emit! (start-drawing drawing-tool))
(st/emit! :interrupt handle-selrect))))
(on-context-menu [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(let [ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
opts {:shift? shift?
:ctrl? ctrl?}]
(st/emit! (uws/mouse-event :context-menu ctrl? shift?))))
(on-mouse-up [event]
(dom/stop-propagation event)
(let [ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
opts {:shift? shift?
:ctrl? ctrl?}]
(st/emit! (uws/mouse-event :up ctrl? shift?))))
(on-click [event]
(dom/stop-propagation event)
(let [ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
opts {:shift? shift?
:ctrl? ctrl?}]
(st/emit! (uws/mouse-event :click ctrl? shift?))))
(on-double-click [event]
(dom/stop-propagation event)
(let [ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
opts {:shift? shift?
:ctrl? ctrl?}]
(st/emit! (uws/mouse-event :double-click ctrl? shift?))))
(translate-point-to-viewport [pt]
(let [viewport (mf/ref-node viewport-ref)
brect (.getBoundingClientRect viewport)
brect (gpt/point (parse-int (.-left brect))
(parse-int (.-top brect)))]
(gpt/subtract pt brect)))
(on-key-down [event]
(let [bevent (.getBrowserEvent event)
key (.-keyCode event)
ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
opts {:key key
:shift? shift?
:ctrl? ctrl?}]
(when-not (.-repeat bevent)
(st/emit! (uws/keyboard-event :down key ctrl? shift?))
(when (kbd/space? event)
(st/emit! handle-viewport-positioning)
#_(st/emit! (dw/start-viewport-positioning))))))
(on-key-up [event]
(let [key (.-keyCode event)
ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
opts {:key key
:shift? shift?
:ctrl? ctrl?}]
(when (kbd/space? event)
(st/emit! ::finish-positioning #_(dw/stop-viewport-positioning)))
(st/emit! (uws/keyboard-event :up key ctrl? shift?))))
(on-mouse-move [event]
(let [pt (gpt/point (.-clientX event)
(.-clientY event))
pt (translate-point-to-viewport pt)]
(st/emit! (uws/->PointerEvent :viewport pt
(kbd/ctrl? event)
(kbd/shift? event)))))
(on-mount []
(let [key1 (events/listen js/document EventType.KEYDOWN on-key-down)
key2 (events/listen js/document EventType.KEYUP on-key-up)]
(fn []
(events/unlistenByKey key1)
(events/unlistenByKey key2))))]
(mf/use-effect on-mount)
[:*
[:& coordinates {:zoom zoom}]
#_[:div.tooltip-container
(when tooltip
[:& cursor-tooltip {:tooltip tooltip}])]
[:svg.viewport {:width (* c/viewport-width zoom)
:height (* c/viewport-height zoom)
:ref viewport-ref
:class (when drawing-tool "drawing")
:on-context-menu on-context-menu
:on-click on-click
:on-double-click on-double-click
:on-mouse-move on-mouse-move
:on-mouse-down on-mouse-down
:on-mouse-up on-mouse-up}
[:g.zoom {:transform (str "scale(" zoom ", " zoom ")")}
(when page
[:*
(for [id (reverse (:shapes page))]
[:& uus/shape-component {:id id :key id}])
(when (seq (:selected wst))
[:& selection-handlers {:wst wst}])
(when-let [dshape (:drawing wst)]
[:& draw-area {:shape dshape
:zoom (:zoom wst)
:modifiers (:modifiers wst)}])])
(if (contains? flags :grid)
[:& grid {:page page}])]
(when (contains? flags :ruler)
[:& ruler {:zoom zoom :ruler (:ruler wst)}])
[:& selrect {:data (:selrect wst)}]]])))