mirror of
https://github.com/penpot/penpot.git
synced 2025-08-06 14:18:24 +02:00
Add proper impl of undo/redo.
This commit is contained in:
parent
675f04ff9c
commit
56d64cf737
4 changed files with 143 additions and 38 deletions
|
@ -55,8 +55,7 @@
|
||||||
(let [data (:data page)
|
(let [data (:data page)
|
||||||
shapes (:shapes data)
|
shapes (:shapes data)
|
||||||
shapes-by-id (:shapes-by-id data)
|
shapes-by-id (:shapes-by-id data)
|
||||||
page (-> page
|
page (-> (dissoc page :data)
|
||||||
(dissoc page :data)
|
|
||||||
(assoc :shapes shapes))]
|
(assoc :shapes shapes))]
|
||||||
(-> state
|
(-> state
|
||||||
(update :shapes-by-id merge shapes-by-id)
|
(update :shapes-by-id merge shapes-by-id)
|
||||||
|
|
|
@ -5,32 +5,19 @@
|
||||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.main.data.undo
|
(ns uxbox.main.data.undo
|
||||||
(:require [cuerdas.core :as str]
|
(:require #_[cljs.pprint :as pp]
|
||||||
[promesa.core :as p]
|
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[lentes.core :as l]
|
|
||||||
[uxbox.util.rstore :as rs]
|
[uxbox.util.rstore :as rs]
|
||||||
[uxbox.util.router :as r]
|
|
||||||
[uxbox.main.repo :as rp]
|
|
||||||
[uxbox.util.i18n :refer (tr)]
|
|
||||||
[uxbox.util.schema :as sc]
|
|
||||||
[uxbox.main.data.pages :as udp]
|
[uxbox.main.data.pages :as udp]
|
||||||
[uxbox.main.state :as st]
|
[uxbox.main.state :as st]))
|
||||||
[uxbox.util.datetime :as dt]
|
|
||||||
[uxbox.util.data :refer (without-keys
|
|
||||||
replace-by-id
|
|
||||||
index-by)]))
|
|
||||||
|
|
||||||
(defrecord SaveUndoEntry [id]
|
;; --- Watch Page Changes
|
||||||
rs/UpdateEvent
|
|
||||||
(-apply-update [_ state]
|
|
||||||
(let [page (udp/pack-page state id)]
|
|
||||||
(update-in state [:undo id :stack] (fnil conj []) page)))
|
|
||||||
|
|
||||||
rs/EffectEvent
|
(declare save-undo-entry)
|
||||||
(-apply-effect [_ state]
|
(declare save-undo-entry?)
|
||||||
(let [undo (get-in state [:undo id])]
|
(declare undo?)
|
||||||
(println (pr-str undo)))))
|
(declare redo?)
|
||||||
|
(declare initialize-undo-for-page)
|
||||||
|
|
||||||
(defn watch-page-changes
|
(defn watch-page-changes
|
||||||
"A function that starts watching for `IPageUpdate`
|
"A function that starts watching for `IPageUpdate`
|
||||||
|
@ -38,10 +25,123 @@
|
||||||
reacts on them emiting an other event that just
|
reacts on them emiting an other event that just
|
||||||
persists the state of the page in an undo stack."
|
persists the state of the page in an undo stack."
|
||||||
[id]
|
[id]
|
||||||
(letfn [(on-value []
|
(rs/emit! (initialize-undo-for-page id))
|
||||||
(rs/emit! (->SaveUndoEntry id)))]
|
(as-> rs/stream $
|
||||||
(as-> rs/stream $
|
(rx/filter #(satisfies? udp/IPageUpdate %) $)
|
||||||
(rx/filter #(satisfies? udp/IPageUpdate %) $)
|
(rx/filter #(not (undo? %)) $)
|
||||||
(rx/debounce 500 $)
|
(rx/filter #(not (redo? %)) $)
|
||||||
(rx/on-next $ on-value))))
|
(rx/debounce 500 $)
|
||||||
|
(rx/on-next $ #(rs/emit! (save-undo-entry id)))))
|
||||||
|
|
||||||
|
;; -- Save Undo Entry
|
||||||
|
|
||||||
|
(defrecord SaveUndoEntry [id]
|
||||||
|
rs/UpdateEvent
|
||||||
|
(-apply-update [_ state]
|
||||||
|
(let [page (udp/pack-page state id)]
|
||||||
|
(-> state
|
||||||
|
(update-in [:undo id :stack] #(cons (:data page) %))
|
||||||
|
(assoc-in [:undo id :selected] 0)))))
|
||||||
|
|
||||||
|
;; rs/EffectEvent
|
||||||
|
;; (-apply-effect [_ state]
|
||||||
|
;; (let [undo (get-in state [:undo id])]
|
||||||
|
;; (println (pr-str undo)))))
|
||||||
|
|
||||||
|
(defn save-undo-entry
|
||||||
|
[id]
|
||||||
|
(SaveUndoEntry. id))
|
||||||
|
|
||||||
|
(defn save-undo-entry?
|
||||||
|
[v]
|
||||||
|
(instance? SaveUndoEntry v))
|
||||||
|
|
||||||
|
;; --- Initialize Undo (For page)
|
||||||
|
|
||||||
|
(defrecord InitializeUndoForPage [id]
|
||||||
|
rs/WatchEvent
|
||||||
|
(-apply-watch [_ state stream]
|
||||||
|
(let [initialized? (get-in state [:undo id])
|
||||||
|
page-loaded? (get-in state [:pages-by-id id])]
|
||||||
|
(cond
|
||||||
|
(and page-loaded? initialized?)
|
||||||
|
(rx/empty)
|
||||||
|
|
||||||
|
page-loaded?
|
||||||
|
(rx/of (save-undo-entry id))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(->> stream
|
||||||
|
(rx/filter udp/pages-fetched?)
|
||||||
|
(rx/take 1)
|
||||||
|
(rx/map #(initialize-undo-for-page id)))))))
|
||||||
|
|
||||||
|
(defn- initialize-undo-for-page
|
||||||
|
[id]
|
||||||
|
(InitializeUndoForPage. id))
|
||||||
|
|
||||||
|
;; --- Select Previous Entry
|
||||||
|
|
||||||
|
(defrecord Undo []
|
||||||
|
udp/IPageUpdate
|
||||||
|
rs/UpdateEvent
|
||||||
|
(-apply-update [_ state]
|
||||||
|
(let [page-id (get-in state [:workspace :page])
|
||||||
|
undo (get-in state [:undo page-id])
|
||||||
|
stack (:stack undo)
|
||||||
|
selected (:selected undo 0)]
|
||||||
|
(if (>= selected (dec (count stack)))
|
||||||
|
state
|
||||||
|
(let [pointer (inc selected)
|
||||||
|
page (get-in state [:pages-by-id page-id])
|
||||||
|
data (nth stack pointer)
|
||||||
|
packed (assoc page :data data)]
|
||||||
|
|
||||||
|
;; (println "Undo: pointer=" pointer)
|
||||||
|
;; (println "Undo: packed=")
|
||||||
|
;; (pp/pprint packed)
|
||||||
|
|
||||||
|
(-> state
|
||||||
|
(udp/unpack-page packed)
|
||||||
|
(assoc-in [:undo page-id :selected] pointer)))))))
|
||||||
|
|
||||||
|
(defn undo
|
||||||
|
[]
|
||||||
|
(Undo.))
|
||||||
|
|
||||||
|
(defn undo?
|
||||||
|
[v]
|
||||||
|
(instance? Undo v))
|
||||||
|
|
||||||
|
;; --- Select Next Entry
|
||||||
|
|
||||||
|
(defrecord Redo []
|
||||||
|
udp/IPageUpdate
|
||||||
|
rs/UpdateEvent
|
||||||
|
(-apply-update [_ state]
|
||||||
|
(let [page-id (get-in state [:workspace :page])
|
||||||
|
undo (get-in state [:undo page-id])
|
||||||
|
stack (:stack undo)
|
||||||
|
selected (:selected undo)]
|
||||||
|
(if (or (nil? selected) (zero? selected))
|
||||||
|
state
|
||||||
|
(let [pointer (dec selected)
|
||||||
|
data (nth stack pointer)
|
||||||
|
page (get-in state [:pages-by-id page-id])
|
||||||
|
packed (assoc page :data data)]
|
||||||
|
|
||||||
|
;; (println "Redo: pointer=" pointer)
|
||||||
|
;; (println "Redo: packed=")
|
||||||
|
;; (pp/pprint packed)
|
||||||
|
|
||||||
|
(-> state
|
||||||
|
(udp/unpack-page packed)
|
||||||
|
(assoc-in [:undo page-id :selected] pointer)))))))
|
||||||
|
|
||||||
|
(defn redo
|
||||||
|
[]
|
||||||
|
(Redo.))
|
||||||
|
|
||||||
|
(defn redo?
|
||||||
|
[v]
|
||||||
|
(instance? Redo v))
|
||||||
|
|
|
@ -47,8 +47,8 @@
|
||||||
(let [[projectid pageid] (:rum/args own)
|
(let [[projectid pageid] (:rum/args own)
|
||||||
sub1 (scroll/watch-scroll-interactions own)
|
sub1 (scroll/watch-scroll-interactions own)
|
||||||
sub2 (udp/watch-page-changes pageid)
|
sub2 (udp/watch-page-changes pageid)
|
||||||
sub3 (udh/watch-page-changes)
|
sub3 (udu/watch-page-changes pageid)
|
||||||
sub4 (udu/watch-page-changes pageid)
|
sub4 (udh/watch-page-changes)
|
||||||
dom (mx/ref-node own "workspace-canvas")]
|
dom (mx/ref-node own "workspace-canvas")]
|
||||||
|
|
||||||
;; Set initial scroll position
|
;; Set initial scroll position
|
||||||
|
@ -78,12 +78,17 @@
|
||||||
(do
|
(do
|
||||||
(rs/emit! (dw/initialize projectid pageid))
|
(rs/emit! (dw/initialize projectid pageid))
|
||||||
(.close (::sub2 old-state))
|
(.close (::sub2 old-state))
|
||||||
|
(.close (::sub3 old-state))
|
||||||
(assoc state
|
(assoc state
|
||||||
::sub1 (::sub1 old-state)
|
::sub1 (::sub1 old-state)
|
||||||
::sub2 (udp/watch-page-changes pageid)))
|
::sub2 (udp/watch-page-changes pageid)
|
||||||
|
::sub3 (udu/watch-page-changes pageid)
|
||||||
|
::sub4 (::sub4 old-state)))
|
||||||
(assoc state
|
(assoc state
|
||||||
::sub1 (::sub1 old-state)
|
::sub1 (::sub1 old-state)
|
||||||
::sub2 (::sub2 old-state)))))
|
::sub2 (::sub2 old-state)
|
||||||
|
::sub3 (::sub3 old-state)
|
||||||
|
::sub4 (::sub4 old-state)))))
|
||||||
|
|
||||||
(defn- on-scroll
|
(defn- on-scroll
|
||||||
[event]
|
[event]
|
||||||
|
|
|
@ -12,8 +12,9 @@
|
||||||
[uxbox.main.data.lightbox :as udl]
|
[uxbox.main.data.lightbox :as udl]
|
||||||
[uxbox.main.data.workspace :as dw]
|
[uxbox.main.data.workspace :as dw]
|
||||||
[uxbox.main.data.shapes :as uds]
|
[uxbox.main.data.shapes :as uds]
|
||||||
[uxbox.main.ui.workspace.sidebar.drawtools :as wsd]
|
[uxbox.main.data.undo :as udu]
|
||||||
[uxbox.main.data.history :as udh])
|
[uxbox.main.data.history :as udh]
|
||||||
|
[uxbox.main.ui.workspace.sidebar.drawtools :as wsd])
|
||||||
(:import goog.events.EventType
|
(:import goog.events.EventType
|
||||||
goog.events.KeyCodes
|
goog.events.KeyCodes
|
||||||
goog.ui.KeyboardShortcutHandler
|
goog.ui.KeyboardShortcutHandler
|
||||||
|
@ -36,13 +37,13 @@
|
||||||
:ctrl+d #(rs/emit! (uds/duplicate-selected))
|
:ctrl+d #(rs/emit! (uds/duplicate-selected))
|
||||||
:ctrl+c #(rs/emit! (dw/copy-to-clipboard))
|
:ctrl+c #(rs/emit! (dw/copy-to-clipboard))
|
||||||
:ctrl+v #(rs/emit! (dw/paste-from-clipboard))
|
:ctrl+v #(rs/emit! (dw/paste-from-clipboard))
|
||||||
:ctrl+z #(rs/emit! (udh/backwards-to-previous-version))
|
:ctrl+shift+v #(udl/open! :clipboard)
|
||||||
|
:ctrl+z #(rs/emit! (udu/undo))
|
||||||
|
:ctrl+shift+z #(rs/emit! (udu/redo))
|
||||||
:ctrl+b #(rs/emit! (dw/select-for-drawing wsd/+draw-tool-rect+))
|
:ctrl+b #(rs/emit! (dw/select-for-drawing wsd/+draw-tool-rect+))
|
||||||
:ctrl+e #(rs/emit! (dw/select-for-drawing wsd/+draw-tool-circle+))
|
:ctrl+e #(rs/emit! (dw/select-for-drawing wsd/+draw-tool-circle+))
|
||||||
:ctrl+l #(rs/emit! (dw/select-for-drawing wsd/+draw-tool-line+))
|
:ctrl+l #(rs/emit! (dw/select-for-drawing wsd/+draw-tool-line+))
|
||||||
:ctrl+t #(rs/emit! (dw/select-for-drawing wsd/+draw-tool-text+))
|
:ctrl+t #(rs/emit! (dw/select-for-drawing wsd/+draw-tool-text+))
|
||||||
:ctrl+shift+z #(rs/emit! (udh/forward-to-next-version))
|
|
||||||
:ctrl+shift+v #(udl/open! :clipboard)
|
|
||||||
:esc #(rs/emit! (uds/deselect-all))
|
:esc #(rs/emit! (uds/deselect-all))
|
||||||
:backspace #(rs/emit! (uds/delete-selected))
|
:backspace #(rs/emit! (uds/delete-selected))
|
||||||
:delete #(rs/emit! (uds/delete-selected))
|
:delete #(rs/emit! (uds/delete-selected))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue