import { useState, useEffect } from 'react'; import { X, Settings as SettingsIcon, ChevronRight } from 'lucide-react'; import { Core } from '@esengine/ecs-framework'; import { SettingsService } from '../services/SettingsService'; import { SettingsRegistry, SettingCategory, SettingDescriptor, ProjectService, PluginManager, IPluginManager } from '@esengine/editor-core'; import { PluginListSetting } from './PluginListSetting'; import '../styles/SettingsWindow.css'; interface SettingsWindowProps { onClose: () => void; settingsRegistry: SettingsRegistry; initialCategoryId?: string; } export function SettingsWindow({ onClose, settingsRegistry, initialCategoryId }: SettingsWindowProps) { const [categories, setCategories] = useState([]); const [selectedCategoryId, setSelectedCategoryId] = useState(initialCategoryId || null); const [values, setValues] = useState>(new Map()); const [errors, setErrors] = useState>(new Map()); useEffect(() => { const allCategories = settingsRegistry.getAllCategories(); setCategories(allCategories); if (allCategories.length > 0 && !selectedCategoryId) { // 如果有 initialCategoryId,尝试使用它 if (initialCategoryId && allCategories.some(c => c.id === initialCategoryId)) { setSelectedCategoryId(initialCategoryId); } else { const firstCategory = allCategories[0]; if (firstCategory) { setSelectedCategoryId(firstCategory.id); } } } const settings = SettingsService.getInstance(); const projectService = Core.services.tryResolve(ProjectService); const allSettings = settingsRegistry.getAllSettings(); const initialValues = new Map(); for (const [key, descriptor] of allSettings.entries()) { // Project-scoped settings are loaded from ProjectService if (key.startsWith('project.') && projectService) { if (key === 'project.uiDesignResolution.width') { const resolution = projectService.getUIDesignResolution(); initialValues.set(key, resolution.width); } else if (key === 'project.uiDesignResolution.height') { const resolution = projectService.getUIDesignResolution(); initialValues.set(key, resolution.height); } else if (key === 'project.uiDesignResolution.preset') { const resolution = projectService.getUIDesignResolution(); initialValues.set(key, `${resolution.width}x${resolution.height}`); } else { // For other project settings, use default initialValues.set(key, descriptor.defaultValue); } } else { const value = settings.get(key, descriptor.defaultValue); initialValues.set(key, value); } } setValues(initialValues); }, [settingsRegistry, selectedCategoryId]); const handleValueChange = (key: string, value: any, descriptor: SettingDescriptor) => { const newValues = new Map(values); newValues.set(key, value); setValues(newValues); const newErrors = new Map(errors); if (!settingsRegistry.validateSetting(descriptor, value)) { newErrors.set(key, descriptor.validator?.errorMessage || 'Invalid value'); } else { newErrors.delete(key); } setErrors(newErrors); }; const handleSave = async () => { if (errors.size > 0) { return; } const settings = SettingsService.getInstance(); const projectService = Core.services.tryResolve(ProjectService); const changedSettings: Record = {}; // Track UI resolution changes for batch saving let uiResolutionChanged = false; let newWidth = 1920; let newHeight = 1080; for (const [key, value] of values.entries()) { // Project-scoped settings are saved to ProjectService if (key.startsWith('project.') && projectService) { if (key === 'project.uiDesignResolution.width') { newWidth = value; uiResolutionChanged = true; } else if (key === 'project.uiDesignResolution.height') { newHeight = value; uiResolutionChanged = true; } else if (key === 'project.uiDesignResolution.preset') { // Preset changes width and height together const [w, h] = value.split('x').map(Number); if (w && h) { newWidth = w; newHeight = h; uiResolutionChanged = true; } } changedSettings[key] = value; } else { settings.set(key, value); changedSettings[key] = value; } } // Save UI resolution if changed if (uiResolutionChanged && projectService) { await projectService.setUIDesignResolution({ width: newWidth, height: newHeight }); } window.dispatchEvent(new CustomEvent('settings:changed', { detail: changedSettings })); onClose(); }; const handleCancel = () => { onClose(); }; const renderSettingInput = (setting: SettingDescriptor) => { const value = values.get(setting.key) ?? setting.defaultValue; const error = errors.get(setting.key); switch (setting.type) { case 'boolean': return (
{error && {error}}
); case 'number': return (
handleValueChange(setting.key, parseInt(e.target.value) || 0, setting)} placeholder={setting.placeholder} min={setting.min} max={setting.max} step={setting.step} /> {error && {error}}
); case 'string': return (
handleValueChange(setting.key, e.target.value, setting)} placeholder={setting.placeholder} /> {error && {error}}
); case 'select': return (
{error && {error}}
); case 'range': return (
handleValueChange(setting.key, parseFloat(e.target.value), setting)} min={setting.min} max={setting.max} step={setting.step} /> {value}
{error && {error}}
); case 'color': return (
handleValueChange(setting.key, e.target.value, setting)} /> {error && {error}}
); case 'pluginList': { const pluginManager = Core.services.tryResolve(IPluginManager); if (!pluginManager) { return (

PluginManager 不可用

); } return (
{error && {error}}
); } default: return null; } }; const selectedCategory = categories.find((c) => c.id === selectedCategoryId); return (

设置

{categories.map((category) => ( ))}
{selectedCategory && selectedCategory.sections.map((section) => (

{section.title}

{section.description && (

{section.description}

)} {section.settings.map((setting) => (
{renderSettingInput(setting)}
))}
))} {!selectedCategory && (

请选择一个设置分类

)}
); }