From 70cc4dca871aa3bf710b81a084ca642b21bb71f7 Mon Sep 17 00:00:00 2001 From: zhandouxiaojiji Date: Wed, 4 Nov 2020 20:16:14 +0800 Subject: [PATCH] add Workspace --- common/MainEventType.ts | 1 + main-process/AppMenu.ts | 148 +++++++++++++++++++++++++------------- main-process/Settings.ts | 35 +++++---- main-process/Workspace.ts | 90 +++++++++++++++++++++++ package.json | 2 +- render-process/index.tsx | 27 ++++--- 6 files changed, 220 insertions(+), 83 deletions(-) create mode 100644 main-process/Workspace.ts diff --git a/common/MainEventType.ts b/common/MainEventType.ts index 193509d..21aff39 100644 --- a/common/MainEventType.ts +++ b/common/MainEventType.ts @@ -1,5 +1,6 @@ export default { OPEN_FILE: "OPEN_FILE", + OPEN_DIR: "OPEN_DIR", OPEN_WORKSPACE: "OPEN_WORKSPACE", CREATE_NODE: 'CREATE_NODE', SAVE: 'SAVE', diff --git a/main-process/AppMenu.ts b/main-process/AppMenu.ts index f8eeeba..f828185 100644 --- a/main-process/AppMenu.ts +++ b/main-process/AppMenu.ts @@ -18,9 +18,9 @@ export default class AppMenu { createMenu() { const menu: Menu = new Menu(); + menu.append(this.createWorkspaceMenu()); menu.append(this.createFileMenu()); menu.append(this.createNodeMenu()); - menu.append(this.createSettingsMenu()); menu.append(this.createToolsMenu()); return menu; } @@ -35,15 +35,7 @@ export default class AppMenu { } }) } - const workspaceItems: MenuItemConstructorOptions[] = []; - for (let path of this.settings.recentWorkspaces) { - workspaceItems.push({ - label: path, - click: () => { - console.log("open recent workspace", path); - } - }) - } + return new MenuItem({ label: "行为树", @@ -71,35 +63,12 @@ export default class AppMenu { })(); } }, - { - label: "打开目录", - accelerator: "Ctrl+Shift+O", - click: () => { - (async () => { - const res = await dialog.showOpenDialog({ - properties: ['openDirectory'] - }); - if (res.filePaths.length > 0) { - const path = res.filePaths[0]; - if (this.settings.recentWorkspaces.indexOf(path) < 0) { - this.settings.recentWorkspaces.unshift(path); - this.settings.save(); - this.mainProcess.rebuildMenu(); - } - this.webContents.send(MainEventType.OPEN_WORKSPACE, path); - } - })(); - } - }, { type: 'separator' }, { label: "最近打开", submenu: fileItems, }, - { - label: "最近目录", - submenu: workspaceItems, - }, + { type: 'separator' }, { label: "保存", @@ -113,21 +82,95 @@ export default class AppMenu { this.webContents.send(MainEventType.SAVE_ALL); }, }, - { type: 'separator' }, - { - label: "关闭", - click: () => { - app.quit(); - } - } ] }); } - private createSettingsMenu() { + private createWorkspaceMenu() { + const openWorkspace = (path: string) => { + this.settings.curWorkspace.setFilepath(path); + this.settings.curWorkspace.load(); + this.settings.pushRecentWorkspace(path); + this.mainProcess.rebuildMenu(); + this.webContents.send(MainEventType.OPEN_DIR, this.settings.curWorkspace.getWorkdir()); + }; + const recentItems: MenuItemConstructorOptions[] = []; + for (let path of this.settings.recentWorkspaces) { + recentItems.push({ + label: path, + click: () => { + console.log("open recent workspace", path); + openWorkspace(path); + } + }) + } return new MenuItem({ - label: "设置", + label: "工作区", submenu: [ + { + label: "打开", + click: () => { + (async () => { + const res = await dialog.showOpenDialog({ + properties: ['openFile'], + filters: [ + { name: "Json", extensions: ['json'] } + ] + }); + if (res.filePaths.length > 0) { + openWorkspace(res.filePaths[0]); + } + })(); + } + }, + { + label: "保存", + click: () => { + if (this.settings.curWorkspace.getFilepath()) { + this.settings.curWorkspace.save(); + } else { + (async () => { + const res = await dialog.showSaveDialog({ + properties: ['showOverwriteConfirmation'], + filters: [ + { name: "Json", extensions: ['json'] } + ] + }); + if (!res.canceled) { + this.settings.curWorkspace.setFilepath(res.filePath); + this.settings.curWorkspace.save(); + + } + })(); + } + } + }, + { + label: "最近打开", + submenu: recentItems, + }, + { type: 'separator' }, + { + label: "打开目录", + accelerator: "Ctrl+Shift+O", + click: () => { + if (!this.settings.curWorkspace.getNodeConfPath()) { + dialog.showErrorBox("警告", "请先指定节点定义配置!"); + return; + } + (async () => { + const res = await dialog.showOpenDialog({ + properties: ['openDirectory'] + }); + if (res.filePaths.length > 0) { + const path = res.filePaths[0]; + this.settings.curWorkspace.setWorkdir(path); + this.settings.curWorkspace.save(); + this.webContents.send(MainEventType.OPEN_DIR, path); + } + })(); + } + }, { label: "节点定义", submenu: [ @@ -143,15 +186,22 @@ export default class AppMenu { }); if (res.filePaths.length > 0) { const nodeConfigPath = res.filePaths[0]; - this.settings.set({ nodeConfigPath }); + this.settings.curWorkspace.setNodeConfPath(nodeConfigPath); this.mainProcess.rebuildMenu(); } })(); } }, { type: 'separator' }, - { label: this.settings.nodeConfigPath, } + { label: this.settings.nodeConfPath, } ] + }, + { type: 'separator' }, + { + label: "关闭", + click: () => { + app.quit(); + } } ] }); @@ -207,7 +257,7 @@ export default class AppMenu { } } let typeItem = map[node.type]; - if(!typeItem) { + if (!typeItem) { typeItem = other; hasOther = true; } @@ -218,7 +268,7 @@ export default class AppMenu { } } - if(hasOther) { + if (hasOther) { classifyItems.push(other); } @@ -227,9 +277,9 @@ export default class AppMenu { click: () => { this.webContents.send(MainEventType.CREATE_NODE, 'unknow'); } - } + } classifyItems.push(unknonwItem); - + return new MenuItem({ label: "新建节点", submenu: classifyItems diff --git a/main-process/Settings.ts b/main-process/Settings.ts index f095f65..50dccbd 100644 --- a/main-process/Settings.ts +++ b/main-process/Settings.ts @@ -1,5 +1,6 @@ import * as fs from 'fs'; import { BehaviorNodeTypeModel } from '../common/BehaviorTreeModel'; +import Workspace from './Workspace'; export interface BehaviorNodeClassify { classify: string; @@ -9,7 +10,6 @@ export interface BehaviorNodeClassify { export interface SettingsModel { recentWorkspaces?: string[]; recentFiles?: string[]; - nodeConfigPath?: string; // 节点配置路径 nodeClassify?: BehaviorNodeClassify[]; } @@ -22,21 +22,18 @@ const sampleNodeClassify: BehaviorNodeClassify[] = [ { classify: "Action", desc: "行为节点" }, ] -const unknowNodeType: BehaviorNodeTypeModel = { - name: 'unknow', - desc: '新建节点', - type: 'Action', -} + export default class Settings { private settings: SettingsModel; - private name2conf: { [name: string]: BehaviorNodeTypeModel } = {}; + curWorkspace?: Workspace; + constructor() { this.load(); } - get nodeConfigPath() { - return this.settings.nodeConfigPath; + get nodeConfPath() { + return this.curWorkspace.getNodeConfPath(); } get recentWorkspaces() { return this.settings.recentWorkspaces; @@ -45,13 +42,7 @@ export default class Settings { return this.settings.recentFiles; } get nodeConfig() { - const str = fs.readFileSync(this.nodeConfigPath, 'utf8'); - const types: BehaviorNodeTypeModel[] = JSON.parse(str); - this.name2conf = {}; - types.forEach(t => { - this.name2conf[t.name] = t; - }) - return types; + return this.curWorkspace.nodeConf; } get nodeClassify() { return this.settings.nodeClassify; @@ -73,11 +64,12 @@ export default class Settings { this.settings = { recentWorkspaces: [], recentFiles: [], - nodeConfigPath: sampleNodeConfig, nodeClassify: sampleNodeClassify, }; this.save(); } + this.curWorkspace = new Workspace(this.settings.recentWorkspaces[0]); + this.curWorkspace.load(); } save() { @@ -85,6 +77,13 @@ export default class Settings { } getNodeConf(name: string) { - return this.name2conf[name] || unknowNodeType; + return this.curWorkspace.getNodeConf(name); + } + + pushRecentWorkspace(path: string) { + if (this.settings.recentWorkspaces.indexOf(path) < 0) { + this.settings.recentWorkspaces.unshift(path); + } + this.save(); } } \ No newline at end of file diff --git a/main-process/Workspace.ts b/main-process/Workspace.ts new file mode 100644 index 0000000..03b229a --- /dev/null +++ b/main-process/Workspace.ts @@ -0,0 +1,90 @@ +import * as fs from 'fs'; +import { BehaviorNodeTypeModel } from '../common/BehaviorTreeModel'; + +const unknowNodeType: BehaviorNodeTypeModel = { + name: 'unknow', + desc: '新建节点', + type: 'Action', +} + +export default class Workspace { + private filepath: string; + private nodeConfPath: string; + private workdir: string; + + private name2conf: { [name: string]: BehaviorNodeTypeModel } = {}; + private types: BehaviorNodeTypeModel[] = []; + + constructor(filepath: string) { + this.filepath = filepath; + } + + load() { + if(!this.filepath) { + return; + } + + const str = fs.readFileSync(this.filepath, 'utf8'); + const data = JSON.parse(str); + this.nodeConfPath = data.nodeConfPath; + this.workdir = data.workdir; + + this.initNodeConf(); + } + + private initNodeConf() { + if (!this.nodeConfPath) { + return; + } + + const types: BehaviorNodeTypeModel[] = JSON.parse(fs.readFileSync(this.nodeConfPath, 'utf8')); + this.name2conf = {}; + types.forEach(t => { + this.name2conf[t.name] = t; + }) + this.types = types; + } + + getNodeConfPath() { + return this.nodeConfPath; + } + setNodeConfPath(path: string) { + this.nodeConfPath = path; + this.initNodeConf(); + } + getWorkdir() { + return this.workdir; + } + setWorkdir(workdir: string) { + this.workdir = workdir; + } + + save(filepath?: string) { + if (filepath) { + this.filepath = filepath; + } + if (!this.filepath) { + return; + } + fs.writeFileSync(this.filepath, JSON.stringify({ + nodeConfPath: this.nodeConfPath, + workdir: this.workdir, + }, null, 2)) + } + + get nodeConf() { + return this.types; + } + + getFilepath() { + return this.filepath; + } + + setFilepath(filepath: string) { + this.filepath = filepath; + } + + getNodeConf(name: string) { + return this.name2conf[name] || unknowNodeType; + } +} \ No newline at end of file diff --git a/package.json b/package.json index 5ebb76b..4c21141 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "behavior3editor", - "version": "0.0.3", + "version": "0.1.0", "description": "行为树编辑器", "main": "./dist/main.js", "scripts": { diff --git a/render-process/index.tsx b/render-process/index.tsx index 75a217e..7ed4067 100644 --- a/render-process/index.tsx +++ b/render-process/index.tsx @@ -23,12 +23,12 @@ const { TabPane } = Tabs; interface MainState { - workspace: string; + workdir: string; } export default class Main extends Component { state: MainState = { - workspace: '', + workdir: '', } settings: Settings; @@ -44,17 +44,14 @@ export default class Main extends Component { this.setState({ curPath: path }); }); - ipcRenderer.on(MainEventType.OPEN_WORKSPACE, (event: any, workspace: any) => { - console.log("on open workspace", workspace); - document.title = workspace; - this.setState({ workspace }); + ipcRenderer.on(MainEventType.OPEN_DIR, (event: any, workdir: any) => { + console.log("on open workspace", workdir); + document.title = workdir; + this.setState({ workdir }); }); - this.setState({ workspace: this.getLastWorkspace() }); - } - - getLastWorkspace() { - return this.settings.recentWorkspaces[0]; + console.log("workdir", this.settings.curWorkspace.getWorkdir()); + this.setState({ workdir: this.settings.curWorkspace.getWorkdir() }); } updateSettings() { @@ -63,13 +60,13 @@ export default class Main extends Component { render() { console.log("render main"); - const { workspace } = this.state; - document.title = `行为树编辑器 - ${workspace}`; + const { workdir } = this.state; + document.title = `行为树编辑器 - ${workdir}`; return ( { this.tabs.openFile(path); }} @@ -77,7 +74,7 @@ export default class Main extends Component { { + ref={ref => { this.tabs = ref; }} />