fix node layout error and support subtree
This commit is contained in:
1 parent
d3c5bf53e7
commit
258a006383
21 files changed
+6019
-11457
No files matched your search
@@ -207,20 +207,32 @@ export default class NodePanel extends React.Component<NodePanelProps> {
|
||||
const required = e.type.indexOf("?") == -1;
|
||||
if (e.type.indexOf("string") >= 0) {
|
||||
return <Input onBlur={this.handleSubmit} />;
|
||||
} else if (e.type.indexOf("int") >= 0 || e.type.indexOf("float") >= 0) {
|
||||
} else if (e.type.indexOf("int") >= 0) {
|
||||
return (
|
||||
<InputNumber
|
||||
precision={0}
|
||||
style={{ width: "100%" }}
|
||||
onBlur={this.handleSubmit}
|
||||
/>
|
||||
);
|
||||
} else if (e.type.indexOf("float") >= 0) {
|
||||
return <InputNumber style={{ width: "100%" }} onBlur={this.handleSubmit} />;
|
||||
} else if (e.type.indexOf("boolean") >= 0) {
|
||||
return <Switch onChange={this.handleSubmit} />;
|
||||
} else if (e.type.indexOf("code") >= 0) {
|
||||
return <Input onBlur={this.handleSubmit} placeholder={"表达式"} />;
|
||||
} else if (e.type.indexOf("enum") >= 0 ) {
|
||||
return <Select style={{ width: 120 }} onChange={this.handleSubmit} >
|
||||
{
|
||||
e.options.map((e)=>{
|
||||
return (<Option key={e.name} value={e.value}>{e.name}</Option>)
|
||||
})
|
||||
}
|
||||
</Select>;
|
||||
} else if (e.type.indexOf("enum") >= 0) {
|
||||
return (
|
||||
<Select style={{ width: 120 }} onChange={this.handleSubmit}>
|
||||
{e.options.map((e) => {
|
||||
return (
|
||||
<Option key={e.name} value={e.value}>
|
||||
{e.name}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+107
-48
@@ -1,22 +1,22 @@
|
||||
import * as React from "react";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { Row, Col, message, Card } 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 { Col, Row, message } from "antd";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import * as React from "react";
|
||||
import {
|
||||
BehaviorNodeModel,
|
||||
BehaviorTreeModel,
|
||||
GraphNodeModel,
|
||||
BehaviorNodeModel,
|
||||
} from "../../common/BehaviorTreeModel";
|
||||
import TreePanel from "./TreePanel";
|
||||
import * as Utils from "../../common/Utils";
|
||||
import Settings from "../../main-process/Settings";
|
||||
import NodePanel from "./NodePanel";
|
||||
import TreePanel from "./TreePanel";
|
||||
|
||||
import "./Editor.css";
|
||||
import { Matrix } from "@antv/g6/lib/types";
|
||||
import { clipboard } from "electron";
|
||||
import { Matrix } from '@antv/g6/lib/types';
|
||||
import "./Editor.css";
|
||||
|
||||
export interface EditorProps {
|
||||
filepath: string;
|
||||
@@ -26,7 +26,7 @@ export interface EditorProps {
|
||||
interface EditorState {
|
||||
curNodeId?: string;
|
||||
blockNodeSelectChange?: boolean;
|
||||
viewportMatrix?: Matrix
|
||||
viewportMatrix?: Matrix;
|
||||
}
|
||||
|
||||
export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
@@ -34,6 +34,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
state: EditorState = {};
|
||||
|
||||
private graph: TreeGraph;
|
||||
private isInEditor: boolean = false;
|
||||
private dragSrcId: string;
|
||||
private dragDstId: string;
|
||||
private autoId: number;
|
||||
@@ -56,7 +57,9 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: EditorProps, nextState: EditorState) {
|
||||
return this.props.filepath != nextProps.filepath || this.state.curNodeId != nextState.curNodeId;
|
||||
return (
|
||||
this.props.filepath != nextProps.filepath || this.state.curNodeId != nextState.curNodeId
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -118,15 +121,23 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
});
|
||||
|
||||
graph.on("viewportchange", (data: any) => {
|
||||
if (data.action == 'translate' || data.action == 'zoom') {
|
||||
this.state.viewportMatrix = data.matrix
|
||||
if (data.action == "translate" || data.action == "zoom") {
|
||||
this.state.viewportMatrix = data.matrix;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
graph.on("contextmenu", (e: G6GraphEvent) => {
|
||||
require("@electron/remote").Menu.getApplicationMenu().popup();
|
||||
});
|
||||
|
||||
graph.on("canvas:mouseover", () => {
|
||||
this.isInEditor = true;
|
||||
});
|
||||
|
||||
graph.on("canvas:mouseleave", () => {
|
||||
this.isInEditor = false;
|
||||
});
|
||||
|
||||
graph.on("node:mouseenter", (e: G6GraphEvent) => {
|
||||
const { item } = e;
|
||||
if (item.hasState("selected")) {
|
||||
@@ -146,7 +157,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
graph.on("nodeselectchange", (e: G6GraphEvent) => {
|
||||
if (this.state.blockNodeSelectChange) {
|
||||
// ** 重置选中效果
|
||||
this.onSelectNode(this.state.curNodeId)
|
||||
this.onSelectNode(this.state.curNodeId);
|
||||
return;
|
||||
}
|
||||
if (e.target) {
|
||||
@@ -237,16 +248,16 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
}
|
||||
|
||||
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());
|
||||
const srcData = graph.findDataById(srcNodeId) as GraphNodeModel;
|
||||
const srcParent = Utils.findParent(this.graph, srcData);
|
||||
const dstData = graph.findDataById(dstNode.getID()) as GraphNodeModel;
|
||||
const dstParent = Utils.findParent(this.graph, dstData);
|
||||
if (!srcParent) {
|
||||
console.log("no parent!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Utils.findFromAllChildren(srcData, dstData.id)) {
|
||||
if (Utils.isAncestor(this.graph, srcData, dstData)) {
|
||||
// 不能将父节点拖到自已的子孙节点
|
||||
console.log("cannot move to child");
|
||||
return;
|
||||
@@ -262,6 +273,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
if (!dstData.children) {
|
||||
dstData.children = [];
|
||||
}
|
||||
srcData.parent = dstData.id;
|
||||
dstData.children.push(srcData);
|
||||
} else if (dragDir == "dragUp") {
|
||||
if (!dstParent) {
|
||||
@@ -269,6 +281,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
}
|
||||
removeSrc();
|
||||
const idx = dstParent.children.findIndex((e) => e.id == dstData.id);
|
||||
srcData.parent = dstParent.id;
|
||||
dstParent.children.splice(idx, 0, srcData);
|
||||
} else if (dragDir == "dragDown") {
|
||||
if (!dstParent) {
|
||||
@@ -276,6 +289,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
}
|
||||
removeSrc();
|
||||
const idx = dstParent.children.findIndex((e) => e.id == dstData.id);
|
||||
srcData.parent = dstParent.id;
|
||||
dstParent.children.splice(idx + 1, 0, srcData);
|
||||
} else {
|
||||
return;
|
||||
@@ -309,6 +323,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
|
||||
if (this.state.curNodeId) {
|
||||
graph.setItemState(this.state.curNodeId, "selected", false);
|
||||
graph.setItemState(this.state.curNodeId, "hover", false);
|
||||
}
|
||||
|
||||
this.setState({ curNodeId });
|
||||
@@ -318,6 +333,10 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
}
|
||||
|
||||
createNode(name: string) {
|
||||
if (!this.isInEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("editor create node", name);
|
||||
const { curNodeId } = this.state;
|
||||
if (!curNodeId) {
|
||||
@@ -333,7 +352,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
if (!curNodeData.children) {
|
||||
curNodeData.children = [];
|
||||
}
|
||||
curNodeData.children.push(Utils.createTreeData(newNodeData, this.settings));
|
||||
curNodeData.children.push(Utils.createTreeData(newNodeData, this.settings, curNodeId));
|
||||
this.changeWithoutAnim();
|
||||
}
|
||||
|
||||
@@ -344,15 +363,17 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (curNodeId == "1") {
|
||||
if (curNodeId === "1") {
|
||||
message.warn("根节点不能删除!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.onSelectNode(null);
|
||||
this.pushUndoStack();
|
||||
const rootData = this.graph.findDataById("1");
|
||||
const parentData = Utils.findParent(rootData, curNodeId);
|
||||
const parentData = Utils.findParent(
|
||||
this.graph,
|
||||
this.graph.findDataById(curNodeId) as GraphNodeModel
|
||||
);
|
||||
parentData.children = parentData.children.filter((e) => e.id != curNodeId);
|
||||
this.changeWithoutAnim();
|
||||
}
|
||||
@@ -380,20 +401,35 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
root,
|
||||
desc: this.treeModel.desc,
|
||||
} as BehaviorTreeModel;
|
||||
fs.writeFileSync(
|
||||
filepath,
|
||||
JSON.stringify(treeModel, null, 2)
|
||||
);
|
||||
fs.writeFileSync(filepath, JSON.stringify(treeModel, null, 2));
|
||||
this.props.onChangeSaveState(false);
|
||||
this.unsave = false;
|
||||
|
||||
const treeData = Utils.createTreeData(root, this.settings);
|
||||
this.autoId = Utils.refreshNodeId(treeData);
|
||||
|
||||
this.graph.set("animate", false);
|
||||
this.graph.changeData(Utils.createTreeData(root, this.settings));
|
||||
this.graph.changeData(treeData);
|
||||
this.graph.layout();
|
||||
this.restoreViewport();
|
||||
this.graph.set("animate", true);
|
||||
}
|
||||
|
||||
reload() {
|
||||
if (!this.unsave) {
|
||||
const data = this.graph.findDataById("1") as GraphNodeModel;
|
||||
this.autoId = Utils.refreshNodeId(data);
|
||||
const root = Utils.createFileData(data);
|
||||
const treeData = Utils.createTreeData(root, this.settings);
|
||||
this.autoId = Utils.refreshNodeId(treeData);
|
||||
this.graph.set("animate", false);
|
||||
this.graph.changeData(treeData);
|
||||
this.graph.layout();
|
||||
this.restoreViewport();
|
||||
this.graph.set("animate", true);
|
||||
}
|
||||
}
|
||||
|
||||
getTreeModel() {
|
||||
const { filepath } = this.props;
|
||||
const data = this.graph.findDataById("1") as GraphNodeModel;
|
||||
@@ -407,28 +443,40 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
}
|
||||
|
||||
copyNode() {
|
||||
if (!this.isInEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("editor copy node");
|
||||
const { curNodeId } = this.state;
|
||||
if (!curNodeId) {
|
||||
return;
|
||||
}
|
||||
const data = this.graph.findDataById(curNodeId) as GraphNodeModel;
|
||||
clipboard.writeText(JSON.stringify(Utils.cloneNodeData(data), null, 2));
|
||||
const str = JSON.stringify(Utils.cloneNodeData(data), null, 2);
|
||||
console.log("copy:", str);
|
||||
clipboard.writeText(str);
|
||||
}
|
||||
|
||||
pasteNode() {
|
||||
if (!this.isInEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 = Utils.createTreeData(JSON.parse(str), this.settings);
|
||||
console.log("paste:", str);
|
||||
const data = Utils.createTreeData(JSON.parse(str), this.settings, curNodeId);
|
||||
this.autoId = Utils.refreshNodeId(data, this.autoId);
|
||||
this.onSelectNode(null);
|
||||
if (!curNodeData.children) {
|
||||
@@ -446,7 +494,9 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
|
||||
useStackData(data: BehaviorNodeModel) {
|
||||
this.graph.set("animate", false);
|
||||
this.graph.changeData(Utils.createTreeData(data, this.settings));
|
||||
this.data = Utils.createTreeData(data, this.settings);
|
||||
this.autoId = Utils.refreshNodeId(this.data);
|
||||
this.graph.changeData(this.data);
|
||||
this.graph.layout();
|
||||
this.restoreViewport();
|
||||
this.graph.set("animate", true);
|
||||
@@ -456,7 +506,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
}
|
||||
|
||||
pushUndoStack(keepRedo?: boolean) {
|
||||
this.undoStack.push(Utils.cloneNodeData(this.graph.findDataById('1') as GraphNodeModel));
|
||||
this.undoStack.push(Utils.cloneNodeData(this.graph.findDataById("1") as GraphNodeModel));
|
||||
console.log("push undo", this.undoStack);
|
||||
if (!keepRedo) {
|
||||
this.redoStack = [];
|
||||
@@ -464,7 +514,7 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
}
|
||||
|
||||
pushRedoStack() {
|
||||
this.redoStack.push(Utils.cloneNodeData(this.graph.findDataById('1') as GraphNodeModel));
|
||||
this.redoStack.push(Utils.cloneNodeData(this.graph.findDataById("1") as GraphNodeModel));
|
||||
console.log("push redo", this.redoStack);
|
||||
}
|
||||
|
||||
@@ -504,22 +554,35 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
return (
|
||||
<div className="editor">
|
||||
<Row className="editorBd">
|
||||
<Col span={18} className="editorContent" ref={this.ref} onMouseDownCapture={(event) => {
|
||||
this.state.blockNodeSelectChange = false;
|
||||
}} />
|
||||
<Col span={6} className="editorSidebar" onMouseDownCapture={(event) => {
|
||||
this.state.blockNodeSelectChange = true;
|
||||
}}>
|
||||
<Col
|
||||
span={18}
|
||||
className="editorContent"
|
||||
ref={this.ref}
|
||||
onMouseDownCapture={(event) => {
|
||||
this.state.blockNodeSelectChange = false;
|
||||
}}
|
||||
/>
|
||||
<Col
|
||||
span={6}
|
||||
className="editorSidebar"
|
||||
onMouseDownCapture={(event) => {
|
||||
this.state.blockNodeSelectChange = true;
|
||||
}}
|
||||
>
|
||||
{curNode ? (
|
||||
<NodePanel
|
||||
model={curNode}
|
||||
settings={this.settings}
|
||||
updateNode={(id, forceUpdate) => {
|
||||
if (forceUpdate) {
|
||||
const data: any = this.graph.findDataById(id);
|
||||
const data: GraphNodeModel = this.graph.findDataById(
|
||||
id
|
||||
) as GraphNodeModel;
|
||||
data.conf = this.settings.getNodeConf(data.name);
|
||||
data.size = Utils.calcTreeNodeSize(data);
|
||||
this.changeWithoutAnim();
|
||||
this.save();
|
||||
return;
|
||||
}
|
||||
const item = this.graph.findById(id);
|
||||
item.draw();
|
||||
@@ -533,12 +596,8 @@ export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
) : (
|
||||
<TreePanel
|
||||
model={this.treeModel}
|
||||
onRenameTree={(name: string) => {
|
||||
|
||||
}}
|
||||
onRemoveTree={() => {
|
||||
|
||||
}}
|
||||
onRenameTree={(name: string) => {}}
|
||||
onRemoveTree={() => {}}
|
||||
onChangeTreeDesc={(desc) => {
|
||||
this.changeTreeDesc(desc);
|
||||
}}
|
||||
|
||||
+111
-51
@@ -1,6 +1,7 @@
|
||||
import G6 from "@antv/g6";
|
||||
import { toBreakWord } from "../common/Utils";
|
||||
import Settings from "../main-process/Settings";
|
||||
import { BehaviorNodeTypeModel } from "../common/BehaviorTreeModel";
|
||||
|
||||
const NODE_COLORS: any = {
|
||||
["Composite"]: "rgb(91,237,32)",
|
||||
@@ -8,8 +9,11 @@ const NODE_COLORS: any = {
|
||||
["Condition"]: "rgb(228,20,139)",
|
||||
["Action"]: "rgb(91,143,249)",
|
||||
["Other"]: "rgb(112,112,112)",
|
||||
["Error"]: "rgb(255,0,0)",
|
||||
};
|
||||
|
||||
const SELECTED_LINE_WIDTH = 4;
|
||||
|
||||
export default function (settings: Settings) {
|
||||
const collapseStyle: any = {
|
||||
"collapse-icon": {
|
||||
@@ -22,6 +26,10 @@ export default function (settings: Settings) {
|
||||
{
|
||||
options: {
|
||||
type: "rect",
|
||||
anchorPoints: [
|
||||
[0, 0.5],
|
||||
[1, 0.5],
|
||||
],
|
||||
labelCfg: {
|
||||
style: {
|
||||
fill: "blue",
|
||||
@@ -35,7 +43,10 @@ export default function (settings: Settings) {
|
||||
selected: {
|
||||
"main-box": {
|
||||
stroke: "yellow",
|
||||
lineWidth: 5,
|
||||
lineWidth: SELECTED_LINE_WIDTH,
|
||||
},
|
||||
"name-bg-selected": {
|
||||
fill: "yellow",
|
||||
},
|
||||
},
|
||||
hover: {
|
||||
@@ -47,35 +58,39 @@ export default function (settings: Settings) {
|
||||
},
|
||||
dragRight: {
|
||||
"drag-right": {
|
||||
fillOpacity: 0.6,
|
||||
fillOpacity: 0.7,
|
||||
},
|
||||
},
|
||||
dragUp: {
|
||||
"drag-up": {
|
||||
fillOpacity: 0.6,
|
||||
fillOpacity: 0.7,
|
||||
},
|
||||
},
|
||||
dragDown: {
|
||||
"drag-down": {
|
||||
fillOpacity: 0.6,
|
||||
fillOpacity: 0.7,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
draw(cfg, group) {
|
||||
const nodeConf = settings.getNodeConf(cfg.name as string);
|
||||
const classify = nodeConf.type || "Other";
|
||||
let classify = nodeConf.type || "Other";
|
||||
if (
|
||||
!settings.hasNodeConf(cfg.name as string) ||
|
||||
(cfg.path && (!cfg.children || (cfg.children as []).length == 0))
|
||||
) {
|
||||
classify = "Error";
|
||||
}
|
||||
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 r = 4;
|
||||
const shape = group.addShape("rect", {
|
||||
attrs: {
|
||||
x: x0,
|
||||
y: y0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: w,
|
||||
height: h,
|
||||
stroke: color,
|
||||
@@ -87,15 +102,45 @@ export default function (settings: Settings) {
|
||||
draggable: true,
|
||||
});
|
||||
|
||||
// is subtree
|
||||
if (cfg.path && cfg.id !== "1") {
|
||||
group.addShape("rect", {
|
||||
attrs: {
|
||||
x: -10,
|
||||
y: -10,
|
||||
width: w + 20,
|
||||
height: h + 20,
|
||||
stroke: "white",
|
||||
lineWidth: 2.5,
|
||||
lineDash: [5, 5],
|
||||
radius: [r, r, r, r],
|
||||
},
|
||||
name: "subtree",
|
||||
draggable: true,
|
||||
});
|
||||
}
|
||||
|
||||
// name bg
|
||||
group.addShape("rect", {
|
||||
attrs: {
|
||||
x: x0 + 1.5,
|
||||
y: y0 + 1.5,
|
||||
width: w - 3,
|
||||
height: 16,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: w,
|
||||
height: 20,
|
||||
fill: color,
|
||||
// radius: r,
|
||||
radius: [r, r, 0, 0],
|
||||
},
|
||||
name: "name-bg-selected",
|
||||
draggable: true,
|
||||
});
|
||||
group.addShape("rect", {
|
||||
attrs: {
|
||||
x: SELECTED_LINE_WIDTH / 2,
|
||||
y: SELECTED_LINE_WIDTH / 2,
|
||||
width: w - 5,
|
||||
height: 20 - SELECTED_LINE_WIDTH / 2,
|
||||
fill: color,
|
||||
radius: [r, r, 0, 0],
|
||||
},
|
||||
name: "name-bg",
|
||||
draggable: true,
|
||||
@@ -105,8 +150,8 @@ export default function (settings: Settings) {
|
||||
group.addShape("text", {
|
||||
attrs: {
|
||||
textBaseline: "top",
|
||||
x: x0 - 3,
|
||||
y: -8,
|
||||
x: -3,
|
||||
y: h / 2 - 8,
|
||||
fontSize: 20,
|
||||
lineHeight: 20,
|
||||
text: cfg.id,
|
||||
@@ -122,8 +167,8 @@ export default function (settings: Settings) {
|
||||
var img = `./static/icons/${classify}.svg`;
|
||||
group.addShape("image", {
|
||||
attrs: {
|
||||
x: x0 + 3,
|
||||
y: y0 + 2,
|
||||
x: 5,
|
||||
y: 2,
|
||||
height: 14,
|
||||
width: 14,
|
||||
img,
|
||||
@@ -135,8 +180,8 @@ export default function (settings: Settings) {
|
||||
if (cfg.debug) {
|
||||
group.addShape("image", {
|
||||
attrs: {
|
||||
x: x0 + 182,
|
||||
y: y0 + 2,
|
||||
x: 182,
|
||||
y: 2,
|
||||
height: 14,
|
||||
width: 14,
|
||||
img: `./static/icons/Debug.svg`,
|
||||
@@ -149,8 +194,8 @@ export default function (settings: Settings) {
|
||||
group.addShape("text", {
|
||||
attrs: {
|
||||
textBaseline: "top",
|
||||
x: x0 + 18,
|
||||
y: y0 + 4,
|
||||
x: 22,
|
||||
y: 3,
|
||||
fontWeight: 800,
|
||||
lineHeight: 20,
|
||||
text: cfg.name,
|
||||
@@ -159,8 +204,8 @@ export default function (settings: Settings) {
|
||||
name: "name-text",
|
||||
});
|
||||
|
||||
var x = x0 + 2;
|
||||
var y = y0 + 23;
|
||||
var x = 5;
|
||||
var y = 24;
|
||||
// desc text
|
||||
const desc = cfg.desc || nodeConf.desc;
|
||||
if (desc) {
|
||||
@@ -171,7 +216,7 @@ export default function (settings: Settings) {
|
||||
y,
|
||||
lineHeight: 20,
|
||||
fontWeight: 800,
|
||||
text: `备注:${desc}`,
|
||||
text: `备注:${desc}`,
|
||||
fill: "black",
|
||||
},
|
||||
name: "desc-text",
|
||||
@@ -180,7 +225,7 @@ export default function (settings: Settings) {
|
||||
|
||||
const args: any = cfg.args;
|
||||
if (nodeConf.args && args && Object.keys(args).length > 0) {
|
||||
const { str, line } = toBreakWord(`参数:${JSON.stringify(args)}`, 35);
|
||||
const { str, line } = toBreakWord(`参数:${JSON.stringify(args)}`, 34);
|
||||
group.addShape("text", {
|
||||
attrs: {
|
||||
textBaseline: "top",
|
||||
@@ -198,7 +243,7 @@ export default function (settings: Settings) {
|
||||
|
||||
const input: [] = cfg.input ? (cfg.input as []) : [];
|
||||
if (nodeConf.input && input.length > 0) {
|
||||
const { str, line } = toBreakWord(`输入:${JSON.stringify(input)}`, 35);
|
||||
const { str, line } = toBreakWord(`输入:${JSON.stringify(input)}`, 35);
|
||||
group.addShape("text", {
|
||||
attrs: {
|
||||
textBaseline: "top",
|
||||
@@ -215,7 +260,7 @@ export default function (settings: Settings) {
|
||||
|
||||
const output: [] = cfg.output ? (cfg.output as []) : [];
|
||||
if (nodeConf.output && output.length > 0) {
|
||||
const { str, line } = toBreakWord(`输出:${JSON.stringify(output)}`, 35);
|
||||
const { str, line } = toBreakWord(`输出:${JSON.stringify(output)}`, 35);
|
||||
group.addShape("text", {
|
||||
attrs: {
|
||||
textBaseline: "top",
|
||||
@@ -230,11 +275,26 @@ export default function (settings: Settings) {
|
||||
y += 20 * line;
|
||||
}
|
||||
|
||||
if (cfg.path) {
|
||||
group.addShape("text", {
|
||||
attrs: {
|
||||
textBaseline: "top",
|
||||
x,
|
||||
y: y + 20,
|
||||
lineHeight: 20,
|
||||
text: `子树:${cfg.path}`,
|
||||
fill: "black",
|
||||
},
|
||||
name: "subtree-text",
|
||||
});
|
||||
y += 20;
|
||||
}
|
||||
|
||||
if (Array.isArray(cfg.children) && cfg.children.length > 0) {
|
||||
group.addShape("marker", {
|
||||
attrs: {
|
||||
x: w / 2,
|
||||
y: 0,
|
||||
x: w,
|
||||
y: h / 2,
|
||||
r: 6,
|
||||
symbol: G6.Marker.collapse,
|
||||
stroke: "#666",
|
||||
@@ -248,43 +308,43 @@ export default function (settings: Settings) {
|
||||
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,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: w,
|
||||
height: h / 2,
|
||||
fill: "blue",
|
||||
fillOpacity: 0,
|
||||
radius: [r, r, 0, 0],
|
||||
},
|
||||
draggable: true,
|
||||
});
|
||||
|
||||
group.addShape("rect", {
|
||||
name: "drag-down",
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: h / 2,
|
||||
width: w,
|
||||
height: h / 2,
|
||||
fill: "blue",
|
||||
fillOpacity: 0,
|
||||
radius: [0, 0, r, r],
|
||||
},
|
||||
draggable: true,
|
||||
// visible: false,
|
||||
});
|
||||
|
||||
group.addShape("rect", {
|
||||
name: "drag-right",
|
||||
attrs: {
|
||||
x: w * 0.1,
|
||||
y: -h / 2,
|
||||
width: w * 0.4,
|
||||
x: w / 2,
|
||||
y: 0,
|
||||
width: w / 2,
|
||||
height: h,
|
||||
fill: "blue",
|
||||
fillOpacity: 0,
|
||||
radius: [0, r, r, 0],
|
||||
},
|
||||
draggable: true,
|
||||
// visible: false,
|
||||
});
|
||||
return shape;
|
||||
},
|
||||
|
||||
+42
-10
@@ -1,12 +1,14 @@
|
||||
import Editor from "./Editor";
|
||||
import React, { Component } from "react";
|
||||
import { Layout, Tabs, message } from "antd";
|
||||
import { Tabs, message } from "antd";
|
||||
import { ipcRenderer } from "electron";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import * as Utils from "../common/Utils";
|
||||
import React, { Component } from "react";
|
||||
import Editor from "./Editor";
|
||||
|
||||
import "antd/dist/antd.dark.css";
|
||||
import MainEventType from "../common/MainEventType";
|
||||
import { BehaviorTreeModel } from "../common/BehaviorTreeModel";
|
||||
import MainEventType from "../common/MainEventType";
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
@@ -70,6 +72,31 @@ export default class TreeTabs extends Component<TreeTabsProps, TreeTabsState> {
|
||||
message.success("已保存所有行为树");
|
||||
});
|
||||
|
||||
ipcRenderer.on(MainEventType.BUILD, () => {
|
||||
for (let k in this.editors) {
|
||||
let editor = this.editors[k];
|
||||
editor.save();
|
||||
}
|
||||
|
||||
const settings = Utils.getRemoteSettings();
|
||||
const workdir = settings.workdir;
|
||||
if (!fs.existsSync(workdir)) {
|
||||
console.log("workdir not found:", workdir);
|
||||
return;
|
||||
}
|
||||
const files = fs.readdirSync(workdir).filter((f) => {
|
||||
return f.endsWith(".json");
|
||||
});
|
||||
for (const path of files) {
|
||||
const builddir = fs.realpathSync(workdir + "/../build", "utf-8");
|
||||
fs.mkdirSync(builddir, { recursive: true });
|
||||
const buildpath = builddir + "/" + path;
|
||||
console.log("build:", buildpath);
|
||||
const treeModel = Utils.createBuildData(path, settings);
|
||||
fs.writeFileSync(buildpath, JSON.stringify(treeModel, null, 2));
|
||||
}
|
||||
});
|
||||
|
||||
ipcRenderer.on(MainEventType.UNDO, () => {
|
||||
const editor = this.getCurEditor();
|
||||
editor?.undo();
|
||||
@@ -88,7 +115,7 @@ export default class TreeTabs extends Component<TreeTabsProps, TreeTabsState> {
|
||||
}
|
||||
|
||||
getOpenTreesModel() {
|
||||
const trees: BehaviorTreeModel[] = []
|
||||
const trees: BehaviorTreeModel[] = [];
|
||||
for (let k in this.editors) {
|
||||
let editor = this.editors[k];
|
||||
if (editor) {
|
||||
@@ -108,7 +135,7 @@ export default class TreeTabs extends Component<TreeTabsProps, TreeTabsState> {
|
||||
}
|
||||
|
||||
openFile(path: string) {
|
||||
if (!this.state.trees.find(e => e.filepath == path)) {
|
||||
if (!this.state.trees.find((e) => e.filepath == path)) {
|
||||
const trees = this.state.trees;
|
||||
trees.push({ filepath: path });
|
||||
this.setState({ trees, curTree: path });
|
||||
@@ -119,7 +146,7 @@ export default class TreeTabs extends Component<TreeTabsProps, TreeTabsState> {
|
||||
|
||||
closeFile(path: string) {
|
||||
let trees = this.state.trees;
|
||||
trees = trees.filter(tree => tree.filepath != path);
|
||||
trees = trees.filter((tree) => tree.filepath != path);
|
||||
const length = trees.length;
|
||||
this.setState({ trees: trees, curTree: length > 0 ? trees[0].filepath : null });
|
||||
}
|
||||
@@ -139,6 +166,8 @@ export default class TreeTabs extends Component<TreeTabsProps, TreeTabsState> {
|
||||
defaultActiveKey={curTree}
|
||||
activeKey={curTree}
|
||||
onChange={(activeKey) => {
|
||||
const editor = this.editors[activeKey];
|
||||
editor?.reload();
|
||||
this.setState({ curTree: activeKey });
|
||||
}}
|
||||
onEdit={(targetKey, action) => {
|
||||
@@ -149,14 +178,17 @@ export default class TreeTabs extends Component<TreeTabsProps, TreeTabsState> {
|
||||
>
|
||||
{trees.map((tree) => {
|
||||
return (
|
||||
<TabPane tab={`${path.basename(tree.filepath)}${tree.unsave?"*":''}`} key={tree.filepath}>
|
||||
<TabPane
|
||||
tab={`${path.basename(tree.filepath)}${tree.unsave ? "*" : ""}`}
|
||||
key={tree.filepath}
|
||||
>
|
||||
<Editor
|
||||
filepath={tree.filepath}
|
||||
onChangeSaveState={(unsave) => {
|
||||
if(tree.unsave != unsave) {
|
||||
if (tree.unsave != unsave) {
|
||||
tree.unsave = unsave;
|
||||
this.forceUpdate();
|
||||
}
|
||||
}
|
||||
}}
|
||||
ref={(ref) => {
|
||||
this.editors[tree.filepath] = ref;
|
||||
|
||||
Reference in new issue
Block a user