style: format codes
This commit is contained in:
1 parent
c3ea18e307
commit
00e79d641e
14 files changed
+2152
-2186
No files matched your search
+44
-44
@@ -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[];
|
||||||
}
|
}
|
||||||
+12
-12
@@ -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",
|
||||||
}
|
};
|
||||||
+130
-131
@@ -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) {
|
||||||
if (node.children) {
|
for (let child of node.children) {
|
||||||
for (let child of node.children) {
|
let found = findFromAllChildren(child, id);
|
||||||
let found = findFromAllChildren(child, id);
|
if (found) {
|
||||||
if (found) {
|
return found;
|
||||||
return found;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return null;
|
||||||
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;
|
};
|
||||||
}
|
|
||||||
+375
-375
@@ -1,375 +1,375 @@
|
|||||||
import {
|
import {
|
||||||
Menu,
|
Menu,
|
||||||
app,
|
app,
|
||||||
dialog,
|
dialog,
|
||||||
BrowserWindow,
|
BrowserWindow,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
WebContents,
|
WebContents,
|
||||||
MenuItemConstructorOptions,
|
MenuItemConstructorOptions,
|
||||||
} from "electron";
|
} from "electron";
|
||||||
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 { MainProcess } from "./MainProcess";
|
import { MainProcess } from "./MainProcess";
|
||||||
import Settings from "./Settings";
|
import Settings from "./Settings";
|
||||||
|
|
||||||
export default class AppMenu {
|
export default class AppMenu {
|
||||||
private mainProcess: MainProcess;
|
private mainProcess: MainProcess;
|
||||||
private mainWindow: BrowserWindow;
|
private mainWindow: BrowserWindow;
|
||||||
private webContents: WebContents;
|
private webContents: WebContents;
|
||||||
private settings: Settings;
|
private settings: Settings;
|
||||||
|
|
||||||
constructor(mainProcess: MainProcess) {
|
constructor(mainProcess: MainProcess) {
|
||||||
this.mainProcess = mainProcess;
|
this.mainProcess = mainProcess;
|
||||||
this.mainWindow = mainProcess.mainWindow;
|
this.mainWindow = mainProcess.mainWindow;
|
||||||
this.webContents = mainProcess.mainWindow.webContents;
|
this.webContents = mainProcess.mainWindow.webContents;
|
||||||
this.settings = mainProcess.settings;
|
this.settings = mainProcess.settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
createMenu() {
|
createMenu() {
|
||||||
const menu: Menu = new Menu();
|
const menu: Menu = new Menu();
|
||||||
menu.append(this.createWorkspaceMenu());
|
menu.append(this.createWorkspaceMenu());
|
||||||
menu.append(this.createFileMenu());
|
menu.append(this.createFileMenu());
|
||||||
menu.append(this.createNodeMenu());
|
menu.append(this.createNodeMenu());
|
||||||
menu.append(this.createToolsMenu());
|
menu.append(this.createToolsMenu());
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createFileMenu() {
|
private createFileMenu() {
|
||||||
const fileItems: MenuItemConstructorOptions[] = [];
|
const fileItems: MenuItemConstructorOptions[] = [];
|
||||||
for (let path of this.settings.recentFiles) {
|
for (let path of this.settings.recentFiles) {
|
||||||
fileItems.push({
|
fileItems.push({
|
||||||
label: path,
|
label: path,
|
||||||
click: () => {
|
click: () => {
|
||||||
console.log("open recent file", path);
|
console.log("open recent file", path);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MenuItem({
|
return new MenuItem({
|
||||||
label: "行为树",
|
label: "行为树",
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: "新建",
|
label: "新建",
|
||||||
accelerator: "ctrl+n",
|
accelerator: "ctrl+n",
|
||||||
click: () => {
|
click: () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const res = await dialog.showSaveDialog({
|
const res = await dialog.showSaveDialog({
|
||||||
properties: ["showOverwriteConfirmation"],
|
properties: ["showOverwriteConfirmation"],
|
||||||
filters: [{ name: "Json", extensions: ["json"] }],
|
filters: [{ name: "Json", extensions: ["json"] }],
|
||||||
});
|
});
|
||||||
if (!res.canceled) {
|
if (!res.canceled) {
|
||||||
const path = res.filePath;
|
const path = res.filePath;
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path,
|
path,
|
||||||
JSON.stringify(Utils.createNewTree(path), null, 2)
|
JSON.stringify(Utils.createNewTree(path), null, 2)
|
||||||
);
|
);
|
||||||
this.webContents.send(MainEventType.CREATE_TREE, path);
|
this.webContents.send(MainEventType.CREATE_TREE, path);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "打开文件",
|
label: "打开文件",
|
||||||
accelerator: "Ctrl+O",
|
accelerator: "Ctrl+O",
|
||||||
click: () => {
|
click: () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const res = await dialog.showOpenDialog({
|
const res = await dialog.showOpenDialog({
|
||||||
properties: ["openFile"],
|
properties: ["openFile"],
|
||||||
filters: [{ name: "Json", extensions: ["json"] }],
|
filters: [{ name: "Json", extensions: ["json"] }],
|
||||||
});
|
});
|
||||||
if (res.filePaths.length > 0) {
|
if (res.filePaths.length > 0) {
|
||||||
const path = res.filePaths[0];
|
const path = res.filePaths[0];
|
||||||
if (this.settings.recentFiles.indexOf(path) < 0) {
|
if (this.settings.recentFiles.indexOf(path) < 0) {
|
||||||
this.settings.recentFiles.unshift(path);
|
this.settings.recentFiles.unshift(path);
|
||||||
this.settings.save();
|
this.settings.save();
|
||||||
this.mainProcess.rebuildMenu();
|
this.mainProcess.rebuildMenu();
|
||||||
}
|
}
|
||||||
this.webContents.send(MainEventType.OPEN_FILE, path);
|
this.webContents.send(MainEventType.OPEN_FILE, path);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: "最近打开",
|
label: "最近打开",
|
||||||
submenu: fileItems,
|
submenu: fileItems,
|
||||||
},
|
},
|
||||||
|
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: "保存",
|
label: "保存",
|
||||||
accelerator: "ctrl+s",
|
accelerator: "ctrl+s",
|
||||||
click: () => {
|
click: () => {
|
||||||
this.webContents.send(MainEventType.SAVE);
|
this.webContents.send(MainEventType.SAVE);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "全部保存",
|
label: "全部保存",
|
||||||
accelerator: "ctrl+shift+s",
|
accelerator: "ctrl+shift+s",
|
||||||
click: () => {
|
click: () => {
|
||||||
this.webContents.send(MainEventType.SAVE_ALL);
|
this.webContents.send(MainEventType.SAVE_ALL);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "合并导出Json",
|
label: "合并导出Json",
|
||||||
click: () => {
|
click: () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const res = await dialog.showSaveDialog({
|
const res = await dialog.showSaveDialog({
|
||||||
properties: ["showOverwriteConfirmation"],
|
properties: ["showOverwriteConfirmation"],
|
||||||
filters: [{ name: "Json", extensions: ["json"] }],
|
filters: [{ name: "Json", extensions: ["json"] }],
|
||||||
});
|
});
|
||||||
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,
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: "新建节点",
|
label: "新建节点",
|
||||||
accelerator: "insert",
|
accelerator: "insert",
|
||||||
click: () => {
|
click: () => {
|
||||||
this.webContents.send(MainEventType.CREATE_NODE, "unknow");
|
this.webContents.send(MainEventType.CREATE_NODE, "unknow");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "删除节点",
|
label: "删除节点",
|
||||||
accelerator: "delete",
|
accelerator: "delete",
|
||||||
click: () => {
|
click: () => {
|
||||||
this.webContents.send(MainEventType.DELETE_NODE);
|
this.webContents.send(MainEventType.DELETE_NODE);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: "复制节点",
|
label: "复制节点",
|
||||||
accelerator: "ctrl+c",
|
accelerator: "ctrl+c",
|
||||||
role: "copy",
|
role: "copy",
|
||||||
click: () => {
|
click: () => {
|
||||||
this.webContents.send(MainEventType.COPY_NODE);
|
this.webContents.send(MainEventType.COPY_NODE);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "粘贴节点",
|
label: "粘贴节点",
|
||||||
accelerator: "ctrl+v",
|
accelerator: "ctrl+v",
|
||||||
click: () => {
|
click: () => {
|
||||||
this.webContents.send(MainEventType.PASTE_NODE);
|
this.webContents.send(MainEventType.PASTE_NODE);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private createWorkspaceMenu() {
|
private createWorkspaceMenu() {
|
||||||
const openWorkspace = (path: string) => {
|
const openWorkspace = (path: string) => {
|
||||||
const curWorkspace = this.settings.curWorkspace;
|
const curWorkspace = this.settings.curWorkspace;
|
||||||
curWorkspace.setFilepath(path);
|
curWorkspace.setFilepath(path);
|
||||||
curWorkspace.load();
|
curWorkspace.load();
|
||||||
this.settings.pushRecentWorkspace(path);
|
this.settings.pushRecentWorkspace(path);
|
||||||
this.mainProcess.rebuildMenu();
|
this.mainProcess.rebuildMenu();
|
||||||
this.webContents.send(
|
this.webContents.send(
|
||||||
MainEventType.OPEN_DIR,
|
MainEventType.OPEN_DIR,
|
||||||
curWorkspace.getWorkdir(),
|
curWorkspace.getWorkdir(),
|
||||||
curWorkspace.getFilepath()
|
curWorkspace.getFilepath()
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const saveToNewPath = () => {
|
const saveToNewPath = () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const res = await dialog.showSaveDialog({
|
const res = await dialog.showSaveDialog({
|
||||||
properties: ["showOverwriteConfirmation"],
|
properties: ["showOverwriteConfirmation"],
|
||||||
filters: [{ name: "Json", extensions: ["json"] }],
|
filters: [{ name: "Json", extensions: ["json"] }],
|
||||||
});
|
});
|
||||||
if (!res.canceled) {
|
if (!res.canceled) {
|
||||||
this.settings.curWorkspace.setFilepath(res.filePath);
|
this.settings.curWorkspace.setFilepath(res.filePath);
|
||||||
this.settings.curWorkspace.save();
|
this.settings.curWorkspace.save();
|
||||||
openWorkspace(res.filePath);
|
openWorkspace(res.filePath);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
};
|
};
|
||||||
const recentItems: MenuItemConstructorOptions[] = [];
|
const recentItems: MenuItemConstructorOptions[] = [];
|
||||||
for (let path of this.settings.recentWorkspaces) {
|
for (let path of this.settings.recentWorkspaces) {
|
||||||
recentItems.push({
|
recentItems.push({
|
||||||
label: path,
|
label: path,
|
||||||
click: () => {
|
click: () => {
|
||||||
console.log("open recent workspace", path);
|
console.log("open recent workspace", path);
|
||||||
openWorkspace(path);
|
openWorkspace(path);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return new MenuItem({
|
return new MenuItem({
|
||||||
label: "工作区",
|
label: "工作区",
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: "打开",
|
label: "打开",
|
||||||
click: () => {
|
click: () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const res = await dialog.showOpenDialog({
|
const res = await dialog.showOpenDialog({
|
||||||
properties: ["openFile"],
|
properties: ["openFile"],
|
||||||
filters: [{ name: "Json", extensions: ["json"] }],
|
filters: [{ name: "Json", extensions: ["json"] }],
|
||||||
});
|
});
|
||||||
if (res.filePaths.length > 0) {
|
if (res.filePaths.length > 0) {
|
||||||
openWorkspace(res.filePaths[0]);
|
openWorkspace(res.filePaths[0]);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "保存",
|
label: "保存",
|
||||||
click: () => {
|
click: () => {
|
||||||
if (this.settings.curWorkspace.getFilepath()) {
|
if (this.settings.curWorkspace.getFilepath()) {
|
||||||
this.settings.curWorkspace.save();
|
this.settings.curWorkspace.save();
|
||||||
} else {
|
} else {
|
||||||
saveToNewPath();
|
saveToNewPath();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "另存为",
|
label: "另存为",
|
||||||
click: () => {
|
click: () => {
|
||||||
saveToNewPath();
|
saveToNewPath();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "最近打开",
|
label: "最近打开",
|
||||||
submenu: recentItems,
|
submenu: recentItems,
|
||||||
},
|
},
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: "打开目录",
|
label: "打开目录",
|
||||||
accelerator: "Ctrl+Shift+O",
|
accelerator: "Ctrl+Shift+O",
|
||||||
click: () => {
|
click: () => {
|
||||||
if (!this.settings.curWorkspace.getNodeConfPath()) {
|
if (!this.settings.curWorkspace.getNodeConfPath()) {
|
||||||
dialog.showErrorBox("警告", "请先指定节点定义配置!");
|
dialog.showErrorBox("警告", "请先指定节点定义配置!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(async () => {
|
(async () => {
|
||||||
const res = await dialog.showOpenDialog({
|
const res = await dialog.showOpenDialog({
|
||||||
properties: ["openDirectory"],
|
properties: ["openDirectory"],
|
||||||
});
|
});
|
||||||
if (res.filePaths.length > 0) {
|
if (res.filePaths.length > 0) {
|
||||||
const curWorkspace = this.settings.curWorkspace;
|
const curWorkspace = this.settings.curWorkspace;
|
||||||
const path = res.filePaths[0];
|
const path = res.filePaths[0];
|
||||||
curWorkspace.setWorkdir(path);
|
curWorkspace.setWorkdir(path);
|
||||||
curWorkspace.save();
|
curWorkspace.save();
|
||||||
this.webContents.send(
|
this.webContents.send(
|
||||||
MainEventType.OPEN_DIR,
|
MainEventType.OPEN_DIR,
|
||||||
path,
|
path,
|
||||||
curWorkspace.getFilepath()
|
curWorkspace.getFilepath()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "节点定义",
|
label: "节点定义",
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: "选择文件",
|
label: "选择文件",
|
||||||
click: () => {
|
click: () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const res = await dialog.showOpenDialog({
|
const res = await dialog.showOpenDialog({
|
||||||
properties: ["openFile"],
|
properties: ["openFile"],
|
||||||
filters: [{ name: "Json", extensions: ["json"] }],
|
filters: [{ name: "Json", extensions: ["json"] }],
|
||||||
});
|
});
|
||||||
if (res.filePaths.length > 0) {
|
if (res.filePaths.length > 0) {
|
||||||
const nodeConfigPath = res.filePaths[0];
|
const nodeConfigPath = res.filePaths[0];
|
||||||
this.settings.curWorkspace.setNodeConfPath(nodeConfigPath);
|
this.settings.curWorkspace.setNodeConfPath(nodeConfigPath);
|
||||||
this.mainProcess.rebuildMenu();
|
this.mainProcess.rebuildMenu();
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{ label: this.settings.nodeConfPath },
|
{ label: this.settings.nodeConfPath },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: "关闭",
|
label: "关闭",
|
||||||
click: () => {
|
click: () => {
|
||||||
app.quit();
|
app.quit();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private createToolsMenu() {
|
private createToolsMenu() {
|
||||||
return new MenuItem({
|
return new MenuItem({
|
||||||
label: "开发工具",
|
label: "开发工具",
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: "打开控制台",
|
label: "打开控制台",
|
||||||
accelerator: "Ctrl+Shift+I",
|
accelerator: "Ctrl+Shift+I",
|
||||||
click: (_, browserWindow) => {
|
click: (_, browserWindow) => {
|
||||||
browserWindow.webContents.toggleDevTools();
|
browserWindow.webContents.toggleDevTools();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "重载",
|
label: "重载",
|
||||||
accelerator: "Ctrl+R",
|
accelerator: "Ctrl+R",
|
||||||
click: (_, browserWindow) => {
|
click: (_, browserWindow) => {
|
||||||
browserWindow.reload();
|
browserWindow.reload();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private createNodeMenu() {
|
private createNodeMenu() {
|
||||||
const classifyItems: MenuItemConstructorOptions[] = [];
|
const classifyItems: MenuItemConstructorOptions[] = [];
|
||||||
const map: { [key: string]: MenuItemConstructorOptions } = {};
|
const map: { [key: string]: MenuItemConstructorOptions } = {};
|
||||||
for (let t of this.settings.nodeClassify) {
|
for (let t of this.settings.nodeClassify) {
|
||||||
let item: MenuItemConstructorOptions = {
|
let item: MenuItemConstructorOptions = {
|
||||||
id: t.classify,
|
id: t.classify,
|
||||||
label: `${t.classify}(${t.desc})`,
|
label: `${t.classify}(${t.desc})`,
|
||||||
submenu: [],
|
submenu: [],
|
||||||
};
|
};
|
||||||
classifyItems.push(item);
|
classifyItems.push(item);
|
||||||
map[t.classify] = item;
|
map[t.classify] = item;
|
||||||
}
|
}
|
||||||
const other: MenuItemConstructorOptions = {
|
const other: MenuItemConstructorOptions = {
|
||||||
id: "other",
|
id: "other",
|
||||||
label: "其它",
|
label: "其它",
|
||||||
submenu: [],
|
submenu: [],
|
||||||
};
|
};
|
||||||
var hasOther = false;
|
var hasOther = false;
|
||||||
|
|
||||||
for (let node of this.settings.nodeConfig) {
|
for (let node of this.settings.nodeConfig) {
|
||||||
const item: MenuItemConstructorOptions = {
|
const item: MenuItemConstructorOptions = {
|
||||||
label: `${node.name}(${node.desc})`,
|
label: `${node.name}(${node.desc})`,
|
||||||
click: () => {
|
click: () => {
|
||||||
console.log("create node", node.name);
|
console.log("create node", node.name);
|
||||||
this.webContents.send(MainEventType.CREATE_NODE, node.name);
|
this.webContents.send(MainEventType.CREATE_NODE, node.name);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let typeItem = map[node.type];
|
let typeItem = map[node.type];
|
||||||
if (!typeItem) {
|
if (!typeItem) {
|
||||||
typeItem = other;
|
typeItem = other;
|
||||||
hasOther = true;
|
hasOther = true;
|
||||||
}
|
}
|
||||||
if (typeItem.submenu instanceof Menu) {
|
if (typeItem.submenu instanceof Menu) {
|
||||||
console.log("typeItem.submenu error", typeItem);
|
console.log("typeItem.submenu error", typeItem);
|
||||||
} else {
|
} else {
|
||||||
typeItem.submenu.push(item);
|
typeItem.submenu.push(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasOther) {
|
if (hasOther) {
|
||||||
classifyItems.push(other);
|
classifyItems.push(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MenuItem({
|
return new MenuItem({
|
||||||
label: "新建节点",
|
label: "新建节点",
|
||||||
submenu: classifyItems,
|
submenu: classifyItems,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
|
||||||
|
|
||||||
+89
-90
@@ -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() {
|
||||||
get nodeConfPath() {
|
return this.curWorkspace.getNodeConfPath();
|
||||||
return this.curWorkspace.getNodeConfPath();
|
}
|
||||||
}
|
get recentWorkspaces() {
|
||||||
get recentWorkspaces() {
|
return this.settings.recentWorkspaces;
|
||||||
return this.settings.recentWorkspaces;
|
}
|
||||||
}
|
get recentFiles() {
|
||||||
get recentFiles() {
|
return this.settings.recentFiles;
|
||||||
return this.settings.recentFiles;
|
}
|
||||||
}
|
get nodeConfig() {
|
||||||
get nodeConfig() {
|
return this.curWorkspace.nodeConf;
|
||||||
return this.curWorkspace.nodeConf;
|
}
|
||||||
}
|
get nodeClassify() {
|
||||||
get nodeClassify() {
|
return this.settings.nodeClassify;
|
||||||
return this.settings.nodeClassify;
|
}
|
||||||
}
|
|
||||||
|
set(config: SettingsModel) {
|
||||||
set(config: SettingsModel) {
|
this.settings = {
|
||||||
this.settings = {
|
...this.settings,
|
||||||
...this.settings,
|
...config,
|
||||||
...config
|
};
|
||||||
};
|
this.save();
|
||||||
this.save();
|
}
|
||||||
}
|
|
||||||
|
load() {
|
||||||
load() {
|
if (fs.existsSync(settingPath)) {
|
||||||
if (fs.existsSync(settingPath)) {
|
const str = fs.readFileSync(settingPath, "utf8");
|
||||||
const str = fs.readFileSync(settingPath, 'utf8');
|
this.settings = JSON.parse(str);
|
||||||
this.settings = JSON.parse(str);
|
} else {
|
||||||
} else {
|
this.settings = {
|
||||||
this.settings = {
|
recentWorkspaces: ["sample/workspace.json"],
|
||||||
recentWorkspaces: ['sample/workspace.json'],
|
recentFiles: [],
|
||||||
recentFiles: [],
|
nodeClassify: sampleNodeClassify,
|
||||||
nodeClassify: sampleNodeClassify,
|
};
|
||||||
};
|
this.save();
|
||||||
this.save();
|
}
|
||||||
}
|
this.curWorkspace = new Workspace(this.settings.recentWorkspaces[0]);
|
||||||
this.curWorkspace = new Workspace(this.settings.recentWorkspaces[0]);
|
this.curWorkspace.load();
|
||||||
this.curWorkspace.load();
|
}
|
||||||
}
|
|
||||||
|
save() {
|
||||||
save() {
|
fs.writeFileSync(settingPath, JSON.stringify(this.settings, null, 2));
|
||||||
fs.writeFileSync(settingPath, JSON.stringify(this.settings, null, 2));
|
}
|
||||||
}
|
|
||||||
|
getNodeConf(name: string) {
|
||||||
getNodeConf(name: string) {
|
return this.curWorkspace.getNodeConf(name);
|
||||||
return this.curWorkspace.getNodeConf(name);
|
}
|
||||||
}
|
|
||||||
|
pushRecentWorkspace(path: string) {
|
||||||
pushRecentWorkspace(path: string) {
|
if (this.settings.recentWorkspaces.indexOf(path) < 0) {
|
||||||
if (this.settings.recentWorkspaces.indexOf(path) < 0) {
|
this.settings.recentWorkspaces.unshift(path);
|
||||||
this.settings.recentWorkspaces.unshift(path);
|
console.log("push", path);
|
||||||
console.log("push", path);
|
} else {
|
||||||
} else {
|
console.log("already in path", this.settings.recentWorkspaces, path);
|
||||||
console.log("already in path", this.settings.recentWorkspaces, path);
|
}
|
||||||
}
|
this.save();
|
||||||
this.save();
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
+139
-137
@@ -1,137 +1,139 @@
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { BehaviorNodeTypeModel } from "../common/BehaviorTreeModel";
|
import { BehaviorNodeTypeModel } from "../common/BehaviorTreeModel";
|
||||||
import * as glob from "glob";
|
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 = {
|
||||||
name: "unknow",
|
name: "unknow",
|
||||||
desc: "新建节点",
|
desc: "新建节点",
|
||||||
type: "Action",
|
type: "Action",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class Workspace {
|
export default class Workspace {
|
||||||
private filepath: string;
|
private filepath: string;
|
||||||
private nodeConfPath: string;
|
private nodeConfPath: string;
|
||||||
private workdir: string;
|
private workdir: string;
|
||||||
|
|
||||||
private name2conf: { [name: string]: BehaviorNodeTypeModel } = {};
|
private name2conf: { [name: string]: BehaviorNodeTypeModel } = {};
|
||||||
private types: BehaviorNodeTypeModel[] = [];
|
private types: BehaviorNodeTypeModel[] = [];
|
||||||
|
|
||||||
constructor(filepath: string) {
|
constructor(filepath: string) {
|
||||||
this.filepath = filepath;
|
this.filepath = filepath;
|
||||||
}
|
}
|
||||||
|
|
||||||
load() {
|
load() {
|
||||||
if (!this.filepath) {
|
if (!this.filepath) {
|
||||||
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();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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[t.name] = t;
|
this.name2conf = {};
|
||||||
})
|
types.forEach((t) => {
|
||||||
this.types = types;
|
this.name2conf[t.name] = t;
|
||||||
}
|
});
|
||||||
|
this.types = types;
|
||||||
getNodeConfPath() {
|
}
|
||||||
return this.nodeConfPath;
|
|
||||||
}
|
getNodeConfPath() {
|
||||||
setNodeConfPath(path: string) {
|
return this.nodeConfPath;
|
||||||
this.nodeConfPath = path;
|
}
|
||||||
this.initNodeConf();
|
setNodeConfPath(path: string) {
|
||||||
}
|
this.nodeConfPath = path;
|
||||||
getWorkdir(): string {
|
this.initNodeConf();
|
||||||
return this.workdir;
|
}
|
||||||
}
|
getWorkdir(): string {
|
||||||
setWorkdir(workdir: string) {
|
return this.workdir;
|
||||||
this.workdir = workdir;
|
}
|
||||||
}
|
setWorkdir(workdir: string) {
|
||||||
|
this.workdir = workdir;
|
||||||
writeAllTrees(outFilePath: string, cb?: (err: string) => void) {
|
}
|
||||||
if (!fs.existsSync(this.getWorkdir())) {
|
|
||||||
cb && cb("请先打开工作区");
|
writeAllTrees(outFilePath: string, cb?: (err: string) => void) {
|
||||||
return;
|
if (!fs.existsSync(this.getWorkdir())) {
|
||||||
}
|
cb && cb("请先打开工作区");
|
||||||
glob.glob(path.join(this.getWorkdir(), "**/*.json"), function (err, files) {
|
return;
|
||||||
if (err) {
|
}
|
||||||
console.error(err);
|
glob.glob(path.join(this.getWorkdir(), "**/*.json"), function (err, files) {
|
||||||
cb && cb(err.message);
|
if (err) {
|
||||||
return;
|
console.error(err);
|
||||||
}
|
cb && cb(err.message);
|
||||||
|
return;
|
||||||
let result = new Array();
|
}
|
||||||
files.forEach((file) => {
|
|
||||||
const data = fs.readFileSync(file, "utf8");
|
let result = new Array();
|
||||||
result.push(JSON.parse(data));
|
files.forEach((file) => {
|
||||||
});
|
const data = fs.readFileSync(file, "utf8");
|
||||||
|
result.push(JSON.parse(data));
|
||||||
fs.writeFileSync(outFilePath, JSON.stringify(result, null, 2));
|
});
|
||||||
cb && cb(null);
|
|
||||||
});
|
fs.writeFileSync(outFilePath, JSON.stringify(result, null, 2));
|
||||||
}
|
cb && cb(null);
|
||||||
|
});
|
||||||
save(filepath?: string) {
|
}
|
||||||
if (filepath) {
|
|
||||||
this.filepath = filepath;
|
save(filepath?: string) {
|
||||||
}
|
if (filepath) {
|
||||||
if (!this.filepath) {
|
this.filepath = filepath;
|
||||||
return;
|
}
|
||||||
}
|
if (!this.filepath) {
|
||||||
fs.writeFileSync(
|
return;
|
||||||
this.filepath,
|
}
|
||||||
JSON.stringify(
|
fs.writeFileSync(
|
||||||
{
|
this.filepath,
|
||||||
nodeConfPath: this.nodeConfPath,
|
JSON.stringify(
|
||||||
workdir: this.workdir,
|
{
|
||||||
},
|
nodeConfPath: this.nodeConfPath,
|
||||||
null,
|
workdir: this.workdir,
|
||||||
2
|
},
|
||||||
)
|
null,
|
||||||
);
|
2
|
||||||
}
|
)
|
||||||
|
);
|
||||||
get nodeConf() {
|
}
|
||||||
return this.types;
|
|
||||||
}
|
get nodeConf() {
|
||||||
|
return this.types;
|
||||||
getFilepath() {
|
}
|
||||||
return this.filepath;
|
|
||||||
}
|
getFilepath() {
|
||||||
|
return this.filepath;
|
||||||
setFilepath(filepath: string) {
|
}
|
||||||
this.filepath = filepath;
|
|
||||||
}
|
setFilepath(filepath: string) {
|
||||||
|
this.filepath = filepath;
|
||||||
getNodeConf(name: string) {
|
}
|
||||||
return this.name2conf[name] || unknowNodeType;
|
|
||||||
}
|
getNodeConf(name: string) {
|
||||||
}
|
return this.name2conf[name] || unknowNodeType;
|
||||||
|
}
|
||||||
|
}
|
||||||
+267
-273
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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() {
|
||||||
|
const { model } = this.props;
|
||||||
render() {
|
if (!model) {
|
||||||
const { model } = this.props;
|
return <div />;
|
||||||
if(!model) {
|
}
|
||||||
return (<div/>)
|
return (
|
||||||
}
|
<Card title="概况" style={{ height: "100%" }}>
|
||||||
return (
|
行为树:{model.name}
|
||||||
<Card title="概况" style={{ height: "100%" }}>
|
</Card>
|
||||||
行为树:{model.name}
|
);
|
||||||
|
}
|
||||||
</Card>
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
+414
-412
@@ -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,
|
||||||
import './Editor.css';
|
} from "../../common/BehaviorTreeModel";
|
||||||
import { clipboard } from 'electron';
|
import TreePanel from "./TreePanel";
|
||||||
|
import Settings from "../../main-process/Settings";
|
||||||
export interface EditorProps {
|
|
||||||
filepath: string;
|
import "./Editor.css";
|
||||||
}
|
import { clipboard } from "electron";
|
||||||
|
|
||||||
interface EditorState {
|
export interface EditorProps {
|
||||||
curNodeId?: string;
|
filepath: string;
|
||||||
treeModel?: BehaviorTreeModel;
|
}
|
||||||
settings?: Settings;
|
|
||||||
}
|
interface EditorState {
|
||||||
|
curNodeId?: string;
|
||||||
export default class Editor extends React.Component<EditorProps, EditorState> {
|
treeModel?: BehaviorTreeModel;
|
||||||
private ref: React.RefObject<any>;
|
settings?: Settings;
|
||||||
state: EditorState = {};
|
}
|
||||||
|
|
||||||
private graph: TreeGraph;
|
export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||||
private dragSrcId: string;
|
private ref: React.RefObject<any>;
|
||||||
private dragDstId: string;
|
state: EditorState = {};
|
||||||
private autoId: number;
|
|
||||||
|
private graph: TreeGraph;
|
||||||
constructor(props: EditorProps) {
|
private dragSrcId: string;
|
||||||
super(props);
|
private dragDstId: string;
|
||||||
this.ref = React.createRef();
|
private autoId: number;
|
||||||
}
|
|
||||||
|
constructor(props: EditorProps) {
|
||||||
componentDidMount() {
|
super(props);
|
||||||
const graph = new TreeGraph({
|
this.ref = React.createRef();
|
||||||
container: this.ref.current,
|
}
|
||||||
width: window.screen.width * 0.66,
|
|
||||||
height: window.screen.height,
|
componentDidMount() {
|
||||||
animate: false,
|
const graph = new TreeGraph({
|
||||||
// fitCenter: true,
|
container: this.ref.current,
|
||||||
modes: {
|
width: window.screen.width * 0.66,
|
||||||
default: [
|
height: window.screen.height,
|
||||||
'drag-canvas',
|
animate: false,
|
||||||
'zoom-canvas',
|
// fitCenter: true,
|
||||||
'click-select',
|
modes: {
|
||||||
'hover',
|
default: [
|
||||||
{
|
"drag-canvas",
|
||||||
type: 'collapse-expand',
|
"zoom-canvas",
|
||||||
trigger: 'dblclick',
|
"click-select",
|
||||||
onChange: (item, collapsed) => {
|
"hover",
|
||||||
this.onSelectNode(item.getID());
|
{
|
||||||
const data = item.getModel();
|
type: "collapse-expand",
|
||||||
data.collapsed = collapsed;
|
trigger: "dblclick",
|
||||||
graph.setItemState(item, 'collapsed', data.collapsed as boolean);
|
onChange: (item, collapsed) => {
|
||||||
const icon = data.collapsed ? G6.Marker.expand : G6.Marker.collapse;
|
this.onSelectNode(item.getID());
|
||||||
const marker = item.get('group').find((ele: any) => ele.get('name') === 'collapse-icon');
|
const data = item.getModel();
|
||||||
marker.attr('symbol', icon);
|
data.collapsed = collapsed;
|
||||||
return true;
|
graph.setItemState(item, "collapsed", data.collapsed as boolean);
|
||||||
},
|
const icon = data.collapsed ? G6.Marker.expand : G6.Marker.collapse;
|
||||||
},
|
const marker = item
|
||||||
]
|
.get("group")
|
||||||
},
|
.find((ele: any) => ele.get("name") === "collapse-icon");
|
||||||
defaultEdge: {
|
marker.attr("symbol", icon);
|
||||||
type: 'cubic-horizontal',
|
return true;
|
||||||
style: {
|
},
|
||||||
stroke: '#A3B1BF'
|
},
|
||||||
}
|
],
|
||||||
},
|
},
|
||||||
defaultNode: {
|
defaultEdge: {
|
||||||
type: "TreeNode",
|
type: "cubic-horizontal",
|
||||||
},
|
style: {
|
||||||
layout: {
|
stroke: "#A3B1BF",
|
||||||
type: 'compactBox',
|
},
|
||||||
direction: 'LR',
|
},
|
||||||
getHGap: () => 50,
|
defaultNode: {
|
||||||
getWidth: (d: GraphNodeModel) => {
|
type: "TreeNode",
|
||||||
return 150;
|
},
|
||||||
},
|
layout: {
|
||||||
getHeight: (d: GraphNodeModel) => {
|
type: "compactBox",
|
||||||
if (d.size) {
|
direction: "LR",
|
||||||
return d.size[1];
|
getHGap: () => 50,
|
||||||
} else {
|
getWidth: (d: GraphNodeModel) => {
|
||||||
return 50;
|
return 150;
|
||||||
}
|
},
|
||||||
}
|
getHeight: (d: GraphNodeModel) => {
|
||||||
}
|
if (d.size) {
|
||||||
});
|
return d.size[1];
|
||||||
|
} else {
|
||||||
graph.on('node:mouseenter', (e: G6GraphEvent) => {
|
return 50;
|
||||||
const { item } = e;
|
}
|
||||||
if (item.hasState('selected')) {
|
},
|
||||||
return;
|
},
|
||||||
}
|
});
|
||||||
graph.setItemState(item, 'hover', true);
|
|
||||||
});
|
graph.on("node:mouseenter", (e: G6GraphEvent) => {
|
||||||
|
const { item } = e;
|
||||||
graph.on('node:mouseleave', (e: G6GraphEvent) => {
|
if (item.hasState("selected")) {
|
||||||
const { item } = e;
|
return;
|
||||||
if (item.hasState('selected')) {
|
}
|
||||||
return;
|
graph.setItemState(item, "hover", true);
|
||||||
}
|
});
|
||||||
graph.setItemState(item, 'hover', false);
|
|
||||||
});
|
graph.on("node:mouseleave", (e: G6GraphEvent) => {
|
||||||
|
const { item } = e;
|
||||||
graph.on('nodeselectchange', (e: G6GraphEvent) => {
|
if (item.hasState("selected")) {
|
||||||
if (e.target) {
|
return;
|
||||||
this.onSelectNode(e.target.getID());
|
}
|
||||||
} else {
|
graph.setItemState(item, "hover", false);
|
||||||
this.onSelectNode(null);
|
});
|
||||||
}
|
|
||||||
});
|
graph.on("nodeselectchange", (e: G6GraphEvent) => {
|
||||||
|
if (e.target) {
|
||||||
const clearDragDstState = () => {
|
this.onSelectNode(e.target.getID());
|
||||||
if (this.dragDstId) {
|
} else {
|
||||||
graph.setItemState(this.dragDstId, 'dragRight', false);
|
this.onSelectNode(null);
|
||||||
graph.setItemState(this.dragDstId, 'dragDown', false);
|
}
|
||||||
graph.setItemState(this.dragDstId, 'dragUp', false);
|
});
|
||||||
this.dragDstId = null;
|
|
||||||
}
|
const clearDragDstState = () => {
|
||||||
}
|
if (this.dragDstId) {
|
||||||
|
graph.setItemState(this.dragDstId, "dragRight", false);
|
||||||
const clearDragSrcState = () => {
|
graph.setItemState(this.dragDstId, "dragDown", false);
|
||||||
if (this.dragSrcId) {
|
graph.setItemState(this.dragDstId, "dragUp", false);
|
||||||
graph.setItemState(this.dragSrcId, 'dragSrc', false);
|
this.dragDstId = null;
|
||||||
this.dragSrcId = null;
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
const clearDragSrcState = () => {
|
||||||
graph.on('node:dragstart', (e: G6GraphEvent) => {
|
if (this.dragSrcId) {
|
||||||
this.dragSrcId = e.item.getID();
|
graph.setItemState(this.dragSrcId, "dragSrc", false);
|
||||||
graph.setItemState(this.dragSrcId, 'dragSrc', true);
|
this.dragSrcId = null;
|
||||||
});
|
}
|
||||||
graph.on('node:dragend', (e: G6GraphEvent) => {
|
};
|
||||||
if (this.dragSrcId) {
|
|
||||||
graph.setItemState(this.dragSrcId, 'dragSrc', false);
|
graph.on("node:dragstart", (e: G6GraphEvent) => {
|
||||||
this.dragSrcId = null;
|
this.dragSrcId = e.item.getID();
|
||||||
}
|
graph.setItemState(this.dragSrcId, "dragSrc", true);
|
||||||
});
|
});
|
||||||
|
graph.on("node:dragend", (e: G6GraphEvent) => {
|
||||||
graph.on('node:dragover', (e: G6GraphEvent) => {
|
if (this.dragSrcId) {
|
||||||
const dstNodeId = e.item.getID();
|
graph.setItemState(this.dragSrcId, "dragSrc", false);
|
||||||
if (dstNodeId == this.dragSrcId) {
|
this.dragSrcId = null;
|
||||||
return;
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if (this.dragDstId) {
|
graph.on("node:dragover", (e: G6GraphEvent) => {
|
||||||
graph.setItemState(this.dragDstId, 'dragRight', false);
|
const dstNodeId = e.item.getID();
|
||||||
graph.setItemState(this.dragDstId, 'dragDown', false);
|
if (dstNodeId == this.dragSrcId) {
|
||||||
graph.setItemState(this.dragDstId, 'dragUp', false);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const box = e.item.getBBox();
|
if (this.dragDstId) {
|
||||||
if (e.x > box.minX + box.width * 0.6) {
|
graph.setItemState(this.dragDstId, "dragRight", false);
|
||||||
graph.setItemState(dstNodeId, 'dragRight', true);
|
graph.setItemState(this.dragDstId, "dragDown", false);
|
||||||
} else if (e.y > box.minY + box.height * 0.5) {
|
graph.setItemState(this.dragDstId, "dragUp", false);
|
||||||
graph.setItemState(dstNodeId, 'dragDown', true);
|
}
|
||||||
} else {
|
|
||||||
graph.setItemState(dstNodeId, 'dragUp', true);
|
const box = e.item.getBBox();
|
||||||
}
|
if (e.x > box.minX + box.width * 0.6) {
|
||||||
this.dragDstId = dstNodeId;
|
graph.setItemState(dstNodeId, "dragRight", true);
|
||||||
});
|
} else if (e.y > box.minY + box.height * 0.5) {
|
||||||
|
graph.setItemState(dstNodeId, "dragDown", true);
|
||||||
graph.on('node:dragleave', (e: G6GraphEvent) => {
|
} else {
|
||||||
clearDragDstState();
|
graph.setItemState(dstNodeId, "dragUp", true);
|
||||||
})
|
}
|
||||||
|
this.dragDstId = dstNodeId;
|
||||||
graph.on('node:drop', (e: G6GraphEvent) => {
|
});
|
||||||
const srcNodeId = this.dragSrcId;
|
|
||||||
const dstNode = e.item;
|
graph.on("node:dragleave", (e: G6GraphEvent) => {
|
||||||
|
clearDragDstState();
|
||||||
var dragDir;
|
});
|
||||||
if (dstNode.hasState('dragRight')) {
|
|
||||||
dragDir = 'dragRight';
|
graph.on("node:drop", (e: G6GraphEvent) => {
|
||||||
} else if (dstNode.hasState('dragDown')) {
|
const srcNodeId = this.dragSrcId;
|
||||||
dragDir = 'dragDown';
|
const dstNode = e.item;
|
||||||
} else if (dstNode.hasState('dragUp')) {
|
|
||||||
dragDir = 'dragUp';
|
var dragDir;
|
||||||
}
|
if (dstNode.hasState("dragRight")) {
|
||||||
|
dragDir = "dragRight";
|
||||||
clearDragSrcState();
|
} else if (dstNode.hasState("dragDown")) {
|
||||||
clearDragDstState();
|
dragDir = "dragDown";
|
||||||
|
} else if (dstNode.hasState("dragUp")) {
|
||||||
if (!srcNodeId) {
|
dragDir = "dragUp";
|
||||||
console.log("no drag src");
|
}
|
||||||
return;
|
|
||||||
}
|
clearDragSrcState();
|
||||||
|
clearDragDstState();
|
||||||
if (srcNodeId == dstNode.getID()) {
|
|
||||||
console.log("drop same node");
|
if (!srcNodeId) {
|
||||||
return;
|
console.log("no drag src");
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
const rootData = graph.findDataById('1');
|
|
||||||
const srcData = graph.findDataById(srcNodeId);
|
if (srcNodeId == dstNode.getID()) {
|
||||||
const srcParent = Utils.findParent(rootData, srcNodeId);
|
console.log("drop same node");
|
||||||
const dstData = graph.findDataById(dstNode.getID());
|
return;
|
||||||
const dstParent = Utils.findParent(rootData, dstNode.getID());
|
}
|
||||||
if (!srcParent) {
|
|
||||||
console.log("no parent!");
|
const rootData = graph.findDataById("1");
|
||||||
return;
|
const srcData = graph.findDataById(srcNodeId);
|
||||||
}
|
const srcParent = Utils.findParent(rootData, srcNodeId);
|
||||||
|
const dstData = graph.findDataById(dstNode.getID());
|
||||||
if (Utils.findFromAllChildren(srcData, dstData.id)) {
|
const dstParent = Utils.findParent(rootData, dstNode.getID());
|
||||||
// 不能将父节点拖到自已的子孙节点
|
if (!srcParent) {
|
||||||
console.log("cannot move to child");
|
console.log("no parent!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeSrc = () => {
|
if (Utils.findFromAllChildren(srcData, dstData.id)) {
|
||||||
srcParent.children = srcParent.children.filter(e => e.id != srcData.id);
|
// 不能将父节点拖到自已的子孙节点
|
||||||
}
|
console.log("cannot move to child");
|
||||||
console.log("dstNode", dstNode);
|
return;
|
||||||
if (dragDir == 'dragRight') {
|
}
|
||||||
removeSrc();
|
|
||||||
if (!dstData.children) {
|
const removeSrc = () => {
|
||||||
dstData.children = [];
|
srcParent.children = srcParent.children.filter((e) => e.id != srcData.id);
|
||||||
}
|
};
|
||||||
dstData.children.push(srcData);
|
console.log("dstNode", dstNode);
|
||||||
} else if (dragDir == 'dragUp') {
|
if (dragDir == "dragRight") {
|
||||||
if (!dstParent) {
|
removeSrc();
|
||||||
return;
|
if (!dstData.children) {
|
||||||
}
|
dstData.children = [];
|
||||||
removeSrc();
|
}
|
||||||
const idx = dstParent.children.findIndex(e => e.id == dstData.id);
|
dstData.children.push(srcData);
|
||||||
dstParent.children.splice(idx, 0, srcData);
|
} else if (dragDir == "dragUp") {
|
||||||
} else if (dragDir == 'dragDown') {
|
if (!dstParent) {
|
||||||
if (!dstParent) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
removeSrc();
|
||||||
removeSrc();
|
const idx = dstParent.children.findIndex((e) => e.id == dstData.id);
|
||||||
const idx = dstParent.children.findIndex(e => e.id == dstData.id);
|
dstParent.children.splice(idx, 0, srcData);
|
||||||
dstParent.children.splice(idx + 1, 0, srcData);
|
} else if (dragDir == "dragDown") {
|
||||||
} else {
|
if (!dstParent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
removeSrc();
|
||||||
// console.log("cur data", graph.findDataById('1'));
|
const idx = dstParent.children.findIndex((e) => e.id == dstData.id);
|
||||||
this.changeWithoutAnim();
|
dstParent.children.splice(idx + 1, 0, srcData);
|
||||||
});
|
} else {
|
||||||
|
return;
|
||||||
const settings = Utils.getRemoteSettings();
|
}
|
||||||
|
|
||||||
const str = fs.readFileSync(this.props.filepath, 'utf8');
|
// console.log("cur data", graph.findDataById('1'));
|
||||||
let tree: BehaviorTreeModel = JSON.parse(str);
|
this.changeWithoutAnim();
|
||||||
const data = Utils.createTreeData(tree.root, settings);
|
});
|
||||||
this.autoId = Utils.refreshNodeId(data);
|
|
||||||
graph.data(data);
|
const settings = Utils.getRemoteSettings();
|
||||||
graph.render();
|
|
||||||
graph.fitCenter();
|
const str = fs.readFileSync(this.props.filepath, "utf8");
|
||||||
graph.set('animate', true);
|
let tree: BehaviorTreeModel = JSON.parse(str);
|
||||||
|
const data = Utils.createTreeData(tree.root, settings);
|
||||||
this.graph = graph;
|
this.autoId = Utils.refreshNodeId(data);
|
||||||
|
graph.data(data);
|
||||||
this.setState({ treeModel: tree, settings });
|
graph.render();
|
||||||
}
|
graph.fitCenter();
|
||||||
|
graph.set("animate", true);
|
||||||
onSelectNode(curNodeId: string | null) {
|
|
||||||
const graph = this.graph;
|
this.graph = graph;
|
||||||
|
|
||||||
if (this.state.curNodeId) {
|
this.setState({ treeModel: tree, settings });
|
||||||
graph.setItemState(this.state.curNodeId, 'selected', false);
|
}
|
||||||
}
|
|
||||||
|
onSelectNode(curNodeId: string | null) {
|
||||||
this.setState({ curNodeId });
|
const graph = this.graph;
|
||||||
if (this.state.curNodeId) {
|
|
||||||
graph.setItemState(this.state.curNodeId, 'selected', true);
|
if (this.state.curNodeId) {
|
||||||
}
|
graph.setItemState(this.state.curNodeId, "selected", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
createNode(name: string) {
|
this.setState({ curNodeId });
|
||||||
console.log("editor create node", name);
|
if (this.state.curNodeId) {
|
||||||
const { curNodeId, settings } = this.state;
|
graph.setItemState(this.state.curNodeId, "selected", true);
|
||||||
if (!curNodeId) {
|
}
|
||||||
message.warn("未选中节点");
|
}
|
||||||
return;
|
|
||||||
}
|
createNode(name: string) {
|
||||||
const curNodeData = this.graph.findDataById(curNodeId);
|
console.log("editor create node", name);
|
||||||
const newNodeData: BehaviorNodeModel = {
|
const { curNodeId, settings } = this.state;
|
||||||
id: this.autoId++,
|
if (!curNodeId) {
|
||||||
name: name,
|
message.warn("未选中节点");
|
||||||
}
|
return;
|
||||||
if (!curNodeData.children) {
|
}
|
||||||
curNodeData.children = [];
|
const curNodeData = this.graph.findDataById(curNodeId);
|
||||||
}
|
const newNodeData: BehaviorNodeModel = {
|
||||||
curNodeData.children.push(Utils.createTreeData(newNodeData, settings));
|
id: this.autoId++,
|
||||||
this.changeWithoutAnim();
|
name: name,
|
||||||
}
|
};
|
||||||
|
if (!curNodeData.children) {
|
||||||
deleteNode() {
|
curNodeData.children = [];
|
||||||
console.log("editor delete node");
|
}
|
||||||
const { curNodeId } = this.state;
|
curNodeData.children.push(Utils.createTreeData(newNodeData, settings));
|
||||||
if (!curNodeId) {
|
this.changeWithoutAnim();
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
deleteNode() {
|
||||||
if(curNodeId == '1') {
|
console.log("editor delete node");
|
||||||
message.warn("根节点不能删除!");
|
const { curNodeId } = this.state;
|
||||||
return;
|
if (!curNodeId) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
this.onSelectNode(null);
|
|
||||||
const rootData = this.graph.findDataById('1');
|
if (curNodeId == "1") {
|
||||||
const parentData = Utils.findParent(rootData, curNodeId);
|
message.warn("根节点不能删除!");
|
||||||
parentData.children = parentData.children.filter(e => e.id != curNodeId);
|
return;
|
||||||
this.changeWithoutAnim();
|
}
|
||||||
}
|
|
||||||
|
this.onSelectNode(null);
|
||||||
updateNode(id: string) {
|
const rootData = this.graph.findDataById("1");
|
||||||
this.graph.changeData();
|
const parentData = Utils.findParent(rootData, curNodeId);
|
||||||
this.graph.layout();
|
parentData.children = parentData.children.filter((e) => e.id != curNodeId);
|
||||||
}
|
this.changeWithoutAnim();
|
||||||
|
}
|
||||||
changeWithoutAnim() {
|
|
||||||
this.graph.set('animate', false);
|
updateNode(id: string) {
|
||||||
this.graph.changeData();
|
this.graph.changeData();
|
||||||
this.graph.layout();
|
this.graph.layout();
|
||||||
this.graph.set('animate', true);
|
}
|
||||||
}
|
|
||||||
|
changeWithoutAnim() {
|
||||||
save() {
|
this.graph.set("animate", false);
|
||||||
const { filepath } = this.props;
|
this.graph.changeData();
|
||||||
const root = Utils.createFileData(this.graph.findDataById('1') as GraphNodeModel);
|
this.graph.layout();
|
||||||
fs.writeFileSync(filepath, JSON.stringify({
|
this.graph.set("animate", true);
|
||||||
name: path.basename(filepath).slice(0, -5),
|
}
|
||||||
root,
|
|
||||||
desc: root.desc,
|
save() {
|
||||||
}));
|
const { filepath } = this.props;
|
||||||
}
|
const root = Utils.createFileData(this.graph.findDataById("1") as GraphNodeModel);
|
||||||
|
fs.writeFileSync(
|
||||||
copyNode() {
|
filepath,
|
||||||
console.log("editor copy node");
|
JSON.stringify({
|
||||||
const { curNodeId } = this.state;
|
name: path.basename(filepath).slice(0, -5),
|
||||||
if (!curNodeId) {
|
root,
|
||||||
return;
|
desc: root.desc,
|
||||||
}
|
})
|
||||||
const data = this.graph.findDataById(curNodeId);
|
);
|
||||||
clipboard.writeText(JSON.stringify(data, null, 2));
|
}
|
||||||
}
|
|
||||||
|
copyNode() {
|
||||||
pasteNode() {
|
console.log("editor copy node");
|
||||||
const { curNodeId } = this.state;
|
const { curNodeId } = this.state;
|
||||||
if (!curNodeId) {
|
if (!curNodeId) {
|
||||||
message.warn("未选中节点");
|
return;
|
||||||
return;
|
}
|
||||||
}
|
const data = this.graph.findDataById(curNodeId);
|
||||||
const curNodeData = this.graph.findDataById(curNodeId);
|
clipboard.writeText(JSON.stringify(data, null, 2));
|
||||||
try {
|
}
|
||||||
const str = clipboard.readText();
|
|
||||||
if(!str || str == '') {
|
pasteNode() {
|
||||||
return;
|
const { curNodeId } = this.state;
|
||||||
}
|
if (!curNodeId) {
|
||||||
const data = JSON.parse(str);
|
message.warn("未选中节点");
|
||||||
if(!curNodeData.children) {
|
return;
|
||||||
curNodeData.children = [];
|
}
|
||||||
}
|
const curNodeData = this.graph.findDataById(curNodeId);
|
||||||
curNodeData.children.push(data);
|
try {
|
||||||
this.autoId = Utils.refreshNodeId(this.graph.findDataById('1') as GraphNodeModel);
|
const str = clipboard.readText();
|
||||||
this.changeWithoutAnim();
|
if (!str || str == "") {
|
||||||
} catch (error) {
|
return;
|
||||||
message.error("粘贴数据有误");
|
}
|
||||||
}
|
const data = JSON.parse(str);
|
||||||
}
|
if (!curNodeData.children) {
|
||||||
|
curNodeData.children = [];
|
||||||
render() {
|
}
|
||||||
const { curNodeId, treeModel, settings } = this.state;
|
curNodeData.children.push(data);
|
||||||
var curNode: any;
|
this.autoId = Utils.refreshNodeId(this.graph.findDataById("1") as GraphNodeModel);
|
||||||
if (curNodeId) {
|
this.changeWithoutAnim();
|
||||||
curNode = this.graph.findDataById(curNodeId);
|
} catch (error) {
|
||||||
}
|
message.error("粘贴数据有误");
|
||||||
|
}
|
||||||
return (
|
}
|
||||||
<div className="editor">
|
|
||||||
<Row className="editorBd">
|
render() {
|
||||||
<Col
|
const { curNodeId, treeModel, settings } = this.state;
|
||||||
span={18}
|
var curNode: any;
|
||||||
className="editorContent"
|
if (curNodeId) {
|
||||||
ref={this.ref}
|
curNode = this.graph.findDataById(curNodeId);
|
||||||
/>
|
}
|
||||||
<Col span={6} className="editorSidebar">
|
|
||||||
{
|
return (
|
||||||
curNode ?
|
<div className="editor">
|
||||||
<NodePanel
|
<Row className="editorBd">
|
||||||
model={curNode}
|
<Col span={18} className="editorContent" ref={this.ref} />
|
||||||
settings={settings}
|
<Col span={6} className="editorSidebar">
|
||||||
updateNode={(id, forceUpdate) => {
|
{curNode ? (
|
||||||
if (forceUpdate) {
|
<NodePanel
|
||||||
const data: any = this.graph.findDataById(id);
|
model={curNode}
|
||||||
data.conf = settings.getNodeConf(data.name);
|
settings={settings}
|
||||||
data.size = Utils.calcTreeNodeSize(data);
|
updateNode={(id, forceUpdate) => {
|
||||||
this.changeWithoutAnim();
|
if (forceUpdate) {
|
||||||
}
|
const data: any = this.graph.findDataById(id);
|
||||||
const item = this.graph.findById(id);
|
data.conf = settings.getNodeConf(data.name);
|
||||||
item.draw();
|
data.size = Utils.calcTreeNodeSize(data);
|
||||||
}}
|
this.changeWithoutAnim();
|
||||||
/>
|
}
|
||||||
:
|
const item = this.graph.findById(id);
|
||||||
<TreePanel
|
item.draw();
|
||||||
model={treeModel}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
) : (
|
||||||
</Col>
|
<TreePanel model={treeModel} />
|
||||||
</Row>
|
)}
|
||||||
</div>
|
</Col>
|
||||||
);
|
</Row>
|
||||||
}
|
</div>
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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() {
|
componentDidMount() {
|
||||||
ipcRenderer.on(MainEventType.CREATE_TREE, (event: any, path: string) => {
|
ipcRenderer.on(MainEventType.CREATE_TREE, (event: any, path: string) => {
|
||||||
console.log("on Create tree", path);
|
console.log("on Create tree", path);
|
||||||
this.props.onOpenTree(path);
|
this.props.onOpenTree(path);
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
});
|
});
|
||||||
|
|
||||||
var workdir = this.props.workdir;
|
var workdir = this.props.workdir;
|
||||||
if (workdir == '') {
|
if (workdir == "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({ treeList: this.getTreeList(workdir) });
|
this.setState({ treeList: this.getTreeList(workdir) });
|
||||||
}
|
}
|
||||||
|
|
||||||
getTreeList(workdir: string) {
|
getTreeList(workdir: string) {
|
||||||
if (workdir == '' || !fs.existsSync(workdir)) {
|
if (workdir == "" || !fs.existsSync(workdir)) {
|
||||||
console.log("workdir not exist", workdir);
|
console.log("workdir not exist", workdir);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const files = fs.readdirSync(workdir);
|
const files = fs.readdirSync(workdir);
|
||||||
const list: TreeFile[] = [];
|
const list: TreeFile[] = [];
|
||||||
files.forEach((filename) => {
|
files.forEach((filename) => {
|
||||||
const stat = fs.statSync(path.join(workdir, filename))
|
const stat = fs.statSync(path.join(workdir, filename));
|
||||||
if (stat.isFile()) {
|
if (stat.isFile()) {
|
||||||
var name = filename.slice(0, -5)
|
var name = filename.slice(0, -5);
|
||||||
list.push({
|
list.push({
|
||||||
name,
|
name,
|
||||||
path: workdir + '/' + name + '.json'
|
path: workdir + "/" + name + ".json",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
console.log("render Properties");
|
console.log("render Properties");
|
||||||
const { onOpenTree, workdir } = this.props;
|
const { onOpenTree, workdir } = this.props;
|
||||||
const treeList = this.getTreeList(workdir);
|
const treeList = this.getTreeList(workdir);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Search
|
<Search placeholder="Search" onChange={() => {}} />
|
||||||
placeholder="Search"
|
<Menu mode="inline">
|
||||||
onChange={() => {
|
{treeList.map((tree) => (
|
||||||
|
<Menu.Item key={tree.name} onClick={() => onOpenTree(tree.path)}>
|
||||||
}}
|
{tree.name}
|
||||||
/>
|
</Menu.Item>
|
||||||
<Menu
|
))}
|
||||||
mode="inline"
|
</Menu>
|
||||||
>
|
</div>
|
||||||
{treeList.map((tree) => (
|
);
|
||||||
<Menu.Item
|
}
|
||||||
key={tree.name}
|
}
|
||||||
onClick={() => onOpenTree(tree.path)}
|
|
||||||
>
|
|
||||||
{tree.name}
|
|
||||||
</Menu.Item>
|
|
||||||
))}
|
|
||||||
</Menu>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+275
-277
@@ -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(
|
||||||
G6.registerNode(
|
"TreeNode",
|
||||||
'TreeNode',
|
{
|
||||||
{
|
options: {
|
||||||
options: {
|
type: "rect",
|
||||||
type: 'rect',
|
labelCfg: {
|
||||||
labelCfg: {
|
style: {
|
||||||
style: {
|
fill: "blue",
|
||||||
fill: 'blue',
|
fontSize: 10,
|
||||||
fontSize: 10
|
},
|
||||||
}
|
},
|
||||||
},
|
style: {
|
||||||
style: {
|
fill: "white",
|
||||||
fill: "white",
|
},
|
||||||
},
|
stateStyles: {
|
||||||
stateStyles: {
|
selected: {
|
||||||
selected: {
|
"main-box": {
|
||||||
'main-box': {
|
stroke: "yellow",
|
||||||
stroke: 'yellow',
|
lineWidth: 5,
|
||||||
lineWidth: 5,
|
},
|
||||||
},
|
},
|
||||||
},
|
hover: {
|
||||||
hover: {
|
stroke: "white",
|
||||||
stroke: 'white',
|
lineWidth: 3,
|
||||||
lineWidth: 3,
|
},
|
||||||
},
|
dragSrc: {
|
||||||
dragSrc: {
|
fill: "gray",
|
||||||
fill: 'gray',
|
},
|
||||||
},
|
dragRight: {
|
||||||
dragRight: {
|
"drag-right": {
|
||||||
'drag-right': {
|
fillOpacity: 0.6,
|
||||||
fillOpacity: 0.6,
|
},
|
||||||
}
|
},
|
||||||
},
|
dragUp: {
|
||||||
dragUp: {
|
"drag-up": {
|
||||||
'drag-up': {
|
fillOpacity: 0.6,
|
||||||
fillOpacity: 0.6,
|
},
|
||||||
}
|
},
|
||||||
},
|
dragDown: {
|
||||||
dragDown: {
|
"drag-down": {
|
||||||
'drag-down': {
|
fillOpacity: 0.6,
|
||||||
fillOpacity: 0.6,
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
},
|
draw(cfg, group) {
|
||||||
draw(cfg, group) {
|
const nodeConf = settings.getNodeConf(cfg.name as string);
|
||||||
const nodeConf = settings.getNodeConf(cfg.name as string);
|
const classify = nodeConf.type || "Other";
|
||||||
const classify = nodeConf.type || 'Other';
|
const color = NODE_COLORS[classify] || "Other";
|
||||||
const color = NODE_COLORS[classify] || 'Other';
|
var size = cfg.size ? (cfg.size as number[]) : [150, 40];
|
||||||
var size = cfg.size ? cfg.size as number[] : [150, 40];
|
const w = size[0];
|
||||||
const w = size[0];
|
const h = size[1];
|
||||||
const h = size[1];
|
const x0 = -w / 2;
|
||||||
const x0 = -w / 2;
|
const y0 = -h / 2;
|
||||||
const y0 = -h / 2;
|
const r = 2;
|
||||||
const r = 2;
|
const shape = group.addShape("rect", {
|
||||||
const shape = group.addShape('rect', {
|
attrs: {
|
||||||
attrs: {
|
x: x0,
|
||||||
x: x0,
|
y: y0,
|
||||||
y: y0,
|
width: w,
|
||||||
width: w,
|
height: h,
|
||||||
height: h,
|
stroke: color,
|
||||||
stroke: color,
|
lineWidth: 2,
|
||||||
lineWidth: 2,
|
fill: "white",
|
||||||
fill: 'white',
|
radius: r,
|
||||||
radius: r,
|
},
|
||||||
},
|
name: "main-box",
|
||||||
name: 'main-box',
|
draggable: true,
|
||||||
draggable: true,
|
});
|
||||||
});
|
|
||||||
|
// name bg
|
||||||
// name bg
|
group.addShape("rect", {
|
||||||
group.addShape('rect', {
|
attrs: {
|
||||||
attrs: {
|
x: x0 + 1.5,
|
||||||
x: x0 + 1.5,
|
y: y0 + 1.5,
|
||||||
y: y0 + 1.5,
|
width: w - 3,
|
||||||
width: w - 3,
|
height: 16,
|
||||||
height: 16,
|
fill: color,
|
||||||
fill: color,
|
// radius: r,
|
||||||
// radius: r,
|
},
|
||||||
},
|
name: "name-bg",
|
||||||
name: 'name-bg',
|
draggable: true,
|
||||||
draggable: true,
|
});
|
||||||
});
|
|
||||||
|
// id text
|
||||||
// id text
|
group.addShape("text", {
|
||||||
group.addShape('text', {
|
attrs: {
|
||||||
attrs: {
|
textBaseline: "top",
|
||||||
textBaseline: 'top',
|
x: x0 - 3,
|
||||||
x: x0 - 3,
|
y: -8,
|
||||||
y: -8,
|
fontSize: 20,
|
||||||
fontSize: 20,
|
lineHeight: 20,
|
||||||
lineHeight: 20,
|
text: cfg.id,
|
||||||
text: cfg.id,
|
textAlign: "right",
|
||||||
textAlign: 'right',
|
fill: "white",
|
||||||
fill: 'white',
|
stroke: "black",
|
||||||
stroke: 'black',
|
lineWidth: 2,
|
||||||
lineWidth: 2,
|
},
|
||||||
},
|
name: "id-text",
|
||||||
name: 'id-text',
|
});
|
||||||
});
|
|
||||||
|
// icon
|
||||||
// icon
|
var img = `./static/icons/${classify}.svg`;
|
||||||
var img = `./static/icons/${classify}.svg`;
|
group.addShape("image", {
|
||||||
group.addShape('image', {
|
attrs: {
|
||||||
attrs: {
|
x: x0 + 3,
|
||||||
x: x0 + 3,
|
y: y0 + 2,
|
||||||
y: y0 + 2,
|
height: 14,
|
||||||
height: 14,
|
width: 14,
|
||||||
width: 14,
|
img,
|
||||||
img,
|
},
|
||||||
},
|
name: "node-icon",
|
||||||
name: 'node-icon',
|
});
|
||||||
});
|
|
||||||
|
// name text
|
||||||
// name text
|
group.addShape("text", {
|
||||||
group.addShape('text', {
|
attrs: {
|
||||||
attrs: {
|
textBaseline: "top",
|
||||||
textBaseline: 'top',
|
x: x0 + 18,
|
||||||
x: x0 + 18,
|
y: y0 + 4,
|
||||||
y: y0 + 4,
|
fontWeight: 800,
|
||||||
fontWeight: 800,
|
lineHeight: 20,
|
||||||
lineHeight: 20,
|
text: cfg.name,
|
||||||
text: cfg.name,
|
fill: "black",
|
||||||
fill: 'black',
|
},
|
||||||
},
|
name: "name-text",
|
||||||
name: 'name-text',
|
});
|
||||||
});
|
|
||||||
|
var x = x0 + 2;
|
||||||
var x = x0 + 2;
|
var y = y0 + 23;
|
||||||
var y = y0 + 23;
|
// desc text
|
||||||
// desc text
|
const desc = cfg.desc || nodeConf.desc;
|
||||||
const desc = cfg.desc || nodeConf.desc;
|
if (desc) {
|
||||||
if (desc) {
|
group.addShape("text", {
|
||||||
group.addShape('text', {
|
attrs: {
|
||||||
attrs: {
|
textBaseline: "top",
|
||||||
textBaseline: 'top',
|
x,
|
||||||
x,
|
y,
|
||||||
y,
|
lineHeight: 20,
|
||||||
lineHeight: 20,
|
fontWeight: 800,
|
||||||
fontWeight: 800,
|
text: `备注:${desc}`,
|
||||||
text: `备注:${desc}`,
|
fill: "black",
|
||||||
fill: 'black',
|
},
|
||||||
},
|
name: "desc-text",
|
||||||
name: 'desc-text',
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
const args: any = cfg.args;
|
||||||
const args: any = cfg.args;
|
if (nodeConf.args && args && Object.keys(args).length > 0) {
|
||||||
if (nodeConf.args && args && Object.keys(args).length > 0) {
|
y += 20;
|
||||||
y += 20;
|
group.addShape("text", {
|
||||||
group.addShape('text', {
|
attrs: {
|
||||||
attrs: {
|
textBaseline: "top",
|
||||||
textBaseline: 'top',
|
x,
|
||||||
x,
|
y,
|
||||||
y,
|
lineHeight: 20,
|
||||||
lineHeight: 20,
|
text: `参数:${JSON.stringify(args)}`,
|
||||||
text: `参数:${JSON.stringify(args)}`,
|
fill: "black",
|
||||||
fill: 'black',
|
},
|
||||||
},
|
name: "args-text",
|
||||||
name: 'args-text',
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
const input: [] = cfg.input ? (cfg.input as []) : [];
|
||||||
const input: [] = cfg.input ? cfg.input as [] : [];
|
if (nodeConf.input && input.length > 0) {
|
||||||
if (nodeConf.input && input.length > 0) {
|
y += 20;
|
||||||
y += 20;
|
group.addShape("text", {
|
||||||
group.addShape('text', {
|
attrs: {
|
||||||
attrs: {
|
textBaseline: "top",
|
||||||
textBaseline: 'top',
|
x,
|
||||||
x,
|
y,
|
||||||
y,
|
lineHeight: 20,
|
||||||
lineHeight: 20,
|
text: `输入:${JSON.stringify(input)}`,
|
||||||
text: `输入:${JSON.stringify(input)}`,
|
fill: "black",
|
||||||
fill: 'black',
|
},
|
||||||
},
|
name: "input-text",
|
||||||
name: 'input-text',
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
const output: [] = cfg.output ? (cfg.output as []) : [];
|
||||||
const output: [] = cfg.output ? cfg.output as [] : [];
|
if (nodeConf.output && output.length > 0) {
|
||||||
if (nodeConf.output && output.length > 0) {
|
y += 20;
|
||||||
y += 20;
|
group.addShape("text", {
|
||||||
group.addShape('text', {
|
attrs: {
|
||||||
attrs: {
|
textBaseline: "top",
|
||||||
textBaseline: 'top',
|
x,
|
||||||
x,
|
y,
|
||||||
y,
|
lineHeight: 20,
|
||||||
lineHeight: 20,
|
text: `输出:${JSON.stringify(output)}`,
|
||||||
text: `输出:${JSON.stringify(output)}`,
|
fill: "black",
|
||||||
fill: 'black',
|
},
|
||||||
},
|
name: "output-text",
|
||||||
name: 'output-text',
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
if (Array.isArray(cfg.children) && cfg.children.length > 0) {
|
||||||
if (Array.isArray(cfg.children) && cfg.children.length > 0) {
|
group.addShape("marker", {
|
||||||
group.addShape('marker', {
|
attrs: {
|
||||||
attrs: {
|
x: w / 2,
|
||||||
x: w / 2,
|
y: 0,
|
||||||
y: 0,
|
r: 6,
|
||||||
r: 6,
|
symbol: G6.Marker.collapse,
|
||||||
symbol: G6.Marker.collapse,
|
stroke: "#666",
|
||||||
stroke: '#666',
|
lineWidth: 1,
|
||||||
lineWidth: 1,
|
fill: "#fff",
|
||||||
fill: '#fff',
|
},
|
||||||
},
|
name: "collapse-icon",
|
||||||
name: 'collapse-icon',
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
group.addShape("rect", {
|
||||||
|
name: "drag-up",
|
||||||
group.addShape('rect', {
|
attrs: {
|
||||||
name: 'drag-up',
|
x: -w / 2,
|
||||||
attrs: {
|
y: -h / 2,
|
||||||
x: -w / 2,
|
width: w,
|
||||||
y: -h / 2,
|
height: h / 2,
|
||||||
width: w,
|
fill: "blue",
|
||||||
height: h / 2,
|
fillOpacity: 0,
|
||||||
fill: 'blue',
|
},
|
||||||
fillOpacity: 0,
|
draggable: true,
|
||||||
},
|
// visible: false,
|
||||||
draggable: true,
|
});
|
||||||
// visible: false,
|
|
||||||
});
|
group.addShape("rect", {
|
||||||
|
name: "drag-down",
|
||||||
group.addShape('rect', {
|
attrs: {
|
||||||
name: 'drag-down',
|
x: -w / 2,
|
||||||
attrs: {
|
y: 0,
|
||||||
x: -w / 2,
|
width: w,
|
||||||
y: 0,
|
height: h / 2,
|
||||||
width: w,
|
fill: "blue",
|
||||||
height: h / 2,
|
fillOpacity: 0,
|
||||||
fill: 'blue',
|
},
|
||||||
fillOpacity: 0,
|
draggable: true,
|
||||||
},
|
// visible: false,
|
||||||
draggable: true,
|
});
|
||||||
// visible: false,
|
|
||||||
});
|
group.addShape("rect", {
|
||||||
|
name: "drag-right",
|
||||||
group.addShape('rect', {
|
attrs: {
|
||||||
name: 'drag-right',
|
x: w * 0.1,
|
||||||
attrs: {
|
y: -h / 2,
|
||||||
x: w * 0.1,
|
width: w * 0.4,
|
||||||
y: -h / 2,
|
height: h,
|
||||||
width: w * 0.4,
|
fill: "blue",
|
||||||
height: h,
|
fillOpacity: 0,
|
||||||
fill: 'blue',
|
},
|
||||||
fillOpacity: 0,
|
draggable: true,
|
||||||
},
|
// visible: false,
|
||||||
draggable: true,
|
});
|
||||||
// visible: false,
|
return shape;
|
||||||
});
|
},
|
||||||
return shape;
|
},
|
||||||
}
|
"single-node"
|
||||||
},
|
);
|
||||||
'single-node',
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
+130
-136
@@ -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() {
|
||||||
|
ipcRenderer.on(MainEventType.CREATE_NODE, (event: any, name: any) => {
|
||||||
componentDidMount() {
|
const editor = this.getCurEditor();
|
||||||
ipcRenderer.on(MainEventType.CREATE_NODE, (event: any, name: any) => {
|
editor?.createNode(name);
|
||||||
const editor = this.getCurEditor();
|
});
|
||||||
editor?.createNode(name);
|
|
||||||
});
|
ipcRenderer.on(MainEventType.DELETE_NODE, () => {
|
||||||
|
const editor = this.getCurEditor();
|
||||||
ipcRenderer.on(MainEventType.DELETE_NODE, () => {
|
editor?.deleteNode();
|
||||||
const editor = this.getCurEditor();
|
});
|
||||||
editor?.deleteNode();
|
|
||||||
});
|
ipcRenderer.on(MainEventType.COPY_NODE, () => {
|
||||||
|
const editor = this.getCurEditor();
|
||||||
ipcRenderer.on(MainEventType.COPY_NODE, () => {
|
editor?.copyNode();
|
||||||
const editor = this.getCurEditor();
|
});
|
||||||
editor?.copyNode();
|
|
||||||
});
|
ipcRenderer.on(MainEventType.PASTE_NODE, () => {
|
||||||
|
const editor = this.getCurEditor();
|
||||||
ipcRenderer.on(MainEventType.PASTE_NODE, () => {
|
editor?.pasteNode();
|
||||||
const editor = this.getCurEditor();
|
});
|
||||||
editor?.pasteNode();
|
|
||||||
});
|
ipcRenderer.on(MainEventType.SAVE, (event: any) => {
|
||||||
|
const { curPath } = this.state;
|
||||||
ipcRenderer.on(MainEventType.SAVE, (event: any) => {
|
if (!curPath) {
|
||||||
const { curPath } = this.state;
|
return;
|
||||||
if (!curPath) {
|
}
|
||||||
return;
|
const editor = this.editors[curPath];
|
||||||
}
|
editor.save();
|
||||||
const editor = this.editors[curPath];
|
message.success("已保存");
|
||||||
editor.save();
|
});
|
||||||
message.success("已保存");
|
|
||||||
});
|
ipcRenderer.on(MainEventType.SAVE_ALL, (event: any) => {
|
||||||
|
for (let k in this.editors) {
|
||||||
ipcRenderer.on(MainEventType.SAVE_ALL, (event: any) => {
|
let editor = this.editors[k];
|
||||||
for (let k in this.editors) {
|
editor.save();
|
||||||
let editor = this.editors[k];
|
}
|
||||||
editor.save();
|
message.success("已保存所有行为树");
|
||||||
}
|
});
|
||||||
message.success("已保存所有行为树");
|
}
|
||||||
});
|
|
||||||
}
|
getCurEditor() {
|
||||||
|
const { curPath } = this.state;
|
||||||
getCurEditor() {
|
if (!curPath) {
|
||||||
const { curPath } = this.state;
|
return;
|
||||||
if (!curPath) {
|
}
|
||||||
return;
|
return this.editors[curPath];
|
||||||
}
|
}
|
||||||
return this.editors[curPath];
|
|
||||||
}
|
openFile(path: string) {
|
||||||
|
console.log("on open file", path);
|
||||||
openFile(path: string) {
|
if (this.state.filepaths.indexOf(path) < 0) {
|
||||||
console.log("on open file", path);
|
const filepaths = this.state.filepaths;
|
||||||
if (this.state.filepaths.indexOf(path) < 0) {
|
filepaths.push(path);
|
||||||
const filepaths = this.state.filepaths;
|
this.setState({ filepaths, curPath: path });
|
||||||
filepaths.push(path);
|
} else {
|
||||||
this.setState({ filepaths, curPath: path });
|
this.setState({ curPath: path });
|
||||||
} else {
|
}
|
||||||
this.setState({ curPath: path });
|
}
|
||||||
}
|
|
||||||
}
|
render() {
|
||||||
|
console.log("render tabs");
|
||||||
render() {
|
const { filepaths, curPath } = this.state;
|
||||||
console.log("render tabs");
|
if (!curPath) {
|
||||||
const { filepaths, curPath } = this.state;
|
return <div />;
|
||||||
if (!curPath) {
|
}
|
||||||
return (<div />);
|
return (
|
||||||
}
|
<Tabs
|
||||||
return (
|
hideAdd
|
||||||
<Tabs
|
className="tabs"
|
||||||
hideAdd
|
type="editable-card"
|
||||||
className="tabs"
|
defaultActiveKey={curPath}
|
||||||
type="editable-card"
|
activeKey={curPath}
|
||||||
defaultActiveKey={curPath}
|
onChange={(activeKey) => {
|
||||||
activeKey={curPath}
|
this.setState({ curPath: activeKey });
|
||||||
onChange={activeKey => {
|
}}
|
||||||
this.setState({ curPath: activeKey })
|
onEdit={(targetKey, action) => {
|
||||||
}}
|
if (action == "remove") {
|
||||||
onEdit={(targetKey, action) => {
|
const idx = filepaths.indexOf(targetKey as string);
|
||||||
if (action == 'remove') {
|
if (idx < 0) {
|
||||||
const idx = filepaths.indexOf(targetKey as string);
|
return;
|
||||||
if (idx < 0) {
|
}
|
||||||
return;
|
filepaths.splice(idx, 1);
|
||||||
}
|
const nextPath = filepaths[idx - 1];
|
||||||
filepaths.splice(idx, 1);
|
this.setState({ filepaths, curPath: nextPath });
|
||||||
const nextPath = filepaths[idx - 1];
|
}
|
||||||
this.setState({ filepaths, curPath: nextPath });
|
}}
|
||||||
}
|
>
|
||||||
}}
|
{filepaths.map((filepath) => {
|
||||||
>
|
return (
|
||||||
{
|
<TabPane tab={path.basename(filepath)} key={filepath}>
|
||||||
filepaths.map(filepath => {
|
<Editor
|
||||||
return (
|
filepath={filepath}
|
||||||
<TabPane tab={path.basename(filepath)} key={filepath}>
|
ref={(ref) => {
|
||||||
<Editor
|
this.editors[filepath] = ref;
|
||||||
filepath={filepath}
|
}}
|
||||||
ref={
|
/>
|
||||||
ref => {
|
</TabPane>
|
||||||
this.editors[filepath] = ref;
|
);
|
||||||
}
|
})}
|
||||||
}
|
</Tabs>
|
||||||
/>
|
);
|
||||||
</TabPane>
|
}
|
||||||
);
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
</Tabs>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
+90
-95
@@ -1,95 +1,90 @@
|
|||||||
|
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";
|
|
||||||
|
const { Header, Sider, Content, Footer } = Layout;
|
||||||
const { Header, Sider, Content, Footer } = Layout;
|
const { TabPane } = Tabs;
|
||||||
const { TabPane } = Tabs;
|
|
||||||
|
interface MainState {
|
||||||
|
workdir: string;
|
||||||
interface MainState {
|
workspace: string;
|
||||||
workdir: string;
|
}
|
||||||
workspace: string;
|
|
||||||
}
|
export default class Main extends Component {
|
||||||
|
state: MainState = {
|
||||||
export default class Main extends Component {
|
workdir: "",
|
||||||
state: MainState = {
|
workspace: "",
|
||||||
workdir: '',
|
};
|
||||||
workspace: '',
|
|
||||||
}
|
settings: Settings;
|
||||||
|
tabs: TreeTabs;
|
||||||
settings: Settings;
|
|
||||||
tabs: TreeTabs;
|
componentWillMount() {
|
||||||
|
this.updateSettings();
|
||||||
componentWillMount() {
|
RegisterNode(this.settings);
|
||||||
this.updateSettings();
|
}
|
||||||
RegisterNode(this.settings);
|
|
||||||
}
|
componentDidMount() {
|
||||||
|
ipcRenderer.on(MainEventType.OPEN_FILE, (event: any, path: any) => {
|
||||||
componentDidMount() {
|
this.setState({ curPath: path });
|
||||||
ipcRenderer.on(MainEventType.OPEN_FILE, (event: any, path: any) => {
|
});
|
||||||
this.setState({ curPath: path });
|
|
||||||
});
|
ipcRenderer.on(MainEventType.OPEN_DIR, (event: any, workdir: any, workspace: string) => {
|
||||||
|
console.log("on open workspace", workspace);
|
||||||
ipcRenderer.on(MainEventType.OPEN_DIR, (event: any, workdir: any, workspace: string) => {
|
document.title = workspace;
|
||||||
console.log("on open workspace", workspace);
|
this.setState({ workdir, workspace });
|
||||||
document.title = workspace;
|
});
|
||||||
this.setState({ workdir, workspace });
|
|
||||||
});
|
console.log("workdir", this.settings.curWorkspace.getWorkdir());
|
||||||
|
this.setState({
|
||||||
console.log("workdir", this.settings.curWorkspace.getWorkdir());
|
workdir: this.settings.curWorkspace.getWorkdir(),
|
||||||
this.setState({
|
workspace: this.settings.curWorkspace.getFilepath(),
|
||||||
workdir: this.settings.curWorkspace.getWorkdir(),
|
});
|
||||||
workspace: this.settings.curWorkspace.getFilepath(),
|
}
|
||||||
});
|
|
||||||
}
|
updateSettings() {
|
||||||
|
this.settings = Utils.getRemoteSettings();
|
||||||
updateSettings() {
|
}
|
||||||
this.settings = Utils.getRemoteSettings();
|
|
||||||
}
|
render() {
|
||||||
|
console.log("render main");
|
||||||
render() {
|
const { workdir, workspace } = this.state;
|
||||||
console.log("render main");
|
document.title = `行为树编辑器 - ${workspace}`;
|
||||||
const { workdir, workspace } = this.state;
|
return (
|
||||||
document.title = `行为树编辑器 - ${workspace}`;
|
<Layout className="body">
|
||||||
return (
|
<Sider className="sider" width={250}>
|
||||||
<Layout className="body">
|
<Properties
|
||||||
<Sider className="sider" width={250}>
|
workdir={workdir}
|
||||||
<Properties
|
onOpenTree={(path) => {
|
||||||
workdir={workdir}
|
this.tabs.openFile(path);
|
||||||
onOpenTree={(path) => {
|
}}
|
||||||
this.tabs.openFile(path);
|
/>
|
||||||
}}
|
</Sider>
|
||||||
/>
|
<Content className="content">
|
||||||
</Sider>
|
<TreeTabs
|
||||||
<Content className="content">
|
ref={(ref) => {
|
||||||
<TreeTabs
|
this.tabs = ref;
|
||||||
ref={ref => {
|
}}
|
||||||
this.tabs = ref;
|
/>
|
||||||
}}
|
</Content>
|
||||||
/>
|
</Layout>
|
||||||
</Content>
|
);
|
||||||
</Layout>
|
}
|
||||||
)
|
}
|
||||||
}
|
|
||||||
};
|
ReactDOM.render(<Main />, document.getElementById("root") as HTMLElement);
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<Main />,
|
|
||||||
document.getElementById('root') as HTMLElement
|
|
||||||
);
|
|
||||||
Reference in new issue
Block a user