import 'package:ainoval/blocs/editor/editor_bloc.dart'; import 'package:ainoval/screens/editor/widgets/custom_dropdown.dart'; import 'package:ainoval/screens/editor/widgets/menu_definitions.dart'; import 'package:ainoval/screens/editor/widgets/preset_menu_definitions.dart'; import 'package:ainoval/services/ai_preset_service.dart'; import 'package:ainoval/models/preset_models.dart'; import 'package:flutter/material.dart'; /// 下拉菜单管理器 /// /// 用于统一构建和管理所有下拉菜单,包括Act、Chapter、Scene和Model的菜单 class DropdownManager { /// 菜单构建上下文 final BuildContext context; /// 编辑器状态管理(模型菜单时可为null) final EditorBloc? editorBloc; /// 菜单显示设置 final DropdownDisplaySettings displaySettings; DropdownManager({ required this.context, required this.editorBloc, this.displaySettings = const DropdownDisplaySettings(), }); /// 构建Act菜单 Widget buildActMenu({ required String actId, Function()? onRenamePressed, IconData? icon, String? tooltip, }) { return _buildMenu( menuItems: ActMenuDefinitions.getMenuItems(), id: actId, secondaryId: null, tertiaryId: null, onRenamePressed: onRenamePressed, icon: icon ?? Icons.more_vert, tooltip: tooltip ?? 'Act操作', width: displaySettings.actMenuWidth, align: displaySettings.actMenuAlign, ); } /// 构建Chapter菜单 Widget buildChapterMenu({ required String actId, required String chapterId, Function()? onRenamePressed, IconData? icon, String? tooltip, }) { // 动态统计该章节下的场景数量,用作菜单顶部信息 int? sceneCount; try { final state = editorBloc?.state; if (state is EditorLoaded) { final novel = state.novel; for (final act in novel.acts) { if (act.id == actId) { for (final chapter in act.chapters) { if (chapter.id == chapterId) { sceneCount = chapter.scenes.length; break; } } break; } } } } catch (_) {} // 构建带有“章节信息:共N个场景”的菜单项,放在最前面 final List items = []; if (sceneCount != null) { items.add(MenuItemData( icon: Icons.info_outline, label: '共${sceneCount}个场景', onTap: null, disabled: true, )); items.add("divider"); } items.addAll(ChapterMenuDefinitions.getMenuItems()); return _buildMenu( menuItems: items, id: actId, secondaryId: chapterId, tertiaryId: null, onRenamePressed: onRenamePressed, icon: icon ?? Icons.more_vert, tooltip: tooltip ?? '章节操作', width: displaySettings.chapterMenuWidth, align: displaySettings.chapterMenuAlign, ); } /// 构建Scene菜单 Widget buildSceneMenu({ required String actId, required String chapterId, required String sceneId, IconData? icon, String? tooltip, }) { return _buildMenu( menuItems: SceneMenuDefinitions.getMenuItems(), id: actId, secondaryId: chapterId, tertiaryId: sceneId, icon: icon ?? Icons.more_horiz, tooltip: tooltip ?? '场景操作', width: displaySettings.sceneMenuWidth, align: displaySettings.sceneMenuAlign, ); } /// 构建Model菜单 Widget buildModelMenu({ required String configId, required bool isValidated, required bool isDefault, required Future Function(String) onValidate, required Future Function(String) onSetDefault, required Future Function(String) onEdit, required Future Function(String) onDelete, IconData? icon, String? tooltip, }) { final menuItems = ModelMenuDefinitions.getMenuItems( isValidated: isValidated, isDefault: isDefault, onValidate: onValidate, onSetDefault: onSetDefault, onEdit: onEdit, onDelete: onDelete, ); return _buildModelMenu( menuItems: menuItems, configId: configId, icon: icon ?? Icons.more_vert, tooltip: tooltip ?? '模型操作', width: displaySettings.modelMenuWidth, align: displaySettings.modelMenuAlign, ); } /// 构建预设菜单 Widget buildPresetMenu({ required String featureType, required Function() onCreatePreset, required Function() onManagePresets, required Function(AIPromptPreset preset) onPresetSelected, IconData? icon, String? tooltip, }) { return CustomDropdown( width: displaySettings.presetMenuWidth, align: displaySettings.presetMenuAlign, trigger: IconButton( icon: Icon(icon ?? Icons.bookmark_border, size: 18), onPressed: null, // 由CustomDropdown处理点击 tooltip: tooltip ?? '预设管理', color: Theme.of(context).colorScheme.onSurfaceVariant, splashRadius: 20, ), child: FutureBuilder>( future: PresetMenuDefinitions.getDynamicMenuItems( featureType: featureType, onCreatePreset: onCreatePreset, onManagePresets: onManagePresets, onPresetSelected: onPresetSelected, ), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Padding( padding: EdgeInsets.all(16.0), child: Center( child: SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ), ), ); } final menuItems = snapshot.data ?? []; return Column( mainAxisSize: MainAxisSize.min, children: _buildPresetMenuItemWidgets( menuItems, featureType, ), ); }, ), ); } /// 内部方法:构建通用菜单 Widget _buildMenu({ required List menuItems, required String id, String? secondaryId, String? tertiaryId, Function()? onRenamePressed, required IconData icon, required String tooltip, double width = 240, String align = 'left', }) { return CustomDropdown( width: width, align: align, trigger: IconButton( icon: Icon(icon, size: 20), onPressed: null, // 由CustomDropdown处理点击 tooltip: tooltip, color: Theme.of(context).colorScheme.onSurfaceVariant, splashRadius: 20, ), child: Column( mainAxisSize: MainAxisSize.min, children: _buildMenuItemWidgets( menuItems, id, secondaryId, tertiaryId, onRenamePressed, ), ), ); } /// 内部方法:构建模型菜单 Widget _buildModelMenu({ required List menuItems, required String configId, required IconData icon, required String tooltip, double width = 180, String align = 'right', }) { return CustomDropdown( width: width, align: align, trigger: IconButton( icon: Icon(icon, size: 16), onPressed: null, // 由CustomDropdown处理点击 tooltip: tooltip, color: Theme.of(context).colorScheme.onSurfaceVariant, splashRadius: 20, ), child: Column( mainAxisSize: MainAxisSize.min, children: _buildModelMenuItemWidgets( menuItems, configId, ), ), ); } /// 构建菜单项列表 List _buildMenuItemWidgets( List menuItems, String id, String? secondaryId, String? tertiaryId, Function()? onRenamePressed, ) { final List widgets = []; for (final item in menuItems) { if (item is String && item == "divider") { widgets.add(const DropdownDivider()); } else if (item is MenuSectionData) { widgets.add( DropdownSection( title: item.title, children: item.items.map((menuItem) { return _buildSingleMenuItem( menuItem, id, secondaryId, tertiaryId, onRenamePressed, ); }).toList(), ), ); } else if (item is MenuItemData) { widgets.add( _buildSingleMenuItem( item, id, secondaryId, tertiaryId, onRenamePressed, ), ); } } return widgets; } /// 构建模型菜单项列表 List _buildModelMenuItemWidgets( List menuItems, String configId, ) { final List widgets = []; for (final item in menuItems) { if (item is String && item == "divider") { widgets.add(const DropdownDivider()); } else if (item is ModelMenuSectionData) { widgets.add( DropdownSection( title: item.title, children: item.items.map((menuItem) { return _buildSingleModelMenuItem(menuItem, configId); }).toList(), ), ); } else if (item is ModelMenuItemData) { widgets.add(_buildSingleModelMenuItem(item, configId)); } } return widgets; } /// 构建单个菜单项 Widget _buildSingleMenuItem( MenuItemData item, String id, String? secondaryId, String? tertiaryId, Function()? onRenamePressed, ) { // 特殊处理重命名操作,因为需要直接访问State Future Function()? onTapHandler; if (item.label == '重命名Act' || item.label == '重命名章节') { onTapHandler = null; } else if (item.onTap != null) { onTapHandler = () async { await item.onTap!(context, editorBloc!, id, secondaryId, tertiaryId); }; } return DropdownItem( icon: item.icon, label: item.label, hasSubmenu: item.hasSubmenu, disabled: item.disabled, isDangerous: item.isDangerous, onTap: onTapHandler, ); } /// 构建单个模型菜单项 Widget _buildSingleModelMenuItem( ModelMenuItemData item, String configId, ) { Future Function()? onTapHandler; if (item.onTap != null) { onTapHandler = () async { await item.onTap!(configId); }; } return DropdownItem( icon: item.icon, label: item.label, hasSubmenu: item.hasSubmenu, disabled: item.disabled, isDangerous: item.isDangerous, onTap: onTapHandler, ); } /// 构建预设菜单项列表 List _buildPresetMenuItemWidgets( List menuItems, String featureType, ) { final List widgets = []; final presetService = AIPresetService(); for (final item in menuItems) { if (item is String && item == "divider") { widgets.add(const DropdownDivider()); } else if (item is PresetMenuSectionData) { widgets.add( DropdownSection( title: item.title, children: item.items.map((menuItem) { return _buildSinglePresetMenuItem(menuItem, presetService, featureType); }).toList(), dividerAtBottom: item.dividerAtBottom, ), ); } else if (item is PresetMenuItemData) { widgets.add(_buildSinglePresetMenuItem(item, presetService, featureType)); } } return widgets; } /// 构建单个预设菜单项 Widget _buildSinglePresetMenuItem( PresetMenuItemData item, AIPresetService presetService, String featureType, ) { Future Function()? onTapHandler; if (item.onTap != null) { onTapHandler = () async { await item.onTap!(context, presetService, featureType); }; } return DropdownItem( icon: item.icon, label: item.label, hasSubmenu: item.hasSubmenu, disabled: item.disabled, isDangerous: item.isDangerous, onTap: onTapHandler, ); } } /// 下拉菜单显示设置 class DropdownDisplaySettings { final double actMenuWidth; final double chapterMenuWidth; final double sceneMenuWidth; final double modelMenuWidth; final double presetMenuWidth; final String actMenuAlign; final String chapterMenuAlign; final String sceneMenuAlign; final String modelMenuAlign; final String presetMenuAlign; const DropdownDisplaySettings({ this.actMenuWidth = 240, this.chapterMenuWidth = 240, this.sceneMenuWidth = 240, this.modelMenuWidth = 180, this.presetMenuWidth = 280, this.actMenuAlign = 'left', this.chapterMenuAlign = 'right', this.sceneMenuAlign = 'right', this.modelMenuAlign = 'right', this.presetMenuAlign = 'right', }); }