add Properties
This commit is contained in:
1 parent
31d986c5d2
commit
d0690f0398
8 files changed
+181
-36
No files matched your search
@@ -0,0 +1,19 @@
|
|||||||
|
export interface BehaviorNodeTypeModel {
|
||||||
|
name: string;
|
||||||
|
type?: string;
|
||||||
|
desc?: string;
|
||||||
|
args?: string[][3];
|
||||||
|
input?: string[];
|
||||||
|
output?: string;
|
||||||
|
doc?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BehaviorNodeModel {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
desc?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BehaviorTreeModel {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>demo</title>
|
<title>行为树编辑器</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -167,16 +167,16 @@ export default class AppMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createNodeMenu() {
|
private createNodeMenu() {
|
||||||
const typeItems: MenuItemConstructorOptions[] = [];
|
const classifyItems: MenuItemConstructorOptions[] = [];
|
||||||
const map: { [key: string]: MenuItemConstructorOptions } = {}
|
const map: { [key: string]: MenuItemConstructorOptions } = {}
|
||||||
for (let t of this.settings.nodeTypes) {
|
for (let t of this.settings.nodeClassify) {
|
||||||
let item: MenuItemConstructorOptions = {
|
let item: MenuItemConstructorOptions = {
|
||||||
id: t.type,
|
id: t.classify,
|
||||||
label: `${t.type}(${t.desc})`,
|
label: `${t.classify}(${t.desc})`,
|
||||||
submenu: [],
|
submenu: [],
|
||||||
};
|
};
|
||||||
typeItems.push(item);
|
classifyItems.push(item);
|
||||||
map[t.type] = item;
|
map[t.classify] = item;
|
||||||
}
|
}
|
||||||
const other: MenuItemConstructorOptions = {
|
const other: MenuItemConstructorOptions = {
|
||||||
id: 'other',
|
id: 'other',
|
||||||
@@ -205,12 +205,12 @@ export default class AppMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(hasOther) {
|
if(hasOther) {
|
||||||
typeItems.push(other);
|
classifyItems.push(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MenuItem({
|
return new MenuItem({
|
||||||
label: "新建节点",
|
label: "新建节点",
|
||||||
submenu: typeItems
|
submenu: classifyItems
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+14
-12
@@ -1,7 +1,8 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
import { BehaviorNodeTypeModel } from '../common/BehaviorTreeModel';
|
||||||
|
|
||||||
export interface BehaviorNodeType {
|
export interface BehaviorNodeClassify {
|
||||||
type: string;
|
classify: string;
|
||||||
desc?: string;
|
desc?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9,16 +10,16 @@ export interface SettingsModel {
|
|||||||
recentWorkspaces?: string[];
|
recentWorkspaces?: string[];
|
||||||
recentFiles?: string[];
|
recentFiles?: string[];
|
||||||
nodeConfigPath?: string; // 节点配置路径
|
nodeConfigPath?: string; // 节点配置路径
|
||||||
nodeTypes?: BehaviorNodeType[];
|
nodeClassify?: BehaviorNodeClassify[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingPath = 'settings.json';
|
const settingPath = 'settings.json';
|
||||||
const sampleNodeConfig = 'sample-node-config.json';
|
const sampleNodeConfig = 'sample-node-config.json';
|
||||||
const sampleNodeTypes: BehaviorNodeType[] = [
|
const sampleNodeClassify: BehaviorNodeClassify[] = [
|
||||||
{ type: "Composite", desc: "复合节点" },
|
{ classify: "Composite", desc: "复合节点" },
|
||||||
{ type: "Decorator", desc: "修饰节点" },
|
{ classify: "Decorator", desc: "修饰节点" },
|
||||||
{ type: "Condition", desc: "条件节点" },
|
{ classify: "Condition", desc: "条件节点" },
|
||||||
{ type: "Action", desc: "行为节点" },
|
{ classify: "Action", desc: "行为节点" },
|
||||||
]
|
]
|
||||||
|
|
||||||
export default class Settings {
|
export default class Settings {
|
||||||
@@ -32,7 +33,7 @@ export default class Settings {
|
|||||||
recentWorkspaces: [],
|
recentWorkspaces: [],
|
||||||
recentFiles: [],
|
recentFiles: [],
|
||||||
nodeConfigPath: sampleNodeConfig,
|
nodeConfigPath: sampleNodeConfig,
|
||||||
nodeTypes: sampleNodeTypes,
|
nodeClassify: sampleNodeClassify,
|
||||||
};
|
};
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
@@ -49,10 +50,11 @@ export default class Settings {
|
|||||||
}
|
}
|
||||||
get nodeConfig() {
|
get nodeConfig() {
|
||||||
const str = fs.readFileSync(this.nodeConfigPath, 'utf8');
|
const str = fs.readFileSync(this.nodeConfigPath, 'utf8');
|
||||||
return JSON.parse(str);
|
const types: BehaviorNodeTypeModel[] = JSON.parse(str);
|
||||||
|
return types;
|
||||||
}
|
}
|
||||||
get nodeTypes() {
|
get nodeClassify() {
|
||||||
return this.settings.nodeTypes;
|
return this.settings.nodeClassify;
|
||||||
}
|
}
|
||||||
|
|
||||||
set(config: SettingsModel) {
|
set(config: SettingsModel) {
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { app, BrowserWindow, Menu, MenuItem, dialog, nativeTheme } from 'electron';
|
import { app, BrowserWindow, Menu, MenuItem, dialog, nativeTheme } from 'electron';
|
||||||
import AppMenu from './AppMenu'
|
import AppMenu from './AppMenu'
|
||||||
import Settings from './Settings';
|
import Settings from './Settings';
|
||||||
|
import MainEventType from '../common/MainEventType';
|
||||||
|
|
||||||
export class MainProcess {
|
export class MainProcess {
|
||||||
mainWindow: BrowserWindow;
|
mainWindow: BrowserWindow;
|
||||||
appMenu: AppMenu;
|
appMenu: AppMenu;
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
constructor() {
|
constructor() {
|
||||||
nativeTheme.themeSource = 'dark';
|
nativeTheme.themeSource = 'dark';
|
||||||
app.on('ready', () => {
|
app.on('ready', () => {
|
||||||
this.createWindow();
|
this.createWindow();
|
||||||
})
|
})
|
||||||
@@ -23,7 +24,7 @@ export class MainProcess {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
createWindow (){
|
createWindow() {
|
||||||
this.settings = new Settings();
|
this.settings = new Settings();
|
||||||
this.mainWindow = new BrowserWindow({
|
this.mainWindow = new BrowserWindow({
|
||||||
width: 1280,
|
width: 1280,
|
||||||
@@ -41,6 +42,11 @@ export class MainProcess {
|
|||||||
});
|
});
|
||||||
this.appMenu = new AppMenu(this);
|
this.appMenu = new AppMenu(this);
|
||||||
this.rebuildMenu();
|
this.rebuildMenu();
|
||||||
|
|
||||||
|
const lastWorkspace = this.settings.recentWorkspaces[0];
|
||||||
|
if(lastWorkspace) {
|
||||||
|
this.mainWindow.webContents.send(MainEventType.OPEN_WORKSPACE, lastWorkspace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rebuildMenu() {
|
rebuildMenu() {
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
.body {
|
||||||
|
width : 100%;
|
||||||
|
height : 100%;
|
||||||
|
position: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sider {
|
||||||
|
height : 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sider:hover {
|
||||||
|
overflow-y : auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width : 100%;
|
||||||
|
height : 100%;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import { Menu, Input } from "antd";
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { BehaviorTreeModel } from "../../common/BehaviorTreeModel";
|
||||||
|
|
||||||
|
const { Search } = Input;
|
||||||
|
|
||||||
|
export interface PropertiesProps {
|
||||||
|
workspace: string;
|
||||||
|
onOpenTree: (path: string) => void;
|
||||||
|
trees: BehaviorTreeModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PropertiesState {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Properties extends Component<PropertiesProps> {
|
||||||
|
state: PropertiesState = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Search
|
||||||
|
placeholder="Search"
|
||||||
|
onChange={() => {
|
||||||
|
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Menu
|
||||||
|
mode="inline"
|
||||||
|
>
|
||||||
|
<Menu.Item>test</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,26 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import { DatePicker } from 'antd';
|
import { Layout } from 'antd';
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import MainEventType from "../../common/MainEventType";
|
import MainEventType from "../../common/MainEventType";
|
||||||
|
import Properties from "./Properties";
|
||||||
|
import './Editor.css';
|
||||||
|
import { BehaviorTreeModel } from "../../common/BehaviorTreeModel";
|
||||||
|
|
||||||
|
const { Header, Sider, Content, Footer } = Layout;
|
||||||
|
|
||||||
interface EditorState {
|
interface EditorState {
|
||||||
workspace: string;
|
workspace: string;
|
||||||
filepaths: string[];
|
filepaths: string[];
|
||||||
|
trees: BehaviorTreeModel[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Editor extends Component {
|
export default class Editor extends Component {
|
||||||
state: EditorState = {
|
state: EditorState = {
|
||||||
workspace: '',
|
workspace: '',
|
||||||
filepaths: [],
|
filepaths: [],
|
||||||
|
trees: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -20,17 +28,51 @@ export default class Editor extends Component {
|
|||||||
console.log("on open file", path);
|
console.log("on open file", path);
|
||||||
const filepaths = this.state.filepaths;
|
const filepaths = this.state.filepaths;
|
||||||
filepaths.push(path);
|
filepaths.push(path);
|
||||||
this.setState({filepaths});
|
this.setState({ filepaths });
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on(MainEventType.OPEN_WORKSPACE, (event: any, workspace: any) => {
|
ipcRenderer.on(MainEventType.OPEN_WORKSPACE, (event: any, workspace: any) => {
|
||||||
console.log("on open workspace", workspace);
|
console.log("on open workspace", workspace);
|
||||||
this.setState({workspace});
|
this.setState({ workspace });
|
||||||
|
this.loadAllTrees();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.loadAllTrees();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAllTrees() {
|
||||||
|
const workspace = this.state.workspace
|
||||||
|
if(workspace == '' || !fs.existsSync(workspace)) {
|
||||||
|
console.log("workspace not exist", workspace);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = fs.readdirSync(workspace);
|
||||||
|
const list = []
|
||||||
|
files.forEach((filename) => {
|
||||||
|
console.log("load path", filename);
|
||||||
|
const stat = fs.statSync(path.join(workspace, filename))
|
||||||
|
if (stat.isFile()) {
|
||||||
|
let tree = this.loadTree(filename.slice(0, -5));
|
||||||
|
list.push(tree);
|
||||||
|
console.log("loaded", filename);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadTree(name: string) {
|
||||||
|
const filename = this.state.workspace + '/' + name + '.json';
|
||||||
|
if (!fs.existsSync(filename)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const str = fs.readFileSync(filename, 'utf8');
|
||||||
|
let tree: BehaviorTreeModel = JSON.parse(str);
|
||||||
|
tree.name = name;
|
||||||
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderJson() {
|
renderJson() {
|
||||||
if(this.state.filepaths.length>0) {
|
if (this.state.filepaths.length > 0) {
|
||||||
const path = this.state.filepaths[0];
|
const path = this.state.filepaths[0];
|
||||||
const str = fs.readFileSync(path, 'utf8');
|
const str = fs.readFileSync(path, 'utf8');
|
||||||
return (<div>{str}</div>)
|
return (<div>{str}</div>)
|
||||||
@@ -38,16 +80,28 @@ export default class Editor extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { trees } = this.state;
|
||||||
return (
|
return (
|
||||||
<div>
|
<Layout className="body">
|
||||||
<div>
|
<Sider className="sider" width={250}>
|
||||||
当前目录:{this.state.workspace}
|
<Properties
|
||||||
</div>
|
workspace={this.state.workspace}
|
||||||
<div>
|
onOpenTree={(path) => {
|
||||||
当前文件:{this.state.filepaths.join("\n")}
|
console.log("open tree", path);
|
||||||
</div>
|
}}
|
||||||
{this.renderJson()}
|
trees={trees}
|
||||||
</div>
|
/>
|
||||||
|
</Sider>
|
||||||
|
<Content className="content">
|
||||||
|
<div>
|
||||||
|
当前目录:{this.state.workspace}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
当前文件:{this.state.filepaths.join("\n")}
|
||||||
|
</div>
|
||||||
|
{this.renderJson()}
|
||||||
|
</Content>
|
||||||
|
</Layout>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Reference in new issue
Block a user