Feature/ecs behavior tree (#188)
* feat(behavior-tree): 完全 ECS 化的行为树系统 * feat(editor-app): 添加行为树可视化编辑器 * chore: 移除 Cocos Creator 扩展目录 * feat(editor-app): 行为树编辑器功能增强 * fix(editor-app): 修复 TypeScript 类型错误 * feat(editor-app): 使用 FlexLayout 重构面板系统并优化资产浏览器 * feat(editor-app): 改进编辑器UI样式并修复行为树执行顺序 * feat(behavior-tree,editor-app): 添加装饰器系统并优化编辑器性能 * feat(behavior-tree,editor-app): 添加属性绑定系统 * feat(editor-app,behavior-tree): 优化编辑器UI并改进行为树功能 * feat(editor-app,behavior-tree): 添加全局黑板系统并增强资产浏览器功能 * feat(behavior-tree,editor-app): 添加运行时资产导出系统 * feat(behavior-tree,editor-app): 添加SubTree系统和资产选择器 * feat(behavior-tree,editor-app): 优化系统架构并改进编辑器文件管理 * fix(behavior-tree,editor-app): 修复SubTree节点错误显示空节点警告 * fix(editor-app): 修复局部黑板类型定义文件扩展名错误
This commit is contained in:
161
packages/editor-app/src/components/FlexLayoutDockContainer.tsx
Normal file
161
packages/editor-app/src/components/FlexLayoutDockContainer.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
import { useRef, useCallback, ReactNode, useMemo } from 'react';
|
||||
import { Layout, Model, TabNode, IJsonModel, Actions, IJsonTabSetNode, IJsonRowNode } from 'flexlayout-react';
|
||||
import 'flexlayout-react/style/light.css';
|
||||
import '../styles/FlexLayoutDock.css';
|
||||
|
||||
export interface FlexDockPanel {
|
||||
id: string;
|
||||
title: string;
|
||||
content: ReactNode;
|
||||
closable?: boolean;
|
||||
}
|
||||
|
||||
interface FlexLayoutDockContainerProps {
|
||||
panels: FlexDockPanel[];
|
||||
onPanelClose?: (panelId: string) => void;
|
||||
}
|
||||
|
||||
export function FlexLayoutDockContainer({ panels, onPanelClose }: FlexLayoutDockContainerProps) {
|
||||
const createDefaultLayout = useCallback((): IJsonModel => {
|
||||
const leftPanels = panels.filter(p => p.id.includes('hierarchy'));
|
||||
const rightPanels = panels.filter(p => p.id.includes('inspector'));
|
||||
const bottomPanels = panels.filter(p => p.id.includes('console') || p.id.includes('asset'))
|
||||
.sort((a, b) => {
|
||||
// 控制台排在前面
|
||||
if (a.id.includes('console')) return -1;
|
||||
if (b.id.includes('console')) return 1;
|
||||
return 0;
|
||||
});
|
||||
const centerPanels = panels.filter(p =>
|
||||
!leftPanels.includes(p) && !rightPanels.includes(p) && !bottomPanels.includes(p)
|
||||
);
|
||||
|
||||
// Build center column children
|
||||
const centerColumnChildren: (IJsonTabSetNode | IJsonRowNode)[] = [];
|
||||
if (centerPanels.length > 0) {
|
||||
centerColumnChildren.push({
|
||||
type: 'tabset',
|
||||
weight: 70,
|
||||
children: centerPanels.map(p => ({
|
||||
type: 'tab',
|
||||
name: p.title,
|
||||
id: p.id,
|
||||
component: p.id,
|
||||
enableClose: p.closable !== false,
|
||||
})),
|
||||
});
|
||||
}
|
||||
if (bottomPanels.length > 0) {
|
||||
centerColumnChildren.push({
|
||||
type: 'tabset',
|
||||
weight: 30,
|
||||
children: bottomPanels.map(p => ({
|
||||
type: 'tab',
|
||||
name: p.title,
|
||||
id: p.id,
|
||||
component: p.id,
|
||||
enableClose: p.closable !== false,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
// Build main row children
|
||||
const mainRowChildren: (IJsonTabSetNode | IJsonRowNode)[] = [];
|
||||
if (leftPanels.length > 0) {
|
||||
mainRowChildren.push({
|
||||
type: 'tabset',
|
||||
weight: 20,
|
||||
children: leftPanels.map(p => ({
|
||||
type: 'tab',
|
||||
name: p.title,
|
||||
id: p.id,
|
||||
component: p.id,
|
||||
enableClose: p.closable !== false,
|
||||
})),
|
||||
});
|
||||
}
|
||||
if (centerColumnChildren.length > 0) {
|
||||
if (centerColumnChildren.length === 1) {
|
||||
const centerChild = centerColumnChildren[0];
|
||||
if (centerChild && centerChild.type === 'tabset') {
|
||||
mainRowChildren.push({
|
||||
type: 'tabset',
|
||||
weight: 60,
|
||||
children: centerChild.children
|
||||
} as IJsonTabSetNode);
|
||||
} else if (centerChild) {
|
||||
mainRowChildren.push({
|
||||
type: 'row',
|
||||
weight: 60,
|
||||
children: centerChild.children
|
||||
} as IJsonRowNode);
|
||||
}
|
||||
} else {
|
||||
mainRowChildren.push({
|
||||
type: 'row',
|
||||
weight: 60,
|
||||
children: centerColumnChildren,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (rightPanels.length > 0) {
|
||||
mainRowChildren.push({
|
||||
type: 'tabset',
|
||||
weight: 20,
|
||||
children: rightPanels.map(p => ({
|
||||
type: 'tab',
|
||||
name: p.title,
|
||||
id: p.id,
|
||||
component: p.id,
|
||||
enableClose: p.closable !== false,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
global: {
|
||||
tabEnableClose: true,
|
||||
tabEnableRename: false,
|
||||
tabSetEnableMaximize: false,
|
||||
tabSetEnableDrop: true,
|
||||
tabSetEnableDrag: true,
|
||||
tabSetEnableDivide: true,
|
||||
borderEnableDrop: true,
|
||||
},
|
||||
borders: [],
|
||||
layout: {
|
||||
type: 'row',
|
||||
weight: 100,
|
||||
children: mainRowChildren,
|
||||
},
|
||||
};
|
||||
}, [panels]);
|
||||
|
||||
const model = useMemo(() => Model.fromJson(createDefaultLayout()), [createDefaultLayout]);
|
||||
|
||||
const factory = useCallback((node: TabNode) => {
|
||||
const component = node.getComponent();
|
||||
const panel = panels.find(p => p.id === component);
|
||||
return panel?.content || <div>Panel not found</div>;
|
||||
}, [panels]);
|
||||
|
||||
const onAction = useCallback((action: any) => {
|
||||
if (action.type === Actions.DELETE_TAB) {
|
||||
const tabId = action.data.node;
|
||||
if (onPanelClose) {
|
||||
onPanelClose(tabId);
|
||||
}
|
||||
}
|
||||
return action;
|
||||
}, [onPanelClose]);
|
||||
|
||||
return (
|
||||
<div className="flexlayout-dock-container">
|
||||
<Layout
|
||||
model={model}
|
||||
factory={factory}
|
||||
onAction={onAction}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user