fix(editor): 修复右键菜单和粒子编辑器问题 (#286)

- 修复右键菜单被状态栏遮挡的问题
- 修复右键菜单边界检测,考虑标题栏和状态栏高度
- 调整右键菜单结构:新建文件夹 → 资源类型 → 工具操作
- 修复 Particle 插件默认未启用的问题(defaultEnabled 的新插件不被旧配置禁用)
- 修复 SizeOverLifetime 模块在预览中无效果的问题
- 移除 MaterialEditorModule 中的重复模板注册
This commit is contained in:
YHH
2025-12-06 11:56:25 +08:00
committed by GitHub
parent 397f79caa5
commit 3cbfa1e4cb
6 changed files with 153 additions and 53 deletions

View File

@@ -164,6 +164,34 @@ export function ContentBrowser({
template: FileCreationTemplate;
} | null>(null);
// 文件创建模板列表(需要状态跟踪以便插件安装后刷新)
// File creation templates list (need state tracking to refresh after plugin installation)
const [fileCreationTemplates, setFileCreationTemplates] = useState<FileCreationTemplate[]>([]);
// 初始化和监听插件安装事件以更新模板列表
// Initialize and listen for plugin installation events to update template list
useEffect(() => {
const updateTemplates = () => {
if (fileActionRegistry) {
const templates = fileActionRegistry.getCreationTemplates();
setFileCreationTemplates([...templates]);
}
};
// 初始加载
updateTemplates();
// 监听插件安装/卸载事件
if (messageHub) {
const unsubInstall = messageHub.subscribe('plugin:installed', updateTemplates);
const unsubUninstall = messageHub.subscribe('plugin:uninstalled', updateTemplates);
return () => {
unsubInstall();
unsubUninstall();
};
}
}, [fileActionRegistry, messageHub]);
const t = {
en: {
favorites: 'Favorites',
@@ -844,7 +872,6 @@ export class ${className} {
const items: ContextMenuItem[] = [];
if (!asset) {
// Background context menu
items.push({
label: t.newFolder,
icon: <FolderClosed size={16} />,
@@ -861,29 +888,55 @@ export class ${className} {
}
});
if (fileActionRegistry) {
const templates = fileActionRegistry.getCreationTemplates();
if (templates.length > 0) {
items.push({ label: '', separator: true, onClick: () => {} });
for (const template of templates) {
const localizedLabel = getTemplateLabel(template.label);
items.push({
label: `${t.newPrefix} ${localizedLabel}`,
icon: getIconComponent(template.icon, 16),
onClick: () => {
setContextMenu(null);
if (currentPath) {
setCreateFileDialog({
parentPath: currentPath,
template
});
}
if (fileCreationTemplates.length > 0) {
items.push({ label: '', separator: true, onClick: () => {} });
for (const template of fileCreationTemplates) {
const localizedLabel = getTemplateLabel(template.label);
items.push({
label: localizedLabel,
icon: getIconComponent(template.icon, 16),
onClick: () => {
setContextMenu(null);
if (currentPath) {
setCreateFileDialog({
parentPath: currentPath,
template
});
}
});
}
}
});
}
}
items.push({ label: '', separator: true, onClick: () => {} });
items.push({
label: locale === 'zh' ? '在资源管理器中显示' : 'Show in Explorer',
icon: <ExternalLink size={16} />,
onClick: async () => {
if (currentPath) {
try {
await TauriAPI.showInFolder(currentPath);
} catch (error) {
console.error('Failed to show in folder:', error);
}
}
setContextMenu(null);
}
});
items.push({
label: locale === 'zh' ? '刷新' : 'Refresh',
icon: <RefreshCw size={16} />,
onClick: async () => {
if (currentPath) {
await loadAssets(currentPath);
}
setContextMenu(null);
}
});
return items;
}
@@ -1093,7 +1146,7 @@ export class ${className} {
});
return items;
}, [currentPath, fileActionRegistry, handleAssetDoubleClick, loadAssets, locale, t.newFolder, setRenameDialog, setDeleteConfirmDialog, setContextMenu, setCreateFileDialog]);
}, [currentPath, fileCreationTemplates, handleAssetDoubleClick, loadAssets, locale, t.newFolder, t.newPrefix, setRenameDialog, setDeleteConfirmDialog, setContextMenu, setCreateFileDialog]);
// Render folder tree node
const renderFolderNode = useCallback((node: FolderNode, depth: number = 0) => {

View File

@@ -129,27 +129,43 @@ export function ContextMenu({ items, position, onClose }: ContextMenuProps) {
const [submenuRect, setSubmenuRect] = useState<DOMRect | null>(null);
useEffect(() => {
if (menuRef.current) {
const menu = menuRef.current;
const rect = menu.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const adjustPosition = () => {
if (menuRef.current) {
const menu = menuRef.current;
const rect = menu.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
let x = position.x;
let y = position.y;
const STATUS_BAR_HEIGHT = 28;
const TITLE_BAR_HEIGHT = 32;
if (x + rect.width > viewportWidth) {
x = Math.max(0, viewportWidth - rect.width - 10);
}
let x = position.x;
let y = position.y;
if (y + rect.height > viewportHeight) {
y = Math.max(0, viewportHeight - rect.height - 10);
}
if (x + rect.width > viewportWidth - 10) {
x = Math.max(10, viewportWidth - rect.width - 10);
}
if (y + rect.height > viewportHeight - STATUS_BAR_HEIGHT - 10) {
y = Math.max(TITLE_BAR_HEIGHT + 10, viewportHeight - STATUS_BAR_HEIGHT - rect.height - 10);
}
if (x < 10) {
x = 10;
}
if (y < TITLE_BAR_HEIGHT + 10) {
y = TITLE_BAR_HEIGHT + 10;
}
if (x !== position.x || y !== position.y) {
setAdjustedPosition({ x, y });
}
}
};
adjustPosition();
const rafId = requestAnimationFrame(adjustPosition);
return () => cancelAnimationFrame(rafId);
}, [position]);
useEffect(() => {

View File

@@ -6,7 +6,9 @@
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
padding: 4px 0;
min-width: 200px;
z-index: var(--z-index-popover);
max-height: calc(100vh - 80px);
overflow-y: auto;
z-index: 10001;
font-size: 13px;
}