double click to edit subtree
This commit is contained in:
1 parent
6b5abae6a3
commit
fed08a0f45
9 files changed
+176
-108
No files matched your search
+2
-25
@@ -1,9 +1,8 @@
|
|||||||
import { TreeGraph } from "@antv/g6";
|
import { message } from "antd";
|
||||||
import * as path from "path";
|
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
import Settings from "../main-process/Settings";
|
import Settings from "../main-process/Settings";
|
||||||
import { BehaviorNodeModel, BehaviorTreeModel, GraphNodeModel } from "./BehaviorTreeModel";
|
import { BehaviorNodeModel, BehaviorTreeModel, GraphNodeModel } from "./BehaviorTreeModel";
|
||||||
import { message } from "antd";
|
|
||||||
|
|
||||||
interface INodeDataLike {
|
interface INodeDataLike {
|
||||||
path?: string;
|
path?: string;
|
||||||
@@ -165,28 +164,6 @@ export const createFileData = (gNode: GraphNodeModel, includeSubtree?: boolean)
|
|||||||
return nodeData;
|
return nodeData;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const findParent = (graph: TreeGraph, node: GraphNodeModel) => {
|
|
||||||
if (node.parent) {
|
|
||||||
return graph.findDataById(node.parent);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isAncestor = (
|
|
||||||
graph: TreeGraph,
|
|
||||||
ancestor: GraphNodeModel,
|
|
||||||
node: GraphNodeModel
|
|
||||||
): boolean => {
|
|
||||||
if (ancestor.id === node.parent) {
|
|
||||||
return true;
|
|
||||||
} else if (node.parent) {
|
|
||||||
return isAncestor(graph, ancestor, graph.findDataById(node.parent) as GraphNodeModel);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getRemoteSettings = () => {
|
export const getRemoteSettings = () => {
|
||||||
return require("@electron/remote").getGlobal("settings") as Settings;
|
return require("@electron/remote").getGlobal("settings") as Settings;
|
||||||
};
|
};
|
||||||
|
|||||||
+12
-16
@@ -55,7 +55,7 @@ export default class AppMenu {
|
|||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: "新建",
|
label: "新建",
|
||||||
accelerator: "CommandOrControl+n",
|
accelerator: "CmdOrCtrl+n",
|
||||||
click: () => {
|
click: () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const res = await dialog.showSaveDialog({
|
const res = await dialog.showSaveDialog({
|
||||||
@@ -75,7 +75,7 @@ export default class AppMenu {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "打开文件",
|
label: "打开文件",
|
||||||
accelerator: "CommandOrControl+O",
|
accelerator: "CmdOrCtrl+O",
|
||||||
click: () => {
|
click: () => {
|
||||||
(async () => {
|
(async () => {
|
||||||
const res = await dialog.showOpenDialog({
|
const res = await dialog.showOpenDialog({
|
||||||
@@ -103,14 +103,14 @@ export default class AppMenu {
|
|||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: "保存",
|
label: "保存",
|
||||||
accelerator: "CommandOrControl+s",
|
accelerator: "CmdOrCtrl+s",
|
||||||
click: () => {
|
click: () => {
|
||||||
this.webContents.send(MainEventType.SAVE);
|
this.webContents.send(MainEventType.SAVE);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "全部保存",
|
label: "全部保存",
|
||||||
accelerator: "CommandOrControl+shift+s",
|
accelerator: "CmdOrCtrl+shift+s",
|
||||||
click: () => {
|
click: () => {
|
||||||
this.webContents.send(MainEventType.SAVE_ALL);
|
this.webContents.send(MainEventType.SAVE_ALL);
|
||||||
},
|
},
|
||||||
@@ -139,7 +139,7 @@ export default class AppMenu {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "构建",
|
label: "构建",
|
||||||
accelerator: "CommandOrControl+b",
|
accelerator: "CmdOrCtrl+b",
|
||||||
click: async () => {
|
click: async () => {
|
||||||
if (!this.buildDir) {
|
if (!this.buildDir) {
|
||||||
const res = await dialog.showOpenDialog({
|
const res = await dialog.showOpenDialog({
|
||||||
@@ -163,17 +163,13 @@ export default class AppMenu {
|
|||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: "撤销",
|
label: "撤销",
|
||||||
accelerator: "CommandOrControl+z",
|
accelerator: "CmdOrCtrl+z",
|
||||||
click: () => {
|
role: "undo",
|
||||||
this.webContents.send(MainEventType.UNDO);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "恢复",
|
label: "恢复",
|
||||||
accelerator: "CommandOrControl+y",
|
accelerator: process.platform === "darwin" ? "cmd+shift+z" : "ctrl+y",
|
||||||
click: () => {
|
role: "redo",
|
||||||
this.webContents.send(MainEventType.REDO);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
@@ -287,7 +283,7 @@ export default class AppMenu {
|
|||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
label: "打开目录",
|
label: "打开目录",
|
||||||
accelerator: "CommandOrControl+Shift+O",
|
accelerator: "CmdOrCtrl+Shift+O",
|
||||||
click: () => {
|
click: () => {
|
||||||
if (!this.settings.curWorkspace.getNodeConfPath()) {
|
if (!this.settings.curWorkspace.getNodeConfPath()) {
|
||||||
dialog.showErrorBox("警告", "请先指定节点定义配置!");
|
dialog.showErrorBox("警告", "请先指定节点定义配置!");
|
||||||
@@ -364,7 +360,7 @@ export default class AppMenu {
|
|||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: "热更",
|
label: "热更",
|
||||||
accelerator: "CommandOrControl+R",
|
accelerator: "CmdOrCtrl+R",
|
||||||
click: () => {
|
click: () => {
|
||||||
this.webContents.send(MainEventType.RELOAD_SERVER);
|
this.webContents.send(MainEventType.RELOAD_SERVER);
|
||||||
},
|
},
|
||||||
@@ -392,7 +388,7 @@ export default class AppMenu {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "打开控制台",
|
label: "打开控制台",
|
||||||
accelerator: "CommandOrControl+Shift+I",
|
accelerator: "CmdOrCtrl+Shift+I",
|
||||||
click: (_, browserWindow) => {
|
click: (_, browserWindow) => {
|
||||||
browserWindow.webContents.toggleDevTools();
|
browserWindow.webContents.toggleDevTools();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,12 +18,18 @@ export class MainProcess {
|
|||||||
nativeTheme.themeSource = "dark";
|
nativeTheme.themeSource = "dark";
|
||||||
app.on("ready", () => {
|
app.on("ready", () => {
|
||||||
this.createWindow();
|
this.createWindow();
|
||||||
electronLocalshortcut.register(this.mainWindow, "CommandOrControl+C", () => {
|
this.registerShortcut("CmdOrCtrl+C", () => {
|
||||||
this.mainWindow.webContents.send(MainEventType.COPY_NODE);
|
this.mainWindow.webContents.send(MainEventType.COPY_NODE);
|
||||||
});
|
});
|
||||||
electronLocalshortcut.register(this.mainWindow, "CommandOrControl+V", () => {
|
this.registerShortcut("CmdOrCtrl+V", () => {
|
||||||
this.mainWindow.webContents.send(MainEventType.PASTE_NODE);
|
this.mainWindow.webContents.send(MainEventType.PASTE_NODE);
|
||||||
});
|
});
|
||||||
|
this.registerShortcut("CmdOrCtrl+Z", () => {
|
||||||
|
this.mainWindow.webContents.send(MainEventType.UNDO);
|
||||||
|
});
|
||||||
|
this.registerShortcut(process.platform === "darwin" ? "Cmd+Shift+Z" : "ctrl+y", () => {
|
||||||
|
this.mainWindow.webContents.send(MainEventType.REDO);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
app.on("window-all-closed", () => {
|
app.on("window-all-closed", () => {
|
||||||
if (process.platform !== "darwin") {
|
if (process.platform !== "darwin") {
|
||||||
@@ -37,6 +43,10 @@ export class MainProcess {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerShortcut(accelerator: string | string[], callback: () => void) {
|
||||||
|
electronLocalshortcut.register(this.mainWindow, accelerator, callback);
|
||||||
|
}
|
||||||
|
|
||||||
createWindow() {
|
createWindow() {
|
||||||
this.settings = new Settings();
|
this.settings = new Settings();
|
||||||
global.settings = this.settings;
|
global.settings = this.settings;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const { Item } = Form;
|
|||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
interface NodePanelProps {
|
interface NodePanelProps {
|
||||||
|
isSubtreeNode: boolean;
|
||||||
model: BehaviorNodeModel;
|
model: BehaviorNodeModel;
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
updateNode: (id: string, forceUpdate: boolean) => void;
|
updateNode: (id: string, forceUpdate: boolean) => void;
|
||||||
@@ -151,7 +152,7 @@ export default class NodePanel extends React.Component<NodePanelProps> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { model, settings } = this.props;
|
const { model, settings, isSubtreeNode } = this.props;
|
||||||
const conf = settings.getNodeConf(model.name);
|
const conf = settings.getNodeConf(model.name);
|
||||||
const title = conf.desc;
|
const title = conf.desc;
|
||||||
|
|
||||||
@@ -178,6 +179,7 @@ export default class NodePanel extends React.Component<NodePanelProps> {
|
|||||||
</Item>
|
</Item>
|
||||||
<Item label="节点名称" name="name">
|
<Item label="节点名称" name="name">
|
||||||
<AutoComplete
|
<AutoComplete
|
||||||
|
disabled={isSubtreeNode}
|
||||||
options={options}
|
options={options}
|
||||||
onBlur={this.handleSubmit}
|
onBlur={this.handleSubmit}
|
||||||
filterOption={(inputValue: string, option: any) => {
|
filterOption={(inputValue: string, option: any) => {
|
||||||
@@ -189,24 +191,24 @@ export default class NodePanel extends React.Component<NodePanelProps> {
|
|||||||
/>
|
/>
|
||||||
</Item>
|
</Item>
|
||||||
<Item label="节点说明" name="desc">
|
<Item label="节点说明" name="desc">
|
||||||
<Input onBlur={this.handleSubmit} />
|
<Input disabled={isSubtreeNode} onBlur={this.handleSubmit} />
|
||||||
</Item>
|
</Item>
|
||||||
<Item label="调试开关" name="debug" valuePropName="checked">
|
<Item label="调试开关" name="debug" valuePropName="checked">
|
||||||
<Switch onChange={this.handleSubmit} />
|
<Switch disabled={isSubtreeNode} onChange={this.handleSubmit} />
|
||||||
</Item>
|
</Item>
|
||||||
<Item label="子树" name="path">
|
<Item label="子树" name="path">
|
||||||
<Input onBlur={this.handleSubmit} />
|
<Input disabled={isSubtreeNode && !model.path} onBlur={this.handleSubmit} />
|
||||||
</Item>
|
</Item>
|
||||||
<Markdown source={conf.doc} />
|
<Markdown source={conf.doc} />
|
||||||
{this.renderInputs(conf)}
|
{this.renderInputs(conf, isSubtreeNode)}
|
||||||
{this.renderArgs(conf)}
|
{this.renderArgs(conf, isSubtreeNode)}
|
||||||
{this.renderOutputs(conf)}
|
{this.renderOutputs(conf, isSubtreeNode)}
|
||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderArgs(conf: BehaviorNodeTypeModel) {
|
renderArgs(conf: BehaviorNodeTypeModel, isSubtreeNode: boolean) {
|
||||||
if (!conf || !conf.args || conf.args.length == 0) {
|
if (!conf || !conf.args || conf.args.length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -215,24 +217,41 @@ export default class NodePanel extends React.Component<NodePanelProps> {
|
|||||||
const normalArgs = (e: ArgsDefType) => {
|
const normalArgs = (e: ArgsDefType) => {
|
||||||
const required = e.type.indexOf("?") == -1;
|
const required = e.type.indexOf("?") == -1;
|
||||||
if (e.type.indexOf("string") >= 0) {
|
if (e.type.indexOf("string") >= 0) {
|
||||||
return <Input onBlur={this.handleSubmit} />;
|
return <Input disabled={isSubtreeNode} onBlur={this.handleSubmit} />;
|
||||||
} else if (e.type.indexOf("int") >= 0) {
|
} else if (e.type.indexOf("int") >= 0) {
|
||||||
return (
|
return (
|
||||||
<InputNumber
|
<InputNumber
|
||||||
|
disabled={isSubtreeNode}
|
||||||
precision={0}
|
precision={0}
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
onBlur={this.handleSubmit}
|
onBlur={this.handleSubmit}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (e.type.indexOf("float") >= 0) {
|
} else if (e.type.indexOf("float") >= 0) {
|
||||||
return <InputNumber style={{ width: "100%" }} onBlur={this.handleSubmit} />;
|
return (
|
||||||
|
<InputNumber
|
||||||
|
disabled={isSubtreeNode}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
onBlur={this.handleSubmit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
} else if (e.type.indexOf("boolean") >= 0) {
|
} else if (e.type.indexOf("boolean") >= 0) {
|
||||||
return <Switch onChange={this.handleSubmit} />;
|
return <Switch disabled={isSubtreeNode} onChange={this.handleSubmit} />;
|
||||||
} else if (e.type.indexOf("code") >= 0) {
|
} else if (e.type.indexOf("code") >= 0) {
|
||||||
return <Input onBlur={this.handleSubmit} placeholder={"表达式"} />;
|
return (
|
||||||
|
<Input
|
||||||
|
disabled={isSubtreeNode}
|
||||||
|
onBlur={this.handleSubmit}
|
||||||
|
placeholder={"表达式"}
|
||||||
|
/>
|
||||||
|
);
|
||||||
} else if (e.type.indexOf("enum") >= 0) {
|
} else if (e.type.indexOf("enum") >= 0) {
|
||||||
return (
|
return (
|
||||||
<Select style={{ width: 120 }} onChange={this.handleSubmit}>
|
<Select
|
||||||
|
disabled={isSubtreeNode}
|
||||||
|
style={{ width: 120 }}
|
||||||
|
onChange={this.handleSubmit}
|
||||||
|
>
|
||||||
{e.options.map((e) => {
|
{e.options.map((e) => {
|
||||||
return (
|
return (
|
||||||
<Option key={e.name} value={e.value}>
|
<Option key={e.name} value={e.value}>
|
||||||
@@ -249,7 +268,11 @@ export default class NodePanel extends React.Component<NodePanelProps> {
|
|||||||
const customArgs = () => {
|
const customArgs = () => {
|
||||||
return (
|
return (
|
||||||
<Item name="customArgs" label="自定义" key="customArgs">
|
<Item name="customArgs" label="自定义" key="customArgs">
|
||||||
<Input.TextArea onBlur={this.handleSubmit} style={{ minHeight: 100 }} />
|
<Input.TextArea
|
||||||
|
disabled={isSubtreeNode}
|
||||||
|
onBlur={this.handleSubmit}
|
||||||
|
style={{ minHeight: 100 }}
|
||||||
|
/>
|
||||||
</Item>
|
</Item>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -283,7 +306,7 @@ export default class NodePanel extends React.Component<NodePanelProps> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInputs(conf: BehaviorNodeTypeModel) {
|
renderInputs(conf: BehaviorNodeTypeModel, isSubtreeNode: boolean) {
|
||||||
if (!conf.input || !conf.input || conf.input.length == 0) {
|
if (!conf.input || !conf.input || conf.input.length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -296,7 +319,7 @@ export default class NodePanel extends React.Component<NodePanelProps> {
|
|||||||
{conf.input.map((e, i) => {
|
{conf.input.map((e, i) => {
|
||||||
return (
|
return (
|
||||||
<Item label={e} name={`input.${i}`} key={`input.${i}`}>
|
<Item label={e} name={`input.${i}`} key={`input.${i}`}>
|
||||||
<Input onBlur={this.handleSubmit} />
|
<Input disabled={isSubtreeNode} onBlur={this.handleSubmit} />
|
||||||
</Item>
|
</Item>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -304,7 +327,7 @@ export default class NodePanel extends React.Component<NodePanelProps> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderOutputs(conf: BehaviorNodeTypeModel) {
|
renderOutputs(conf: BehaviorNodeTypeModel, isSubtreeNode: boolean) {
|
||||||
if (!conf.output || !conf.output || conf.output.length == 0) {
|
if (!conf.output || !conf.output || conf.output.length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -316,7 +339,7 @@ export default class NodePanel extends React.Component<NodePanelProps> {
|
|||||||
{conf.output.map((e, i) => {
|
{conf.output.map((e, i) => {
|
||||||
return (
|
return (
|
||||||
<Item label={e} name={`output.${i}`} key={`output.${i}`}>
|
<Item label={e} name={`output.${i}`} key={`output.${i}`}>
|
||||||
<Input onBlur={this.handleSubmit} />
|
<Input disabled={isSubtreeNode} onBlur={this.handleSubmit} />
|
||||||
</Item>
|
</Item>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import "./Editor.css";
|
|||||||
export interface EditorProps {
|
export interface EditorProps {
|
||||||
filepath: string;
|
filepath: string;
|
||||||
onChangeSaveState: (unsave: boolean) => void;
|
onChangeSaveState: (unsave: boolean) => void;
|
||||||
|
onOpenSubtree: (path: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EditorState {
|
interface EditorState {
|
||||||
@@ -32,9 +33,9 @@ interface EditorState {
|
|||||||
export default class Editor extends React.Component<EditorProps, EditorState> {
|
export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||||
private ref: React.RefObject<any>;
|
private ref: React.RefObject<any>;
|
||||||
state: EditorState = {};
|
state: EditorState = {};
|
||||||
|
isInEditor: boolean = false;
|
||||||
|
|
||||||
private graph: TreeGraph;
|
private graph: TreeGraph;
|
||||||
private isInEditor: boolean = false;
|
|
||||||
private dragSrcId: string;
|
private dragSrcId: string;
|
||||||
private dragDstId: string;
|
private dragDstId: string;
|
||||||
private autoId: number;
|
private autoId: number;
|
||||||
@@ -71,6 +72,47 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findParent(node: GraphNodeModel) {
|
||||||
|
if (node.parent) {
|
||||||
|
return this.graph.findDataById(node.parent) as GraphNodeModel;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findSubtree(node?: GraphNodeModel): GraphNodeModel | null {
|
||||||
|
if (node?.path) {
|
||||||
|
return node;
|
||||||
|
} else if (node?.parent) {
|
||||||
|
return this.findSubtree(this.findParent(node));
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubtreeNode(node?: GraphNodeModel): boolean {
|
||||||
|
if (node?.path) {
|
||||||
|
return true;
|
||||||
|
} else if (node?.parent) {
|
||||||
|
return this.isSubtreeNode(this.findParent(node));
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isAncestor(ancestor: GraphNodeModel, node: GraphNodeModel): boolean {
|
||||||
|
if (ancestor.id === node.parent) {
|
||||||
|
return true;
|
||||||
|
} else if (node.parent) {
|
||||||
|
return this.isAncestor(
|
||||||
|
ancestor,
|
||||||
|
this.graph.findDataById(node.parent) as GraphNodeModel
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const graph = new TreeGraph({
|
const graph = new TreeGraph({
|
||||||
container: this.ref.current,
|
container: this.ref.current,
|
||||||
@@ -80,28 +122,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
|||||||
maxZoom: 2,
|
maxZoom: 2,
|
||||||
// fitCenter: true,
|
// fitCenter: true,
|
||||||
modes: {
|
modes: {
|
||||||
default: [
|
default: ["drag-canvas", "zoom-canvas", "click-select", "hover"],
|
||||||
"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: {
|
defaultEdge: {
|
||||||
type: "cubic-horizontal",
|
type: "cubic-horizontal",
|
||||||
@@ -147,6 +168,27 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
|||||||
this.isInEditor = false;
|
this.isInEditor = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
graph.on("node:click", (e: G6GraphEvent) => {
|
||||||
|
if (e.shape.cfg.name === "collapse-icon") {
|
||||||
|
const item = e.item;
|
||||||
|
this.onSelectNode(item.getID());
|
||||||
|
const data = item.getModel();
|
||||||
|
if ((data as unknown as GraphNodeModel).children?.length > 0) {
|
||||||
|
data.collapsed = !data.collapsed;
|
||||||
|
graph.setItemState(item, "collapsed", data.collapsed as boolean);
|
||||||
|
e.shape.attr("symbol", data.collapsed ? G6.Marker.expand : G6.Marker.collapse);
|
||||||
|
this.graph.layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
graph.on("node:dblclick", (e: G6GraphEvent) => {
|
||||||
|
const data = this.findSubtree(e.item.getModel() as unknown as GraphNodeModel);
|
||||||
|
if (data) {
|
||||||
|
this.props.onOpenSubtree(data.path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
graph.on("node:mouseenter", (e: G6GraphEvent) => {
|
graph.on("node:mouseenter", (e: G6GraphEvent) => {
|
||||||
const { item } = e;
|
const { item } = e;
|
||||||
if (item.hasState("selected")) {
|
if (item.hasState("selected")) {
|
||||||
@@ -258,15 +300,15 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
|||||||
|
|
||||||
const rootData = graph.findDataById("1");
|
const rootData = graph.findDataById("1");
|
||||||
const srcData = graph.findDataById(srcNodeId) as GraphNodeModel;
|
const srcData = graph.findDataById(srcNodeId) as GraphNodeModel;
|
||||||
const srcParent = Utils.findParent(this.graph, srcData);
|
const srcParent = this.findParent(srcData);
|
||||||
const dstData = graph.findDataById(dstNode.getID()) as GraphNodeModel;
|
const dstData = graph.findDataById(dstNode.getID()) as GraphNodeModel;
|
||||||
const dstParent = Utils.findParent(this.graph, dstData);
|
const dstParent = this.findParent(dstData);
|
||||||
if (!srcParent) {
|
if (!srcParent) {
|
||||||
console.log("no parent!");
|
console.log("no parent!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Utils.isAncestor(this.graph, srcData, dstData)) {
|
if (this.isAncestor(srcData, dstData)) {
|
||||||
// 不能将父节点拖到自已的子孙节点
|
// 不能将父节点拖到自已的子孙节点
|
||||||
console.log("cannot move to child");
|
console.log("cannot move to child");
|
||||||
return;
|
return;
|
||||||
@@ -379,10 +421,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
|||||||
|
|
||||||
this.onSelectNode(null);
|
this.onSelectNode(null);
|
||||||
this.pushUndoStack();
|
this.pushUndoStack();
|
||||||
const parentData = Utils.findParent(
|
const parentData = this.findParent(this.graph.findDataById(curNodeId) as GraphNodeModel);
|
||||||
this.graph,
|
|
||||||
this.graph.findDataById(curNodeId) as GraphNodeModel
|
|
||||||
);
|
|
||||||
parentData.children = parentData.children.filter((e) => e.id != curNodeId);
|
parentData.children = parentData.children.filter((e) => e.id != curNodeId);
|
||||||
this.changeWithoutAnim();
|
this.changeWithoutAnim();
|
||||||
}
|
}
|
||||||
@@ -583,6 +622,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
|||||||
{curNode ? (
|
{curNode ? (
|
||||||
<NodePanel
|
<NodePanel
|
||||||
model={curNode}
|
model={curNode}
|
||||||
|
isSubtreeNode={this.isSubtreeNode(curNode)}
|
||||||
settings={this.settings}
|
settings={this.settings}
|
||||||
updateNode={(id, forceUpdate) => {
|
updateNode={(id, forceUpdate) => {
|
||||||
if (forceUpdate) {
|
if (forceUpdate) {
|
||||||
|
|||||||
@@ -290,21 +290,6 @@ export default function (settings: Settings) {
|
|||||||
y += 20;
|
y += 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(cfg.children) && cfg.children.length > 0) {
|
|
||||||
group.addShape("marker", {
|
|
||||||
attrs: {
|
|
||||||
x: w,
|
|
||||||
y: h / 2,
|
|
||||||
r: 6,
|
|
||||||
symbol: G6.Marker.collapse,
|
|
||||||
stroke: "#666",
|
|
||||||
lineWidth: 1,
|
|
||||||
fill: "#fff",
|
|
||||||
},
|
|
||||||
name: "collapse-icon",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
group.addShape("rect", {
|
group.addShape("rect", {
|
||||||
name: "drag-up",
|
name: "drag-up",
|
||||||
attrs: {
|
attrs: {
|
||||||
@@ -346,6 +331,22 @@ export default function (settings: Settings) {
|
|||||||
},
|
},
|
||||||
draggable: true,
|
draggable: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (Array.isArray(cfg.children) && cfg.children.length > 0) {
|
||||||
|
group.addShape("marker", {
|
||||||
|
attrs: {
|
||||||
|
x: w,
|
||||||
|
y: h / 2,
|
||||||
|
r: 6,
|
||||||
|
symbol: G6.Marker.collapse,
|
||||||
|
stroke: "#666",
|
||||||
|
lineWidth: 1,
|
||||||
|
fill: "#fff",
|
||||||
|
},
|
||||||
|
name: "collapse-icon",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return shape;
|
return shape;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -93,12 +93,16 @@ export default class TreeTabs extends Component<TreeTabsProps, TreeTabsState> {
|
|||||||
|
|
||||||
ipcRenderer.on(MainEventType.UNDO, () => {
|
ipcRenderer.on(MainEventType.UNDO, () => {
|
||||||
const editor = this.getCurEditor();
|
const editor = this.getCurEditor();
|
||||||
editor?.undo();
|
if (editor?.isInEditor) {
|
||||||
|
editor.undo();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on(MainEventType.REDO, () => {
|
ipcRenderer.on(MainEventType.REDO, () => {
|
||||||
const editor = this.getCurEditor();
|
const editor = this.getCurEditor();
|
||||||
editor?.redo();
|
if (editor?.isInEditor) {
|
||||||
|
editor.redo();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +147,10 @@ export default class TreeTabs extends Component<TreeTabsProps, TreeTabsState> {
|
|||||||
trees = trees.filter((tree) => tree.filepath != path);
|
trees = trees.filter((tree) => tree.filepath != path);
|
||||||
const length = trees.length;
|
const length = trees.length;
|
||||||
this.setState({ trees: trees, curTree: length > 0 ? trees[0].filepath : null });
|
this.setState({ trees: trees, curTree: length > 0 ? trees[0].filepath : null });
|
||||||
|
if (length > 0) {
|
||||||
|
const editor = this.editors[trees[0].filepath];
|
||||||
|
editor?.reload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -184,6 +192,10 @@ export default class TreeTabs extends Component<TreeTabsProps, TreeTabsState> {
|
|||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
onOpenSubtree={(path) => {
|
||||||
|
const settings = Utils.getRemoteSettings();
|
||||||
|
this.openFile(settings.workdir + "/" + path);
|
||||||
|
}}
|
||||||
ref={(ref) => {
|
ref={(ref) => {
|
||||||
this.editors[tree.filepath] = ref;
|
this.editors[tree.filepath] = ref;
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
"id": 4,
|
"id": 4,
|
||||||
"name": "Sequence",
|
"name": "Sequence",
|
||||||
"desc": "新建行为树",
|
"desc": "新建行为树",
|
||||||
|
"args": {},
|
||||||
"path": "subtree2.json"
|
"path": "subtree2.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,6 +7,14 @@
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
|
"name": "Cmp",
|
||||||
|
"args": {},
|
||||||
|
"input": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
"name": "unknow"
|
"name": "unknow"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in new issue
Block a user