From 00e79d641ee8da17e65dad4c8f205a2c5c9b0655 Mon Sep 17 00:00:00 2001 From: duanhongbo <6860300@qq.com> Date: Thu, 12 Nov 2020 17:24:08 +0800 Subject: [PATCH] style: format codes --- common/BehaviorTreeModel.ts | 88 +-- common/MainEventType.ts | 24 +- common/Utils.ts | 261 +++++---- main-process/AppMenu.ts | 750 ++++++++++++------------- main-process/MainProcess.tsx | 142 +++-- main-process/Settings.ts | 179 +++--- main-process/Workspace.ts | 276 +++++----- render-process/Editor/NodePanel.tsx | 540 +++++++++--------- render-process/Editor/TreePanel.tsx | 61 +- render-process/Editor/index.tsx | 826 ++++++++++++++-------------- render-process/Properties.tsx | 188 +++---- render-process/RegisterNode.ts | 552 +++++++++---------- render-process/TreeTabs.tsx | 266 +++++---- render-process/index.tsx | 185 +++---- 14 files changed, 2152 insertions(+), 2186 deletions(-) diff --git a/common/BehaviorTreeModel.ts b/common/BehaviorTreeModel.ts index ca108a7..e8d2cd4 100644 --- a/common/BehaviorTreeModel.ts +++ b/common/BehaviorTreeModel.ts @@ -1,44 +1,44 @@ -import { TreeGraphData } from "@antv/g6/lib/types"; - -export interface ArgsDefType { - name: string, - type: string, - desc: string, -} -export interface BehaviorNodeTypeModel { - name: string; - type?: string; - desc?: string; - args?: ArgsDefType[]; - input?: string[]; - output?: string[]; - doc?: string; -} - -export interface BehaviorNodeModel { - id: number; - name: string; - desc?: string; - args?: {[key: string]: any}; - input?: string[]; - output?: string[]; - children?: BehaviorNodeModel[]; - debug?: boolean; -} - -export interface BehaviorTreeModel { - name: string; - root: BehaviorNodeModel; -} - -export interface GraphNodeModel extends TreeGraphData { - name: string; - desc?: string; - args?: {[key: string]: any}; - input?: string[]; - output?: string[]; - children?: GraphNodeModel[]; - conf: BehaviorNodeTypeModel; - - size?: number[]; -} \ No newline at end of file +import { TreeGraphData } from "@antv/g6/lib/types"; + +export interface ArgsDefType { + name: string; + type: string; + desc: string; +} +export interface BehaviorNodeTypeModel { + name: string; + type?: string; + desc?: string; + args?: ArgsDefType[]; + input?: string[]; + output?: string[]; + doc?: string; +} + +export interface BehaviorNodeModel { + id: number; + name: string; + desc?: string; + args?: { [key: string]: any }; + input?: string[]; + output?: string[]; + children?: BehaviorNodeModel[]; + debug?: boolean; +} + +export interface BehaviorTreeModel { + name: string; + root: BehaviorNodeModel; +} + +export interface GraphNodeModel extends TreeGraphData { + name: string; + desc?: string; + args?: { [key: string]: any }; + input?: string[]; + output?: string[]; + children?: GraphNodeModel[]; + conf: BehaviorNodeTypeModel; + + size?: number[]; +} diff --git a/common/MainEventType.ts b/common/MainEventType.ts index 6fde49e..14d9ab3 100644 --- a/common/MainEventType.ts +++ b/common/MainEventType.ts @@ -1,12 +1,12 @@ -export default { - OPEN_FILE: "OPEN_FILE", - OPEN_DIR: "OPEN_DIR", - OPEN_WORKSPACE: "OPEN_WORKSPACE", - CREATE_TREE: 'CREATE_TREE', - CREATE_NODE: 'CREATE_NODE', - DELETE_NODE: 'DELETE_NODE', - COPY_NODE: 'COPY_NODE', - PASTE_NODE: 'PASTE_NODE', - SAVE: 'SAVE', - SAVE_ALL: 'SAVE_ALL', -} \ No newline at end of file +export default { + OPEN_FILE: "OPEN_FILE", + OPEN_DIR: "OPEN_DIR", + OPEN_WORKSPACE: "OPEN_WORKSPACE", + CREATE_TREE: "CREATE_TREE", + CREATE_NODE: "CREATE_NODE", + DELETE_NODE: "DELETE_NODE", + COPY_NODE: "COPY_NODE", + PASTE_NODE: "PASTE_NODE", + SAVE: "SAVE", + SAVE_ALL: "SAVE_ALL", +}; diff --git a/common/Utils.ts b/common/Utils.ts index 4edc9d2..633261d 100644 --- a/common/Utils.ts +++ b/common/Utils.ts @@ -1,131 +1,130 @@ -import { TreeGraphData } from "@antv/g6/lib/types" -import { BehaviorNodeModel, BehaviorTreeModel, GraphNodeModel } from "./BehaviorTreeModel"; -import Settings from "../main-process/Settings"; -import { remote } from "electron"; -import * as path from 'path'; - -export const cloneNodeData = (nodeData: GraphNodeModel) => { - const newData: GraphNodeModel = { - id: nodeData.id + "new", - name: nodeData.name, - conf: nodeData.conf, - } - return newData; -} - -export const refreshNodeId = (nodeData: GraphNodeModel, id?: number) => { - if (!id) { - id = 1; - } - nodeData.id = (id++).toString(); - if (nodeData.children) { - nodeData.children.forEach(child => { - id = refreshNodeId(child, id); - }) - } - return id; -} - -export const calcTreeNodeSize = (treeNode: GraphNodeModel) => { - var height = 40; - const updateHeight = (obj: any) => { - if (Array.isArray(obj) || (obj && Object.keys(obj).length > 0)) { - height += 25; - } - } - updateHeight(treeNode.conf.args); - updateHeight(treeNode.conf.input); - updateHeight(treeNode.conf.output); - return [200, height]; -} - -export const createTreeData = (bNode: BehaviorNodeModel, settings: Settings) => { - const treeData: GraphNodeModel = { - id: bNode.id.toString(), - name: bNode.name, - desc: bNode.desc, - args: bNode.args, - input: bNode.input, - output: bNode.output, - conf: settings.getNodeConf(bNode.name), - } - treeData.size = calcTreeNodeSize(treeData); - if (bNode.children) { - treeData.children = []; - bNode.children.forEach(child => { - treeData.children.push(createTreeData(child, settings)); - }) - } - calcTreeNodeSize(treeData); - return treeData; -} - -export const createFileData = (gNode: GraphNodeModel) => { - const nodeData: BehaviorNodeModel = { - id: Number(gNode.id), - name: gNode.name, - desc: gNode.desc || undefined, - args: gNode.args || undefined, - input: gNode.input || undefined, - output: gNode.output || undefined, - } - if (gNode.children) { - nodeData.children = []; - gNode.children.forEach(child => { - nodeData.children.push(createFileData(child)); - }) - } - return nodeData; -} - -export const findParent = (node: TreeGraphData, id: string): TreeGraphData | null => { - if (node.children) { - for (let child of node.children) { - if (child.id == id) { - return node; - } else { - let parent = findParent(child, id); - if (parent) { - return parent; - } - } - - } - } - return null; -} - -export const findFromAllChildren = (node: TreeGraphData, id: string): TreeGraphData | null => { - if (node.id == id) { - return node; - } - if (node.children) { - for (let child of node.children) { - let found = findFromAllChildren(child, id); - if (found) { - return found; - } - } - } - return null; -} - -export const getRemoteSettings = () => { - return remote.getGlobal("settings") as Settings; -} - -export const fileName2treeName = (filename: string) => { - return path.basename(filename).slice(0, -5); -} - -export const createNewTree = (filename: string) => { - const tree: BehaviorTreeModel = { - name: fileName2treeName(filename), - root: { - id: 1, - name: 'Sequence', - desc: '新建行为树' - } - } - return tree; -} \ No newline at end of file +import { TreeGraphData } from "@antv/g6/lib/types"; +import { BehaviorNodeModel, BehaviorTreeModel, GraphNodeModel } from "./BehaviorTreeModel"; +import Settings from "../main-process/Settings"; +import { remote } from "electron"; +import * as path from "path"; + +export const cloneNodeData = (nodeData: GraphNodeModel) => { + const newData: GraphNodeModel = { + id: nodeData.id + "new", + name: nodeData.name, + conf: nodeData.conf, + }; + return newData; +}; + +export const refreshNodeId = (nodeData: GraphNodeModel, id?: number) => { + if (!id) { + id = 1; + } + nodeData.id = (id++).toString(); + if (nodeData.children) { + nodeData.children.forEach((child) => { + id = refreshNodeId(child, id); + }); + } + return id; +}; + +export const calcTreeNodeSize = (treeNode: GraphNodeModel) => { + var height = 40; + const updateHeight = (obj: any) => { + if (Array.isArray(obj) || (obj && Object.keys(obj).length > 0)) { + height += 25; + } + }; + updateHeight(treeNode.conf.args); + updateHeight(treeNode.conf.input); + updateHeight(treeNode.conf.output); + return [200, height]; +}; + +export const createTreeData = (bNode: BehaviorNodeModel, settings: Settings) => { + const treeData: GraphNodeModel = { + id: bNode.id.toString(), + name: bNode.name, + desc: bNode.desc, + args: bNode.args, + input: bNode.input, + output: bNode.output, + conf: settings.getNodeConf(bNode.name), + }; + treeData.size = calcTreeNodeSize(treeData); + if (bNode.children) { + treeData.children = []; + bNode.children.forEach((child) => { + treeData.children.push(createTreeData(child, settings)); + }); + } + calcTreeNodeSize(treeData); + return treeData; +}; + +export const createFileData = (gNode: GraphNodeModel) => { + const nodeData: BehaviorNodeModel = { + id: Number(gNode.id), + name: gNode.name, + desc: gNode.desc || undefined, + args: gNode.args || undefined, + input: gNode.input || undefined, + output: gNode.output || undefined, + }; + if (gNode.children) { + nodeData.children = []; + gNode.children.forEach((child) => { + nodeData.children.push(createFileData(child)); + }); + } + return nodeData; +}; + +export const findParent = (node: TreeGraphData, id: string): TreeGraphData | null => { + if (node.children) { + for (let child of node.children) { + if (child.id == id) { + return node; + } else { + let parent = findParent(child, id); + if (parent) { + return parent; + } + } + } + } + return null; +}; + +export const findFromAllChildren = (node: TreeGraphData, id: string): TreeGraphData | null => { + if (node.id == id) { + return node; + } + if (node.children) { + for (let child of node.children) { + let found = findFromAllChildren(child, id); + if (found) { + return found; + } + } + } + return null; +}; + +export const getRemoteSettings = () => { + return remote.getGlobal("settings") as Settings; +}; + +export const fileName2treeName = (filename: string) => { + return path.basename(filename).slice(0, -5); +}; + +export const createNewTree = (filename: string) => { + const tree: BehaviorTreeModel = { + name: fileName2treeName(filename), + root: { + id: 1, + name: "Sequence", + desc: "新建行为树", + }, + }; + return tree; +}; diff --git a/main-process/AppMenu.ts b/main-process/AppMenu.ts index 10315de..16760a8 100644 --- a/main-process/AppMenu.ts +++ b/main-process/AppMenu.ts @@ -1,375 +1,375 @@ -import { - Menu, - app, - dialog, - BrowserWindow, - MenuItem, - WebContents, - MenuItemConstructorOptions, -} from "electron"; -import * as fs from "fs"; -import * as Utils from "../common/Utils"; -import MainEventType from "../common/MainEventType"; -import { MainProcess } from "./MainProcess"; -import Settings from "./Settings"; - -export default class AppMenu { - private mainProcess: MainProcess; - private mainWindow: BrowserWindow; - private webContents: WebContents; - private settings: Settings; - - constructor(mainProcess: MainProcess) { - this.mainProcess = mainProcess; - this.mainWindow = mainProcess.mainWindow; - this.webContents = mainProcess.mainWindow.webContents; - this.settings = mainProcess.settings; - } - - createMenu() { - const menu: Menu = new Menu(); - menu.append(this.createWorkspaceMenu()); - menu.append(this.createFileMenu()); - menu.append(this.createNodeMenu()); - menu.append(this.createToolsMenu()); - return menu; - } - - private createFileMenu() { - const fileItems: MenuItemConstructorOptions[] = []; - for (let path of this.settings.recentFiles) { - fileItems.push({ - label: path, - click: () => { - console.log("open recent file", path); - }, - }); - } - - return new MenuItem({ - label: "行为树", - submenu: [ - { - label: "新建", - accelerator: "ctrl+n", - click: () => { - (async () => { - const res = await dialog.showSaveDialog({ - properties: ["showOverwriteConfirmation"], - filters: [{ name: "Json", extensions: ["json"] }], - }); - if (!res.canceled) { - const path = res.filePath; - fs.writeFileSync( - path, - JSON.stringify(Utils.createNewTree(path), null, 2) - ); - this.webContents.send(MainEventType.CREATE_TREE, path); - } - })(); - }, - }, - { - label: "打开文件", - accelerator: "Ctrl+O", - click: () => { - (async () => { - const res = await dialog.showOpenDialog({ - properties: ["openFile"], - filters: [{ name: "Json", extensions: ["json"] }], - }); - if (res.filePaths.length > 0) { - const path = res.filePaths[0]; - if (this.settings.recentFiles.indexOf(path) < 0) { - this.settings.recentFiles.unshift(path); - this.settings.save(); - this.mainProcess.rebuildMenu(); - } - this.webContents.send(MainEventType.OPEN_FILE, path); - } - })(); - }, - }, - { type: "separator" }, - { - label: "最近打开", - submenu: fileItems, - }, - - { type: "separator" }, - { - label: "保存", - accelerator: "ctrl+s", - click: () => { - this.webContents.send(MainEventType.SAVE); - }, - }, - { - label: "全部保存", - accelerator: "ctrl+shift+s", - click: () => { - this.webContents.send(MainEventType.SAVE_ALL); - }, - }, - { - label: "合并导出Json", - click: () => { - (async () => { - const res = await dialog.showSaveDialog({ - properties: ["showOverwriteConfirmation"], - filters: [{ name: "Json", extensions: ["json"] }], - }); - if (res.filePath) { - const curWorkspace = this.settings.curWorkspace; - curWorkspace.writeAllTrees(res.filePath,(err)=>{ - const msg = err?err:"导出成功"; - dialog.showMessageBox({ - type:"info", - buttons:["ok"], - message:msg - }) - }); - } - })(); - }, - }, - { type: "separator" }, - { - label: "新建节点", - accelerator: "insert", - click: () => { - this.webContents.send(MainEventType.CREATE_NODE, "unknow"); - }, - }, - { - label: "删除节点", - accelerator: "delete", - click: () => { - this.webContents.send(MainEventType.DELETE_NODE); - }, - }, - { type: "separator" }, - { - label: "复制节点", - accelerator: "ctrl+c", - role: "copy", - click: () => { - this.webContents.send(MainEventType.COPY_NODE); - }, - }, - { - label: "粘贴节点", - accelerator: "ctrl+v", - click: () => { - this.webContents.send(MainEventType.PASTE_NODE); - }, - }, - ], - }); - } - - private createWorkspaceMenu() { - const openWorkspace = (path: string) => { - const curWorkspace = this.settings.curWorkspace; - curWorkspace.setFilepath(path); - curWorkspace.load(); - this.settings.pushRecentWorkspace(path); - this.mainProcess.rebuildMenu(); - this.webContents.send( - MainEventType.OPEN_DIR, - curWorkspace.getWorkdir(), - curWorkspace.getFilepath() - ); - }; - const saveToNewPath = () => { - (async () => { - const res = await dialog.showSaveDialog({ - properties: ["showOverwriteConfirmation"], - filters: [{ name: "Json", extensions: ["json"] }], - }); - if (!res.canceled) { - this.settings.curWorkspace.setFilepath(res.filePath); - this.settings.curWorkspace.save(); - openWorkspace(res.filePath); - } - })(); - }; - const recentItems: MenuItemConstructorOptions[] = []; - for (let path of this.settings.recentWorkspaces) { - recentItems.push({ - label: path, - click: () => { - console.log("open recent workspace", path); - openWorkspace(path); - }, - }); - } - return new MenuItem({ - label: "工作区", - submenu: [ - { - label: "打开", - click: () => { - (async () => { - const res = await dialog.showOpenDialog({ - properties: ["openFile"], - filters: [{ name: "Json", extensions: ["json"] }], - }); - if (res.filePaths.length > 0) { - openWorkspace(res.filePaths[0]); - } - })(); - }, - }, - { - label: "保存", - click: () => { - if (this.settings.curWorkspace.getFilepath()) { - this.settings.curWorkspace.save(); - } else { - saveToNewPath(); - } - }, - }, - { - label: "另存为", - click: () => { - saveToNewPath(); - }, - }, - { - label: "最近打开", - submenu: recentItems, - }, - { type: "separator" }, - { - label: "打开目录", - accelerator: "Ctrl+Shift+O", - click: () => { - if (!this.settings.curWorkspace.getNodeConfPath()) { - dialog.showErrorBox("警告", "请先指定节点定义配置!"); - return; - } - (async () => { - const res = await dialog.showOpenDialog({ - properties: ["openDirectory"], - }); - if (res.filePaths.length > 0) { - const curWorkspace = this.settings.curWorkspace; - const path = res.filePaths[0]; - curWorkspace.setWorkdir(path); - curWorkspace.save(); - this.webContents.send( - MainEventType.OPEN_DIR, - path, - curWorkspace.getFilepath() - ); - } - })(); - }, - }, - { - label: "节点定义", - submenu: [ - { - label: "选择文件", - click: () => { - (async () => { - const res = await dialog.showOpenDialog({ - properties: ["openFile"], - filters: [{ name: "Json", extensions: ["json"] }], - }); - if (res.filePaths.length > 0) { - const nodeConfigPath = res.filePaths[0]; - this.settings.curWorkspace.setNodeConfPath(nodeConfigPath); - this.mainProcess.rebuildMenu(); - } - })(); - }, - }, - { type: "separator" }, - { label: this.settings.nodeConfPath }, - ], - }, - { type: "separator" }, - { - label: "关闭", - click: () => { - app.quit(); - }, - }, - ], - }); - } - - private createToolsMenu() { - return new MenuItem({ - label: "开发工具", - submenu: [ - { - label: "打开控制台", - accelerator: "Ctrl+Shift+I", - click: (_, browserWindow) => { - browserWindow.webContents.toggleDevTools(); - }, - }, - { - label: "重载", - accelerator: "Ctrl+R", - click: (_, browserWindow) => { - browserWindow.reload(); - }, - }, - ], - }); - } - - private createNodeMenu() { - const classifyItems: MenuItemConstructorOptions[] = []; - const map: { [key: string]: MenuItemConstructorOptions } = {}; - for (let t of this.settings.nodeClassify) { - let item: MenuItemConstructorOptions = { - id: t.classify, - label: `${t.classify}(${t.desc})`, - submenu: [], - }; - classifyItems.push(item); - map[t.classify] = item; - } - const other: MenuItemConstructorOptions = { - id: "other", - label: "其它", - submenu: [], - }; - var hasOther = false; - - for (let node of this.settings.nodeConfig) { - const item: MenuItemConstructorOptions = { - label: `${node.name}(${node.desc})`, - click: () => { - console.log("create node", node.name); - this.webContents.send(MainEventType.CREATE_NODE, node.name); - }, - }; - let typeItem = map[node.type]; - if (!typeItem) { - typeItem = other; - hasOther = true; - } - if (typeItem.submenu instanceof Menu) { - console.log("typeItem.submenu error", typeItem); - } else { - typeItem.submenu.push(item); - } - } - - if (hasOther) { - classifyItems.push(other); - } - - return new MenuItem({ - label: "新建节点", - submenu: classifyItems, - }); - } -} +import { + Menu, + app, + dialog, + BrowserWindow, + MenuItem, + WebContents, + MenuItemConstructorOptions, +} from "electron"; +import * as fs from "fs"; +import * as Utils from "../common/Utils"; +import MainEventType from "../common/MainEventType"; +import { MainProcess } from "./MainProcess"; +import Settings from "./Settings"; + +export default class AppMenu { + private mainProcess: MainProcess; + private mainWindow: BrowserWindow; + private webContents: WebContents; + private settings: Settings; + + constructor(mainProcess: MainProcess) { + this.mainProcess = mainProcess; + this.mainWindow = mainProcess.mainWindow; + this.webContents = mainProcess.mainWindow.webContents; + this.settings = mainProcess.settings; + } + + createMenu() { + const menu: Menu = new Menu(); + menu.append(this.createWorkspaceMenu()); + menu.append(this.createFileMenu()); + menu.append(this.createNodeMenu()); + menu.append(this.createToolsMenu()); + return menu; + } + + private createFileMenu() { + const fileItems: MenuItemConstructorOptions[] = []; + for (let path of this.settings.recentFiles) { + fileItems.push({ + label: path, + click: () => { + console.log("open recent file", path); + }, + }); + } + + return new MenuItem({ + label: "行为树", + submenu: [ + { + label: "新建", + accelerator: "ctrl+n", + click: () => { + (async () => { + const res = await dialog.showSaveDialog({ + properties: ["showOverwriteConfirmation"], + filters: [{ name: "Json", extensions: ["json"] }], + }); + if (!res.canceled) { + const path = res.filePath; + fs.writeFileSync( + path, + JSON.stringify(Utils.createNewTree(path), null, 2) + ); + this.webContents.send(MainEventType.CREATE_TREE, path); + } + })(); + }, + }, + { + label: "打开文件", + accelerator: "Ctrl+O", + click: () => { + (async () => { + const res = await dialog.showOpenDialog({ + properties: ["openFile"], + filters: [{ name: "Json", extensions: ["json"] }], + }); + if (res.filePaths.length > 0) { + const path = res.filePaths[0]; + if (this.settings.recentFiles.indexOf(path) < 0) { + this.settings.recentFiles.unshift(path); + this.settings.save(); + this.mainProcess.rebuildMenu(); + } + this.webContents.send(MainEventType.OPEN_FILE, path); + } + })(); + }, + }, + { type: "separator" }, + { + label: "最近打开", + submenu: fileItems, + }, + + { type: "separator" }, + { + label: "保存", + accelerator: "ctrl+s", + click: () => { + this.webContents.send(MainEventType.SAVE); + }, + }, + { + label: "全部保存", + accelerator: "ctrl+shift+s", + click: () => { + this.webContents.send(MainEventType.SAVE_ALL); + }, + }, + { + label: "合并导出Json", + click: () => { + (async () => { + const res = await dialog.showSaveDialog({ + properties: ["showOverwriteConfirmation"], + filters: [{ name: "Json", extensions: ["json"] }], + }); + if (res.filePath) { + const curWorkspace = this.settings.curWorkspace; + curWorkspace.writeAllTrees(res.filePath, (err) => { + const msg = err ? err : "导出成功"; + dialog.showMessageBox({ + type: "info", + buttons: ["ok"], + message: msg, + }); + }); + } + })(); + }, + }, + { type: "separator" }, + { + label: "新建节点", + accelerator: "insert", + click: () => { + this.webContents.send(MainEventType.CREATE_NODE, "unknow"); + }, + }, + { + label: "删除节点", + accelerator: "delete", + click: () => { + this.webContents.send(MainEventType.DELETE_NODE); + }, + }, + { type: "separator" }, + { + label: "复制节点", + accelerator: "ctrl+c", + role: "copy", + click: () => { + this.webContents.send(MainEventType.COPY_NODE); + }, + }, + { + label: "粘贴节点", + accelerator: "ctrl+v", + click: () => { + this.webContents.send(MainEventType.PASTE_NODE); + }, + }, + ], + }); + } + + private createWorkspaceMenu() { + const openWorkspace = (path: string) => { + const curWorkspace = this.settings.curWorkspace; + curWorkspace.setFilepath(path); + curWorkspace.load(); + this.settings.pushRecentWorkspace(path); + this.mainProcess.rebuildMenu(); + this.webContents.send( + MainEventType.OPEN_DIR, + curWorkspace.getWorkdir(), + curWorkspace.getFilepath() + ); + }; + const saveToNewPath = () => { + (async () => { + const res = await dialog.showSaveDialog({ + properties: ["showOverwriteConfirmation"], + filters: [{ name: "Json", extensions: ["json"] }], + }); + if (!res.canceled) { + this.settings.curWorkspace.setFilepath(res.filePath); + this.settings.curWorkspace.save(); + openWorkspace(res.filePath); + } + })(); + }; + const recentItems: MenuItemConstructorOptions[] = []; + for (let path of this.settings.recentWorkspaces) { + recentItems.push({ + label: path, + click: () => { + console.log("open recent workspace", path); + openWorkspace(path); + }, + }); + } + return new MenuItem({ + label: "工作区", + submenu: [ + { + label: "打开", + click: () => { + (async () => { + const res = await dialog.showOpenDialog({ + properties: ["openFile"], + filters: [{ name: "Json", extensions: ["json"] }], + }); + if (res.filePaths.length > 0) { + openWorkspace(res.filePaths[0]); + } + })(); + }, + }, + { + label: "保存", + click: () => { + if (this.settings.curWorkspace.getFilepath()) { + this.settings.curWorkspace.save(); + } else { + saveToNewPath(); + } + }, + }, + { + label: "另存为", + click: () => { + saveToNewPath(); + }, + }, + { + label: "最近打开", + submenu: recentItems, + }, + { type: "separator" }, + { + label: "打开目录", + accelerator: "Ctrl+Shift+O", + click: () => { + if (!this.settings.curWorkspace.getNodeConfPath()) { + dialog.showErrorBox("警告", "请先指定节点定义配置!"); + return; + } + (async () => { + const res = await dialog.showOpenDialog({ + properties: ["openDirectory"], + }); + if (res.filePaths.length > 0) { + const curWorkspace = this.settings.curWorkspace; + const path = res.filePaths[0]; + curWorkspace.setWorkdir(path); + curWorkspace.save(); + this.webContents.send( + MainEventType.OPEN_DIR, + path, + curWorkspace.getFilepath() + ); + } + })(); + }, + }, + { + label: "节点定义", + submenu: [ + { + label: "选择文件", + click: () => { + (async () => { + const res = await dialog.showOpenDialog({ + properties: ["openFile"], + filters: [{ name: "Json", extensions: ["json"] }], + }); + if (res.filePaths.length > 0) { + const nodeConfigPath = res.filePaths[0]; + this.settings.curWorkspace.setNodeConfPath(nodeConfigPath); + this.mainProcess.rebuildMenu(); + } + })(); + }, + }, + { type: "separator" }, + { label: this.settings.nodeConfPath }, + ], + }, + { type: "separator" }, + { + label: "关闭", + click: () => { + app.quit(); + }, + }, + ], + }); + } + + private createToolsMenu() { + return new MenuItem({ + label: "开发工具", + submenu: [ + { + label: "打开控制台", + accelerator: "Ctrl+Shift+I", + click: (_, browserWindow) => { + browserWindow.webContents.toggleDevTools(); + }, + }, + { + label: "重载", + accelerator: "Ctrl+R", + click: (_, browserWindow) => { + browserWindow.reload(); + }, + }, + ], + }); + } + + private createNodeMenu() { + const classifyItems: MenuItemConstructorOptions[] = []; + const map: { [key: string]: MenuItemConstructorOptions } = {}; + for (let t of this.settings.nodeClassify) { + let item: MenuItemConstructorOptions = { + id: t.classify, + label: `${t.classify}(${t.desc})`, + submenu: [], + }; + classifyItems.push(item); + map[t.classify] = item; + } + const other: MenuItemConstructorOptions = { + id: "other", + label: "其它", + submenu: [], + }; + var hasOther = false; + + for (let node of this.settings.nodeConfig) { + const item: MenuItemConstructorOptions = { + label: `${node.name}(${node.desc})`, + click: () => { + console.log("create node", node.name); + this.webContents.send(MainEventType.CREATE_NODE, node.name); + }, + }; + let typeItem = map[node.type]; + if (!typeItem) { + typeItem = other; + hasOther = true; + } + if (typeItem.submenu instanceof Menu) { + console.log("typeItem.submenu error", typeItem); + } else { + typeItem.submenu.push(item); + } + } + + if (hasOther) { + classifyItems.push(other); + } + + return new MenuItem({ + label: "新建节点", + submenu: classifyItems, + }); + } +} diff --git a/main-process/MainProcess.tsx b/main-process/MainProcess.tsx index 6cd3383..575c45a 100644 --- a/main-process/MainProcess.tsx +++ b/main-process/MainProcess.tsx @@ -1,72 +1,70 @@ -import { app, BrowserWindow, Menu, MenuItem, dialog, nativeTheme } from 'electron'; -import AppMenu from './AppMenu' -import Settings from './Settings'; -import MainEventType from '../common/MainEventType'; - -const electronLocalshortcut = require('electron-localshortcut'); - -// 一些暴露给render-process的全局变量 -export interface Global { - settings: Settings -} -declare var global: Global; - -export class MainProcess { - mainWindow: BrowserWindow; - appMenu: AppMenu; - settings: Settings; - constructor() { - nativeTheme.themeSource = 'dark'; - app.on('ready', () => { - this.createWindow(); - electronLocalshortcut.register(this.mainWindow, 'Ctrl+C', () => { - this.mainWindow.webContents.send(MainEventType.COPY_NODE); - }); - electronLocalshortcut.register(this.mainWindow, 'Ctrl+V', () => { - this.mainWindow.webContents.send(MainEventType.PASTE_NODE); - }); - }) - app.on('window-all-closed', () => { - if (process.platform !== 'darwin') { - app.quit() - } - }) - app.on('activate', () => { - if (this.mainWindow === null) { - this.createWindow() - } - }); - } - - createWindow() { - this.settings = new Settings(); - global.settings = this.settings; - - this.mainWindow = new BrowserWindow({ - width: 1280, - height: 800, - webPreferences: { - nodeIntegration: true, - enableRemoteModule: true, - } - // fullscreenable:false, - // maximizable:false - }); - this.mainWindow.maximize(); - // mainWindow.webContents.openDevTools(); - this.mainWindow.loadFile('index.html'); - this.mainWindow.on('closed', function () { - this.mainWindow = null - }); - this.appMenu = new AppMenu(this); - this.rebuildMenu(); - } - - rebuildMenu() { - Menu.setApplicationMenu(this.appMenu.createMenu()); - } -} - - -export default new MainProcess(); - +import { app, BrowserWindow, Menu, MenuItem, dialog, nativeTheme } from "electron"; +import AppMenu from "./AppMenu"; +import Settings from "./Settings"; +import MainEventType from "../common/MainEventType"; + +const electronLocalshortcut = require("electron-localshortcut"); + +// 一些暴露给render-process的全局变量 +export interface Global { + settings: Settings; +} +declare var global: Global; + +export class MainProcess { + mainWindow: BrowserWindow; + appMenu: AppMenu; + settings: Settings; + constructor() { + nativeTheme.themeSource = "dark"; + app.on("ready", () => { + this.createWindow(); + electronLocalshortcut.register(this.mainWindow, "Ctrl+C", () => { + this.mainWindow.webContents.send(MainEventType.COPY_NODE); + }); + electronLocalshortcut.register(this.mainWindow, "Ctrl+V", () => { + this.mainWindow.webContents.send(MainEventType.PASTE_NODE); + }); + }); + app.on("window-all-closed", () => { + if (process.platform !== "darwin") { + app.quit(); + } + }); + app.on("activate", () => { + if (this.mainWindow === null) { + this.createWindow(); + } + }); + } + + createWindow() { + this.settings = new Settings(); + global.settings = this.settings; + + this.mainWindow = new BrowserWindow({ + width: 1280, + height: 800, + webPreferences: { + nodeIntegration: true, + enableRemoteModule: true, + }, + // fullscreenable:false, + // maximizable:false + }); + this.mainWindow.maximize(); + // mainWindow.webContents.openDevTools(); + this.mainWindow.loadFile("index.html"); + this.mainWindow.on("closed", function () { + this.mainWindow = null; + }); + this.appMenu = new AppMenu(this); + this.rebuildMenu(); + } + + rebuildMenu() { + Menu.setApplicationMenu(this.appMenu.createMenu()); + } +} + +export default new MainProcess(); diff --git a/main-process/Settings.ts b/main-process/Settings.ts index 20fa839..c3ddabd 100644 --- a/main-process/Settings.ts +++ b/main-process/Settings.ts @@ -1,90 +1,89 @@ -import * as fs from 'fs'; -import { BehaviorNodeTypeModel } from '../common/BehaviorTreeModel'; -import Workspace from './Workspace'; - -export interface BehaviorNodeClassify { - classify: string; - desc?: string; -} - -export interface SettingsModel { - recentWorkspaces?: string[]; - recentFiles?: string[]; - nodeClassify?: BehaviorNodeClassify[]; -} - -const settingPath = 'settings.json'; -const sampleNodeClassify: BehaviorNodeClassify[] = [ - { classify: "Composite", desc: "复合节点" }, - { classify: "Decorator", desc: "修饰节点" }, - { classify: "Condition", desc: "条件节点" }, - { classify: "Action", desc: "行为节点" }, -] - - -export default class Settings { - private settings: SettingsModel; - curWorkspace?: Workspace; - - constructor() { - this.load(); - } - - get nodeConfPath() { - return this.curWorkspace.getNodeConfPath(); - } - get recentWorkspaces() { - return this.settings.recentWorkspaces; - } - get recentFiles() { - return this.settings.recentFiles; - } - get nodeConfig() { - return this.curWorkspace.nodeConf; - } - get nodeClassify() { - return this.settings.nodeClassify; - } - - set(config: SettingsModel) { - this.settings = { - ...this.settings, - ...config - }; - this.save(); - } - - load() { - if (fs.existsSync(settingPath)) { - const str = fs.readFileSync(settingPath, 'utf8'); - this.settings = JSON.parse(str); - } else { - this.settings = { - recentWorkspaces: ['sample/workspace.json'], - recentFiles: [], - nodeClassify: sampleNodeClassify, - }; - this.save(); - } - this.curWorkspace = new Workspace(this.settings.recentWorkspaces[0]); - this.curWorkspace.load(); - } - - save() { - fs.writeFileSync(settingPath, JSON.stringify(this.settings, null, 2)); - } - - getNodeConf(name: string) { - return this.curWorkspace.getNodeConf(name); - } - - pushRecentWorkspace(path: string) { - if (this.settings.recentWorkspaces.indexOf(path) < 0) { - this.settings.recentWorkspaces.unshift(path); - console.log("push", path); - } else { - console.log("already in path", this.settings.recentWorkspaces, path); - } - this.save(); - } -} \ No newline at end of file +import * as fs from "fs"; +import { BehaviorNodeTypeModel } from "../common/BehaviorTreeModel"; +import Workspace from "./Workspace"; + +export interface BehaviorNodeClassify { + classify: string; + desc?: string; +} + +export interface SettingsModel { + recentWorkspaces?: string[]; + recentFiles?: string[]; + nodeClassify?: BehaviorNodeClassify[]; +} + +const settingPath = "settings.json"; +const sampleNodeClassify: BehaviorNodeClassify[] = [ + { classify: "Composite", desc: "复合节点" }, + { classify: "Decorator", desc: "修饰节点" }, + { classify: "Condition", desc: "条件节点" }, + { classify: "Action", desc: "行为节点" }, +]; + +export default class Settings { + private settings: SettingsModel; + curWorkspace?: Workspace; + + constructor() { + this.load(); + } + + get nodeConfPath() { + return this.curWorkspace.getNodeConfPath(); + } + get recentWorkspaces() { + return this.settings.recentWorkspaces; + } + get recentFiles() { + return this.settings.recentFiles; + } + get nodeConfig() { + return this.curWorkspace.nodeConf; + } + get nodeClassify() { + return this.settings.nodeClassify; + } + + set(config: SettingsModel) { + this.settings = { + ...this.settings, + ...config, + }; + this.save(); + } + + load() { + if (fs.existsSync(settingPath)) { + const str = fs.readFileSync(settingPath, "utf8"); + this.settings = JSON.parse(str); + } else { + this.settings = { + recentWorkspaces: ["sample/workspace.json"], + recentFiles: [], + nodeClassify: sampleNodeClassify, + }; + this.save(); + } + this.curWorkspace = new Workspace(this.settings.recentWorkspaces[0]); + this.curWorkspace.load(); + } + + save() { + fs.writeFileSync(settingPath, JSON.stringify(this.settings, null, 2)); + } + + getNodeConf(name: string) { + return this.curWorkspace.getNodeConf(name); + } + + pushRecentWorkspace(path: string) { + if (this.settings.recentWorkspaces.indexOf(path) < 0) { + this.settings.recentWorkspaces.unshift(path); + console.log("push", path); + } else { + console.log("already in path", this.settings.recentWorkspaces, path); + } + this.save(); + } +} diff --git a/main-process/Workspace.ts b/main-process/Workspace.ts index 6c08822..65b2dcf 100644 --- a/main-process/Workspace.ts +++ b/main-process/Workspace.ts @@ -1,137 +1,139 @@ -import * as fs from "fs"; -import { BehaviorNodeTypeModel } from "../common/BehaviorTreeModel"; -import * as glob from "glob"; -import * as path from "path"; - -export interface WorkspaceModel { - isRelative?: boolean; - nodeConfPath: string; - workdir: string; -} - -const unknowNodeType: BehaviorNodeTypeModel = { - name: "unknow", - desc: "新建节点", - type: "Action", -}; - -export default class Workspace { - private filepath: string; - private nodeConfPath: string; - private workdir: string; - - private name2conf: { [name: string]: BehaviorNodeTypeModel } = {}; - private types: BehaviorNodeTypeModel[] = []; - - constructor(filepath: string) { - this.filepath = filepath; - } - - load() { - if (!this.filepath) { - return; - } - try { - const str = fs.readFileSync(this.filepath, 'utf8'); - const model = JSON.parse(str) as WorkspaceModel; - if (model.isRelative) { - const root = path.dirname(this.filepath); - this.nodeConfPath = root + '/' + model.nodeConfPath; - this.workdir = root + '/' + model.workdir; - } else { - this.nodeConfPath = model.nodeConfPath; - this.workdir = model.workdir; - } - - this.initNodeConf(); - } catch (error) { - console.log(error); - } - } - - private initNodeConf() { - if (!this.nodeConfPath) { - return; - } - - const types: BehaviorNodeTypeModel[] = JSON.parse(fs.readFileSync(this.nodeConfPath, 'utf8')); - this.name2conf = {}; - types.forEach(t => { - this.name2conf[t.name] = t; - }) - this.types = types; - } - - getNodeConfPath() { - return this.nodeConfPath; - } - setNodeConfPath(path: string) { - this.nodeConfPath = path; - this.initNodeConf(); - } - getWorkdir(): string { - return this.workdir; - } - setWorkdir(workdir: string) { - this.workdir = workdir; - } - - writeAllTrees(outFilePath: string, cb?: (err: string) => void) { - if (!fs.existsSync(this.getWorkdir())) { - cb && cb("请先打开工作区"); - return; - } - glob.glob(path.join(this.getWorkdir(), "**/*.json"), function (err, files) { - if (err) { - console.error(err); - cb && cb(err.message); - return; - } - - let result = new Array(); - 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); - }); - } - - save(filepath?: string) { - if (filepath) { - this.filepath = filepath; - } - if (!this.filepath) { - return; - } - fs.writeFileSync( - this.filepath, - JSON.stringify( - { - nodeConfPath: this.nodeConfPath, - workdir: this.workdir, - }, - null, - 2 - ) - ); - } - - get nodeConf() { - return this.types; - } - - getFilepath() { - return this.filepath; - } - - setFilepath(filepath: string) { - this.filepath = filepath; - } - - getNodeConf(name: string) { - return this.name2conf[name] || unknowNodeType; - } -} +import * as fs from "fs"; +import { BehaviorNodeTypeModel } from "../common/BehaviorTreeModel"; +import * as glob from "glob"; +import * as path from "path"; + +export interface WorkspaceModel { + isRelative?: boolean; + nodeConfPath: string; + workdir: string; +} + +const unknowNodeType: BehaviorNodeTypeModel = { + name: "unknow", + desc: "新建节点", + type: "Action", +}; + +export default class Workspace { + private filepath: string; + private nodeConfPath: string; + private workdir: string; + + private name2conf: { [name: string]: BehaviorNodeTypeModel } = {}; + private types: BehaviorNodeTypeModel[] = []; + + constructor(filepath: string) { + this.filepath = filepath; + } + + load() { + if (!this.filepath) { + return; + } + try { + const str = fs.readFileSync(this.filepath, "utf8"); + const model = JSON.parse(str) as WorkspaceModel; + if (model.isRelative) { + const root = path.dirname(this.filepath); + this.nodeConfPath = root + "/" + model.nodeConfPath; + this.workdir = root + "/" + model.workdir; + } else { + this.nodeConfPath = model.nodeConfPath; + this.workdir = model.workdir; + } + + this.initNodeConf(); + } catch (error) { + console.log(error); + } + } + + private initNodeConf() { + if (!this.nodeConfPath) { + return; + } + + const types: BehaviorNodeTypeModel[] = JSON.parse( + fs.readFileSync(this.nodeConfPath, "utf8") + ); + this.name2conf = {}; + types.forEach((t) => { + this.name2conf[t.name] = t; + }); + this.types = types; + } + + getNodeConfPath() { + return this.nodeConfPath; + } + setNodeConfPath(path: string) { + this.nodeConfPath = path; + this.initNodeConf(); + } + getWorkdir(): string { + return this.workdir; + } + setWorkdir(workdir: string) { + this.workdir = workdir; + } + + writeAllTrees(outFilePath: string, cb?: (err: string) => void) { + if (!fs.existsSync(this.getWorkdir())) { + cb && cb("请先打开工作区"); + return; + } + glob.glob(path.join(this.getWorkdir(), "**/*.json"), function (err, files) { + if (err) { + console.error(err); + cb && cb(err.message); + return; + } + + let result = new Array(); + 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); + }); + } + + save(filepath?: string) { + if (filepath) { + this.filepath = filepath; + } + if (!this.filepath) { + return; + } + fs.writeFileSync( + this.filepath, + JSON.stringify( + { + nodeConfPath: this.nodeConfPath, + workdir: this.workdir, + }, + null, + 2 + ) + ); + } + + get nodeConf() { + return this.types; + } + + getFilepath() { + return this.filepath; + } + + setFilepath(filepath: string) { + this.filepath = filepath; + } + + getNodeConf(name: string) { + return this.name2conf[name] || unknowNodeType; + } +} diff --git a/render-process/Editor/NodePanel.tsx b/render-process/Editor/NodePanel.tsx index a4a55a8..f68cb82 100644 --- a/render-process/Editor/NodePanel.tsx +++ b/render-process/Editor/NodePanel.tsx @@ -1,297 +1,291 @@ -import React from 'react'; -import { Card, Divider, Form, Input, 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'; +import React from "react"; +import { + Card, + Divider, + Form, + Input, + 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; interface NodePanelProps { - model: BehaviorNodeModel; - settings: Settings; - updateNode: (id: string, forceUpdate:boolean) => void; + model: BehaviorNodeModel; + settings: Settings; + updateNode: (id: string, forceUpdate: boolean) => void; } -interface NodePanelState { - -} +interface NodePanelState {} export default class NodePanel extends React.Component { - formRef = React.createRef(); + formRef = React.createRef(); - componentDidUpdate() { - this.formRef.current.resetFields(); - 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; - } + componentDidUpdate() { + this.formRef.current.resetFields(); + this.formRef.current.setFieldsValue(this.getInitialValues()); } - if (conf.args) { - conf.args.forEach(e => { - const k = 'args.' + e.name; - if (e.type.indexOf('number') > 0) { - args[e.name] = Number(values[k]); + 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) { + 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 { - args[e.name] = values[k]; + model.input = null; } - }) - } - model.args = args; - this.formRef.current.setFieldsValue({ customArgs: model.args ? JSON.stringify(model.args, null, " ") : '' }); + if (conf.output) { + model.output = []; + conf.output.forEach((e, i) => { + model.output.push(values["output." + i] || ""); + }); + } else { + model.output = null; + } - if (conf.input) { - model.input = []; - conf.input.forEach((e, i) => { - model.input.push(values['input.' + i] || ''); - }) - } 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 }, + if (forceUpdate) { + this.forceUpdate(); + } + updateNode(model.id.toString(), forceUpdate); }; - return ( - -
- - - - - { - return option.label.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1 - }} - /> - - - - - - - - - {this.renderInputs(conf)} - {this.renderArgs(conf)} - {this.renderOutputs(conf)} - -
- ) - } - renderArgs(conf: BehaviorNodeTypeModel) { - if (!conf || !conf.args || conf.args.length == 0) { - return null; + 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 ( + +
+ + + + + { + return ( + option.label.toUpperCase().indexOf(inputValue.toUpperCase()) !== + -1 + ); + }} + /> + + + + + + + + + {this.renderInputs(conf)} + {this.renderArgs(conf)} + {this.renderOutputs(conf)} + +
+ ); } - // 普通参数 - const normalArgs = (e: ArgsDefType) => { - const required = e.type.indexOf("?") == -1; - if (e.type.indexOf("string") >= 0) { - return ; - } else if (e.type.indexOf("int") >= 0) { - return ; - } else if (e.type.indexOf("boolean") >= 0) { - return ; - } else if (e.type.indexOf("lua") >= 0) { - return ; - } - } - - // 自定义参数 - const customArgs = () => { - return ( - - - - ) - - } - - return ( -
-

常量参数

- {conf && conf.args && conf.args.map((e, i: number) => { - const required = e.type.indexOf("?") == -1; - return ( - = 0 ? 'checked' : undefined} - rules={[ - { required, message: `${e.desc}(${e.name})为必填字段` } - ]} - > - {normalArgs(e)} - - ) - })} - {customArgs()} -
- ); - } - - - renderInputs(conf: BehaviorNodeTypeModel) { - if (!conf.input || !conf.input || conf.input.length == 0) { - return null; - } - - return ( -
-

输入变量

- { - conf.input.map((e, i) => { - return ( - - - - ) - }) + renderArgs(conf: BehaviorNodeTypeModel) { + if (!conf || !conf.args || conf.args.length == 0) { + return null; } -
- ) - } - renderOutputs(conf: BehaviorNodeTypeModel) { - if (!conf.output || !conf.output || conf.output.length == 0) { - return null; - } - return ( -
-

输出变量

- { - conf.output.map((e, i) => { + // 普通参数 + const normalArgs = (e: ArgsDefType) => { + const required = e.type.indexOf("?") == -1; + if (e.type.indexOf("string") >= 0) { + return ; + } else if (e.type.indexOf("int") >= 0) { + return ; + } else if (e.type.indexOf("boolean") >= 0) { + return ; + } else if (e.type.indexOf("lua") >= 0) { + return ; + } + }; + + // 自定义参数 + const customArgs = () => { return ( - - - - ) - }) - } -
- ) - } + + + + ); + }; + return ( +
+ +

常量参数

+
+ {conf && + conf.args && + conf.args.map((e, i: number) => { + const required = e.type.indexOf("?") == -1; + return ( + = 0 ? "checked" : undefined + } + rules={[{ required, message: `${e.desc}(${e.name})为必填字段` }]} + > + {normalArgs(e)} + + ); + })} + {customArgs()} +
+ ); + } + + renderInputs(conf: BehaviorNodeTypeModel) { + if (!conf.input || !conf.input || conf.input.length == 0) { + return null; + } + + return ( +
+ +

输入变量

+
+ {conf.input.map((e, i) => { + return ( + + + + ); + })} +
+ ); + } + + renderOutputs(conf: BehaviorNodeTypeModel) { + if (!conf.output || !conf.output || conf.output.length == 0) { + return null; + } + return ( +
+ +

输出变量

+
+ {conf.output.map((e, i) => { + return ( + + + + ); + })} +
+ ); + } } - diff --git a/render-process/Editor/TreePanel.tsx b/render-process/Editor/TreePanel.tsx index f0be514..97e0d66 100644 --- a/render-process/Editor/TreePanel.tsx +++ b/render-process/Editor/TreePanel.tsx @@ -1,33 +1,28 @@ -import React from 'react'; -import { Card } from 'antd'; -import { INode } from '@antv/g6/lib/interface/item'; -import { BehaviorTreeModel } from '../../common/BehaviorTreeModel'; - -interface TreePanelProps { - model?: BehaviorTreeModel; -} - -interface TreePanelState { -} - -export default class TreePanel extends React.Component { - state: TreePanelState = {}; - - componentDidMount() { - } - - render() { - const { model } = this.props; - if(!model) { - return (
) - } - return ( - - 行为树:{model.name} - - - ) - } - -} - +import React from "react"; +import { Card } from "antd"; +import { INode } from "@antv/g6/lib/interface/item"; +import { BehaviorTreeModel } from "../../common/BehaviorTreeModel"; + +interface TreePanelProps { + model?: BehaviorTreeModel; +} + +interface TreePanelState {} + +export default class TreePanel extends React.Component { + state: TreePanelState = {}; + + componentDidMount() {} + + render() { + const { model } = this.props; + if (!model) { + return
; + } + return ( + + 行为树:{model.name} + + ); + } +} diff --git a/render-process/Editor/index.tsx b/render-process/Editor/index.tsx index c7ebda8..35c9fed 100644 --- a/render-process/Editor/index.tsx +++ b/render-process/Editor/index.tsx @@ -1,412 +1,414 @@ -import * as React from 'react'; -import * as fs from 'fs'; -import * as path from 'path'; -import { Row, Col, message } from 'antd'; -import NodePanel from './NodePanel'; -import G6, { TreeGraph } from '@antv/g6'; -import { G6GraphEvent } from '@antv/g6/lib/interface/behavior'; -import * as Utils from '../../common/Utils'; -import { BehaviorTreeModel, GraphNodeModel, BehaviorNodeModel } from '../../common/BehaviorTreeModel'; -import TreePanel from './TreePanel'; -import Settings from '../../main-process/Settings'; - -import './Editor.css'; -import { clipboard } from 'electron'; - -export interface EditorProps { - filepath: string; -} - -interface EditorState { - curNodeId?: string; - treeModel?: BehaviorTreeModel; - settings?: Settings; -} - -export default class Editor extends React.Component { - private ref: React.RefObject; - state: EditorState = {}; - - private graph: TreeGraph; - private dragSrcId: string; - private dragDstId: string; - private autoId: number; - - constructor(props: EditorProps) { - super(props); - this.ref = React.createRef(); - } - - componentDidMount() { - const graph = new TreeGraph({ - container: this.ref.current, - width: window.screen.width * 0.66, - height: window.screen.height, - animate: false, - // fitCenter: true, - modes: { - default: [ - 'drag-canvas', - 'zoom-canvas', - 'click-select', - 'hover', - { - type: 'collapse-expand', - trigger: 'dblclick', - onChange: (item, collapsed) => { - this.onSelectNode(item.getID()); - const data = item.getModel(); - data.collapsed = collapsed; - 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'); - marker.attr('symbol', icon); - return true; - }, - }, - ] - }, - defaultEdge: { - type: 'cubic-horizontal', - style: { - stroke: '#A3B1BF' - } - }, - defaultNode: { - type: "TreeNode", - }, - layout: { - type: 'compactBox', - direction: 'LR', - getHGap: () => 50, - getWidth: (d: GraphNodeModel) => { - return 150; - }, - getHeight: (d: GraphNodeModel) => { - if (d.size) { - return d.size[1]; - } else { - return 50; - } - } - } - }); - - graph.on('node:mouseenter', (e: G6GraphEvent) => { - const { item } = e; - if (item.hasState('selected')) { - return; - } - graph.setItemState(item, 'hover', true); - }); - - graph.on('node:mouseleave', (e: G6GraphEvent) => { - const { item } = e; - if (item.hasState('selected')) { - return; - } - graph.setItemState(item, 'hover', false); - }); - - graph.on('nodeselectchange', (e: G6GraphEvent) => { - if (e.target) { - this.onSelectNode(e.target.getID()); - } else { - this.onSelectNode(null); - } - }); - - const clearDragDstState = () => { - if (this.dragDstId) { - graph.setItemState(this.dragDstId, 'dragRight', false); - graph.setItemState(this.dragDstId, 'dragDown', false); - graph.setItemState(this.dragDstId, 'dragUp', false); - this.dragDstId = null; - } - } - - const clearDragSrcState = () => { - if (this.dragSrcId) { - graph.setItemState(this.dragSrcId, 'dragSrc', false); - this.dragSrcId = null; - } - } - - graph.on('node:dragstart', (e: G6GraphEvent) => { - this.dragSrcId = e.item.getID(); - graph.setItemState(this.dragSrcId, 'dragSrc', true); - }); - graph.on('node:dragend', (e: G6GraphEvent) => { - if (this.dragSrcId) { - graph.setItemState(this.dragSrcId, 'dragSrc', false); - this.dragSrcId = null; - } - }); - - graph.on('node:dragover', (e: G6GraphEvent) => { - const dstNodeId = e.item.getID(); - if (dstNodeId == this.dragSrcId) { - return; - } - - if (this.dragDstId) { - graph.setItemState(this.dragDstId, 'dragRight', false); - graph.setItemState(this.dragDstId, 'dragDown', false); - graph.setItemState(this.dragDstId, 'dragUp', false); - } - - const box = e.item.getBBox(); - if (e.x > box.minX + box.width * 0.6) { - graph.setItemState(dstNodeId, 'dragRight', true); - } else if (e.y > box.minY + box.height * 0.5) { - graph.setItemState(dstNodeId, 'dragDown', true); - } else { - graph.setItemState(dstNodeId, 'dragUp', true); - } - this.dragDstId = dstNodeId; - }); - - graph.on('node:dragleave', (e: G6GraphEvent) => { - clearDragDstState(); - }) - - graph.on('node:drop', (e: G6GraphEvent) => { - const srcNodeId = this.dragSrcId; - const dstNode = e.item; - - var dragDir; - if (dstNode.hasState('dragRight')) { - dragDir = 'dragRight'; - } else if (dstNode.hasState('dragDown')) { - dragDir = 'dragDown'; - } else if (dstNode.hasState('dragUp')) { - dragDir = 'dragUp'; - } - - clearDragSrcState(); - clearDragDstState(); - - if (!srcNodeId) { - console.log("no drag src"); - return; - } - - if (srcNodeId == dstNode.getID()) { - console.log("drop same node"); - return; - } - - const rootData = graph.findDataById('1'); - const srcData = graph.findDataById(srcNodeId); - const srcParent = Utils.findParent(rootData, srcNodeId); - const dstData = graph.findDataById(dstNode.getID()); - const dstParent = Utils.findParent(rootData, dstNode.getID()); - if (!srcParent) { - console.log("no parent!"); - return; - } - - if (Utils.findFromAllChildren(srcData, dstData.id)) { - // 不能将父节点拖到自已的子孙节点 - console.log("cannot move to child"); - return; - } - - const removeSrc = () => { - srcParent.children = srcParent.children.filter(e => e.id != srcData.id); - } - console.log("dstNode", dstNode); - if (dragDir == 'dragRight') { - removeSrc(); - if (!dstData.children) { - dstData.children = []; - } - dstData.children.push(srcData); - } else if (dragDir == 'dragUp') { - if (!dstParent) { - return; - } - removeSrc(); - const idx = dstParent.children.findIndex(e => e.id == dstData.id); - dstParent.children.splice(idx, 0, srcData); - } else if (dragDir == 'dragDown') { - if (!dstParent) { - return; - } - removeSrc(); - const idx = dstParent.children.findIndex(e => e.id == dstData.id); - dstParent.children.splice(idx + 1, 0, srcData); - } else { - return; - } - - // console.log("cur data", graph.findDataById('1')); - this.changeWithoutAnim(); - }); - - const settings = Utils.getRemoteSettings(); - - const str = fs.readFileSync(this.props.filepath, 'utf8'); - let tree: BehaviorTreeModel = JSON.parse(str); - const data = Utils.createTreeData(tree.root, settings); - this.autoId = Utils.refreshNodeId(data); - graph.data(data); - graph.render(); - graph.fitCenter(); - graph.set('animate', true); - - this.graph = graph; - - this.setState({ treeModel: tree, settings }); - } - - onSelectNode(curNodeId: string | null) { - const graph = this.graph; - - if (this.state.curNodeId) { - graph.setItemState(this.state.curNodeId, 'selected', false); - } - - this.setState({ curNodeId }); - if (this.state.curNodeId) { - graph.setItemState(this.state.curNodeId, 'selected', true); - } - } - - createNode(name: string) { - console.log("editor create node", name); - const { curNodeId, settings } = this.state; - if (!curNodeId) { - message.warn("未选中节点"); - return; - } - const curNodeData = this.graph.findDataById(curNodeId); - const newNodeData: BehaviorNodeModel = { - id: this.autoId++, - name: name, - } - if (!curNodeData.children) { - curNodeData.children = []; - } - curNodeData.children.push(Utils.createTreeData(newNodeData, settings)); - this.changeWithoutAnim(); - } - - deleteNode() { - console.log("editor delete node"); - const { curNodeId } = this.state; - if (!curNodeId) { - return; - } - - if(curNodeId == '1') { - message.warn("根节点不能删除!"); - return; - } - - this.onSelectNode(null); - const rootData = this.graph.findDataById('1'); - const parentData = Utils.findParent(rootData, curNodeId); - parentData.children = parentData.children.filter(e => e.id != curNodeId); - this.changeWithoutAnim(); - } - - updateNode(id: string) { - this.graph.changeData(); - this.graph.layout(); - } - - changeWithoutAnim() { - this.graph.set('animate', false); - this.graph.changeData(); - this.graph.layout(); - this.graph.set('animate', true); - } - - save() { - const { filepath } = this.props; - const root = Utils.createFileData(this.graph.findDataById('1') as GraphNodeModel); - fs.writeFileSync(filepath, JSON.stringify({ - name: path.basename(filepath).slice(0, -5), - root, - desc: root.desc, - })); - } - - copyNode() { - console.log("editor copy node"); - const { curNodeId } = this.state; - if (!curNodeId) { - return; - } - const data = this.graph.findDataById(curNodeId); - clipboard.writeText(JSON.stringify(data, null, 2)); - } - - pasteNode() { - const { curNodeId } = this.state; - if (!curNodeId) { - message.warn("未选中节点"); - return; - } - const curNodeData = this.graph.findDataById(curNodeId); - try { - const str = clipboard.readText(); - if(!str || str == '') { - return; - } - const data = JSON.parse(str); - if(!curNodeData.children) { - curNodeData.children = []; - } - curNodeData.children.push(data); - this.autoId = Utils.refreshNodeId(this.graph.findDataById('1') as GraphNodeModel); - this.changeWithoutAnim(); - } catch (error) { - message.error("粘贴数据有误"); - } - } - - render() { - const { curNodeId, treeModel, settings } = this.state; - var curNode: any; - if (curNodeId) { - curNode = this.graph.findDataById(curNodeId); - } - - return ( -
- - - - { - curNode ? - { - if (forceUpdate) { - const data: any = this.graph.findDataById(id); - data.conf = settings.getNodeConf(data.name); - data.size = Utils.calcTreeNodeSize(data); - this.changeWithoutAnim(); - } - const item = this.graph.findById(id); - item.draw(); - }} - /> - : - - } - - -
- ); - } -} \ No newline at end of file +import * as React from "react"; +import * as fs from "fs"; +import * as path from "path"; +import { Row, Col, message } from "antd"; +import NodePanel from "./NodePanel"; +import G6, { TreeGraph } from "@antv/g6"; +import { G6GraphEvent } from "@antv/g6/lib/interface/behavior"; +import * as Utils from "../../common/Utils"; +import { + BehaviorTreeModel, + GraphNodeModel, + BehaviorNodeModel, +} from "../../common/BehaviorTreeModel"; +import TreePanel from "./TreePanel"; +import Settings from "../../main-process/Settings"; + +import "./Editor.css"; +import { clipboard } from "electron"; + +export interface EditorProps { + filepath: string; +} + +interface EditorState { + curNodeId?: string; + treeModel?: BehaviorTreeModel; + settings?: Settings; +} + +export default class Editor extends React.Component { + private ref: React.RefObject; + state: EditorState = {}; + + private graph: TreeGraph; + private dragSrcId: string; + private dragDstId: string; + private autoId: number; + + constructor(props: EditorProps) { + super(props); + this.ref = React.createRef(); + } + + componentDidMount() { + const graph = new TreeGraph({ + container: this.ref.current, + width: window.screen.width * 0.66, + height: window.screen.height, + animate: false, + // fitCenter: true, + modes: { + default: [ + "drag-canvas", + "zoom-canvas", + "click-select", + "hover", + { + type: "collapse-expand", + trigger: "dblclick", + onChange: (item, collapsed) => { + this.onSelectNode(item.getID()); + const data = item.getModel(); + data.collapsed = collapsed; + 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"); + marker.attr("symbol", icon); + return true; + }, + }, + ], + }, + defaultEdge: { + type: "cubic-horizontal", + style: { + stroke: "#A3B1BF", + }, + }, + defaultNode: { + type: "TreeNode", + }, + layout: { + type: "compactBox", + direction: "LR", + getHGap: () => 50, + getWidth: (d: GraphNodeModel) => { + return 150; + }, + getHeight: (d: GraphNodeModel) => { + if (d.size) { + return d.size[1]; + } else { + return 50; + } + }, + }, + }); + + graph.on("node:mouseenter", (e: G6GraphEvent) => { + const { item } = e; + if (item.hasState("selected")) { + return; + } + graph.setItemState(item, "hover", true); + }); + + graph.on("node:mouseleave", (e: G6GraphEvent) => { + const { item } = e; + if (item.hasState("selected")) { + return; + } + graph.setItemState(item, "hover", false); + }); + + graph.on("nodeselectchange", (e: G6GraphEvent) => { + if (e.target) { + this.onSelectNode(e.target.getID()); + } else { + this.onSelectNode(null); + } + }); + + const clearDragDstState = () => { + if (this.dragDstId) { + graph.setItemState(this.dragDstId, "dragRight", false); + graph.setItemState(this.dragDstId, "dragDown", false); + graph.setItemState(this.dragDstId, "dragUp", false); + this.dragDstId = null; + } + }; + + const clearDragSrcState = () => { + if (this.dragSrcId) { + graph.setItemState(this.dragSrcId, "dragSrc", false); + this.dragSrcId = null; + } + }; + + graph.on("node:dragstart", (e: G6GraphEvent) => { + this.dragSrcId = e.item.getID(); + graph.setItemState(this.dragSrcId, "dragSrc", true); + }); + graph.on("node:dragend", (e: G6GraphEvent) => { + if (this.dragSrcId) { + graph.setItemState(this.dragSrcId, "dragSrc", false); + this.dragSrcId = null; + } + }); + + graph.on("node:dragover", (e: G6GraphEvent) => { + const dstNodeId = e.item.getID(); + if (dstNodeId == this.dragSrcId) { + return; + } + + if (this.dragDstId) { + graph.setItemState(this.dragDstId, "dragRight", false); + graph.setItemState(this.dragDstId, "dragDown", false); + graph.setItemState(this.dragDstId, "dragUp", false); + } + + const box = e.item.getBBox(); + if (e.x > box.minX + box.width * 0.6) { + graph.setItemState(dstNodeId, "dragRight", true); + } else if (e.y > box.minY + box.height * 0.5) { + graph.setItemState(dstNodeId, "dragDown", true); + } else { + graph.setItemState(dstNodeId, "dragUp", true); + } + this.dragDstId = dstNodeId; + }); + + graph.on("node:dragleave", (e: G6GraphEvent) => { + clearDragDstState(); + }); + + graph.on("node:drop", (e: G6GraphEvent) => { + const srcNodeId = this.dragSrcId; + const dstNode = e.item; + + var dragDir; + if (dstNode.hasState("dragRight")) { + dragDir = "dragRight"; + } else if (dstNode.hasState("dragDown")) { + dragDir = "dragDown"; + } else if (dstNode.hasState("dragUp")) { + dragDir = "dragUp"; + } + + clearDragSrcState(); + clearDragDstState(); + + if (!srcNodeId) { + console.log("no drag src"); + return; + } + + if (srcNodeId == dstNode.getID()) { + console.log("drop same node"); + return; + } + + const rootData = graph.findDataById("1"); + const srcData = graph.findDataById(srcNodeId); + const srcParent = Utils.findParent(rootData, srcNodeId); + const dstData = graph.findDataById(dstNode.getID()); + const dstParent = Utils.findParent(rootData, dstNode.getID()); + if (!srcParent) { + console.log("no parent!"); + return; + } + + if (Utils.findFromAllChildren(srcData, dstData.id)) { + // 不能将父节点拖到自已的子孙节点 + console.log("cannot move to child"); + return; + } + + const removeSrc = () => { + srcParent.children = srcParent.children.filter((e) => e.id != srcData.id); + }; + console.log("dstNode", dstNode); + if (dragDir == "dragRight") { + removeSrc(); + if (!dstData.children) { + dstData.children = []; + } + dstData.children.push(srcData); + } else if (dragDir == "dragUp") { + if (!dstParent) { + return; + } + removeSrc(); + const idx = dstParent.children.findIndex((e) => e.id == dstData.id); + dstParent.children.splice(idx, 0, srcData); + } else if (dragDir == "dragDown") { + if (!dstParent) { + return; + } + removeSrc(); + const idx = dstParent.children.findIndex((e) => e.id == dstData.id); + dstParent.children.splice(idx + 1, 0, srcData); + } else { + return; + } + + // console.log("cur data", graph.findDataById('1')); + this.changeWithoutAnim(); + }); + + const settings = Utils.getRemoteSettings(); + + const str = fs.readFileSync(this.props.filepath, "utf8"); + let tree: BehaviorTreeModel = JSON.parse(str); + const data = Utils.createTreeData(tree.root, settings); + this.autoId = Utils.refreshNodeId(data); + graph.data(data); + graph.render(); + graph.fitCenter(); + graph.set("animate", true); + + this.graph = graph; + + this.setState({ treeModel: tree, settings }); + } + + onSelectNode(curNodeId: string | null) { + const graph = this.graph; + + if (this.state.curNodeId) { + graph.setItemState(this.state.curNodeId, "selected", false); + } + + this.setState({ curNodeId }); + if (this.state.curNodeId) { + graph.setItemState(this.state.curNodeId, "selected", true); + } + } + + createNode(name: string) { + console.log("editor create node", name); + const { curNodeId, settings } = this.state; + if (!curNodeId) { + message.warn("未选中节点"); + return; + } + const curNodeData = this.graph.findDataById(curNodeId); + const newNodeData: BehaviorNodeModel = { + id: this.autoId++, + name: name, + }; + if (!curNodeData.children) { + curNodeData.children = []; + } + curNodeData.children.push(Utils.createTreeData(newNodeData, settings)); + this.changeWithoutAnim(); + } + + deleteNode() { + console.log("editor delete node"); + const { curNodeId } = this.state; + if (!curNodeId) { + return; + } + + if (curNodeId == "1") { + message.warn("根节点不能删除!"); + return; + } + + this.onSelectNode(null); + const rootData = this.graph.findDataById("1"); + const parentData = Utils.findParent(rootData, curNodeId); + parentData.children = parentData.children.filter((e) => e.id != curNodeId); + this.changeWithoutAnim(); + } + + updateNode(id: string) { + this.graph.changeData(); + this.graph.layout(); + } + + changeWithoutAnim() { + this.graph.set("animate", false); + this.graph.changeData(); + this.graph.layout(); + this.graph.set("animate", true); + } + + save() { + const { filepath } = this.props; + const root = Utils.createFileData(this.graph.findDataById("1") as GraphNodeModel); + fs.writeFileSync( + filepath, + JSON.stringify({ + name: path.basename(filepath).slice(0, -5), + root, + desc: root.desc, + }) + ); + } + + copyNode() { + console.log("editor copy node"); + const { curNodeId } = this.state; + if (!curNodeId) { + return; + } + const data = this.graph.findDataById(curNodeId); + clipboard.writeText(JSON.stringify(data, null, 2)); + } + + pasteNode() { + const { curNodeId } = this.state; + if (!curNodeId) { + message.warn("未选中节点"); + return; + } + const curNodeData = this.graph.findDataById(curNodeId); + try { + const str = clipboard.readText(); + if (!str || str == "") { + return; + } + const data = JSON.parse(str); + if (!curNodeData.children) { + curNodeData.children = []; + } + curNodeData.children.push(data); + this.autoId = Utils.refreshNodeId(this.graph.findDataById("1") as GraphNodeModel); + this.changeWithoutAnim(); + } catch (error) { + message.error("粘贴数据有误"); + } + } + + render() { + const { curNodeId, treeModel, settings } = this.state; + var curNode: any; + if (curNodeId) { + curNode = this.graph.findDataById(curNodeId); + } + + return ( +
+ + + + {curNode ? ( + { + if (forceUpdate) { + const data: any = this.graph.findDataById(id); + data.conf = settings.getNodeConf(data.name); + data.size = Utils.calcTreeNodeSize(data); + this.changeWithoutAnim(); + } + const item = this.graph.findById(id); + item.draw(); + }} + /> + ) : ( + + )} + + +
+ ); + } +} diff --git a/render-process/Properties.tsx b/render-process/Properties.tsx index bdd3ded..81600f2 100644 --- a/render-process/Properties.tsx +++ b/render-process/Properties.tsx @@ -1,99 +1,89 @@ -import React, { Component } from "react"; -import { Menu, Input } from "antd"; -import * as fs from 'fs'; -import * as path from 'path'; -import { ipcRenderer } from "electron"; -import MainEventType from "../common/MainEventType"; - -const { Search } = Input; - -export interface TreeFile { - name: string; - path: string; -} - -export interface PropertiesProps { - workdir: string; - onOpenTree: (path: string) => void; -} - -interface PropertiesState { - treeList: TreeFile[] -} - -export default class Properties extends Component { - state: PropertiesState = { - treeList: [] - }; - - curWorkdir: string = ''; - - shouldComponentUpdate(nextProps: PropertiesProps) { - const shouldUpdate = this.curWorkdir != nextProps.workdir; - this.curWorkdir = nextProps.workdir; - return shouldUpdate; - } - - componentDidMount() { - ipcRenderer.on(MainEventType.CREATE_TREE, (event: any, path: string) => { - console.log("on Create tree", path); - this.props.onOpenTree(path); - this.forceUpdate(); - }); - - var workdir = this.props.workdir; - if (workdir == '') { - return; - } - this.setState({ treeList: this.getTreeList(workdir) }); - } - - getTreeList(workdir: string) { - if (workdir == '' || !fs.existsSync(workdir)) { - console.log("workdir not exist", workdir); - return []; - } - - const files = fs.readdirSync(workdir); - const list: TreeFile[] = []; - files.forEach((filename) => { - const stat = fs.statSync(path.join(workdir, filename)) - if (stat.isFile()) { - var name = filename.slice(0, -5) - list.push({ - name, - path: workdir + '/' + name + '.json' - }); - } - }) - return list; - } - - render() { - console.log("render Properties"); - const { onOpenTree, workdir } = this.props; - const treeList = this.getTreeList(workdir); - return ( -
- { - - }} - /> - - {treeList.map((tree) => ( - onOpenTree(tree.path)} - > - {tree.name} - - ))} - -
- ) - } -} \ No newline at end of file +import React, { Component } from "react"; +import { Menu, Input } from "antd"; +import * as fs from "fs"; +import * as path from "path"; +import { ipcRenderer } from "electron"; +import MainEventType from "../common/MainEventType"; + +const { Search } = Input; + +export interface TreeFile { + name: string; + path: string; +} + +export interface PropertiesProps { + workdir: string; + onOpenTree: (path: string) => void; +} + +interface PropertiesState { + treeList: TreeFile[]; +} + +export default class Properties extends Component { + state: PropertiesState = { + treeList: [], + }; + + curWorkdir: string = ""; + + shouldComponentUpdate(nextProps: PropertiesProps) { + const shouldUpdate = this.curWorkdir != nextProps.workdir; + this.curWorkdir = nextProps.workdir; + return shouldUpdate; + } + + componentDidMount() { + ipcRenderer.on(MainEventType.CREATE_TREE, (event: any, path: string) => { + console.log("on Create tree", path); + this.props.onOpenTree(path); + this.forceUpdate(); + }); + + var workdir = this.props.workdir; + if (workdir == "") { + return; + } + this.setState({ treeList: this.getTreeList(workdir) }); + } + + getTreeList(workdir: string) { + if (workdir == "" || !fs.existsSync(workdir)) { + console.log("workdir not exist", workdir); + return []; + } + + const files = fs.readdirSync(workdir); + const list: TreeFile[] = []; + files.forEach((filename) => { + const stat = fs.statSync(path.join(workdir, filename)); + if (stat.isFile()) { + var name = filename.slice(0, -5); + list.push({ + name, + path: workdir + "/" + name + ".json", + }); + } + }); + return list; + } + + render() { + console.log("render Properties"); + const { onOpenTree, workdir } = this.props; + const treeList = this.getTreeList(workdir); + return ( +
+ {}} /> + + {treeList.map((tree) => ( + onOpenTree(tree.path)}> + {tree.name} + + ))} + +
+ ); + } +} diff --git a/render-process/RegisterNode.ts b/render-process/RegisterNode.ts index d4f08b5..7a92467 100644 --- a/render-process/RegisterNode.ts +++ b/render-process/RegisterNode.ts @@ -1,277 +1,275 @@ -import G6 from '@antv/g6'; -import Settings from '../main-process/Settings'; - -const NODE_COLORS: any = { - ['Composite']: 'rgb(91,237,32)', - ['Decorator']: 'rgb(218,167,16)', - ['Condition']: 'rgb(228,20,139)', - ['Action']: 'rgb(91,143,249)', - ['Other']: 'rgb(112,112,112)', -} - - -export default function (settings: Settings) { - const collapseStyle: any = { - 'collapse-icon': { - // symbol: G6.Marker.expand, - fill: 'blue', - } - }; - G6.registerNode( - 'TreeNode', - { - options: { - type: 'rect', - labelCfg: { - style: { - fill: 'blue', - fontSize: 10 - } - }, - style: { - fill: "white", - }, - stateStyles: { - selected: { - 'main-box': { - stroke: 'yellow', - lineWidth: 5, - }, - }, - hover: { - stroke: 'white', - lineWidth: 3, - }, - dragSrc: { - fill: 'gray', - }, - dragRight: { - 'drag-right': { - fillOpacity: 0.6, - } - }, - dragUp: { - 'drag-up': { - fillOpacity: 0.6, - } - }, - dragDown: { - 'drag-down': { - fillOpacity: 0.6, - } - } - }, - }, - draw(cfg, group) { - const nodeConf = settings.getNodeConf(cfg.name as string); - const classify = nodeConf.type || 'Other'; - const color = NODE_COLORS[classify] || 'Other'; - var size = cfg.size ? cfg.size as number[] : [150, 40]; - const w = size[0]; - const h = size[1]; - const x0 = -w / 2; - const y0 = -h / 2; - const r = 2; - const shape = group.addShape('rect', { - attrs: { - x: x0, - y: y0, - width: w, - height: h, - stroke: color, - lineWidth: 2, - fill: 'white', - radius: r, - }, - name: 'main-box', - draggable: true, - }); - - // name bg - group.addShape('rect', { - attrs: { - x: x0 + 1.5, - y: y0 + 1.5, - width: w - 3, - height: 16, - fill: color, - // radius: r, - }, - name: 'name-bg', - draggable: true, - }); - - // id text - group.addShape('text', { - attrs: { - textBaseline: 'top', - x: x0 - 3, - y: -8, - fontSize: 20, - lineHeight: 20, - text: cfg.id, - textAlign: 'right', - fill: 'white', - stroke: 'black', - lineWidth: 2, - }, - name: 'id-text', - }); - - // icon - var img = `./static/icons/${classify}.svg`; - group.addShape('image', { - attrs: { - x: x0 + 3, - y: y0 + 2, - height: 14, - width: 14, - img, - }, - name: 'node-icon', - }); - - // name text - group.addShape('text', { - attrs: { - textBaseline: 'top', - x: x0 + 18, - y: y0 + 4, - fontWeight: 800, - lineHeight: 20, - text: cfg.name, - fill: 'black', - }, - name: 'name-text', - }); - - var x = x0 + 2; - var y = y0 + 23; - // desc text - const desc = cfg.desc || nodeConf.desc; - if (desc) { - group.addShape('text', { - attrs: { - textBaseline: 'top', - x, - y, - lineHeight: 20, - fontWeight: 800, - text: `备注:${desc}`, - fill: 'black', - }, - name: 'desc-text', - }); - } - - const args: any = cfg.args; - if (nodeConf.args && args && Object.keys(args).length > 0) { - y += 20; - group.addShape('text', { - attrs: { - textBaseline: 'top', - x, - y, - lineHeight: 20, - text: `参数:${JSON.stringify(args)}`, - fill: 'black', - }, - name: 'args-text', - }); - } - - const input: [] = cfg.input ? cfg.input as [] : []; - if (nodeConf.input && input.length > 0) { - y += 20; - group.addShape('text', { - attrs: { - textBaseline: 'top', - x, - y, - lineHeight: 20, - text: `输入:${JSON.stringify(input)}`, - fill: 'black', - }, - name: 'input-text', - }); - } - - const output: [] = cfg.output ? cfg.output as [] : []; - if (nodeConf.output && output.length > 0) { - y += 20; - group.addShape('text', { - attrs: { - textBaseline: 'top', - x, - y, - lineHeight: 20, - text: `输出:${JSON.stringify(output)}`, - fill: 'black', - }, - name: 'output-text', - }); - } - - if (Array.isArray(cfg.children) && cfg.children.length > 0) { - group.addShape('marker', { - attrs: { - x: w / 2, - y: 0, - r: 6, - symbol: G6.Marker.collapse, - stroke: '#666', - lineWidth: 1, - fill: '#fff', - }, - name: 'collapse-icon', - }); - } - - - group.addShape('rect', { - name: 'drag-up', - attrs: { - x: -w / 2, - y: -h / 2, - width: w, - height: h / 2, - fill: 'blue', - fillOpacity: 0, - }, - draggable: true, - // visible: false, - }); - - group.addShape('rect', { - name: 'drag-down', - attrs: { - x: -w / 2, - y: 0, - width: w, - height: h / 2, - fill: 'blue', - fillOpacity: 0, - }, - draggable: true, - // visible: false, - }); - - group.addShape('rect', { - name: 'drag-right', - attrs: { - x: w * 0.1, - y: -h / 2, - width: w * 0.4, - height: h, - fill: 'blue', - fillOpacity: 0, - }, - draggable: true, - // visible: false, - }); - return shape; - } - }, - 'single-node', - ); -} \ No newline at end of file +import G6 from "@antv/g6"; +import Settings from "../main-process/Settings"; + +const NODE_COLORS: any = { + ["Composite"]: "rgb(91,237,32)", + ["Decorator"]: "rgb(218,167,16)", + ["Condition"]: "rgb(228,20,139)", + ["Action"]: "rgb(91,143,249)", + ["Other"]: "rgb(112,112,112)", +}; + +export default function (settings: Settings) { + const collapseStyle: any = { + "collapse-icon": { + // symbol: G6.Marker.expand, + fill: "blue", + }, + }; + G6.registerNode( + "TreeNode", + { + options: { + type: "rect", + labelCfg: { + style: { + fill: "blue", + fontSize: 10, + }, + }, + style: { + fill: "white", + }, + stateStyles: { + selected: { + "main-box": { + stroke: "yellow", + lineWidth: 5, + }, + }, + hover: { + stroke: "white", + lineWidth: 3, + }, + dragSrc: { + fill: "gray", + }, + dragRight: { + "drag-right": { + fillOpacity: 0.6, + }, + }, + dragUp: { + "drag-up": { + fillOpacity: 0.6, + }, + }, + dragDown: { + "drag-down": { + fillOpacity: 0.6, + }, + }, + }, + }, + draw(cfg, group) { + const nodeConf = settings.getNodeConf(cfg.name as string); + const classify = nodeConf.type || "Other"; + const color = NODE_COLORS[classify] || "Other"; + var size = cfg.size ? (cfg.size as number[]) : [150, 40]; + const w = size[0]; + const h = size[1]; + const x0 = -w / 2; + const y0 = -h / 2; + const r = 2; + const shape = group.addShape("rect", { + attrs: { + x: x0, + y: y0, + width: w, + height: h, + stroke: color, + lineWidth: 2, + fill: "white", + radius: r, + }, + name: "main-box", + draggable: true, + }); + + // name bg + group.addShape("rect", { + attrs: { + x: x0 + 1.5, + y: y0 + 1.5, + width: w - 3, + height: 16, + fill: color, + // radius: r, + }, + name: "name-bg", + draggable: true, + }); + + // id text + group.addShape("text", { + attrs: { + textBaseline: "top", + x: x0 - 3, + y: -8, + fontSize: 20, + lineHeight: 20, + text: cfg.id, + textAlign: "right", + fill: "white", + stroke: "black", + lineWidth: 2, + }, + name: "id-text", + }); + + // icon + var img = `./static/icons/${classify}.svg`; + group.addShape("image", { + attrs: { + x: x0 + 3, + y: y0 + 2, + height: 14, + width: 14, + img, + }, + name: "node-icon", + }); + + // name text + group.addShape("text", { + attrs: { + textBaseline: "top", + x: x0 + 18, + y: y0 + 4, + fontWeight: 800, + lineHeight: 20, + text: cfg.name, + fill: "black", + }, + name: "name-text", + }); + + var x = x0 + 2; + var y = y0 + 23; + // desc text + const desc = cfg.desc || nodeConf.desc; + if (desc) { + group.addShape("text", { + attrs: { + textBaseline: "top", + x, + y, + lineHeight: 20, + fontWeight: 800, + text: `备注:${desc}`, + fill: "black", + }, + name: "desc-text", + }); + } + + const args: any = cfg.args; + if (nodeConf.args && args && Object.keys(args).length > 0) { + y += 20; + group.addShape("text", { + attrs: { + textBaseline: "top", + x, + y, + lineHeight: 20, + text: `参数:${JSON.stringify(args)}`, + fill: "black", + }, + name: "args-text", + }); + } + + const input: [] = cfg.input ? (cfg.input as []) : []; + if (nodeConf.input && input.length > 0) { + y += 20; + group.addShape("text", { + attrs: { + textBaseline: "top", + x, + y, + lineHeight: 20, + text: `输入:${JSON.stringify(input)}`, + fill: "black", + }, + name: "input-text", + }); + } + + const output: [] = cfg.output ? (cfg.output as []) : []; + if (nodeConf.output && output.length > 0) { + y += 20; + group.addShape("text", { + attrs: { + textBaseline: "top", + x, + y, + lineHeight: 20, + text: `输出:${JSON.stringify(output)}`, + fill: "black", + }, + name: "output-text", + }); + } + + if (Array.isArray(cfg.children) && cfg.children.length > 0) { + group.addShape("marker", { + attrs: { + x: w / 2, + y: 0, + r: 6, + symbol: G6.Marker.collapse, + stroke: "#666", + lineWidth: 1, + fill: "#fff", + }, + name: "collapse-icon", + }); + } + + group.addShape("rect", { + name: "drag-up", + attrs: { + x: -w / 2, + y: -h / 2, + width: w, + height: h / 2, + fill: "blue", + fillOpacity: 0, + }, + draggable: true, + // visible: false, + }); + + group.addShape("rect", { + name: "drag-down", + attrs: { + x: -w / 2, + y: 0, + width: w, + height: h / 2, + fill: "blue", + fillOpacity: 0, + }, + draggable: true, + // visible: false, + }); + + group.addShape("rect", { + name: "drag-right", + attrs: { + x: w * 0.1, + y: -h / 2, + width: w * 0.4, + height: h, + fill: "blue", + fillOpacity: 0, + }, + draggable: true, + // visible: false, + }); + return shape; + }, + }, + "single-node" + ); +} diff --git a/render-process/TreeTabs.tsx b/render-process/TreeTabs.tsx index 4c2f34e..9f099e2 100644 --- a/render-process/TreeTabs.tsx +++ b/render-process/TreeTabs.tsx @@ -1,136 +1,130 @@ -import Editor from "./Editor"; -import React, { Component } from "react"; -import { Layout, Tabs, message } from 'antd'; -import { ipcRenderer, remote } from 'electron'; -import * as path from 'path'; - -import 'antd/dist/antd.dark.css'; -import MainEventType from "../common/MainEventType"; - -const { TabPane } = Tabs; - -interface TreeTabsProps { -} - -interface TreeTabsState { - filepaths: string[]; - curPath?: string; -} - -export default class TreeTabs extends Component { - state: TreeTabsState = { - filepaths: [], - } - - editors: { [path: string]: Editor } = {} - - componentWillMount() { - } - - componentDidMount() { - ipcRenderer.on(MainEventType.CREATE_NODE, (event: any, name: any) => { - const editor = this.getCurEditor(); - editor?.createNode(name); - }); - - ipcRenderer.on(MainEventType.DELETE_NODE, () => { - const editor = this.getCurEditor(); - editor?.deleteNode(); - }); - - ipcRenderer.on(MainEventType.COPY_NODE, () => { - const editor = this.getCurEditor(); - editor?.copyNode(); - }); - - ipcRenderer.on(MainEventType.PASTE_NODE, () => { - const editor = this.getCurEditor(); - editor?.pasteNode(); - }); - - ipcRenderer.on(MainEventType.SAVE, (event: any) => { - const { curPath } = this.state; - if (!curPath) { - return; - } - const editor = this.editors[curPath]; - editor.save(); - message.success("已保存"); - }); - - ipcRenderer.on(MainEventType.SAVE_ALL, (event: any) => { - for (let k in this.editors) { - let editor = this.editors[k]; - editor.save(); - } - message.success("已保存所有行为树"); - }); - } - - getCurEditor() { - const { curPath } = this.state; - if (!curPath) { - return; - } - return this.editors[curPath]; - } - - openFile(path: string) { - console.log("on open file", path); - if (this.state.filepaths.indexOf(path) < 0) { - const filepaths = this.state.filepaths; - filepaths.push(path); - this.setState({ filepaths, curPath: path }); - } else { - this.setState({ curPath: path }); - } - } - - render() { - console.log("render tabs"); - const { filepaths, curPath } = this.state; - if (!curPath) { - return (
); - } - return ( - { - this.setState({ curPath: activeKey }) - }} - onEdit={(targetKey, action) => { - if (action == 'remove') { - const idx = filepaths.indexOf(targetKey as string); - if (idx < 0) { - return; - } - filepaths.splice(idx, 1); - const nextPath = filepaths[idx - 1]; - this.setState({ filepaths, curPath: nextPath }); - } - }} - > - { - filepaths.map(filepath => { - return ( - - { - this.editors[filepath] = ref; - } - } - /> - - ); - }) - } - - ) - } -}; \ No newline at end of file +import Editor from "./Editor"; +import React, { Component } from "react"; +import { Layout, Tabs, message } from "antd"; +import { ipcRenderer, remote } from "electron"; +import * as path from "path"; + +import "antd/dist/antd.dark.css"; +import MainEventType from "../common/MainEventType"; + +const { TabPane } = Tabs; + +interface TreeTabsProps {} + +interface TreeTabsState { + filepaths: string[]; + curPath?: string; +} + +export default class TreeTabs extends Component { + state: TreeTabsState = { + filepaths: [], + }; + + editors: { [path: string]: Editor } = {}; + + componentWillMount() {} + + componentDidMount() { + ipcRenderer.on(MainEventType.CREATE_NODE, (event: any, name: any) => { + const editor = this.getCurEditor(); + editor?.createNode(name); + }); + + ipcRenderer.on(MainEventType.DELETE_NODE, () => { + const editor = this.getCurEditor(); + editor?.deleteNode(); + }); + + ipcRenderer.on(MainEventType.COPY_NODE, () => { + const editor = this.getCurEditor(); + editor?.copyNode(); + }); + + ipcRenderer.on(MainEventType.PASTE_NODE, () => { + const editor = this.getCurEditor(); + editor?.pasteNode(); + }); + + ipcRenderer.on(MainEventType.SAVE, (event: any) => { + const { curPath } = this.state; + if (!curPath) { + return; + } + const editor = this.editors[curPath]; + editor.save(); + message.success("已保存"); + }); + + ipcRenderer.on(MainEventType.SAVE_ALL, (event: any) => { + for (let k in this.editors) { + let editor = this.editors[k]; + editor.save(); + } + message.success("已保存所有行为树"); + }); + } + + getCurEditor() { + const { curPath } = this.state; + if (!curPath) { + return; + } + return this.editors[curPath]; + } + + openFile(path: string) { + console.log("on open file", path); + if (this.state.filepaths.indexOf(path) < 0) { + const filepaths = this.state.filepaths; + filepaths.push(path); + this.setState({ filepaths, curPath: path }); + } else { + this.setState({ curPath: path }); + } + } + + render() { + console.log("render tabs"); + const { filepaths, curPath } = this.state; + if (!curPath) { + return
; + } + return ( + { + this.setState({ curPath: activeKey }); + }} + onEdit={(targetKey, action) => { + if (action == "remove") { + const idx = filepaths.indexOf(targetKey as string); + if (idx < 0) { + return; + } + filepaths.splice(idx, 1); + const nextPath = filepaths[idx - 1]; + this.setState({ filepaths, curPath: nextPath }); + } + }} + > + {filepaths.map((filepath) => { + return ( + + { + this.editors[filepath] = ref; + }} + /> + + ); + })} + + ); + } +} diff --git a/render-process/index.tsx b/render-process/index.tsx index d9ef1ea..c7a4b4f 100644 --- a/render-process/index.tsx +++ b/render-process/index.tsx @@ -1,95 +1,90 @@ - -import * as ReactDOM from "react-dom"; -import Editor from "./Editor"; -import React, { Component } from "react"; -import { Layout, Tabs } from 'antd'; -import { ipcRenderer, remote } from 'electron'; -import * as path from 'path'; -import * as fs from 'fs'; -import * as Utils from '../common/Utils'; -import MainEventType from "../common/MainEventType"; -import Properties from "./Properties"; -import Settings from "../main-process/Settings"; -import G6 from '@antv/g6'; - -import 'antd/dist/antd.dark.css'; -import './index.css'; -import { ModelConfig, Item } from "@antv/g6/lib/types"; -import RegisterNode from "./RegisterNode"; -import TreeTabs from "./TreeTabs"; - -const { Header, Sider, Content, Footer } = Layout; -const { TabPane } = Tabs; - - -interface MainState { - workdir: string; - workspace: string; -} - -export default class Main extends Component { - state: MainState = { - workdir: '', - workspace: '', - } - - settings: Settings; - tabs: TreeTabs; - - componentWillMount() { - this.updateSettings(); - RegisterNode(this.settings); - } - - componentDidMount() { - 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); - document.title = workspace; - this.setState({ workdir, workspace }); - }); - - console.log("workdir", this.settings.curWorkspace.getWorkdir()); - this.setState({ - workdir: this.settings.curWorkspace.getWorkdir(), - workspace: this.settings.curWorkspace.getFilepath(), - }); - } - - updateSettings() { - this.settings = Utils.getRemoteSettings(); - } - - render() { - console.log("render main"); - const { workdir, workspace } = this.state; - document.title = `行为树编辑器 - ${workspace}`; - return ( - - - { - this.tabs.openFile(path); - }} - /> - - - { - this.tabs = ref; - }} - /> - - - ) - } -}; - -ReactDOM.render( -
, - document.getElementById('root') as HTMLElement -); \ No newline at end of file +import * as ReactDOM from "react-dom"; +import Editor from "./Editor"; +import React, { Component } from "react"; +import { Layout, Tabs } from "antd"; +import { ipcRenderer, remote } from "electron"; +import * as path from "path"; +import * as fs from "fs"; +import * as Utils from "../common/Utils"; +import MainEventType from "../common/MainEventType"; +import Properties from "./Properties"; +import Settings from "../main-process/Settings"; +import G6 from "@antv/g6"; + +import "antd/dist/antd.dark.css"; +import "./index.css"; +import { ModelConfig, Item } from "@antv/g6/lib/types"; +import RegisterNode from "./RegisterNode"; +import TreeTabs from "./TreeTabs"; + +const { Header, Sider, Content, Footer } = Layout; +const { TabPane } = Tabs; + +interface MainState { + workdir: string; + workspace: string; +} + +export default class Main extends Component { + state: MainState = { + workdir: "", + workspace: "", + }; + + settings: Settings; + tabs: TreeTabs; + + componentWillMount() { + this.updateSettings(); + RegisterNode(this.settings); + } + + componentDidMount() { + 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); + document.title = workspace; + this.setState({ workdir, workspace }); + }); + + console.log("workdir", this.settings.curWorkspace.getWorkdir()); + this.setState({ + workdir: this.settings.curWorkspace.getWorkdir(), + workspace: this.settings.curWorkspace.getFilepath(), + }); + } + + updateSettings() { + this.settings = Utils.getRemoteSettings(); + } + + render() { + console.log("render main"); + const { workdir, workspace } = this.state; + document.title = `行为树编辑器 - ${workspace}`; + return ( + + + { + this.tabs.openFile(path); + }} + /> + + + { + this.tabs = ref; + }} + /> + + + ); + } +} + +ReactDOM.render(
, document.getElementById("root") as HTMLElement);