0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-24 23:49:45 -05:00

🎉 Add interactions with the mouse

This commit is contained in:
Andrés Moya 2020-05-11 15:19:38 +02:00
parent 23ba635aab
commit e602a8cc36
4 changed files with 150 additions and 85 deletions

View file

@ -77,7 +77,8 @@
:drawing nil
:drawing-tool nil
:tooltip nil
:options-mode :design})
:options-mode :design
:draw-interaction-to nil})
(def initialize-layout
(ptk/reify ::initialize-layout
@ -1418,6 +1419,63 @@
:index index-in-parent}]]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interactions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare move-create-interaction)
(declare finish-create-interaction)
(defn start-create-interaction
[]
(ptk/reify ::start-create-interaction
ptk/WatchEvent
(watch [_ state stream]
(let [initial-pos @ms/mouse-position
selected (get-in state [:workspace-local :selected])
stopper (rx/filter ms/mouse-up? stream)]
(when (= 1 (count selected))
(rx/concat
(->> ms/mouse-position
(rx/take-until stopper)
(rx/map #(move-create-interaction initial-pos %)))
(rx/of (finish-create-interaction initial-pos))))))))
(defn move-create-interaction
[initial-pos position]
(ptk/reify ::move-create-interaction
ptk/UpdateEvent
(update [_ state]
(if (= position initial-pos)
state
(assoc-in state [:workspace-local :draw-interaction-to] position)))))
(defn finish-create-interaction
[initial-pos]
(ptk/reify ::finish-create-interaction
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-local :draw-interaction-to] nil))
ptk/WatchEvent
(watch [_ state stream]
(let [position @ms/mouse-position
page-id (:current-page-id state)
objects (get-in state [:workspace-data page-id :objects])
frame (dwc/get-frame-at-point objects position)
shape-id (first (get-in state [:workspace-local :selected]))]
(when-not (= position initial-pos)
(if (and frame shape-id (not= (:id frame) shape-id))
(rx/of (update-shape shape-id
{:interactions [{:event-type :click
:action-type :navigate
:destination (:id frame)}]}))
(rx/of (update-shape shape-id
{:interactions []}))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Exports
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -210,6 +210,15 @@
(when-not (empty? rch)
(rx/of (commit-changes rch uch {:commit-local? true})))))))
(defn get-frame-at-point
[objects point]
(let [frames (cp/select-frames objects)]
(loop [frame (first frames)
rest (rest frames)]
(d/seek #(geom/has-point? % point) frames))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Undo / Redo
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -24,6 +24,7 @@
(let [selected @refs/selected-shapes
selected? (contains? selected id)
drawing? @refs/selected-drawing-tool
options-mode @refs/options-mode
button (.-which (.-nativeEvent event))]
(when-not (:blocked shape)
(cond
@ -36,27 +37,20 @@
(= type :frame)
(when selected?
(dom/stop-propagation event)
(st/emit! (dw/start-move-selected)))
(if (= options-mode :design)
(st/emit! (dw/start-move-selected))
(st/emit! (dw/start-create-interaction))))
(and (not selected?) (empty? selected))
(do
(dom/stop-propagation event)
(st/emit! dw/deselect-all
(dw/select-shape id)
(dw/start-move-selected)))
(and (not selected?) (not (empty? selected)))
(do
(dom/stop-propagation event)
(if (kbd/shift? event)
(st/emit! (dw/select-shape id))
(st/emit! dw/deselect-all
(dw/select-shape id)
(dw/start-move-selected))))
:else
(do
(dom/stop-propagation event)
(st/emit! (dw/start-move-selected)))))))
(when-not selected?
(when-not (or (empty? selected) (kbd/shift? event))
(st/emit! dw/deselect-all))
(st/emit! (dw/select-shape id)))
(if (= options-mode :design)
(st/emit! (dw/start-move-selected))
(st/emit! (dw/start-create-interaction))))))))
(defn on-context-menu
[event shape]

View file

@ -26,62 +26,21 @@
[shape]
(first (filter #(= (:event-type %) :click) (:interactions shape))))
(defn- on-mouse-down-unselected
(defn- on-mouse-down
[event {:keys [id type] :as shape} selected]
(do
(dom/stop-propagation event)
(if (empty? selected)
(st/emit! (dw/select-shape id)
(dw/start-move-selected))
(when-not (empty? selected)
(st/emit! dw/deselect-all))
(st/emit! (dw/select-shape id))
(st/emit! (dw/start-create-interaction))))
(if (kbd/shift? event)
(st/emit! (dw/select-shape id))
(st/emit! dw/deselect-all
(dw/select-shape id)
(dw/start-move-selected))))))
;; TODO: add more mouse behavior, to create interactions by drag&drop
;; (defn- on-mouse-down
;; [event {:keys [id type] :as shape}]
;; (let [selected @refs/selected-shapes
;; selected? (contains? selected id)
;; drawing? @refs/selected-drawing-tool
;; button (.-which (.-nativeEvent event))]
;; (when-not (:blocked shape)
;; (cond
;; (not= 1 button)
;; nil
;;
;; drawing?
;; nil
;;
;; (= type :frame)
;; (when selected?
;; (dom/stop-propagation event)
;; (st/emit! (dw/start-move-selected)))
;;
;; (and (not selected?) (empty? selected))
;; (do
;; (dom/stop-propagation event)
;; (st/emit! dw/deselect-all
;; (dw/select-shape id)
;; (dw/start-move-selected)))
;;
;; (and (not selected?) (not (empty? selected)))
;; (do
;; (dom/stop-propagation event)
;; (if (kbd/shift? event)
;; (st/emit! (dw/select-shape id))
;; (st/emit! dw/deselect-all
;; (dw/select-shape id)
;; (dw/start-move-selected))))
;; :else
;; (do
;; (dom/stop-propagation event)
;; (st/emit! (dw/start-move-selected)))))))
(mf/defc interaction-path
[{:keys [orig-shape dest-shape selected selected?] :as props}]
(defn connect-to-shape
"Calculate the best position to draw an interaction line
between two shapes"
[orig-shape dest-shape]
(let [orig-rect (geom/selection-rect-shape orig-shape)
dest-rect (geom/selection-rect-shape dest-shape)
@ -103,12 +62,48 @@
orig-x (if (= orig-pos :right) orig-x-right orig-x-left)
dest-x (if (= dest-pos :right) dest-x-right dest-x-left)
orig-y (+ (:y orig-rect) (/ (:height orig-rect) 2))
dest-y (+ (:y dest-rect) (/ (:height dest-rect) 2))]
[orig-pos orig-x orig-y dest-pos dest-x dest-y]))
(defn connect-to-point
"Calculate the best position to draw an interaction line
between one shape and one point"
[orig-shape dest-point]
(let [orig-rect (geom/selection-rect-shape orig-shape)
orig-x-left (:x orig-rect)
orig-x-right (+ orig-x-left (:width orig-rect))
orig-x-center (+ orig-x-left (/ (:width orig-rect) 2))
dest-x (:x dest-point)
dest-y (:y dest-point)
orig-pos (if (<= orig-x-right dest-x) :right
(if (>= orig-x-left dest-x) :left
(if (<= orig-x-center dest-x) :right :left)))
dest-pos (if (<= orig-x-right dest-x) :left
(if (>= orig-x-left dest-x) :right
(if (<= orig-x-center dest-x) :right :left)))
orig-x (if (= orig-pos :right) orig-x-right orig-x-left)
orig-y (+ (:y orig-rect) (/ (:height orig-rect) 2))]
[orig-pos orig-x orig-y dest-pos dest-x dest-y]))
(mf/defc interaction-path
[{:keys [orig-shape dest-shape dest-point selected selected?] :as props}]
(let [[orig-pos orig-x orig-y dest-pos dest-x dest-y]
(if dest-shape
(connect-to-shape orig-shape dest-shape)
(connect-to-point orig-shape dest-point))
orig-dx (if (= orig-pos :right) 100 -100)
dest-dx (if (= dest-pos :right) 100 -100)
orig-y (+ (:y orig-rect) (/ (:height orig-rect) 2))
dest-y (+ (:y dest-rect) (/ (:height dest-rect) 2))
path ["M" orig-x orig-y "C" (+ orig-x orig-dx) orig-y (+ dest-x dest-dx) dest-y dest-x dest-y]
pdata (str/join " " path)
@ -122,16 +117,16 @@
:fill "transparent"
:stroke-width 2
:d pdata
:on-mouse-down #(on-mouse-down-unselected % orig-shape selected)}]
:on-mouse-down #(on-mouse-down % orig-shape selected)}]
[:g
[:g {:on-mouse-down #(on-mouse-down % orig-shape selected)}
[:path {:stroke "#31EFB8"
:fill "transparent"
:stroke-width 2
:d pdata}]
[:circle {:cx orig-x
:cy orig-y
:r 4
:r 8
:stroke "#31EFB8"
:stroke-width 2
:fill "#FFFFFF"}]
@ -149,9 +144,12 @@
(mf/defc interactions
[{:keys [selected] :as props}]
(let [data (mf/deref refs/workspace-data)
local (mf/deref refs/workspace-local)
objects (:objects data)
active-shapes (filter #(first (get-click-interaction %)) (vals objects))
selected-shapes (map #(get objects %) selected)]
selected-shapes (map #(get objects %) selected)
draw-interaction-to (:draw-interaction-to local)
first-selected (first selected-shapes)]
[:*
(for [shape active-shapes]
(let [interaction (get-click-interaction shape)
@ -164,13 +162,19 @@
:selected selected
:selected? false}])))
(for [shape selected-shapes]
(let [interaction (get-click-interaction shape)
dest-shape (get objects (:destination interaction))]
(when dest-shape
[:& interaction-path {:key (:id shape)
:orig-shape shape
:dest-shape dest-shape
:selected selected
:selected? true}])))]))
(if (and draw-interaction-to first-selected)
[:& interaction-path {:key "interactive"
:orig-shape first-selected
:dest-point draw-interaction-to
:selected? true}]
(for [shape selected-shapes]
(let [interaction (get-click-interaction shape)
dest-shape (get objects (:destination interaction))]
(when dest-shape
[:& interaction-path {:key (:id shape)
:orig-shape shape
:dest-shape dest-shape
:selected selected
:selected? true}]))))]))