refresh layout on switch file

This commit is contained in:
codetypes committed 2025-03-21 23:54:35 +08:00
1 parent bc8ee05844
commit 600191eae8
8 files changed
+167 -108

No files matched your search

+7 -4
View File
@@ -1,19 +1,20 @@
{
"about": "About Behavior3",
"add": "Add",
"actuaSize": "Actual Size",
"add": "Add",
"alertNewVersion": "This file is created by a newer version of Behavior3({{version}}), please upgrade to the latest version.",
"build": "Build",
"batch": "Batch Script",
"batchCompleted": "Batch completed",
"batchFailed": "Batch failed, more information can be found in the console.",
"build": "Build",
"buildCompleted": "Build completed",
"buildFailed": "Build failed, more information can be found in the console.",
"cancel": "Cancel",
"copy": "Copy",
"compact": "Compact",
"console": "Console",
"cut": "Cut",
"copy": "Copy",
"createProject": "Create Project",
"cut": "Cut",
"delete": "Delete",
"deleteNode": "Delete Node",
"devTools": "Toggle Developer Tools",
@@ -33,6 +34,8 @@
"newFolder": "New Folder...",
"newWindow": "New Window",
"nodeDefinition": "NODES",
"nodeLayout": "Node Layout",
"normal": "Normal",
"open": "Open...",
"openProject": "Open Project",
"paste": "Paste",
+7 -4
View File
@@ -1,19 +1,20 @@
{
"about": "关于 Behavior3",
"add": "添加",
"actualSize": "实际大小",
"add": "添加",
"alertNewVersion": "此文件由新版本 Behavior3({{version}}) 创建,请升级到最新版本。",
"build": "构建",
"batch": "批处理脚本",
"batchCompleted": "批处理完成",
"batchFailed": "批处理出现错误,详情请查看控制台!",
"build": "构建",
"buildCompleted": "构建完成",
"buildFailed": "构建出现错误,详情请查看控制台!",
"cancel": "取消",
"copy": "复制",
"compact": "紧凑",
"console": "控制台",
"cut": "剪切",
"copy": "复制",
"createProject": "新建项目",
"cut": "剪切",
"delete": "删除",
"deleteNode": "删除节点",
"devTools": "切换开发者工具",
@@ -33,6 +34,8 @@
"newFolder": "新建目录...",
"newWindow": "新建窗口",
"nodeDefinition": "节点定义",
"nodeLayout": "节点布局",
"normal": "正常",
"open": "打开...",
"openProject": "打开项目",
"paste": "粘贴",
+6 -10
View File
@@ -468,6 +468,9 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
editor.autoId = b3util.refreshTreeDataId(editor.root, editor.data.firstid);
editor.graph.changeData(editor.root);
editor.graph.layout();
if (editor.selectedId) {
selectNode(editor.selectedId);
}
restoreViewport();
};
@@ -945,18 +948,10 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
direction: "LR",
getHGap: () => 30,
getWidth: (d: TreeGraphData) => {
if (d.size) {
return d.size[0];
} else {
return 200;
}
return d.size![0];
},
getHeight: (d: TreeGraphData) => {
if (d.size) {
return d.size[1];
} else {
return 50;
}
return d.size![1];
},
},
});
@@ -1279,6 +1274,7 @@ export const Editor: FC<EditorProps> = ({ onUpdate: updateState, data: editor, .
useEffect(() => {
if (workspace.editing === editor) {
checkSubtree();
editor.graph && refresh();
}
}, [workspace.editing]);
+31 -2
View File
@@ -58,7 +58,12 @@ export const Menu: FC<LayoutProps> = () => {
);
const enabled = !!workspace.workdir;
const homedir = app.getPath("home");
const { settings } = useSetting(useShallow((state) => ({ settings: state.data })));
const { settings, setLayout } = useSetting(
useShallow((state) => ({
settings: state.data,
setLayout: state.setLayout,
}))
);
const menuTemplate: MenuItemConstructorOptions[] = useMemo(() => {
const recentWorkspaces: MenuItemConstructorOptions[] = settings.recent.map((path, i) => ({
@@ -349,6 +354,30 @@ export const Menu: FC<LayoutProps> = () => {
},
],
},
{
id: "menu.view.layout",
label: t("nodeLayout"),
submenu: [
{
id: "menu.view.layout.compact",
label: t("compact"),
type: "radio",
checked: settings.layout === "compact",
click: () => {
setLayout("compact");
},
},
{
id: "menu.view.layout.normal",
label: t("normal"),
type: "radio",
checked: settings.layout === "normal",
click: () => {
setLayout("normal");
},
},
],
},
],
},
{
@@ -365,7 +394,7 @@ export const Menu: FC<LayoutProps> = () => {
],
},
] as MenuItemConstructorOptions[];
}, [t, workspace.workdir, settings.recent, workspace.editing]);
}, [t, workspace.workdir, settings.recent, workspace.editing, settings]);
if (isMacos) {
const menu = AppMenu.buildFromTemplate(menuTemplate);
+100 -87
View File
@@ -4,12 +4,16 @@ import { useWorkspace } from "../contexts/workspace-context";
import { TreeGraphData, getNodeType, isExprType } from "../misc/b3type";
import { checkTreeData, nodeDefs, parseExpr, usingGroups, usingVars } from "../misc/b3util";
import i18n from "../misc/i18n";
import { isMacos } from "../misc/keys";
let ctx: CanvasRenderingContext2D | null = null;
let defaultFontSize = "";
const textWidthMap = new Map<string, number>();
const textLines: Record<string, string[]> = {};
let layoutWidth = 220;
let layoutStyle: "compact" | "normal" = "compact";
const calcTextWith = (text: string, fontSize?: string) => {
fontSize = fontSize ?? defaultFontSize;
let b3Workspace: HTMLDivElement | null;
@@ -35,7 +39,7 @@ const calcTextWith = (text: string, fontSize?: string) => {
const metrics = ctx.measureText(text);
width = metrics.width;
// width = width - (isMacos ? 1.6 : 0.8);
// width *= isMacos ? 0.88 : 0.95;
width *= isMacos ? 0.88 : 0.95;
textWidthMap.set(key, width);
}
}
@@ -143,7 +147,16 @@ export const calcTreeDataSize = (data: TreeGraphData) => {
updateHeight(data.args);
updateHeight(data.input);
updateHeight(data.output);
return [260, height];
return [layoutWidth, height];
};
export const setLayoutStyle = (style: "compact" | "normal") => {
layoutStyle = style;
if (style === "compact") {
layoutWidth = 220;
} else {
layoutWidth = 260;
}
};
const NODE_COLORS = {
@@ -216,10 +229,8 @@ G6.registerNode(
classify = "Error";
color = NODE_COLORS[classify];
}
const size = data.size ? data.size : [200, 40];
const w = size[0];
const h = size[1];
const r = 4;
const [width, height] = data.size;
const radius = 4;
let bgColor = "white";
let textColor = "black";
@@ -234,7 +245,6 @@ G6.registerNode(
const addShape = (type: string, shapeCfg: ShapeCfg, clickEnabled: boolean = false) => {
shapeCfg.draggable = clickEnabled;
shapeCfg.capture = clickEnabled;
// shapeCfg.attrs.fontFamily = "Menlo, Monaco, 'Courier New', monospace";
return group.addShape(type, shapeCfg);
};
@@ -242,11 +252,11 @@ G6.registerNode(
attrs: {
x: -15,
y: -15,
width: w + 30,
height: h + 30,
width: width + 30,
height: height + 30,
fill: "#fff",
fillOpacity: 0,
radius: r + 4,
radius: radius + 4,
},
name: "main-box-selected",
draggable: true,
@@ -258,12 +268,12 @@ G6.registerNode(
attrs: {
x: 0,
y: 0,
width: w,
height: h,
width: width,
height: height,
stroke: color,
lineWidth: 2,
fill: bgColor,
radius: r,
radius: radius,
},
name: "main-box",
draggable: true,
@@ -271,17 +281,19 @@ G6.registerNode(
true
);
// name line
addShape("path", {
attrs: {
path: [
["M", 46, 23],
["L", w - 40, 23],
],
stroke: "#666",
lineWidth: 1,
},
});
if (layoutStyle === "normal") {
// name line
addShape("path", {
attrs: {
path: [
["M", 46, 23],
["L", width - 40, 23],
],
stroke: "#666",
lineWidth: 1,
},
});
}
// is subtree
if (data.path && data.id !== "1") {
@@ -289,12 +301,12 @@ G6.registerNode(
attrs: {
x: -10,
y: -10,
width: w + 20,
height: h + 20,
width: width + 20,
height: height + 20,
stroke: "#a5b1be",
lineWidth: 2.5,
lineDash: [6, 6],
radius: [r, r, r, r],
radius: [radius, radius, radius, radius],
},
name: "subtree",
draggable: true,
@@ -306,10 +318,10 @@ G6.registerNode(
attrs: {
x: 0,
y: 0,
width: 40,
height: h,
width: layoutStyle === "compact" ? width : 40,
height: layoutStyle === "compact" ? 25 : height,
fill: color,
radius: [r, 0, 0, r],
radius: layoutStyle === "compact" ? [radius, radius, 0, 0] : [radius, 0, 0, radius],
},
name: "name-bg",
draggable: true,
@@ -320,7 +332,7 @@ G6.registerNode(
attrs: {
textBaseline: "top",
x: -3,
y: h / 2 - 8,
y: height / 2 - 8,
fontSize: 20,
lineHeight: 20,
text: data.id,
@@ -339,9 +351,9 @@ G6.registerNode(
addShape("image", {
attrs: {
x: 5,
y: h / 2 - 16,
height: 30,
width: 30,
y: layoutStyle === "compact" ? 3 : height / 2 - 16,
height: layoutStyle === "compact" ? 18 : 30,
width: layoutStyle === "compact" ? 18 : 30,
img,
},
name: "node-icon",
@@ -351,10 +363,10 @@ G6.registerNode(
const status = ((data.status ?? 0) & 0b111).toString(2).padStart(3, "0");
addShape("image", {
attrs: {
x: w - 18,
x: width - 18,
y: 3,
height: 20,
width: 20,
height: layoutStyle === "compact" ? 18 : 20,
width: layoutStyle === "compact" ? 18 : 20,
img: `./icons/status${status}.svg`,
},
name: "status-icon",
@@ -364,12 +376,12 @@ G6.registerNode(
addShape("text", {
attrs: {
textBaseline: "top",
x: 46,
x: layoutStyle === "compact" ? 26 : 46,
y: 5,
fontWeight: 900,
fontWeight: "bolder",
text: data.name,
fill: textColor,
fontSize: 14,
fontSize: layoutStyle === "compact" ? 13 : 14,
},
name: "name-text",
});
@@ -378,7 +390,7 @@ G6.registerNode(
if (data.debug) {
addShape("image", {
attrs: {
x: w - 30,
x: width - 30,
y: 4,
height: 16,
width: 16,
@@ -391,7 +403,7 @@ G6.registerNode(
if (data.disabled) {
addShape("image", {
attrs: {
x: w - 30 - (data.debug ? 18 : 0),
x: width - 30 - (data.debug ? 18 : 0),
y: 4,
height: 16,
width: 16,
@@ -401,20 +413,21 @@ G6.registerNode(
});
}
const x = 46;
let y = 32;
const contentWidth = 220;
const contentX = layoutStyle === "compact" ? 6 : 46;
let contentY = 32;
// desc text
let desc = (data.desc || nodeDef.desc) as string;
if (desc || desc === "") {
desc = i18n.t("regnode.mark") + desc;
desc = cutWordTo(desc, w - 40 - 15);
desc = cutWordTo(desc, contentWidth - 15);
addShape("text", {
attrs: {
textBaseline: "top",
x,
y,
x: contentX,
y: contentY,
lineHeight: 20,
fontWeight: 800,
fontWeight: "bolder",
text: `${desc}`,
fill: textColor,
},
@@ -428,12 +441,12 @@ G6.registerNode(
if (data.highlightArgs) {
addShape("rect", {
attrs: {
x: x - 2,
y: y + 17,
width: w - 40 - 6,
x: contentX - 2,
y: contentY + 17,
width: contentWidth - 6,
height: 18,
fill: "#0d1117",
radius: [r, r, r, r],
radius: [radius, radius, radius, radius],
},
name: "args-text-bg",
});
@@ -441,9 +454,9 @@ G6.registerNode(
addShape("text", {
attrs: {
textBaseline: "top",
x,
y: y + 20,
w,
x: contentX,
y: contentY + 20,
w: width,
lineHeight: 20,
text: str,
fill: data.highlightArgs ? "white" : textColor,
@@ -451,7 +464,7 @@ G6.registerNode(
},
name: "args-text",
});
y += 20 * line;
contentY += 20 * line;
}
const input = data.input ?? [];
@@ -463,12 +476,12 @@ G6.registerNode(
if (data.highlightInput) {
addShape("rect", {
attrs: {
x: x - 2,
y: y + 17,
width: w - 40 - 6,
x: contentX - 2,
y: contentY + 17,
width: contentWidth - 6,
height: 18,
fill: "#0d1117",
radius: [r, r, r, r],
radius: [radius, radius, radius, radius],
},
name: "input-text-bg",
});
@@ -478,8 +491,8 @@ G6.registerNode(
{
attrs: {
textBaseline: "top",
x,
y: y + 20,
x: contentX,
y: contentY + 20,
lineHeight: 20,
text: str,
fill: data.highlightInput ? "white" : textColor,
@@ -489,7 +502,7 @@ G6.registerNode(
},
true
);
y += 20 * line;
contentY += 20 * line;
}
const output = data.output ?? [];
@@ -501,12 +514,12 @@ G6.registerNode(
if (data.highlightOutput) {
addShape("rect", {
attrs: {
x: x - 2,
y: y + 17,
width: w - 40 - 6,
x: contentX - 2,
y: contentY + 17,
width: contentWidth - 6,
height: 18,
fill: "#0d1117",
radius: [r, r, r, r],
radius: [radius, radius, radius, radius],
},
name: "output-text-bg",
});
@@ -516,8 +529,8 @@ G6.registerNode(
{
attrs: {
textBaseline: "top",
x,
y: y + 20,
x: contentX,
y: contentY + 20,
lineHeight: 20,
text: str,
fill: data.highlightOutput ? "white" : textColor,
@@ -527,24 +540,24 @@ G6.registerNode(
},
true
);
y += 20 * line;
contentY += 20 * line;
}
if (data.path) {
let path = (i18n.t("regnode.subtree") + data.path) as string;
path = cutWordTo(path, w - 40 - 15);
path = cutWordTo(path, contentWidth - 15);
addShape("text", {
attrs: {
textBaseline: "top",
x,
y: y + 20,
x: contentX,
y: contentY + 20,
lineHeight: 20,
text: `${path}`,
fill: textColor,
},
name: "subtree-text",
});
y += 20;
contentY += 20;
}
addShape("rect", {
@@ -552,14 +565,14 @@ G6.registerNode(
attrs: {
x: 0,
y: 0,
width: w,
height: h / 2,
width: width,
height: height / 2,
lineWidth: 2,
stroke: "#ff0000",
strokeOpacity: 0,
fill: "#ff0000",
fillOpacity: 0,
radius: [r, r, 0, 0],
radius: [radius, radius, 0, 0],
},
draggable: true,
});
@@ -568,15 +581,15 @@ G6.registerNode(
name: "drag-down",
attrs: {
x: 0,
y: h / 2,
width: w,
height: h / 2,
y: height / 2,
width: width,
height: height / 2,
lineWidth: 2,
stroke: "#ff0000",
strokeOpacity: 0,
fill: "#ff0000",
fillOpacity: 0,
radius: [0, 0, r, r],
radius: [0, 0, radius, radius],
},
draggable: true,
});
@@ -584,16 +597,16 @@ G6.registerNode(
addShape("rect", {
name: "drag-right",
attrs: {
x: w / 2,
x: width / 2,
y: 0,
width: w / 2,
height: h,
width: width / 2,
height: height,
lineWidth: 2,
stroke: "#ff0000",
strokeOpacity: 0,
fill: "#ff0000",
fillOpacity: 0,
radius: [0, r, r, 0],
radius: [0, radius, radius, 0],
},
draggable: true,
});
@@ -601,8 +614,8 @@ G6.registerNode(
if (data.children?.length) {
addShape("marker", {
attrs: {
x: w,
y: h / 2,
x: width,
y: height / 2,
r: 6,
symbol: G6.Marker.collapse,
stroke: "#666",
+14
View File
@@ -1,13 +1,16 @@
import { app } from "@electron/remote";
import * as fs from "fs";
import { create } from "zustand";
import { setLayoutStyle } from "../components/register-node";
import { readJson, writeJson } from "../misc/util";
import { useWorkspace } from "./workspace-context";
const settingPath = app.getPath("userData") + "/settings.json";
export type SettingModel = {
recent: string[];
buildDir: string;
layout?: "compact" | "normal";
};
export type SettingStore = {
@@ -18,18 +21,22 @@ export type SettingStore = {
appendRecent: (path: string) => void;
removeRecent: (path: string) => void;
setBuildDir: (dir: string) => void;
setLayout: (layout: "compact" | "normal") => void;
};
export const useSetting = create<SettingStore>((set, get) => ({
data: {
recent: [],
buildDir: "",
layout: "compact",
},
load: () => {
try {
if (fs.existsSync(settingPath)) {
const settings = readJson(settingPath) as SettingModel;
settings.layout = settings.layout || "compact";
set({ data: settings });
setLayoutStyle(settings.layout);
}
} catch (error) {
console.error(error);
@@ -56,4 +63,11 @@ export const useSetting = create<SettingStore>((set, get) => ({
set({ data: { ...data, buildDir: dir } });
save();
},
setLayout: (layout: "compact" | "normal") => {
const { data, save } = get();
set({ data: { ...data, layout } });
save();
setLayoutStyle(layout);
useWorkspace.getState().editing?.dispatch("refresh");
},
}));
+1 -1
View File
@@ -85,7 +85,7 @@ export interface TreeGraphData extends G6TreeGraphData {
path?: string;
lastModified?: number;
size?: number[];
size: number[];
highlightInput?: boolean;
highlightOutput?: boolean;
highlightArgs?: boolean;
+1
View File
@@ -671,6 +671,7 @@ export const createTreeData = (
disabled: node.disabled,
def: nodeDefs.get(node.name),
parent: parent,
size: [0, 0],
};
treeData.def.args?.forEach((arg) => {