From d87cba3622af65094414c9d892901daa0f81cb49 Mon Sep 17 00:00:00 2001 From: zhandouxiaojiji Date: Tue, 1 Dec 2020 21:15:08 +0800 Subject: [PATCH] undo & redo --- README.md | 2 -- common/MainEventType.ts | 2 ++ main-process/AppMenu.ts | 29 ++++++++++++++++++++--- package.json | 2 +- render-process/Editor/index.tsx | 41 +++++++++++++++++++++++++++++---- render-process/TreeTabs.tsx | 10 ++++++++ 6 files changed, 76 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ad5cc9a..c602797 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,6 @@ npm run dist # 编译exe可执行文件 + js/ts版本 计划中。 ## TODO -+ 历史记录(撤销/恢复) -+ 资源面板支持树型结构 + 右键菜单 ## About diff --git a/common/MainEventType.ts b/common/MainEventType.ts index 369a01b..55865fa 100644 --- a/common/MainEventType.ts +++ b/common/MainEventType.ts @@ -10,4 +10,6 @@ export default { SAVE: "SAVE", SAVE_ALL: "SAVE_ALL", RELOAD_SERVER: "RELOAD_SERVER", + UNDO: "UNDO", + REDO: "REDO", }; diff --git a/main-process/AppMenu.ts b/main-process/AppMenu.ts index 7639a09..7fc4a6b 100644 --- a/main-process/AppMenu.ts +++ b/main-process/AppMenu.ts @@ -28,9 +28,10 @@ export default class AppMenu { createMenu() { const menu: Menu = new Menu(); - menu.append(this.createWorkspaceMenu()); menu.append(this.createFileMenu()); + menu.append(this.createEditMenu()); menu.append(this.createNodeMenu()); + menu.append(this.createWorkspaceMenu()); menu.append(this.createToolsMenu()); return menu; } @@ -133,6 +134,28 @@ export default class AppMenu { })(); }, }, + ], + }); + } + + private createEditMenu() { + return new MenuItem({ + label: "编辑", + submenu: [ + { + label: "撤销", + accelerator: "ctrl+z", + click: () => { + this.webContents.send(MainEventType.UNDO); + } + }, + { + label: "恢复", + accelerator: "ctrl+y", + click: () => { + this.webContents.send(MainEventType.REDO); + } + }, { type: "separator" }, { label: "新建节点", @@ -164,8 +187,8 @@ export default class AppMenu { this.webContents.send(MainEventType.PASTE_NODE); }, }, - ], - }); + ] + }) } private createWorkspaceMenu() { diff --git a/package.json b/package.json index 3be0a79..05631b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "behavior3editor", - "version": "0.1.3", + "version": "0.1.4", "description": "行为树编辑器", "main": "./dist/main.js", "scripts": { diff --git a/render-process/Editor/index.tsx b/render-process/Editor/index.tsx index e91a088..bf47cde 100644 --- a/render-process/Editor/index.tsx +++ b/render-process/Editor/index.tsx @@ -16,6 +16,7 @@ import Settings from "../../main-process/Settings"; import "./Editor.css"; import { clipboard } from "electron"; +import { TreeGraphData } from "@antv/g6/lib/types"; export interface EditorProps { filepath: string; @@ -35,6 +36,8 @@ export default class Editor extends React.Component { private dragSrcId: string; private dragDstId: string; private autoId: number; + private undoStack: BehaviorNodeModel[] = []; + private redoStack: BehaviorNodeModel[] = []; constructor(props: EditorProps) { super(props); @@ -219,6 +222,7 @@ export default class Editor extends React.Component { } const removeSrc = () => { + this.pushUndoStack(); srcParent.children = srcParent.children.filter((e) => e.id != srcData.id); }; console.log("dstNode", dstNode); @@ -286,6 +290,7 @@ export default class Editor extends React.Component { message.warn("未选中节点"); return; } + this.pushUndoStack(); const curNodeData = this.graph.findDataById(curNodeId); const newNodeData: BehaviorNodeModel = { id: this.autoId++, @@ -311,15 +316,17 @@ export default class Editor extends React.Component { } this.onSelectNode(null); + this.pushUndoStack(); const rootData = this.graph.findDataById("1"); const parentData = Utils.findParent(rootData, curNodeId); parentData.children = parentData.children.filter((e) => e.id != curNodeId); this.changeWithoutAnim(); } - updateNode(id: string) { - this.graph.changeData(); - this.graph.layout(); + pushUndoStack() { + this.undoStack.push(Utils.cloneNodeData(this.graph.findDataById('1') as GraphNodeModel)); + console.log("push", this.undoStack); + this.redoStack = []; } changeWithoutAnim() { @@ -366,7 +373,8 @@ export default class Editor extends React.Component { if (!str || str == "") { return; } - const data = Utils.createTreeData(JSON.parse(str), this.state.settings); + this.pushUndoStack(); + const data = Utils.createTreeData(JSON.parse(str), this.state.settings); this.autoId = Utils.refreshNodeId(data, this.autoId); this.onSelectNode(null); if (!curNodeData.children) { @@ -381,6 +389,30 @@ export default class Editor extends React.Component { } } + undo() { + if (this.undoStack.length == 0) { + return; + } + const data = this.undoStack.pop(); + this.graph.set("animate", false); + this.graph.changeData(Utils.createTreeData(data, this.state.settings)); + this.graph.layout(); + this.graph.set("animate", true); + this.redoStack.push(data); + } + + redo() { + if (this.redoStack.length == 0) { + return; + } + const data = this.redoStack.pop(); + this.graph.set("animate", false); + this.graph.changeData(Utils.createTreeData(data, this.state.settings)); + this.graph.layout(); + this.graph.set("animate", true); + this.undoStack.push(data); + } + render() { const { curNodeId, treeModel, settings } = this.state; var curNode: any; @@ -399,6 +431,7 @@ export default class Editor extends React.Component { settings={settings} updateNode={(id, forceUpdate) => { if (forceUpdate) { + this.pushUndoStack(); const data: any = this.graph.findDataById(id); data.conf = settings.getNodeConf(data.name); data.size = Utils.calcTreeNodeSize(data); diff --git a/render-process/TreeTabs.tsx b/render-process/TreeTabs.tsx index 44d9517..d69dcd1 100644 --- a/render-process/TreeTabs.tsx +++ b/render-process/TreeTabs.tsx @@ -62,6 +62,16 @@ export default class TreeTabs extends Component { } message.success("已保存所有行为树"); }); + + ipcRenderer.on(MainEventType.UNDO, () => { + const editor = this.getCurEditor(); + editor?.undo(); + }); + + ipcRenderer.on(MainEventType.REDO, () => { + const editor = this.getCurEditor(); + editor?.redo(); + }); } getOpenTreesModel() {