import 'package:flutter/material.dart'; import 'package:ainoval/utils/web_theme.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:ainoval/blocs/editor/editor_bloc.dart' as editor_bloc; import 'package:ainoval/screens/editor/controllers/editor_screen_controller.dart'; import 'package:ainoval/utils/logger.dart'; import 'package:provider/provider.dart'; /// 🚀 沉浸模式导航组件 /// 包含模式切换按钮和章节导航按钮 class ImmersiveModeNavigation extends StatelessWidget { const ImmersiveModeNavigation({ super.key, required this.editorBloc, }); final editor_bloc.EditorBloc editorBloc; @override Widget build(BuildContext context) { return BlocBuilder( bloc: editorBloc, builder: (context, state) { if (state is! editor_bloc.EditorLoaded) { return const SizedBox.shrink(); } return Row( mainAxisSize: MainAxisSize.min, children: [ // 沉浸模式切换按钮 _buildModeToggleButton(context, state), // 保留章节导航按钮(普通/沉浸模式均可用) const SizedBox(width: 8), _buildChapterNavigationButtons(context, state), ], ); }, ); } /// 构建模式切换按钮 Widget _buildModeToggleButton(BuildContext context, editor_bloc.EditorLoaded state) { final theme = Theme.of(context); final isImmersive = state.isImmersiveMode; final editorController = Provider.of(context, listen: false); final label = isImmersive ? '沉浸模式' : '普通模式'; return Tooltip( message: isImmersive ? '切换到普通模式' : '切换到沉浸模式', child: TextButton.icon( icon: Icon( isImmersive ? Icons.center_focus_strong : Icons.view_stream, size: 20, color: isImmersive ? WebTheme.getPrimaryColor(context) : theme.colorScheme.onSurfaceVariant, ), label: Text( label, style: TextStyle( color: isImmersive ? WebTheme.getPrimaryColor(context) : theme.colorScheme.onSurfaceVariant, fontSize: 14, ), ), style: TextButton.styleFrom( backgroundColor: isImmersive ? WebTheme.getPrimaryColor(context).withAlpha(76) : Colors.transparent, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6), ), ), onPressed: () { AppLogger.i('ImmersiveModeNavigation', '用户点击模式切换按钮'); editorController.toggleImmersiveMode(); }, ), ); } /// 构建章节导航按钮组 Widget _buildChapterNavigationButtons(BuildContext context, editor_bloc.EditorLoaded state) { final editorController = Provider.of(context, listen: false); return Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(20), border: Border.all( color: Theme.of(context).colorScheme.outline.withOpacity(0.2), ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ // 上一章按钮 _buildNavigationButton( context: context, icon: Icons.navigate_before, tooltip: '上一章', onPressed: editorController.canNavigateToPreviousChapter ? () { AppLogger.i('ImmersiveModeNavigation', '导航到上一章'); editorController.navigateToPreviousChapter(); } : null, ), // 分隔线 Container( height: 24, width: 1, color: Theme.of(context).colorScheme.outline.withOpacity(0.2), ), // 章节信息 _buildChapterInfo(context, state), // 分隔线 Container( height: 24, width: 1, color: Theme.of(context).colorScheme.outline.withOpacity(0.2), ), // 下一章按钮 _buildNavigationButton( context: context, icon: Icons.navigate_next, tooltip: '下一章', onPressed: editorController.canNavigateToNextChapter ? () { AppLogger.i('ImmersiveModeNavigation', '导航到下一章'); editorController.navigateToNextChapter(); } : null, ), ], ), ); } /// 构建导航按钮 Widget _buildNavigationButton({ required BuildContext context, required IconData icon, required String tooltip, required VoidCallback? onPressed, }) { return Tooltip( message: tooltip, child: IconButton( onPressed: onPressed, icon: Icon( icon, size: 20, ), style: IconButton.styleFrom( minimumSize: const Size(32, 32), padding: const EdgeInsets.all(4), foregroundColor: onPressed != null ? Theme.of(context).colorScheme.onSurface : Theme.of(context).colorScheme.onSurface.withOpacity(0.3), ), ), ); } /// 构建章节信息显示 Widget _buildChapterInfo(BuildContext context, editor_bloc.EditorLoaded state) { final String? currentChapterId = state.immersiveChapterId ?? state.activeChapterId; if (currentChapterId == null) { return const Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: Text('未知章节'), ); } // 查找当前章节信息 String chapterTitle = '未知章节'; String chapterInfo = ''; for (int actIndex = 0; actIndex < state.novel.acts.length; actIndex++) { final act = state.novel.acts[actIndex]; for (int chapterIndex = 0; chapterIndex < act.chapters.length; chapterIndex++) { final chapter = act.chapters[chapterIndex]; if (chapter.id == currentChapterId) { chapterTitle = chapter.title.isNotEmpty ? chapter.title : '第${chapterIndex + 1}章'; chapterInfo = '第${actIndex + 1}卷 第${chapterIndex + 1}章'; break; } } } return Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( chapterTitle, style: Theme.of(context).textTheme.labelMedium?.copyWith( fontWeight: FontWeight.w600, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), Text( chapterInfo, style: Theme.of(context).textTheme.labelSmall?.copyWith( color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ), ], ), ); } } /// 🚀 沉浸模式边界提示组件 class ImmersiveModeBoundaryIndicator extends StatelessWidget { const ImmersiveModeBoundaryIndicator({ super.key, required this.isFirstChapter, required this.isLastChapter, this.onNavigatePrevious, this.onNavigateNext, }); final bool isFirstChapter; final bool isLastChapter; final VoidCallback? onNavigatePrevious; final VoidCallback? onNavigateNext; @override Widget build(BuildContext context) { if (!isFirstChapter && !isLastChapter) { return const SizedBox.shrink(); } return Container( margin: const EdgeInsets.symmetric(vertical: 20), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.3), borderRadius: BorderRadius.circular(12), border: Border.all( color: Theme.of(context).colorScheme.outline.withOpacity(0.2), ), ), child: Row( children: [ Icon( isFirstChapter ? Icons.first_page : Icons.last_page, color: Theme.of(context).colorScheme.onSurfaceVariant, ), const SizedBox(width: 12), Expanded( child: Text( isFirstChapter ? '这是第一章' : '这是最后一章', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), ), if ((isFirstChapter && onNavigateNext != null) || (isLastChapter && onNavigatePrevious != null)) TextButton.icon( onPressed: isFirstChapter ? onNavigateNext : onNavigatePrevious, icon: Icon( isFirstChapter ? Icons.arrow_forward : Icons.arrow_back, size: 16, ), label: Text(isFirstChapter ? '下一章' : '上一章'), style: TextButton.styleFrom( foregroundColor: WebTheme.getPrimaryColor(context), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), ), ), ], ), ); } } /// 🚀 沉浸模式工具栏 class ImmersiveModeToolbar extends StatelessWidget { const ImmersiveModeToolbar({ super.key, required this.editorBloc, }); final editor_bloc.EditorBloc editorBloc; @override Widget build(BuildContext context) { return BlocBuilder( bloc: editorBloc, builder: (context, state) { if (state is! editor_bloc.EditorLoaded || !state.isImmersiveMode) { return const SizedBox.shrink(); } final editorController = Provider.of(context, listen: false); return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.vertical(bottom: Radius.circular(12)), boxShadow: [ BoxShadow( color: Theme.of(context).shadowColor.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( children: [ // 沉浸模式指示器 Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: WebTheme.getPrimaryColor(context).withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.center_focus_strong, size: 16, color: WebTheme.getPrimaryColor(context), ), const SizedBox(width: 4), Text( '沉浸模式', style: Theme.of(context).textTheme.labelSmall?.copyWith( color: WebTheme.getPrimaryColor(context), fontWeight: FontWeight.w600, ), ), ], ), ), const Spacer(), // 快捷操作按钮 Row( mainAxisSize: MainAxisSize.min, children: [ // 返回普通模式按钮 TextButton.icon( onPressed: () { editorController.switchToNormalMode(); }, icon: const Icon(Icons.view_stream, size: 16), label: const Text('普通模式'), style: TextButton.styleFrom( foregroundColor: Theme.of(context).colorScheme.onSurface, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), ), ), ], ), ], ), ); }, ); } }