style: format codes

This commit is contained in:
duanhongbo committed 2020-11-12 17:24:08 +08:00
1 parent c3ea18e307
commit 00e79d641e
14 files changed
+1484 -1518

No files matched your search

+28 -28
View File
@@ -1,44 +1,44 @@
import { TreeGraphData } from "@antv/g6/lib/types"; import { TreeGraphData } from "@antv/g6/lib/types";
export interface ArgsDefType { export interface ArgsDefType {
name: string, name: string;
type: string, type: string;
desc: string, desc: string;
} }
export interface BehaviorNodeTypeModel { export interface BehaviorNodeTypeModel {
name: string; name: string;
type?: string; type?: string;
desc?: string; desc?: string;
args?: ArgsDefType[]; args?: ArgsDefType[];
input?: string[]; input?: string[];
output?: string[]; output?: string[];
doc?: string; doc?: string;
} }
export interface BehaviorNodeModel { export interface BehaviorNodeModel {
id: number; id: number;
name: string; name: string;
desc?: string; desc?: string;
args?: {[key: string]: any}; args?: { [key: string]: any };
input?: string[]; input?: string[];
output?: string[]; output?: string[];
children?: BehaviorNodeModel[]; children?: BehaviorNodeModel[];
debug?: boolean; debug?: boolean;
} }
export interface BehaviorTreeModel { export interface BehaviorTreeModel {
name: string; name: string;
root: BehaviorNodeModel; root: BehaviorNodeModel;
} }
export interface GraphNodeModel extends TreeGraphData { export interface GraphNodeModel extends TreeGraphData {
name: string; name: string;
desc?: string; desc?: string;
args?: {[key: string]: any}; args?: { [key: string]: any };
input?: string[]; input?: string[];
output?: string[]; output?: string[];
children?: GraphNodeModel[]; children?: GraphNodeModel[];
conf: BehaviorNodeTypeModel; conf: BehaviorNodeTypeModel;
size?: number[]; size?: number[];
} }
+11 -11
View File
@@ -1,12 +1,12 @@
export default { export default {
OPEN_FILE: "OPEN_FILE", OPEN_FILE: "OPEN_FILE",
OPEN_DIR: "OPEN_DIR", OPEN_DIR: "OPEN_DIR",
OPEN_WORKSPACE: "OPEN_WORKSPACE", OPEN_WORKSPACE: "OPEN_WORKSPACE",
CREATE_TREE: 'CREATE_TREE', CREATE_TREE: "CREATE_TREE",
CREATE_NODE: 'CREATE_NODE', CREATE_NODE: "CREATE_NODE",
DELETE_NODE: 'DELETE_NODE', DELETE_NODE: "DELETE_NODE",
COPY_NODE: 'COPY_NODE', COPY_NODE: "COPY_NODE",
PASTE_NODE: 'PASTE_NODE', PASTE_NODE: "PASTE_NODE",
SAVE: 'SAVE', SAVE: "SAVE",
SAVE_ALL: 'SAVE_ALL', SAVE_ALL: "SAVE_ALL",
} };
+104 -105
View File
@@ -1,131 +1,130 @@
import { TreeGraphData } from "@antv/g6/lib/types" import { TreeGraphData } from "@antv/g6/lib/types";
import { BehaviorNodeModel, BehaviorTreeModel, GraphNodeModel } from "./BehaviorTreeModel"; import { BehaviorNodeModel, BehaviorTreeModel, GraphNodeModel } from "./BehaviorTreeModel";
import Settings from "../main-process/Settings"; import Settings from "../main-process/Settings";
import { remote } from "electron"; import { remote } from "electron";
import * as path from 'path'; import * as path from "path";
export const cloneNodeData = (nodeData: GraphNodeModel) => { export const cloneNodeData = (nodeData: GraphNodeModel) => {
const newData: GraphNodeModel = { const newData: GraphNodeModel = {
id: nodeData.id + "new", id: nodeData.id + "new",
name: nodeData.name, name: nodeData.name,
conf: nodeData.conf, conf: nodeData.conf,
} };
return newData; return newData;
} };
export const refreshNodeId = (nodeData: GraphNodeModel, id?: number) => { export const refreshNodeId = (nodeData: GraphNodeModel, id?: number) => {
if (!id) { if (!id) {
id = 1; id = 1;
} }
nodeData.id = (id++).toString(); nodeData.id = (id++).toString();
if (nodeData.children) { if (nodeData.children) {
nodeData.children.forEach(child => { nodeData.children.forEach((child) => {
id = refreshNodeId(child, id); id = refreshNodeId(child, id);
}) });
} }
return id; return id;
} };
export const calcTreeNodeSize = (treeNode: GraphNodeModel) => { export const calcTreeNodeSize = (treeNode: GraphNodeModel) => {
var height = 40; var height = 40;
const updateHeight = (obj: any) => { const updateHeight = (obj: any) => {
if (Array.isArray(obj) || (obj && Object.keys(obj).length > 0)) { if (Array.isArray(obj) || (obj && Object.keys(obj).length > 0)) {
height += 25; height += 25;
} }
} };
updateHeight(treeNode.conf.args); updateHeight(treeNode.conf.args);
updateHeight(treeNode.conf.input); updateHeight(treeNode.conf.input);
updateHeight(treeNode.conf.output); updateHeight(treeNode.conf.output);
return [200, height]; return [200, height];
} };
export const createTreeData = (bNode: BehaviorNodeModel, settings: Settings) => { export const createTreeData = (bNode: BehaviorNodeModel, settings: Settings) => {
const treeData: GraphNodeModel = { const treeData: GraphNodeModel = {
id: bNode.id.toString(), id: bNode.id.toString(),
name: bNode.name, name: bNode.name,
desc: bNode.desc, desc: bNode.desc,
args: bNode.args, args: bNode.args,
input: bNode.input, input: bNode.input,
output: bNode.output, output: bNode.output,
conf: settings.getNodeConf(bNode.name), conf: settings.getNodeConf(bNode.name),
} };
treeData.size = calcTreeNodeSize(treeData); treeData.size = calcTreeNodeSize(treeData);
if (bNode.children) { if (bNode.children) {
treeData.children = []; treeData.children = [];
bNode.children.forEach(child => { bNode.children.forEach((child) => {
treeData.children.push(createTreeData(child, settings)); treeData.children.push(createTreeData(child, settings));
}) });
} }
calcTreeNodeSize(treeData); calcTreeNodeSize(treeData);
return treeData; return treeData;
} };
export const createFileData = (gNode: GraphNodeModel) => { export const createFileData = (gNode: GraphNodeModel) => {
const nodeData: BehaviorNodeModel = { const nodeData: BehaviorNodeModel = {
id: Number(gNode.id), id: Number(gNode.id),
name: gNode.name, name: gNode.name,
desc: gNode.desc || undefined, desc: gNode.desc || undefined,
args: gNode.args || undefined, args: gNode.args || undefined,
input: gNode.input || undefined, input: gNode.input || undefined,
output: gNode.output || undefined, output: gNode.output || undefined,
} };
if (gNode.children) { if (gNode.children) {
nodeData.children = []; nodeData.children = [];
gNode.children.forEach(child => { gNode.children.forEach((child) => {
nodeData.children.push(createFileData(child)); nodeData.children.push(createFileData(child));
}) });
} }
return nodeData; return nodeData;
} };
export const findParent = (node: TreeGraphData, id: string): TreeGraphData | null => { export const findParent = (node: TreeGraphData, id: string): TreeGraphData | null => {
if (node.children) { if (node.children) {
for (let child of node.children) { for (let child of node.children) {
if (child.id == id) { if (child.id == id) {
return node; return node;
} else { } else {
let parent = findParent(child, id); let parent = findParent(child, id);
if (parent) { if (parent) {
return parent; return parent;
}
}
} }
}
} }
} return null;
return null; };
}
export const findFromAllChildren = (node: TreeGraphData, id: string): TreeGraphData | null => { export const findFromAllChildren = (node: TreeGraphData, id: string): TreeGraphData | null => {
if (node.id == id) { if (node.id == id) {
return node; return node;
}
if (node.children) {
for (let child of node.children) {
let found = findFromAllChildren(child, id);
if (found) {
return found;
}
} }
} if (node.children) {
return null; for (let child of node.children) {
} let found = findFromAllChildren(child, id);
if (found) {
return found;
}
}
}
return null;
};
export const getRemoteSettings = () => { export const getRemoteSettings = () => {
return remote.getGlobal("settings") as Settings; return remote.getGlobal("settings") as Settings;
} };
export const fileName2treeName = (filename: string) => { export const fileName2treeName = (filename: string) => {
return path.basename(filename).slice(0, -5); return path.basename(filename).slice(0, -5);
} };
export const createNewTree = (filename: string) => { export const createNewTree = (filename: string) => {
const tree: BehaviorTreeModel = { const tree: BehaviorTreeModel = {
name: fileName2treeName(filename), name: fileName2treeName(filename),
root: { root: {
id: 1, id: 1,
name: 'Sequence', name: "Sequence",
desc: '新建行为树' desc: "新建行为树",
} },
} };
return tree; return tree;
} };
+6 -6
View File
@@ -121,13 +121,13 @@ export default class AppMenu {
}); });
if (res.filePath) { if (res.filePath) {
const curWorkspace = this.settings.curWorkspace; const curWorkspace = this.settings.curWorkspace;
curWorkspace.writeAllTrees(res.filePath,(err)=>{ curWorkspace.writeAllTrees(res.filePath, (err) => {
const msg = err?err:"导出成功"; const msg = err ? err : "导出成功";
dialog.showMessageBox({ dialog.showMessageBox({
type:"info", type: "info",
buttons:["ok"], buttons: ["ok"],
message:msg message: msg,
}) });
}); });
} }
})(); })();
+56 -58
View File
@@ -1,72 +1,70 @@
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'; import MainEventType from "../common/MainEventType";
const electronLocalshortcut = require('electron-localshortcut'); const electronLocalshortcut = require("electron-localshortcut");
// 一些暴露给render-process的全局变量 // 一些暴露给render-process的全局变量
export interface Global { export interface Global {
settings: Settings settings: Settings;
} }
declare var global: Global; declare var global: Global;
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();
electronLocalshortcut.register(this.mainWindow, 'Ctrl+C', () => { electronLocalshortcut.register(this.mainWindow, "Ctrl+C", () => {
this.mainWindow.webContents.send(MainEventType.COPY_NODE); this.mainWindow.webContents.send(MainEventType.COPY_NODE);
}); });
electronLocalshortcut.register(this.mainWindow, 'Ctrl+V', () => { electronLocalshortcut.register(this.mainWindow, "Ctrl+V", () => {
this.mainWindow.webContents.send(MainEventType.PASTE_NODE); this.mainWindow.webContents.send(MainEventType.PASTE_NODE);
}); });
}) });
app.on('window-all-closed', () => { app.on("window-all-closed", () => {
if (process.platform !== 'darwin') { if (process.platform !== "darwin") {
app.quit() app.quit();
} }
}) });
app.on('activate', () => { app.on("activate", () => {
if (this.mainWindow === null) { if (this.mainWindow === null) {
this.createWindow() this.createWindow();
} }
}); });
} }
createWindow() { createWindow() {
this.settings = new Settings(); this.settings = new Settings();
global.settings = this.settings; global.settings = this.settings;
this.mainWindow = new BrowserWindow({ this.mainWindow = new BrowserWindow({
width: 1280, width: 1280,
height: 800, height: 800,
webPreferences: { webPreferences: {
nodeIntegration: true, nodeIntegration: true,
enableRemoteModule: true, enableRemoteModule: true,
} },
// fullscreenable:false, // fullscreenable:false,
// maximizable:false // maximizable:false
}); });
this.mainWindow.maximize(); this.mainWindow.maximize();
// mainWindow.webContents.openDevTools(); // mainWindow.webContents.openDevTools();
this.mainWindow.loadFile('index.html'); this.mainWindow.loadFile("index.html");
this.mainWindow.on('closed', function () { this.mainWindow.on("closed", function () {
this.mainWindow = null this.mainWindow = null;
}); });
this.appMenu = new AppMenu(this); this.appMenu = new AppMenu(this);
this.rebuildMenu(); this.rebuildMenu();
} }
rebuildMenu() { rebuildMenu() {
Menu.setApplicationMenu(this.appMenu.createMenu()); Menu.setApplicationMenu(this.appMenu.createMenu());
} }
} }
export default new MainProcess(); export default new MainProcess();
+74 -75
View File
@@ -1,90 +1,89 @@
import * as fs from 'fs'; import * as fs from "fs";
import { BehaviorNodeTypeModel } from '../common/BehaviorTreeModel'; import { BehaviorNodeTypeModel } from "../common/BehaviorTreeModel";
import Workspace from './Workspace'; import Workspace from "./Workspace";
export interface BehaviorNodeClassify { export interface BehaviorNodeClassify {
classify: string; classify: string;
desc?: string; desc?: string;
} }
export interface SettingsModel { export interface SettingsModel {
recentWorkspaces?: string[]; recentWorkspaces?: string[];
recentFiles?: string[]; recentFiles?: string[];
nodeClassify?: BehaviorNodeClassify[]; nodeClassify?: BehaviorNodeClassify[];
} }
const settingPath = 'settings.json'; const settingPath = "settings.json";
const sampleNodeClassify: BehaviorNodeClassify[] = [ const sampleNodeClassify: BehaviorNodeClassify[] = [
{ classify: "Composite", desc: "复合节点" }, { classify: "Composite", desc: "复合节点" },
{ classify: "Decorator", desc: "修饰节点" }, { classify: "Decorator", desc: "修饰节点" },
{ classify: "Condition", desc: "条件节点" }, { classify: "Condition", desc: "条件节点" },
{ classify: "Action", desc: "行为节点" }, { classify: "Action", desc: "行为节点" },
] ];
export default class Settings { export default class Settings {
private settings: SettingsModel; private settings: SettingsModel;
curWorkspace?: Workspace; curWorkspace?: Workspace;
constructor() { constructor() {
this.load(); this.load();
}
get nodeConfPath() {
return this.curWorkspace.getNodeConfPath();
}
get recentWorkspaces() {
return this.settings.recentWorkspaces;
}
get recentFiles() {
return this.settings.recentFiles;
}
get nodeConfig() {
return this.curWorkspace.nodeConf;
}
get nodeClassify() {
return this.settings.nodeClassify;
}
set(config: SettingsModel) {
this.settings = {
...this.settings,
...config
};
this.save();
}
load() {
if (fs.existsSync(settingPath)) {
const str = fs.readFileSync(settingPath, 'utf8');
this.settings = JSON.parse(str);
} else {
this.settings = {
recentWorkspaces: ['sample/workspace.json'],
recentFiles: [],
nodeClassify: sampleNodeClassify,
};
this.save();
} }
this.curWorkspace = new Workspace(this.settings.recentWorkspaces[0]);
this.curWorkspace.load();
}
save() { get nodeConfPath() {
fs.writeFileSync(settingPath, JSON.stringify(this.settings, null, 2)); return this.curWorkspace.getNodeConfPath();
} }
get recentWorkspaces() {
getNodeConf(name: string) { return this.settings.recentWorkspaces;
return this.curWorkspace.getNodeConf(name); }
} get recentFiles() {
return this.settings.recentFiles;
pushRecentWorkspace(path: string) { }
if (this.settings.recentWorkspaces.indexOf(path) < 0) { get nodeConfig() {
this.settings.recentWorkspaces.unshift(path); return this.curWorkspace.nodeConf;
console.log("push", path); }
} else { get nodeClassify() {
console.log("already in path", this.settings.recentWorkspaces, path); return this.settings.nodeClassify;
}
set(config: SettingsModel) {
this.settings = {
...this.settings,
...config,
};
this.save();
}
load() {
if (fs.existsSync(settingPath)) {
const str = fs.readFileSync(settingPath, "utf8");
this.settings = JSON.parse(str);
} else {
this.settings = {
recentWorkspaces: ["sample/workspace.json"],
recentFiles: [],
nodeClassify: sampleNodeClassify,
};
this.save();
}
this.curWorkspace = new Workspace(this.settings.recentWorkspaces[0]);
this.curWorkspace.load();
}
save() {
fs.writeFileSync(settingPath, JSON.stringify(this.settings, null, 2));
}
getNodeConf(name: string) {
return this.curWorkspace.getNodeConf(name);
}
pushRecentWorkspace(path: string) {
if (this.settings.recentWorkspaces.indexOf(path) < 0) {
this.settings.recentWorkspaces.unshift(path);
console.log("push", path);
} else {
console.log("already in path", this.settings.recentWorkspaces, path);
}
this.save();
} }
this.save();
}
} }
+17 -15
View File
@@ -4,9 +4,9 @@ import * as glob from "glob";
import * as path from "path"; import * as path from "path";
export interface WorkspaceModel { export interface WorkspaceModel {
isRelative?: boolean; isRelative?: boolean;
nodeConfPath: string; nodeConfPath: string;
workdir: string; workdir: string;
} }
const unknowNodeType: BehaviorNodeTypeModel = { const unknowNodeType: BehaviorNodeTypeModel = {
@@ -32,15 +32,15 @@ export default class Workspace {
return; return;
} }
try { try {
const str = fs.readFileSync(this.filepath, 'utf8'); const str = fs.readFileSync(this.filepath, "utf8");
const model = JSON.parse(str) as WorkspaceModel; const model = JSON.parse(str) as WorkspaceModel;
if (model.isRelative) { if (model.isRelative) {
const root = path.dirname(this.filepath); const root = path.dirname(this.filepath);
this.nodeConfPath = root + '/' + model.nodeConfPath; this.nodeConfPath = root + "/" + model.nodeConfPath;
this.workdir = root + '/' + model.workdir; this.workdir = root + "/" + model.workdir;
} else { } else {
this.nodeConfPath = model.nodeConfPath; this.nodeConfPath = model.nodeConfPath;
this.workdir = model.workdir; this.workdir = model.workdir;
} }
this.initNodeConf(); this.initNodeConf();
@@ -52,14 +52,16 @@ export default class Workspace {
private initNodeConf() { private initNodeConf() {
if (!this.nodeConfPath) { if (!this.nodeConfPath) {
return; return;
} }
const types: BehaviorNodeTypeModel[] = JSON.parse(fs.readFileSync(this.nodeConfPath, 'utf8')); const types: BehaviorNodeTypeModel[] = JSON.parse(
this.name2conf = {}; fs.readFileSync(this.nodeConfPath, "utf8")
types.forEach(t => { );
this.name2conf = {};
types.forEach((t) => {
this.name2conf[t.name] = t; this.name2conf[t.name] = t;
}) });
this.types = types; this.types = types;
} }
getNodeConfPath() { getNodeConfPath() {
+267 -273
View File
@@ -1,297 +1,291 @@
import React from 'react'; import React from "react";
import { Card, Divider, Form, Input, AutoComplete, Select, Switch, InputNumber, notification, message } from 'antd'; import {
import { INode } from '@antv/g6/lib/interface/item'; Card,
import { BehaviorNodeModel, BehaviorNodeTypeModel, ArgsDefType } from '../../common/BehaviorTreeModel'; Divider,
import Settings from '../../main-process/Settings'; Form,
import { FormInstance } from 'antd/lib/form'; Input,
import Markdown from 'react-markdown'; AutoComplete,
Select,
Switch,
InputNumber,
notification,
message,
} from "antd";
import { INode } from "@antv/g6/lib/interface/item";
import {
BehaviorNodeModel,
BehaviorNodeTypeModel,
ArgsDefType,
} from "../../common/BehaviorTreeModel";
import Settings from "../../main-process/Settings";
import { FormInstance } from "antd/lib/form";
import Markdown from "react-markdown";
const { Item } = Form; const { Item } = Form;
interface NodePanelProps { interface NodePanelProps {
model: BehaviorNodeModel; model: BehaviorNodeModel;
settings: Settings; settings: Settings;
updateNode: (id: string, forceUpdate:boolean) => void; updateNode: (id: string, forceUpdate: boolean) => void;
} }
interface NodePanelState { interface NodePanelState {}
}
export default class NodePanel extends React.Component<NodePanelProps> { export default class NodePanel extends React.Component<NodePanelProps> {
formRef = React.createRef<FormInstance>(); formRef = React.createRef<FormInstance>();
componentDidUpdate() { componentDidUpdate() {
this.formRef.current.resetFields(); this.formRef.current.resetFields();
this.formRef.current.setFieldsValue(this.getInitialValues()); this.formRef.current.setFieldsValue(this.getInitialValues());
}
getInitialValues() {
const { model } = this.props;
const initialValues: any = {
name: model.name,
desc: model.desc,
debug: model.debug,
customArgs: model.args ? JSON.stringify(model.args, null, " ") : '',
};
if (model.args) {
for (let k in model.args) {
initialValues[`args.${k}`] = model.args[k];
}
}
if (model.input) {
model.input.forEach((v, i) => {
initialValues[`input.${i}`] = v;
})
}
if (model.output) {
model.output.forEach((v, i) => {
initialValues[`output.${i}`] = v;
})
}
return initialValues;
}
onFinish = (values: any) => {
console.log('Success:', values);
const { updateNode, model, settings } = this.props;
const conf = settings.getNodeConf(values.name)
if (!conf) {
notification.warn({ message: `节点${values.name}未定义` });
return;
}
var forceUpdate = false;
if (model.name != values.name) {
model.name = values.name;
forceUpdate = true;
}
model.desc = values.desc;
var args: any = {};
if (values.customArgs) {
try {
args = JSON.parse(values.customArgs);
} catch (e) {
message.warn(`您输入的自定义参数不符合json格式${values.customArgs}`);
return;
}
} }
if (conf.args) { getInitialValues() {
conf.args.forEach(e => { const { model } = this.props;
const k = 'args.' + e.name; const initialValues: any = {
if (e.type.indexOf('number') > 0) { name: model.name,
args[e.name] = Number(values[k]); desc: model.desc,
debug: model.debug,
customArgs: model.args ? JSON.stringify(model.args, null, " ") : "",
};
if (model.args) {
for (let k in model.args) {
initialValues[`args.${k}`] = model.args[k];
}
}
if (model.input) {
model.input.forEach((v, i) => {
initialValues[`input.${i}`] = v;
});
}
if (model.output) {
model.output.forEach((v, i) => {
initialValues[`output.${i}`] = v;
});
}
return initialValues;
}
onFinish = (values: any) => {
console.log("Success:", values);
const { updateNode, model, settings } = this.props;
const conf = settings.getNodeConf(values.name);
if (!conf) {
notification.warn({ message: `节点${values.name}未定义` });
return;
}
var forceUpdate = false;
if (model.name != values.name) {
model.name = values.name;
forceUpdate = true;
}
model.desc = values.desc;
var args: any = {};
if (values.customArgs) {
try {
args = JSON.parse(values.customArgs);
} catch (e) {
message.warn(`您输入的自定义参数不符合json格式${values.customArgs}`);
return;
}
}
if (conf.args) {
conf.args.forEach((e) => {
const k = "args." + e.name;
if (e.type.indexOf("number") > 0) {
args[e.name] = Number(values[k]);
} else {
args[e.name] = values[k];
}
});
}
model.args = args;
this.formRef.current.setFieldsValue({
customArgs: model.args ? JSON.stringify(model.args, null, " ") : "",
});
if (conf.input) {
model.input = [];
conf.input.forEach((e, i) => {
model.input.push(values["input." + i] || "");
});
} else { } else {
args[e.name] = values[k]; model.input = null;
} }
})
}
model.args = args; if (conf.output) {
this.formRef.current.setFieldsValue({ customArgs: model.args ? JSON.stringify(model.args, null, " ") : '' }); model.output = [];
conf.output.forEach((e, i) => {
model.output.push(values["output." + i] || "");
});
} else {
model.output = null;
}
if (conf.input) { if (forceUpdate) {
model.input = []; this.forceUpdate();
conf.input.forEach((e, i) => { }
model.input.push(values['input.' + i] || ''); updateNode(model.id.toString(), forceUpdate);
})
} else {
model.input = null;
}
if (conf.output) {
model.output = [];
conf.output.forEach((e, i) => {
model.output.push(values['output.' + i] || '');
})
} else {
model.output = null;
}
if (forceUpdate) {
this.forceUpdate();
}
updateNode(model.id.toString(), forceUpdate);
};
onFinishFailed = (errorInfo: any) => {
console.log('Failed:', errorInfo);
};
handleSubmit = () => {
console.log("handleSubmit");
this.formRef.current.submit();
}
render() {
const { model, settings } = this.props;
const conf = settings.getNodeConf(model.name);
const title = conf.desc;
const options: any = [];
settings.nodeConfig.map((e) => {
options.push({ label: `${e.name}(${e.desc})`, value: e.name });
})
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 18 },
}; };
return (
<Card title={title} style={{ height: window.screen.height - 100, overflow: 'auto' }}>
<Form
{...layout}
name="basic"
onFinish={this.onFinish}
initialValues={this.getInitialValues()}
ref={this.formRef}
>
<Item
label="节点id"
>
<Input value={model.id} disabled={true} />
</Item>
<Item
label="节点名称"
name="name"
>
<AutoComplete
options={options}
onBlur={this.handleSubmit}
filterOption={(inputValue: string, option: any) => {
return option.label.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
}}
/>
</Item>
<Item
label="节点说明"
name="desc"
>
<Input onBlur={this.handleSubmit} />
</Item>
<Item
label="调试开关"
name="debug"
valuePropName='checked'
>
<Switch onChange={this.handleSubmit} />
</Item>
<Markdown source={conf.doc} />
{this.renderInputs(conf)}
{this.renderArgs(conf)}
{this.renderOutputs(conf)}
</Form>
</Card >
)
}
renderArgs(conf: BehaviorNodeTypeModel) { onFinishFailed = (errorInfo: any) => {
if (!conf || !conf.args || conf.args.length == 0) { console.log("Failed:", errorInfo);
return null; };
handleSubmit = () => {
console.log("handleSubmit");
this.formRef.current.submit();
};
render() {
const { model, settings } = this.props;
const conf = settings.getNodeConf(model.name);
const title = conf.desc;
const options: any = [];
settings.nodeConfig.map((e) => {
options.push({ label: `${e.name}(${e.desc})`, value: e.name });
});
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 18 },
};
return (
<Card title={title} style={{ height: window.screen.height - 100, overflow: "auto" }}>
<Form
{...layout}
name="basic"
onFinish={this.onFinish}
initialValues={this.getInitialValues()}
ref={this.formRef}
>
<Item label="节点id">
<Input value={model.id} disabled={true} />
</Item>
<Item label="节点名称" name="name">
<AutoComplete
options={options}
onBlur={this.handleSubmit}
filterOption={(inputValue: string, option: any) => {
return (
option.label.toUpperCase().indexOf(inputValue.toUpperCase()) !==
-1
);
}}
/>
</Item>
<Item label="节点说明" name="desc">
<Input onBlur={this.handleSubmit} />
</Item>
<Item label="调试开关" name="debug" valuePropName="checked">
<Switch onChange={this.handleSubmit} />
</Item>
<Markdown source={conf.doc} />
{this.renderInputs(conf)}
{this.renderArgs(conf)}
{this.renderOutputs(conf)}
</Form>
</Card>
);
} }
// 普通参数 renderArgs(conf: BehaviorNodeTypeModel) {
const normalArgs = (e: ArgsDefType) => { if (!conf || !conf.args || conf.args.length == 0) {
const required = e.type.indexOf("?") == -1; return null;
if (e.type.indexOf("string") >= 0) {
return <Input onBlur={this.handleSubmit} />;
} else if (e.type.indexOf("int") >= 0) {
return <InputNumber style={{ width: '100%' }} onBlur={this.handleSubmit} />;
} else if (e.type.indexOf("boolean") >= 0) {
return <Switch onChange={this.handleSubmit} />;
} else if (e.type.indexOf("lua") >= 0) {
return <Input onBlur={this.handleSubmit} placeholder={'公式'} />;
}
}
// 自定义参数
const customArgs = () => {
return (
<Item
name='customArgs'
label='自定义'
key='customArgs'
>
<Input.TextArea onBlur={this.handleSubmit} style={{ minHeight: 100 }} />
</Item>
)
}
return (
<div>
<Divider orientation="left"><h3>常量参数</h3></Divider>
{conf && conf.args && conf.args.map((e, i: number) => {
const required = e.type.indexOf("?") == -1;
return (
<Item
name={`args.${e.name}`}
label={e.desc}
key={`args.${e.name}`}
valuePropName={e.type.indexOf("boolean") >= 0 ? 'checked' : undefined}
rules={[
{ required, message: `${e.desc}(${e.name})为必填字段` }
]}
>
{normalArgs(e)}
</Item>
)
})}
{customArgs()}
</div >
);
}
renderInputs(conf: BehaviorNodeTypeModel) {
if (!conf.input || !conf.input || conf.input.length == 0) {
return null;
}
return (
<div>
<Divider orientation="left"><h3>输入变量</h3></Divider>
{
conf.input.map((e, i) => {
return (
<Item
label={e}
name={`input.${i}`}
key={`input.${i}`}
>
<Input onBlur={this.handleSubmit} />
</Item>
)
})
} }
</div>
)
}
renderOutputs(conf: BehaviorNodeTypeModel) { // 普通参数
if (!conf.output || !conf.output || conf.output.length == 0) { const normalArgs = (e: ArgsDefType) => {
return null; const required = e.type.indexOf("?") == -1;
} if (e.type.indexOf("string") >= 0) {
return ( return <Input onBlur={this.handleSubmit} />;
<div> } else if (e.type.indexOf("int") >= 0) {
<Divider orientation="left"><h3>输出变量</h3></Divider> return <InputNumber style={{ width: "100%" }} onBlur={this.handleSubmit} />;
{ } else if (e.type.indexOf("boolean") >= 0) {
conf.output.map((e, i) => { return <Switch onChange={this.handleSubmit} />;
} else if (e.type.indexOf("lua") >= 0) {
return <Input onBlur={this.handleSubmit} placeholder={"公式"} />;
}
};
// 自定义参数
const customArgs = () => {
return ( return (
<Item <Item name="customArgs" label="自定义" key="customArgs">
label={e} <Input.TextArea onBlur={this.handleSubmit} style={{ minHeight: 100 }} />
name={`output.${i}`} </Item>
key={`output.${i}`} );
> };
<Input onBlur={this.handleSubmit} />
</Item>
)
})
}
</div>
)
}
return (
<div>
<Divider orientation="left">
<h3>常量参数</h3>
</Divider>
{conf &&
conf.args &&
conf.args.map((e, i: number) => {
const required = e.type.indexOf("?") == -1;
return (
<Item
name={`args.${e.name}`}
label={e.desc}
key={`args.${e.name}`}
valuePropName={
e.type.indexOf("boolean") >= 0 ? "checked" : undefined
}
rules={[{ required, message: `${e.desc}(${e.name})为必填字段` }]}
>
{normalArgs(e)}
</Item>
);
})}
{customArgs()}
</div>
);
}
renderInputs(conf: BehaviorNodeTypeModel) {
if (!conf.input || !conf.input || conf.input.length == 0) {
return null;
}
return (
<div>
<Divider orientation="left">
<h3>输入变量</h3>
</Divider>
{conf.input.map((e, i) => {
return (
<Item label={e} name={`input.${i}`} key={`input.${i}`}>
<Input onBlur={this.handleSubmit} />
</Item>
);
})}
</div>
);
}
renderOutputs(conf: BehaviorNodeTypeModel) {
if (!conf.output || !conf.output || conf.output.length == 0) {
return null;
}
return (
<div>
<Divider orientation="left">
<h3>输出变量</h3>
</Divider>
{conf.output.map((e, i) => {
return (
<Item label={e} name={`output.${i}`} key={`output.${i}`}>
<Input onBlur={this.handleSubmit} />
</Item>
);
})}
</div>
);
}
} }
+18 -23
View File
@@ -1,33 +1,28 @@
import React from 'react'; import React from "react";
import { Card } from 'antd'; import { Card } from "antd";
import { INode } from '@antv/g6/lib/interface/item'; import { INode } from "@antv/g6/lib/interface/item";
import { BehaviorTreeModel } from '../../common/BehaviorTreeModel'; import { BehaviorTreeModel } from "../../common/BehaviorTreeModel";
interface TreePanelProps { interface TreePanelProps {
model?: BehaviorTreeModel; model?: BehaviorTreeModel;
} }
interface TreePanelState { interface TreePanelState {}
}
export default class TreePanel extends React.Component<TreePanelProps, TreePanelState> { export default class TreePanel extends React.Component<TreePanelProps, TreePanelState> {
state: TreePanelState = {}; state: TreePanelState = {};
componentDidMount() { componentDidMount() {}
}
render() { render() {
const { model } = this.props; const { model } = this.props;
if(!model) { if (!model) {
return (<div/>) return <div />;
}
return (
<Card title="概况" style={{ height: "100%" }}>
行为树:{model.name}
</Card>
);
} }
return (
<Card title="概况" style={{ height: "100%" }}>
行为树:{model.name}
</Card>
)
}
} }
+398 -396
View File
@@ -1,412 +1,414 @@
import * as React from 'react'; import * as React from "react";
import * as fs from 'fs'; import * as fs from "fs";
import * as path from 'path'; import * as path from "path";
import { Row, Col, message } from 'antd'; import { Row, Col, message } from "antd";
import NodePanel from './NodePanel'; import NodePanel from "./NodePanel";
import G6, { TreeGraph } from '@antv/g6'; import G6, { TreeGraph } from "@antv/g6";
import { G6GraphEvent } from '@antv/g6/lib/interface/behavior'; import { G6GraphEvent } from "@antv/g6/lib/interface/behavior";
import * as Utils from '../../common/Utils'; import * as Utils from "../../common/Utils";
import { BehaviorTreeModel, GraphNodeModel, BehaviorNodeModel } from '../../common/BehaviorTreeModel'; import {
import TreePanel from './TreePanel'; BehaviorTreeModel,
import Settings from '../../main-process/Settings'; GraphNodeModel,
BehaviorNodeModel,
} from "../../common/BehaviorTreeModel";
import TreePanel from "./TreePanel";
import Settings from "../../main-process/Settings";
import './Editor.css'; import "./Editor.css";
import { clipboard } from 'electron'; import { clipboard } from "electron";
export interface EditorProps { export interface EditorProps {
filepath: string; filepath: string;
} }
interface EditorState { interface EditorState {
curNodeId?: string; curNodeId?: string;
treeModel?: BehaviorTreeModel; treeModel?: BehaviorTreeModel;
settings?: Settings; settings?: Settings;
} }
export default class Editor extends React.Component<EditorProps, EditorState> { export default class Editor extends React.Component<EditorProps, EditorState> {
private ref: React.RefObject<any>; private ref: React.RefObject<any>;
state: EditorState = {}; state: EditorState = {};
private graph: TreeGraph; private graph: TreeGraph;
private dragSrcId: string; private dragSrcId: string;
private dragDstId: string; private dragDstId: string;
private autoId: number; private autoId: number;
constructor(props: EditorProps) { constructor(props: EditorProps) {
super(props); super(props);
this.ref = React.createRef(); this.ref = React.createRef();
} }
componentDidMount() { componentDidMount() {
const graph = new TreeGraph({ const graph = new TreeGraph({
container: this.ref.current, container: this.ref.current,
width: window.screen.width * 0.66, width: window.screen.width * 0.66,
height: window.screen.height, height: window.screen.height,
animate: false, animate: false,
// fitCenter: true, // fitCenter: true,
modes: { modes: {
default: [ default: [
'drag-canvas', "drag-canvas",
'zoom-canvas', "zoom-canvas",
'click-select', "click-select",
'hover', "hover",
{ {
type: 'collapse-expand', type: "collapse-expand",
trigger: 'dblclick', trigger: "dblclick",
onChange: (item, collapsed) => { onChange: (item, collapsed) => {
this.onSelectNode(item.getID()); this.onSelectNode(item.getID());
const data = item.getModel(); const data = item.getModel();
data.collapsed = collapsed; data.collapsed = collapsed;
graph.setItemState(item, 'collapsed', data.collapsed as boolean); graph.setItemState(item, "collapsed", data.collapsed as boolean);
const icon = data.collapsed ? G6.Marker.expand : G6.Marker.collapse; const icon = data.collapsed ? G6.Marker.expand : G6.Marker.collapse;
const marker = item.get('group').find((ele: any) => ele.get('name') === 'collapse-icon'); const marker = item
marker.attr('symbol', icon); .get("group")
return true; .find((ele: any) => ele.get("name") === "collapse-icon");
marker.attr("symbol", icon);
return true;
},
},
],
}, },
}, defaultEdge: {
] type: "cubic-horizontal",
}, style: {
defaultEdge: { stroke: "#A3B1BF",
type: 'cubic-horizontal', },
style: { },
stroke: '#A3B1BF' defaultNode: {
} type: "TreeNode",
}, },
defaultNode: { layout: {
type: "TreeNode", type: "compactBox",
}, direction: "LR",
layout: { getHGap: () => 50,
type: 'compactBox', getWidth: (d: GraphNodeModel) => {
direction: 'LR', return 150;
getHGap: () => 50, },
getWidth: (d: GraphNodeModel) => { getHeight: (d: GraphNodeModel) => {
return 150; if (d.size) {
}, return d.size[1];
getHeight: (d: GraphNodeModel) => { } else {
if (d.size) { return 50;
return d.size[1];
} else {
return 50;
}
}
}
});
graph.on('node:mouseenter', (e: G6GraphEvent) => {
const { item } = e;
if (item.hasState('selected')) {
return;
}
graph.setItemState(item, 'hover', true);
});
graph.on('node:mouseleave', (e: G6GraphEvent) => {
const { item } = e;
if (item.hasState('selected')) {
return;
}
graph.setItemState(item, 'hover', false);
});
graph.on('nodeselectchange', (e: G6GraphEvent) => {
if (e.target) {
this.onSelectNode(e.target.getID());
} else {
this.onSelectNode(null);
}
});
const clearDragDstState = () => {
if (this.dragDstId) {
graph.setItemState(this.dragDstId, 'dragRight', false);
graph.setItemState(this.dragDstId, 'dragDown', false);
graph.setItemState(this.dragDstId, 'dragUp', false);
this.dragDstId = null;
}
}
const clearDragSrcState = () => {
if (this.dragSrcId) {
graph.setItemState(this.dragSrcId, 'dragSrc', false);
this.dragSrcId = null;
}
}
graph.on('node:dragstart', (e: G6GraphEvent) => {
this.dragSrcId = e.item.getID();
graph.setItemState(this.dragSrcId, 'dragSrc', true);
});
graph.on('node:dragend', (e: G6GraphEvent) => {
if (this.dragSrcId) {
graph.setItemState(this.dragSrcId, 'dragSrc', false);
this.dragSrcId = null;
}
});
graph.on('node:dragover', (e: G6GraphEvent) => {
const dstNodeId = e.item.getID();
if (dstNodeId == this.dragSrcId) {
return;
}
if (this.dragDstId) {
graph.setItemState(this.dragDstId, 'dragRight', false);
graph.setItemState(this.dragDstId, 'dragDown', false);
graph.setItemState(this.dragDstId, 'dragUp', false);
}
const box = e.item.getBBox();
if (e.x > box.minX + box.width * 0.6) {
graph.setItemState(dstNodeId, 'dragRight', true);
} else if (e.y > box.minY + box.height * 0.5) {
graph.setItemState(dstNodeId, 'dragDown', true);
} else {
graph.setItemState(dstNodeId, 'dragUp', true);
}
this.dragDstId = dstNodeId;
});
graph.on('node:dragleave', (e: G6GraphEvent) => {
clearDragDstState();
})
graph.on('node:drop', (e: G6GraphEvent) => {
const srcNodeId = this.dragSrcId;
const dstNode = e.item;
var dragDir;
if (dstNode.hasState('dragRight')) {
dragDir = 'dragRight';
} else if (dstNode.hasState('dragDown')) {
dragDir = 'dragDown';
} else if (dstNode.hasState('dragUp')) {
dragDir = 'dragUp';
}
clearDragSrcState();
clearDragDstState();
if (!srcNodeId) {
console.log("no drag src");
return;
}
if (srcNodeId == dstNode.getID()) {
console.log("drop same node");
return;
}
const rootData = graph.findDataById('1');
const srcData = graph.findDataById(srcNodeId);
const srcParent = Utils.findParent(rootData, srcNodeId);
const dstData = graph.findDataById(dstNode.getID());
const dstParent = Utils.findParent(rootData, dstNode.getID());
if (!srcParent) {
console.log("no parent!");
return;
}
if (Utils.findFromAllChildren(srcData, dstData.id)) {
// 不能将父节点拖到自已的子孙节点
console.log("cannot move to child");
return;
}
const removeSrc = () => {
srcParent.children = srcParent.children.filter(e => e.id != srcData.id);
}
console.log("dstNode", dstNode);
if (dragDir == 'dragRight') {
removeSrc();
if (!dstData.children) {
dstData.children = [];
}
dstData.children.push(srcData);
} else if (dragDir == 'dragUp') {
if (!dstParent) {
return;
}
removeSrc();
const idx = dstParent.children.findIndex(e => e.id == dstData.id);
dstParent.children.splice(idx, 0, srcData);
} else if (dragDir == 'dragDown') {
if (!dstParent) {
return;
}
removeSrc();
const idx = dstParent.children.findIndex(e => e.id == dstData.id);
dstParent.children.splice(idx + 1, 0, srcData);
} else {
return;
}
// console.log("cur data", graph.findDataById('1'));
this.changeWithoutAnim();
});
const settings = Utils.getRemoteSettings();
const str = fs.readFileSync(this.props.filepath, 'utf8');
let tree: BehaviorTreeModel = JSON.parse(str);
const data = Utils.createTreeData(tree.root, settings);
this.autoId = Utils.refreshNodeId(data);
graph.data(data);
graph.render();
graph.fitCenter();
graph.set('animate', true);
this.graph = graph;
this.setState({ treeModel: tree, settings });
}
onSelectNode(curNodeId: string | null) {
const graph = this.graph;
if (this.state.curNodeId) {
graph.setItemState(this.state.curNodeId, 'selected', false);
}
this.setState({ curNodeId });
if (this.state.curNodeId) {
graph.setItemState(this.state.curNodeId, 'selected', true);
}
}
createNode(name: string) {
console.log("editor create node", name);
const { curNodeId, settings } = this.state;
if (!curNodeId) {
message.warn("未选中节点");
return;
}
const curNodeData = this.graph.findDataById(curNodeId);
const newNodeData: BehaviorNodeModel = {
id: this.autoId++,
name: name,
}
if (!curNodeData.children) {
curNodeData.children = [];
}
curNodeData.children.push(Utils.createTreeData(newNodeData, settings));
this.changeWithoutAnim();
}
deleteNode() {
console.log("editor delete node");
const { curNodeId } = this.state;
if (!curNodeId) {
return;
}
if(curNodeId == '1') {
message.warn("根节点不能删除!");
return;
}
this.onSelectNode(null);
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();
}
changeWithoutAnim() {
this.graph.set('animate', false);
this.graph.changeData();
this.graph.layout();
this.graph.set('animate', true);
}
save() {
const { filepath } = this.props;
const root = Utils.createFileData(this.graph.findDataById('1') as GraphNodeModel);
fs.writeFileSync(filepath, JSON.stringify({
name: path.basename(filepath).slice(0, -5),
root,
desc: root.desc,
}));
}
copyNode() {
console.log("editor copy node");
const { curNodeId } = this.state;
if (!curNodeId) {
return;
}
const data = this.graph.findDataById(curNodeId);
clipboard.writeText(JSON.stringify(data, null, 2));
}
pasteNode() {
const { curNodeId } = this.state;
if (!curNodeId) {
message.warn("未选中节点");
return;
}
const curNodeData = this.graph.findDataById(curNodeId);
try {
const str = clipboard.readText();
if(!str || str == '') {
return;
}
const data = JSON.parse(str);
if(!curNodeData.children) {
curNodeData.children = [];
}
curNodeData.children.push(data);
this.autoId = Utils.refreshNodeId(this.graph.findDataById('1') as GraphNodeModel);
this.changeWithoutAnim();
} catch (error) {
message.error("粘贴数据有误");
}
}
render() {
const { curNodeId, treeModel, settings } = this.state;
var curNode: any;
if (curNodeId) {
curNode = this.graph.findDataById(curNodeId);
}
return (
<div className="editor">
<Row className="editorBd">
<Col
span={18}
className="editorContent"
ref={this.ref}
/>
<Col span={6} className="editorSidebar">
{
curNode ?
<NodePanel
model={curNode}
settings={settings}
updateNode={(id, forceUpdate) => {
if (forceUpdate) {
const data: any = this.graph.findDataById(id);
data.conf = settings.getNodeConf(data.name);
data.size = Utils.calcTreeNodeSize(data);
this.changeWithoutAnim();
} }
const item = this.graph.findById(id); },
item.draw(); },
}} });
/>
: graph.on("node:mouseenter", (e: G6GraphEvent) => {
<TreePanel const { item } = e;
model={treeModel} if (item.hasState("selected")) {
/> return;
} }
</Col> graph.setItemState(item, "hover", true);
</Row> });
</div>
); graph.on("node:mouseleave", (e: G6GraphEvent) => {
} const { item } = e;
if (item.hasState("selected")) {
return;
}
graph.setItemState(item, "hover", false);
});
graph.on("nodeselectchange", (e: G6GraphEvent) => {
if (e.target) {
this.onSelectNode(e.target.getID());
} else {
this.onSelectNode(null);
}
});
const clearDragDstState = () => {
if (this.dragDstId) {
graph.setItemState(this.dragDstId, "dragRight", false);
graph.setItemState(this.dragDstId, "dragDown", false);
graph.setItemState(this.dragDstId, "dragUp", false);
this.dragDstId = null;
}
};
const clearDragSrcState = () => {
if (this.dragSrcId) {
graph.setItemState(this.dragSrcId, "dragSrc", false);
this.dragSrcId = null;
}
};
graph.on("node:dragstart", (e: G6GraphEvent) => {
this.dragSrcId = e.item.getID();
graph.setItemState(this.dragSrcId, "dragSrc", true);
});
graph.on("node:dragend", (e: G6GraphEvent) => {
if (this.dragSrcId) {
graph.setItemState(this.dragSrcId, "dragSrc", false);
this.dragSrcId = null;
}
});
graph.on("node:dragover", (e: G6GraphEvent) => {
const dstNodeId = e.item.getID();
if (dstNodeId == this.dragSrcId) {
return;
}
if (this.dragDstId) {
graph.setItemState(this.dragDstId, "dragRight", false);
graph.setItemState(this.dragDstId, "dragDown", false);
graph.setItemState(this.dragDstId, "dragUp", false);
}
const box = e.item.getBBox();
if (e.x > box.minX + box.width * 0.6) {
graph.setItemState(dstNodeId, "dragRight", true);
} else if (e.y > box.minY + box.height * 0.5) {
graph.setItemState(dstNodeId, "dragDown", true);
} else {
graph.setItemState(dstNodeId, "dragUp", true);
}
this.dragDstId = dstNodeId;
});
graph.on("node:dragleave", (e: G6GraphEvent) => {
clearDragDstState();
});
graph.on("node:drop", (e: G6GraphEvent) => {
const srcNodeId = this.dragSrcId;
const dstNode = e.item;
var dragDir;
if (dstNode.hasState("dragRight")) {
dragDir = "dragRight";
} else if (dstNode.hasState("dragDown")) {
dragDir = "dragDown";
} else if (dstNode.hasState("dragUp")) {
dragDir = "dragUp";
}
clearDragSrcState();
clearDragDstState();
if (!srcNodeId) {
console.log("no drag src");
return;
}
if (srcNodeId == dstNode.getID()) {
console.log("drop same node");
return;
}
const rootData = graph.findDataById("1");
const srcData = graph.findDataById(srcNodeId);
const srcParent = Utils.findParent(rootData, srcNodeId);
const dstData = graph.findDataById(dstNode.getID());
const dstParent = Utils.findParent(rootData, dstNode.getID());
if (!srcParent) {
console.log("no parent!");
return;
}
if (Utils.findFromAllChildren(srcData, dstData.id)) {
// 不能将父节点拖到自已的子孙节点
console.log("cannot move to child");
return;
}
const removeSrc = () => {
srcParent.children = srcParent.children.filter((e) => e.id != srcData.id);
};
console.log("dstNode", dstNode);
if (dragDir == "dragRight") {
removeSrc();
if (!dstData.children) {
dstData.children = [];
}
dstData.children.push(srcData);
} else if (dragDir == "dragUp") {
if (!dstParent) {
return;
}
removeSrc();
const idx = dstParent.children.findIndex((e) => e.id == dstData.id);
dstParent.children.splice(idx, 0, srcData);
} else if (dragDir == "dragDown") {
if (!dstParent) {
return;
}
removeSrc();
const idx = dstParent.children.findIndex((e) => e.id == dstData.id);
dstParent.children.splice(idx + 1, 0, srcData);
} else {
return;
}
// console.log("cur data", graph.findDataById('1'));
this.changeWithoutAnim();
});
const settings = Utils.getRemoteSettings();
const str = fs.readFileSync(this.props.filepath, "utf8");
let tree: BehaviorTreeModel = JSON.parse(str);
const data = Utils.createTreeData(tree.root, settings);
this.autoId = Utils.refreshNodeId(data);
graph.data(data);
graph.render();
graph.fitCenter();
graph.set("animate", true);
this.graph = graph;
this.setState({ treeModel: tree, settings });
}
onSelectNode(curNodeId: string | null) {
const graph = this.graph;
if (this.state.curNodeId) {
graph.setItemState(this.state.curNodeId, "selected", false);
}
this.setState({ curNodeId });
if (this.state.curNodeId) {
graph.setItemState(this.state.curNodeId, "selected", true);
}
}
createNode(name: string) {
console.log("editor create node", name);
const { curNodeId, settings } = this.state;
if (!curNodeId) {
message.warn("未选中节点");
return;
}
const curNodeData = this.graph.findDataById(curNodeId);
const newNodeData: BehaviorNodeModel = {
id: this.autoId++,
name: name,
};
if (!curNodeData.children) {
curNodeData.children = [];
}
curNodeData.children.push(Utils.createTreeData(newNodeData, settings));
this.changeWithoutAnim();
}
deleteNode() {
console.log("editor delete node");
const { curNodeId } = this.state;
if (!curNodeId) {
return;
}
if (curNodeId == "1") {
message.warn("根节点不能删除!");
return;
}
this.onSelectNode(null);
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();
}
changeWithoutAnim() {
this.graph.set("animate", false);
this.graph.changeData();
this.graph.layout();
this.graph.set("animate", true);
}
save() {
const { filepath } = this.props;
const root = Utils.createFileData(this.graph.findDataById("1") as GraphNodeModel);
fs.writeFileSync(
filepath,
JSON.stringify({
name: path.basename(filepath).slice(0, -5),
root,
desc: root.desc,
})
);
}
copyNode() {
console.log("editor copy node");
const { curNodeId } = this.state;
if (!curNodeId) {
return;
}
const data = this.graph.findDataById(curNodeId);
clipboard.writeText(JSON.stringify(data, null, 2));
}
pasteNode() {
const { curNodeId } = this.state;
if (!curNodeId) {
message.warn("未选中节点");
return;
}
const curNodeData = this.graph.findDataById(curNodeId);
try {
const str = clipboard.readText();
if (!str || str == "") {
return;
}
const data = JSON.parse(str);
if (!curNodeData.children) {
curNodeData.children = [];
}
curNodeData.children.push(data);
this.autoId = Utils.refreshNodeId(this.graph.findDataById("1") as GraphNodeModel);
this.changeWithoutAnim();
} catch (error) {
message.error("粘贴数据有误");
}
}
render() {
const { curNodeId, treeModel, settings } = this.state;
var curNode: any;
if (curNodeId) {
curNode = this.graph.findDataById(curNodeId);
}
return (
<div className="editor">
<Row className="editorBd">
<Col span={18} className="editorContent" ref={this.ref} />
<Col span={6} className="editorSidebar">
{curNode ? (
<NodePanel
model={curNode}
settings={settings}
updateNode={(id, forceUpdate) => {
if (forceUpdate) {
const data: any = this.graph.findDataById(id);
data.conf = settings.getNodeConf(data.name);
data.size = Utils.calcTreeNodeSize(data);
this.changeWithoutAnim();
}
const item = this.graph.findById(id);
item.draw();
}}
/>
) : (
<TreePanel model={treeModel} />
)}
</Col>
</Row>
</div>
);
}
} }
+64 -74
View File
@@ -1,99 +1,89 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Menu, Input } from "antd"; import { Menu, Input } from "antd";
import * as fs from 'fs'; import * as fs from "fs";
import * as path from 'path'; import * as path from "path";
import { ipcRenderer } from "electron"; import { ipcRenderer } from "electron";
import MainEventType from "../common/MainEventType"; import MainEventType from "../common/MainEventType";
const { Search } = Input; const { Search } = Input;
export interface TreeFile { export interface TreeFile {
name: string; name: string;
path: string; path: string;
} }
export interface PropertiesProps { export interface PropertiesProps {
workdir: string; workdir: string;
onOpenTree: (path: string) => void; onOpenTree: (path: string) => void;
} }
interface PropertiesState { interface PropertiesState {
treeList: TreeFile[] treeList: TreeFile[];
} }
export default class Properties extends Component<PropertiesProps> { export default class Properties extends Component<PropertiesProps> {
state: PropertiesState = { state: PropertiesState = {
treeList: [] treeList: [],
}; };
curWorkdir: string = ''; curWorkdir: string = "";
shouldComponentUpdate(nextProps: PropertiesProps) { shouldComponentUpdate(nextProps: PropertiesProps) {
const shouldUpdate = this.curWorkdir != nextProps.workdir; const shouldUpdate = this.curWorkdir != nextProps.workdir;
this.curWorkdir = nextProps.workdir; this.curWorkdir = nextProps.workdir;
return shouldUpdate; return shouldUpdate;
}
componentDidMount() {
ipcRenderer.on(MainEventType.CREATE_TREE, (event: any, path: string) => {
console.log("on Create tree", path);
this.props.onOpenTree(path);
this.forceUpdate();
});
var workdir = this.props.workdir;
if (workdir == '') {
return;
}
this.setState({ treeList: this.getTreeList(workdir) });
}
getTreeList(workdir: string) {
if (workdir == '' || !fs.existsSync(workdir)) {
console.log("workdir not exist", workdir);
return [];
} }
const files = fs.readdirSync(workdir); componentDidMount() {
const list: TreeFile[] = []; ipcRenderer.on(MainEventType.CREATE_TREE, (event: any, path: string) => {
files.forEach((filename) => { console.log("on Create tree", path);
const stat = fs.statSync(path.join(workdir, filename)) this.props.onOpenTree(path);
if (stat.isFile()) { this.forceUpdate();
var name = filename.slice(0, -5)
list.push({
name,
path: workdir + '/' + name + '.json'
}); });
}
})
return list;
}
render() { var workdir = this.props.workdir;
console.log("render Properties"); if (workdir == "") {
const { onOpenTree, workdir } = this.props; return;
const treeList = this.getTreeList(workdir); }
return ( this.setState({ treeList: this.getTreeList(workdir) });
<div> }
<Search
placeholder="Search"
onChange={() => {
}} getTreeList(workdir: string) {
/> if (workdir == "" || !fs.existsSync(workdir)) {
<Menu console.log("workdir not exist", workdir);
mode="inline" return [];
> }
{treeList.map((tree) => (
<Menu.Item const files = fs.readdirSync(workdir);
key={tree.name} const list: TreeFile[] = [];
onClick={() => onOpenTree(tree.path)} files.forEach((filename) => {
> const stat = fs.statSync(path.join(workdir, filename));
{tree.name} if (stat.isFile()) {
</Menu.Item> var name = filename.slice(0, -5);
))} list.push({
</Menu> name,
</div> path: workdir + "/" + name + ".json",
) });
} }
});
return list;
}
render() {
console.log("render Properties");
const { onOpenTree, workdir } = this.props;
const treeList = this.getTreeList(workdir);
return (
<div>
<Search placeholder="Search" onChange={() => {}} />
<Menu mode="inline">
{treeList.map((tree) => (
<Menu.Item key={tree.name} onClick={() => onOpenTree(tree.path)}>
{tree.name}
</Menu.Item>
))}
</Menu>
</div>
);
}
} }
+268 -270
View File
@@ -1,277 +1,275 @@
import G6 from '@antv/g6'; import G6 from "@antv/g6";
import Settings from '../main-process/Settings'; import Settings from "../main-process/Settings";
const NODE_COLORS: any = { const NODE_COLORS: any = {
['Composite']: 'rgb(91,237,32)', ["Composite"]: "rgb(91,237,32)",
['Decorator']: 'rgb(218,167,16)', ["Decorator"]: "rgb(218,167,16)",
['Condition']: 'rgb(228,20,139)', ["Condition"]: "rgb(228,20,139)",
['Action']: 'rgb(91,143,249)', ["Action"]: "rgb(91,143,249)",
['Other']: 'rgb(112,112,112)', ["Other"]: "rgb(112,112,112)",
} };
export default function (settings: Settings) { export default function (settings: Settings) {
const collapseStyle: any = { const collapseStyle: any = {
'collapse-icon': { "collapse-icon": {
// symbol: G6.Marker.expand, // symbol: G6.Marker.expand,
fill: 'blue', fill: "blue",
}
};
G6.registerNode(
'TreeNode',
{
options: {
type: 'rect',
labelCfg: {
style: {
fill: 'blue',
fontSize: 10
}
}, },
style: { };
fill: "white", G6.registerNode(
"TreeNode",
{
options: {
type: "rect",
labelCfg: {
style: {
fill: "blue",
fontSize: 10,
},
},
style: {
fill: "white",
},
stateStyles: {
selected: {
"main-box": {
stroke: "yellow",
lineWidth: 5,
},
},
hover: {
stroke: "white",
lineWidth: 3,
},
dragSrc: {
fill: "gray",
},
dragRight: {
"drag-right": {
fillOpacity: 0.6,
},
},
dragUp: {
"drag-up": {
fillOpacity: 0.6,
},
},
dragDown: {
"drag-down": {
fillOpacity: 0.6,
},
},
},
},
draw(cfg, group) {
const nodeConf = settings.getNodeConf(cfg.name as string);
const classify = nodeConf.type || "Other";
const color = NODE_COLORS[classify] || "Other";
var size = cfg.size ? (cfg.size as number[]) : [150, 40];
const w = size[0];
const h = size[1];
const x0 = -w / 2;
const y0 = -h / 2;
const r = 2;
const shape = group.addShape("rect", {
attrs: {
x: x0,
y: y0,
width: w,
height: h,
stroke: color,
lineWidth: 2,
fill: "white",
radius: r,
},
name: "main-box",
draggable: true,
});
// name bg
group.addShape("rect", {
attrs: {
x: x0 + 1.5,
y: y0 + 1.5,
width: w - 3,
height: 16,
fill: color,
// radius: r,
},
name: "name-bg",
draggable: true,
});
// id text
group.addShape("text", {
attrs: {
textBaseline: "top",
x: x0 - 3,
y: -8,
fontSize: 20,
lineHeight: 20,
text: cfg.id,
textAlign: "right",
fill: "white",
stroke: "black",
lineWidth: 2,
},
name: "id-text",
});
// icon
var img = `./static/icons/${classify}.svg`;
group.addShape("image", {
attrs: {
x: x0 + 3,
y: y0 + 2,
height: 14,
width: 14,
img,
},
name: "node-icon",
});
// name text
group.addShape("text", {
attrs: {
textBaseline: "top",
x: x0 + 18,
y: y0 + 4,
fontWeight: 800,
lineHeight: 20,
text: cfg.name,
fill: "black",
},
name: "name-text",
});
var x = x0 + 2;
var y = y0 + 23;
// desc text
const desc = cfg.desc || nodeConf.desc;
if (desc) {
group.addShape("text", {
attrs: {
textBaseline: "top",
x,
y,
lineHeight: 20,
fontWeight: 800,
text: `备注:${desc}`,
fill: "black",
},
name: "desc-text",
});
}
const args: any = cfg.args;
if (nodeConf.args && args && Object.keys(args).length > 0) {
y += 20;
group.addShape("text", {
attrs: {
textBaseline: "top",
x,
y,
lineHeight: 20,
text: `参数:${JSON.stringify(args)}`,
fill: "black",
},
name: "args-text",
});
}
const input: [] = cfg.input ? (cfg.input as []) : [];
if (nodeConf.input && input.length > 0) {
y += 20;
group.addShape("text", {
attrs: {
textBaseline: "top",
x,
y,
lineHeight: 20,
text: `输入:${JSON.stringify(input)}`,
fill: "black",
},
name: "input-text",
});
}
const output: [] = cfg.output ? (cfg.output as []) : [];
if (nodeConf.output && output.length > 0) {
y += 20;
group.addShape("text", {
attrs: {
textBaseline: "top",
x,
y,
lineHeight: 20,
text: `输出:${JSON.stringify(output)}`,
fill: "black",
},
name: "output-text",
});
}
if (Array.isArray(cfg.children) && cfg.children.length > 0) {
group.addShape("marker", {
attrs: {
x: w / 2,
y: 0,
r: 6,
symbol: G6.Marker.collapse,
stroke: "#666",
lineWidth: 1,
fill: "#fff",
},
name: "collapse-icon",
});
}
group.addShape("rect", {
name: "drag-up",
attrs: {
x: -w / 2,
y: -h / 2,
width: w,
height: h / 2,
fill: "blue",
fillOpacity: 0,
},
draggable: true,
// visible: false,
});
group.addShape("rect", {
name: "drag-down",
attrs: {
x: -w / 2,
y: 0,
width: w,
height: h / 2,
fill: "blue",
fillOpacity: 0,
},
draggable: true,
// visible: false,
});
group.addShape("rect", {
name: "drag-right",
attrs: {
x: w * 0.1,
y: -h / 2,
width: w * 0.4,
height: h,
fill: "blue",
fillOpacity: 0,
},
draggable: true,
// visible: false,
});
return shape;
},
}, },
stateStyles: { "single-node"
selected: { );
'main-box': {
stroke: 'yellow',
lineWidth: 5,
},
},
hover: {
stroke: 'white',
lineWidth: 3,
},
dragSrc: {
fill: 'gray',
},
dragRight: {
'drag-right': {
fillOpacity: 0.6,
}
},
dragUp: {
'drag-up': {
fillOpacity: 0.6,
}
},
dragDown: {
'drag-down': {
fillOpacity: 0.6,
}
}
},
},
draw(cfg, group) {
const nodeConf = settings.getNodeConf(cfg.name as string);
const classify = nodeConf.type || 'Other';
const color = NODE_COLORS[classify] || 'Other';
var size = cfg.size ? cfg.size as number[] : [150, 40];
const w = size[0];
const h = size[1];
const x0 = -w / 2;
const y0 = -h / 2;
const r = 2;
const shape = group.addShape('rect', {
attrs: {
x: x0,
y: y0,
width: w,
height: h,
stroke: color,
lineWidth: 2,
fill: 'white',
radius: r,
},
name: 'main-box',
draggable: true,
});
// name bg
group.addShape('rect', {
attrs: {
x: x0 + 1.5,
y: y0 + 1.5,
width: w - 3,
height: 16,
fill: color,
// radius: r,
},
name: 'name-bg',
draggable: true,
});
// id text
group.addShape('text', {
attrs: {
textBaseline: 'top',
x: x0 - 3,
y: -8,
fontSize: 20,
lineHeight: 20,
text: cfg.id,
textAlign: 'right',
fill: 'white',
stroke: 'black',
lineWidth: 2,
},
name: 'id-text',
});
// icon
var img = `./static/icons/${classify}.svg`;
group.addShape('image', {
attrs: {
x: x0 + 3,
y: y0 + 2,
height: 14,
width: 14,
img,
},
name: 'node-icon',
});
// name text
group.addShape('text', {
attrs: {
textBaseline: 'top',
x: x0 + 18,
y: y0 + 4,
fontWeight: 800,
lineHeight: 20,
text: cfg.name,
fill: 'black',
},
name: 'name-text',
});
var x = x0 + 2;
var y = y0 + 23;
// desc text
const desc = cfg.desc || nodeConf.desc;
if (desc) {
group.addShape('text', {
attrs: {
textBaseline: 'top',
x,
y,
lineHeight: 20,
fontWeight: 800,
text: `备注:${desc}`,
fill: 'black',
},
name: 'desc-text',
});
}
const args: any = cfg.args;
if (nodeConf.args && args && Object.keys(args).length > 0) {
y += 20;
group.addShape('text', {
attrs: {
textBaseline: 'top',
x,
y,
lineHeight: 20,
text: `参数:${JSON.stringify(args)}`,
fill: 'black',
},
name: 'args-text',
});
}
const input: [] = cfg.input ? cfg.input as [] : [];
if (nodeConf.input && input.length > 0) {
y += 20;
group.addShape('text', {
attrs: {
textBaseline: 'top',
x,
y,
lineHeight: 20,
text: `输入:${JSON.stringify(input)}`,
fill: 'black',
},
name: 'input-text',
});
}
const output: [] = cfg.output ? cfg.output as [] : [];
if (nodeConf.output && output.length > 0) {
y += 20;
group.addShape('text', {
attrs: {
textBaseline: 'top',
x,
y,
lineHeight: 20,
text: `输出:${JSON.stringify(output)}`,
fill: 'black',
},
name: 'output-text',
});
}
if (Array.isArray(cfg.children) && cfg.children.length > 0) {
group.addShape('marker', {
attrs: {
x: w / 2,
y: 0,
r: 6,
symbol: G6.Marker.collapse,
stroke: '#666',
lineWidth: 1,
fill: '#fff',
},
name: 'collapse-icon',
});
}
group.addShape('rect', {
name: 'drag-up',
attrs: {
x: -w / 2,
y: -h / 2,
width: w,
height: h / 2,
fill: 'blue',
fillOpacity: 0,
},
draggable: true,
// visible: false,
});
group.addShape('rect', {
name: 'drag-down',
attrs: {
x: -w / 2,
y: 0,
width: w,
height: h / 2,
fill: 'blue',
fillOpacity: 0,
},
draggable: true,
// visible: false,
});
group.addShape('rect', {
name: 'drag-right',
attrs: {
x: w * 0.1,
y: -h / 2,
width: w * 0.4,
height: h,
fill: 'blue',
fillOpacity: 0,
},
draggable: true,
// visible: false,
});
return shape;
}
},
'single-node',
);
} }
+109 -115
View File
@@ -1,136 +1,130 @@
import Editor from "./Editor"; import Editor from "./Editor";
import React, { Component } from "react"; import React, { Component } from "react";
import { Layout, Tabs, message } from 'antd'; import { Layout, Tabs, message } from "antd";
import { ipcRenderer, remote } from 'electron'; import { ipcRenderer, remote } from "electron";
import * as path from 'path'; import * as path from "path";
import 'antd/dist/antd.dark.css'; import "antd/dist/antd.dark.css";
import MainEventType from "../common/MainEventType"; import MainEventType from "../common/MainEventType";
const { TabPane } = Tabs; const { TabPane } = Tabs;
interface TreeTabsProps { interface TreeTabsProps {}
}
interface TreeTabsState { interface TreeTabsState {
filepaths: string[]; filepaths: string[];
curPath?: string; curPath?: string;
} }
export default class TreeTabs extends Component<TreeTabsProps, TreeTabsState> { export default class TreeTabs extends Component<TreeTabsProps, TreeTabsState> {
state: TreeTabsState = { state: TreeTabsState = {
filepaths: [], filepaths: [],
} };
editors: { [path: string]: Editor } = {} editors: { [path: string]: Editor } = {};
componentWillMount() { componentWillMount() {}
}
componentDidMount() { componentDidMount() {
ipcRenderer.on(MainEventType.CREATE_NODE, (event: any, name: any) => { ipcRenderer.on(MainEventType.CREATE_NODE, (event: any, name: any) => {
const editor = this.getCurEditor(); const editor = this.getCurEditor();
editor?.createNode(name); editor?.createNode(name);
}); });
ipcRenderer.on(MainEventType.DELETE_NODE, () => { ipcRenderer.on(MainEventType.DELETE_NODE, () => {
const editor = this.getCurEditor(); const editor = this.getCurEditor();
editor?.deleteNode(); editor?.deleteNode();
}); });
ipcRenderer.on(MainEventType.COPY_NODE, () => { ipcRenderer.on(MainEventType.COPY_NODE, () => {
const editor = this.getCurEditor(); const editor = this.getCurEditor();
editor?.copyNode(); editor?.copyNode();
}); });
ipcRenderer.on(MainEventType.PASTE_NODE, () => { ipcRenderer.on(MainEventType.PASTE_NODE, () => {
const editor = this.getCurEditor(); const editor = this.getCurEditor();
editor?.pasteNode(); editor?.pasteNode();
}); });
ipcRenderer.on(MainEventType.SAVE, (event: any) => { ipcRenderer.on(MainEventType.SAVE, (event: any) => {
const { curPath } = this.state; const { curPath } = this.state;
if (!curPath) { if (!curPath) {
return; return;
}
const editor = this.editors[curPath];
editor.save();
message.success("已保存");
});
ipcRenderer.on(MainEventType.SAVE_ALL, (event: any) => {
for (let k in this.editors) {
let editor = this.editors[k];
editor.save();
}
message.success("已保存所有行为树");
});
}
getCurEditor() {
const { curPath } = this.state;
if (!curPath) {
return;
}
return this.editors[curPath];
}
openFile(path: string) {
console.log("on open file", path);
if (this.state.filepaths.indexOf(path) < 0) {
const filepaths = this.state.filepaths;
filepaths.push(path);
this.setState({ filepaths, curPath: path });
} else {
this.setState({ curPath: path });
}
}
render() {
console.log("render tabs");
const { filepaths, curPath } = this.state;
if (!curPath) {
return (<div />);
}
return (
<Tabs
hideAdd
className="tabs"
type="editable-card"
defaultActiveKey={curPath}
activeKey={curPath}
onChange={activeKey => {
this.setState({ curPath: activeKey })
}}
onEdit={(targetKey, action) => {
if (action == 'remove') {
const idx = filepaths.indexOf(targetKey as string);
if (idx < 0) {
return;
} }
filepaths.splice(idx, 1); const editor = this.editors[curPath];
const nextPath = filepaths[idx - 1]; editor.save();
this.setState({ filepaths, curPath: nextPath }); message.success("已保存");
} });
}}
> ipcRenderer.on(MainEventType.SAVE_ALL, (event: any) => {
{ for (let k in this.editors) {
filepaths.map(filepath => { let editor = this.editors[k];
return ( editor.save();
<TabPane tab={path.basename(filepath)} key={filepath}> }
<Editor message.success("已保存所有行为树");
filepath={filepath} });
ref={ }
ref => {
this.editors[filepath] = ref; getCurEditor() {
} const { curPath } = this.state;
} if (!curPath) {
/> return;
</TabPane>
);
})
} }
</Tabs> return this.editors[curPath];
) }
}
}; openFile(path: string) {
console.log("on open file", path);
if (this.state.filepaths.indexOf(path) < 0) {
const filepaths = this.state.filepaths;
filepaths.push(path);
this.setState({ filepaths, curPath: path });
} else {
this.setState({ curPath: path });
}
}
render() {
console.log("render tabs");
const { filepaths, curPath } = this.state;
if (!curPath) {
return <div />;
}
return (
<Tabs
hideAdd
className="tabs"
type="editable-card"
defaultActiveKey={curPath}
activeKey={curPath}
onChange={(activeKey) => {
this.setState({ curPath: activeKey });
}}
onEdit={(targetKey, action) => {
if (action == "remove") {
const idx = filepaths.indexOf(targetKey as string);
if (idx < 0) {
return;
}
filepaths.splice(idx, 1);
const nextPath = filepaths[idx - 1];
this.setState({ filepaths, curPath: nextPath });
}
}}
>
{filepaths.map((filepath) => {
return (
<TabPane tab={path.basename(filepath)} key={filepath}>
<Editor
filepath={filepath}
ref={(ref) => {
this.editors[filepath] = ref;
}}
/>
</TabPane>
);
})}
</Tabs>
);
}
}
+64 -69
View File
@@ -1,19 +1,18 @@
import * as ReactDOM from "react-dom"; import * as ReactDOM from "react-dom";
import Editor from "./Editor"; import Editor from "./Editor";
import React, { Component } from "react"; import React, { Component } from "react";
import { Layout, Tabs } from 'antd'; import { Layout, Tabs } from "antd";
import { ipcRenderer, remote } from 'electron'; import { ipcRenderer, remote } from "electron";
import * as path from 'path'; import * as path from "path";
import * as fs from 'fs'; import * as fs from "fs";
import * as Utils from '../common/Utils'; import * as Utils from "../common/Utils";
import MainEventType from "../common/MainEventType"; import MainEventType from "../common/MainEventType";
import Properties from "./Properties"; import Properties from "./Properties";
import Settings from "../main-process/Settings"; import Settings from "../main-process/Settings";
import G6 from '@antv/g6'; import G6 from "@antv/g6";
import 'antd/dist/antd.dark.css'; import "antd/dist/antd.dark.css";
import './index.css'; import "./index.css";
import { ModelConfig, Item } from "@antv/g6/lib/types"; import { ModelConfig, Item } from "@antv/g6/lib/types";
import RegisterNode from "./RegisterNode"; import RegisterNode from "./RegisterNode";
import TreeTabs from "./TreeTabs"; import TreeTabs from "./TreeTabs";
@@ -21,75 +20,71 @@ import TreeTabs from "./TreeTabs";
const { Header, Sider, Content, Footer } = Layout; const { Header, Sider, Content, Footer } = Layout;
const { TabPane } = Tabs; const { TabPane } = Tabs;
interface MainState { interface MainState {
workdir: string; workdir: string;
workspace: string; workspace: string;
} }
export default class Main extends Component { export default class Main extends Component {
state: MainState = { state: MainState = {
workdir: '', workdir: "",
workspace: '', workspace: "",
} };
settings: Settings; settings: Settings;
tabs: TreeTabs; tabs: TreeTabs;
componentWillMount() { componentWillMount() {
this.updateSettings(); this.updateSettings();
RegisterNode(this.settings); RegisterNode(this.settings);
} }
componentDidMount() { componentDidMount() {
ipcRenderer.on(MainEventType.OPEN_FILE, (event: any, path: any) => { ipcRenderer.on(MainEventType.OPEN_FILE, (event: any, path: any) => {
this.setState({ curPath: path }); this.setState({ curPath: path });
}); });
ipcRenderer.on(MainEventType.OPEN_DIR, (event: any, workdir: any, workspace: string) => { ipcRenderer.on(MainEventType.OPEN_DIR, (event: any, workdir: any, workspace: string) => {
console.log("on open workspace", workspace); console.log("on open workspace", workspace);
document.title = workspace; document.title = workspace;
this.setState({ workdir, workspace }); this.setState({ workdir, workspace });
}); });
console.log("workdir", this.settings.curWorkspace.getWorkdir()); console.log("workdir", this.settings.curWorkspace.getWorkdir());
this.setState({ this.setState({
workdir: this.settings.curWorkspace.getWorkdir(), workdir: this.settings.curWorkspace.getWorkdir(),
workspace: this.settings.curWorkspace.getFilepath(), workspace: this.settings.curWorkspace.getFilepath(),
}); });
} }
updateSettings() { updateSettings() {
this.settings = Utils.getRemoteSettings(); this.settings = Utils.getRemoteSettings();
} }
render() { render() {
console.log("render main"); console.log("render main");
const { workdir, workspace } = this.state; const { workdir, workspace } = this.state;
document.title = `行为树编辑器 - ${workspace}`; document.title = `行为树编辑器 - ${workspace}`;
return ( return (
<Layout className="body"> <Layout className="body">
<Sider className="sider" width={250}> <Sider className="sider" width={250}>
<Properties <Properties
workdir={workdir} workdir={workdir}
onOpenTree={(path) => { onOpenTree={(path) => {
this.tabs.openFile(path); this.tabs.openFile(path);
}} }}
/> />
</Sider> </Sider>
<Content className="content"> <Content className="content">
<TreeTabs <TreeTabs
ref={ref => { ref={(ref) => {
this.tabs = ref; this.tabs = ref;
}} }}
/> />
</Content> </Content>
</Layout> </Layout>
) );
} }
}; }
ReactDOM.render( ReactDOM.render(<Main />, document.getElementById("root") as HTMLElement);
<Main />,
document.getElementById('root') as HTMLElement
);