0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-13 02:28:18 -05:00

Undo/redo paths

This commit is contained in:
alonso.torres 2021-04-16 11:13:20 +02:00
parent e2cf3a5a98
commit 0455aaa4cd
7 changed files with 217 additions and 7 deletions

View file

@ -0,0 +1,60 @@
;; 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) UXBOX Labs SL
(ns app.common.data.undo-stack
(:refer-clojure :exclude [peek])
(:require
#?(:cljs [cljs.core :as core]
:clj [clojure.core :as core])))
(defonce MAX-UNDO-SIZE 100)
(defn make-stack
[]
{:index -1
:items []})
(defn peek
[{index :index items :items :as stack}]
(when (and (>= index 0) (< index (count items)))
(nth items index)))
(defn append
[{index :index items :items :as stack} value]
(if (and (some? stack) (not= value (peek stack)))
(let [items (cond-> items
(> index 0)
(subvec 0 (inc index))
(> (+ index 2) MAX-UNDO-SIZE)
(subvec 1 (inc index))
:always
(conj value))
index (min (dec MAX-UNDO-SIZE) (inc index))]
{:index index
:items items})
stack))
(defn fixup
[{index :index :as stack} value]
(assoc-in stack [:items index] value))
(defn undo
[{index :index items :items :as stack}]
(update stack :index dec))
(defn redo
[{index :index items :items :as stack}]
(cond-> stack
(< index (dec (count items)))
(update :index inc)))
(defn size
[{index :index items :items :as stack}]
(inc index))

View file

@ -356,13 +356,15 @@
(>= index 0) (accumulate-undo-entry (get-in state [:workspace-undo :items index]))
(>= index 0) (update-in [:workspace-undo :index] dec))))))
;; If these functions change modules review /src/app/main/data/workspace/path/undo.cljs
(def undo
(ptk/reify ::undo
ptk/WatchEvent
(watch [_ state stream]
(let [edition (get-in state [:workspace-local :edition])]
(let [edition (get-in state [:workspace-local :edition])
drawing (get state :workspace-drawing)]
;; Editors handle their own undo's
(when-not (some? edition)
(when-not (or (some? edition) (some? drawing))
(let [undo (:workspace-undo state)
items (:items undo)
index (or (:index undo) (dec (count items)))]
@ -375,8 +377,9 @@
(ptk/reify ::redo
ptk/WatchEvent
(watch [_ state stream]
(let [edition (get-in state [:workspace-local :edition])]
(when-not (some? edition)
(let [edition (get-in state [:workspace-local :edition])
drawing (get state :workspace-drawing)]
(when-not (or (some? edition) (some? drawing))
(let [undo (:workspace-undo state)
items (:items undo)
index (or (:index undo) (dec (count items)))]
@ -543,6 +546,7 @@
(rx/take 1)
(rx/map (constantly clear-edition-mode)))))))
;; If these event change modules review /src/app/main/data/workspace/path/undo.cljs
(def clear-edition-mode
(ptk/reify ::clear-edition-mode
ptk/UpdateEvent

View file

@ -18,6 +18,7 @@
[app.main.data.workspace.path.state :as st]
[app.main.data.workspace.path.streams :as streams]
[app.main.data.workspace.path.tools :as tools]
[app.main.data.workspace.path.undo :as undo]
[app.main.streams :as ms]
[app.util.geom.path :as ugp]
[beicon.core :as rx]
@ -245,6 +246,7 @@
(make-drag-stream stream snap-toggled zoom points %))))]
(rx/concat
(rx/of (undo/start-path-undo))
(rx/of (common/init-path))
(rx/merge mousemove-events
mousedown-events)

View file

@ -17,6 +17,7 @@
[app.main.data.workspace.path.state :as st]
[app.main.data.workspace.path.streams :as streams]
[app.main.data.workspace.path.drawing :as drawing]
[app.main.data.workspace.path.undo :as undo]
[app.main.streams :as ms]
[app.util.geom.path :as ugp]
[beicon.core :as rx]
@ -221,6 +222,7 @@
(watch [_ state stream]
(let [mode (get-in state [:workspace-local :edit-path id :edit-mode])]
(rx/concat
(rx/of (undo/start-path-undo))
(rx/of (drawing/change-edit-mode mode))
(->> stream
(rx/take-until (->> stream (rx/filter (ptk/type? ::start-path-edit))))

View file

@ -0,0 +1,140 @@
;; 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) UXBOX Labs SL
(ns app.main.data.workspace.path.undo
(:require
[app.common.data :as d]
[app.common.data.undo-stack :as u]
[app.common.uuid :as uuid]
[app.main.data.workspace.path.state :as st]
[app.main.store :as store]
[beicon.core :as rx]
[okulary.core :as l]
[potok.core :as ptk]))
(defn undo-event?
[event]
(= :app.main.data.workspace.common/undo (ptk/type event)))
(defn redo-event?
[event]
(= :app.main.data.workspace.common/redo (ptk/type event)))
(defn- make-entry [state]
(let [id (st/get-path-id state)]
{:content (get-in state (st/get-path state :content))
:preview (get-in state [:workspace-local :edit-path id :preview])
:last-point (get-in state [:workspace-local :edit-path id :last-point])
:prev-handler (get-in state [:workspace-local :edit-path id :prev-handler])}))
(defn- load-entry [state {:keys [content preview last-point prev-handler]}]
(let [id (st/get-path-id state)]
(-> state
(d/assoc-in-when (st/get-path state :content) content)
(d/update-in-when
[:workspace-local :edit-path id]
assoc
:preview preview
:last-point last-point
:prev-handler prev-handler))))
(defn undo []
(ptk/reify ::undo
ptk/UpdateEvent
(update [_ state]
(let [id (st/get-path-id state)
undo-stack (-> (get-in state [:workspace-local :edit-path id :undo-stack])
(u/undo))
entry (u/peek undo-stack)]
(cond-> state
(some? entry)
(-> (load-entry entry)
(d/assoc-in-when
[:workspace-local :edit-path id :undo-stack]
undo-stack)))))))
(defn redo []
(ptk/reify ::redo
ptk/UpdateEvent
(update [_ state]
(let [id (st/get-path-id state)
undo-stack (-> (get-in state [:workspace-local :edit-path id :undo-stack])
(u/redo))
entry (u/peek undo-stack)]
(-> state
(load-entry entry)
(d/assoc-in-when
[:workspace-local :edit-path id :undo-stack]
undo-stack))))))
(defn add-undo-entry []
(ptk/reify ::add-undo-entry
ptk/UpdateEvent
(update [_ state]
(let [id (st/get-path-id state)
entry (make-entry state)]
(-> state
(d/update-in-when
[:workspace-local :edit-path id :undo-stack]
u/append entry))))))
(defn end-path-undo
[]
(ptk/reify ::end-path-undo
ptk/UpdateEvent
(update [_ state]
(-> state
(d/update-in-when
[:workspace-local :edit-path (st/get-path-id state)]
dissoc :undo-lock :undo-stack)))))
(defn- stop-undo? [event]
(= :app.main.data.workspace.common/clear-edition-mode (ptk/type event)))
(def path-content-ref
(letfn [(selector [state]
(get-in state (st/get-path state :content)))]
(l/derived selector store/state)))
(defn start-path-undo
[]
(let [lock (uuid/next)]
(ptk/reify ::start-path-undo
ptk/UpdateEvent
(update [_ state]
(let [undo-lock (get-in state [:workspace-local :edit-path (st/get-path-id state) :undo-lock])]
(cond-> state
(not undo-lock)
(update-in [:workspace-local :edit-path (st/get-path-id state)]
assoc
:undo-lock lock
:undo-stack (u/make-stack)))))
ptk/WatchEvent
(watch [_ state stream]
(let [undo-lock (get-in state [:workspace-local :edit-path (st/get-path-id state) :undo-lock])]
(when (= undo-lock lock)
(let [stop-undo-stream (->> stream
(rx/filter stop-undo?)
(rx/take 1))]
(rx/concat
(->> (rx/merge
(->> (rx/from-atom path-content-ref {:emit-current-value? true})
(rx/filter (comp not nil?))
(rx/map #(add-undo-entry)))
(->> stream
(rx/filter undo-event?)
(rx/map #(undo)))
(->> stream
(rx/filter redo-event?)
(rx/map #(redo))))
(rx/take-until stop-undo-stream))
(rx/of (end-path-undo))))))))))

View file

@ -205,8 +205,10 @@
(->> points (remove selected-points) (into #{}))])
show-snap? (and snap-toggled
(empty? hover-points)
(or (some? drag-handler) (some? preview) (some? moving-handler) moving-nodes))
(or (some? drag-handler)
(some? preview)
(some? moving-handler)
moving-nodes))
handle-double-click-outside
(fn [event]

View file

@ -13,7 +13,7 @@
#{:app.main.data.workspace.notifications/handle-pointer-update
:app.main.data.workspace.selection/change-hover-state})
(defonce ^:dynamic *debug* (atom #{}))
(defonce ^:dynamic *debug* (atom #{#_:events}))
(defn debug-all! [] (reset! *debug* debug-options))
(defn debug-none! [] (reset! *debug* #{}))