add Workspace
This commit is contained in:
1 parent
c4eb53ac68
commit
70cc4dca87
6 files changed
+220
-83
No files matched your search
@@ -1,5 +1,6 @@
|
|||||||
export default {
|
export default {
|
||||||
OPEN_FILE: "OPEN_FILE",
|
OPEN_FILE: "OPEN_FILE",
|
||||||
|
OPEN_DIR: "OPEN_DIR",
|
||||||
OPEN_WORKSPACE: "OPEN_WORKSPACE",
|
OPEN_WORKSPACE: "OPEN_WORKSPACE",
|
||||||
CREATE_NODE: 'CREATE_NODE',
|
CREATE_NODE: 'CREATE_NODE',
|
||||||
SAVE: 'SAVE',
|
SAVE: 'SAVE',
|
||||||
|
|||||||
+99
-49
@@ -18,9 +18,9 @@ export default class AppMenu {
|
|||||||
|
|
||||||
createMenu() {
|
createMenu() {
|
||||||
const menu: Menu = new Menu();
|
const menu: Menu = new Menu();
|
||||||
|
menu.append(this.createWorkspaceMenu());
|
||||||
menu.append(this.createFileMenu());
|
menu.append(this.createFileMenu());
|
||||||
menu.append(this.createNodeMenu());
|
menu.append(this.createNodeMenu());
|
||||||
menu.append(this.createSettingsMenu());
|
|
||||||
menu.append(this.createToolsMenu());
|
menu.append(this.createToolsMenu());
|
||||||
return menu;
|
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({
|
return new MenuItem({
|
||||||
label: "行为树",
|
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' },
|
{ type: 'separator' },
|
||||||
{
|
{
|
||||||
label: "最近打开",
|
label: "最近打开",
|
||||||
submenu: fileItems,
|
submenu: fileItems,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: "最近目录",
|
|
||||||
submenu: workspaceItems,
|
|
||||||
},
|
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{
|
{
|
||||||
label: "保存",
|
label: "保存",
|
||||||
@@ -113,21 +82,95 @@ export default class AppMenu {
|
|||||||
this.webContents.send(MainEventType.SAVE_ALL);
|
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({
|
return new MenuItem({
|
||||||
label: "设置",
|
label: "工作区",
|
||||||
submenu: [
|
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: "节点定义",
|
label: "节点定义",
|
||||||
submenu: [
|
submenu: [
|
||||||
@@ -143,15 +186,22 @@ export default class AppMenu {
|
|||||||
});
|
});
|
||||||
if (res.filePaths.length > 0) {
|
if (res.filePaths.length > 0) {
|
||||||
const nodeConfigPath = res.filePaths[0];
|
const nodeConfigPath = res.filePaths[0];
|
||||||
this.settings.set({ nodeConfigPath });
|
this.settings.curWorkspace.setNodeConfPath(nodeConfigPath);
|
||||||
this.mainProcess.rebuildMenu();
|
this.mainProcess.rebuildMenu();
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ 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];
|
let typeItem = map[node.type];
|
||||||
if(!typeItem) {
|
if (!typeItem) {
|
||||||
typeItem = other;
|
typeItem = other;
|
||||||
hasOther = true;
|
hasOther = true;
|
||||||
}
|
}
|
||||||
@@ -218,7 +268,7 @@ export default class AppMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(hasOther) {
|
if (hasOther) {
|
||||||
classifyItems.push(other);
|
classifyItems.push(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,9 +277,9 @@ export default class AppMenu {
|
|||||||
click: () => {
|
click: () => {
|
||||||
this.webContents.send(MainEventType.CREATE_NODE, 'unknow');
|
this.webContents.send(MainEventType.CREATE_NODE, 'unknow');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
classifyItems.push(unknonwItem);
|
classifyItems.push(unknonwItem);
|
||||||
|
|
||||||
return new MenuItem({
|
return new MenuItem({
|
||||||
label: "新建节点",
|
label: "新建节点",
|
||||||
submenu: classifyItems
|
submenu: classifyItems
|
||||||
|
|||||||
+17
-18
@@ -1,5 +1,6 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { BehaviorNodeTypeModel } from '../common/BehaviorTreeModel';
|
import { BehaviorNodeTypeModel } from '../common/BehaviorTreeModel';
|
||||||
|
import Workspace from './Workspace';
|
||||||
|
|
||||||
export interface BehaviorNodeClassify {
|
export interface BehaviorNodeClassify {
|
||||||
classify: string;
|
classify: string;
|
||||||
@@ -9,7 +10,6 @@ export interface BehaviorNodeClassify {
|
|||||||
export interface SettingsModel {
|
export interface SettingsModel {
|
||||||
recentWorkspaces?: string[];
|
recentWorkspaces?: string[];
|
||||||
recentFiles?: string[];
|
recentFiles?: string[];
|
||||||
nodeConfigPath?: string; // 节点配置路径
|
|
||||||
nodeClassify?: BehaviorNodeClassify[];
|
nodeClassify?: BehaviorNodeClassify[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,21 +22,18 @@ const sampleNodeClassify: BehaviorNodeClassify[] = [
|
|||||||
{ classify: "Action", desc: "行为节点" },
|
{ classify: "Action", desc: "行为节点" },
|
||||||
]
|
]
|
||||||
|
|
||||||
const unknowNodeType: BehaviorNodeTypeModel = {
|
|
||||||
name: 'unknow',
|
|
||||||
desc: '新建节点',
|
|
||||||
type: 'Action',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class Settings {
|
export default class Settings {
|
||||||
private settings: SettingsModel;
|
private settings: SettingsModel;
|
||||||
private name2conf: { [name: string]: BehaviorNodeTypeModel } = {};
|
curWorkspace?: Workspace;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.load();
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
get nodeConfigPath() {
|
get nodeConfPath() {
|
||||||
return this.settings.nodeConfigPath;
|
return this.curWorkspace.getNodeConfPath();
|
||||||
}
|
}
|
||||||
get recentWorkspaces() {
|
get recentWorkspaces() {
|
||||||
return this.settings.recentWorkspaces;
|
return this.settings.recentWorkspaces;
|
||||||
@@ -45,13 +42,7 @@ export default class Settings {
|
|||||||
return this.settings.recentFiles;
|
return this.settings.recentFiles;
|
||||||
}
|
}
|
||||||
get nodeConfig() {
|
get nodeConfig() {
|
||||||
const str = fs.readFileSync(this.nodeConfigPath, 'utf8');
|
return this.curWorkspace.nodeConf;
|
||||||
const types: BehaviorNodeTypeModel[] = JSON.parse(str);
|
|
||||||
this.name2conf = {};
|
|
||||||
types.forEach(t => {
|
|
||||||
this.name2conf[t.name] = t;
|
|
||||||
})
|
|
||||||
return types;
|
|
||||||
}
|
}
|
||||||
get nodeClassify() {
|
get nodeClassify() {
|
||||||
return this.settings.nodeClassify;
|
return this.settings.nodeClassify;
|
||||||
@@ -73,11 +64,12 @@ export default class Settings {
|
|||||||
this.settings = {
|
this.settings = {
|
||||||
recentWorkspaces: [],
|
recentWorkspaces: [],
|
||||||
recentFiles: [],
|
recentFiles: [],
|
||||||
nodeConfigPath: sampleNodeConfig,
|
|
||||||
nodeClassify: sampleNodeClassify,
|
nodeClassify: sampleNodeClassify,
|
||||||
};
|
};
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
this.curWorkspace = new Workspace(this.settings.recentWorkspaces[0]);
|
||||||
|
this.curWorkspace.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
@@ -85,6 +77,13 @@ export default class Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getNodeConf(name: string) {
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "behavior3editor",
|
"name": "behavior3editor",
|
||||||
"version": "0.0.3",
|
"version": "0.1.0",
|
||||||
"description": "行为树编辑器",
|
"description": "行为树编辑器",
|
||||||
"main": "./dist/main.js",
|
"main": "./dist/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+12
-15
@@ -23,12 +23,12 @@ const { TabPane } = Tabs;
|
|||||||
|
|
||||||
|
|
||||||
interface MainState {
|
interface MainState {
|
||||||
workspace: string;
|
workdir: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Main extends Component {
|
export default class Main extends Component {
|
||||||
state: MainState = {
|
state: MainState = {
|
||||||
workspace: '',
|
workdir: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
@@ -44,17 +44,14 @@ export default class Main extends Component {
|
|||||||
this.setState({ curPath: path });
|
this.setState({ curPath: path });
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on(MainEventType.OPEN_WORKSPACE, (event: any, workspace: any) => {
|
ipcRenderer.on(MainEventType.OPEN_DIR, (event: any, workdir: any) => {
|
||||||
console.log("on open workspace", workspace);
|
console.log("on open workspace", workdir);
|
||||||
document.title = workspace;
|
document.title = workdir;
|
||||||
this.setState({ workspace });
|
this.setState({ workdir });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({ workspace: this.getLastWorkspace() });
|
console.log("workdir", this.settings.curWorkspace.getWorkdir());
|
||||||
}
|
this.setState({ workdir: this.settings.curWorkspace.getWorkdir() });
|
||||||
|
|
||||||
getLastWorkspace() {
|
|
||||||
return this.settings.recentWorkspaces[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSettings() {
|
updateSettings() {
|
||||||
@@ -63,13 +60,13 @@ export default class Main extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
console.log("render main");
|
console.log("render main");
|
||||||
const { workspace } = this.state;
|
const { workdir } = this.state;
|
||||||
document.title = `行为树编辑器 - ${workspace}`;
|
document.title = `行为树编辑器 - ${workdir}`;
|
||||||
return (
|
return (
|
||||||
<Layout className="body">
|
<Layout className="body">
|
||||||
<Sider className="sider" width={250}>
|
<Sider className="sider" width={250}>
|
||||||
<Properties
|
<Properties
|
||||||
workspace={this.state.workspace}
|
workspace={this.state.workdir}
|
||||||
onOpenTree={(path) => {
|
onOpenTree={(path) => {
|
||||||
this.tabs.openFile(path);
|
this.tabs.openFile(path);
|
||||||
}}
|
}}
|
||||||
@@ -77,7 +74,7 @@ export default class Main extends Component {
|
|||||||
</Sider>
|
</Sider>
|
||||||
<Content className="content">
|
<Content className="content">
|
||||||
<TreeTabs
|
<TreeTabs
|
||||||
ref = {ref => {
|
ref={ref => {
|
||||||
this.tabs = ref;
|
this.tabs = ref;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in new issue
Block a user