fix(editor): 修复右键菜单和粒子编辑器问题 (#286)
- 修复右键菜单被状态栏遮挡的问题 - 修复右键菜单边界检测,考虑标题栏和状态栏高度 - 调整右键菜单结构:新建文件夹 → 资源类型 → 工具操作 - 修复 Particle 插件默认未启用的问题(defaultEnabled 的新插件不被旧配置禁用) - 修复 SizeOverLifetime 模块在预览中无效果的问题 - 移除 MaterialEditorModule 中的重复模板注册
This commit is contained in:
@@ -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) => {
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user