From f8c5661c1d7052f0d9844c04678cee4746816e89 Mon Sep 17 00:00:00 2001 From: hejl Date: Fri, 9 May 2025 18:01:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=80=E6=90=9E=E6=A8=A1=E6=9D=BF=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- win_text_editor/lib/app/menus/app_menu.dart | 5 + .../lib/app/menus/menu_actions.dart | 12 ++ .../lib/app/menus/menu_constants.dart | 4 + .../lib/app/models/editor_tab.dart | 21 +-- .../lib/app/providers/editor_provider.dart | 105 +++---------- .../lib/app/widgets/editor_pane.dart | 148 +++++++++--------- .../lib/app/widgets/file_explorer.dart | 4 +- win_text_editor/lib/app/widgets/tab_bar.dart | 83 ---------- .../lib/app/widgets/template_parser.dart | 113 +++++++++++++ 9 files changed, 249 insertions(+), 246 deletions(-) delete mode 100644 win_text_editor/lib/app/widgets/tab_bar.dart create mode 100644 win_text_editor/lib/app/widgets/template_parser.dart diff --git a/win_text_editor/lib/app/menus/app_menu.dart b/win_text_editor/lib/app/menus/app_menu.dart index 7c146f7..29c5c05 100644 --- a/win_text_editor/lib/app/menus/app_menu.dart +++ b/win_text_editor/lib/app/menus/app_menu.dart @@ -14,6 +14,7 @@ class AppMenu extends StatelessWidget { child: Row( children: [ _buildMenuButton(context, '文件', _buildFileMenuItems()), + _buildMenuButton(context, '工具', _buildToolsMenuItems()), _buildMenuButton(context, '编辑', _buildEditMenuItems()), _buildMenuButton(context, '窗口', _buildWindowMenuItems()), _buildMenuButton(context, '帮助', _buildHelpMenuItems()), @@ -22,6 +23,10 @@ class AppMenu extends StatelessWidget { ); } + List> _buildToolsMenuItems() { + return [const PopupMenuItem(value: MenuConstants.templateParser, child: Text('模板解析'))]; + } + Widget _buildMenuButton(BuildContext context, String label, List> items) { return PopupMenuButton( offset: const Offset(0, 30), diff --git a/win_text_editor/lib/app/menus/menu_actions.dart b/win_text_editor/lib/app/menus/menu_actions.dart index dbaae43..acc3ecb 100644 --- a/win_text_editor/lib/app/menus/menu_actions.dart +++ b/win_text_editor/lib/app/menus/menu_actions.dart @@ -5,12 +5,19 @@ import 'package:win_text_editor/app/menus/menu_constants.dart'; import 'package:win_text_editor/app/providers/file_provider.dart'; import 'dart:io'; +import '../models/editor_tab.dart'; +import '../providers/editor_provider.dart'; +import '../widgets/template_parser.dart'; // 确保导入了TemplateParser + class MenuActions { static Future handleMenuAction(String value, BuildContext context) async { switch (value) { case MenuConstants.openFolder: await _openFolder(context); break; + case MenuConstants.templateParser: + _openTemplateParser(context); + break; case MenuConstants.exit: _exitApplication(); break; @@ -27,6 +34,11 @@ class MenuActions { } } + static void _openTemplateParser(BuildContext context) { + final editorProvider = Provider.of(context, listen: false); + editorProvider.addTemplateParserTab(); + } + static void _exitApplication() { exit(0); } diff --git a/win_text_editor/lib/app/menus/menu_constants.dart b/win_text_editor/lib/app/menus/menu_constants.dart index ba2c1ad..a9a5ee6 100644 --- a/win_text_editor/lib/app/menus/menu_constants.dart +++ b/win_text_editor/lib/app/menus/menu_constants.dart @@ -1,6 +1,7 @@ class MenuConstants { // 菜单项类型 static const String fileMenu = 'file'; + static const String toolsMenu = 'tools'; static const String editMenu = 'edit'; static const String windowMenu = 'window'; static const String helpMenu = 'help'; @@ -11,6 +12,9 @@ class MenuConstants { static const String saveAs = 'save_as'; static const String exit = 'exit'; + // 工具菜单项 + static const String templateParser = 'template_parser'; + // 编辑菜单项 static const String undo = 'undo'; static const String redo = 'redo'; diff --git a/win_text_editor/lib/app/models/editor_tab.dart b/win_text_editor/lib/app/models/editor_tab.dart index a0b2525..df1e46e 100644 --- a/win_text_editor/lib/app/models/editor_tab.dart +++ b/win_text_editor/lib/app/models/editor_tab.dart @@ -1,15 +1,16 @@ +// models/editor_tab.dart +enum EditorTabType { + blank, // 空白 + template, // 模板解析 + fileEditor, // 文件编辑器 + // 可以添加更多类型 +} + class EditorTab { final String id; final String title; - final String path; - String content; - final String fileType; + final EditorTabType type; + final dynamic content; // 可存储不同类型的内容 - EditorTab({ - required this.id, - required this.title, - required this.path, - required this.content, - required this.fileType, - }); + EditorTab({required this.id, required this.title, required this.type, this.content}); } diff --git a/win_text_editor/lib/app/providers/editor_provider.dart b/win_text_editor/lib/app/providers/editor_provider.dart index 4f7f8d1..4206fd9 100644 --- a/win_text_editor/lib/app/providers/editor_provider.dart +++ b/win_text_editor/lib/app/providers/editor_provider.dart @@ -1,97 +1,40 @@ import 'package:flutter/material.dart'; import 'package:win_text_editor/app/models/editor_tab.dart'; -import 'package:win_text_editor/app/services/file_service.dart'; -import 'package:win_text_editor/app/services/syntax_service.dart'; +// providers/editor_provider.dart class EditorProvider with ChangeNotifier { - final List _openTabs = []; - int _activeTabIndex = 0; - int _currentLayout = 0; // 0=平铺, 1=层叠, 2=单页 + List _tabs = []; + String? _activeTabId; - List get openTabs => _openTabs; - int get activeTabIndex => _activeTabIndex; - int get currentLayout => _currentLayout; + List get tabs => _tabs; + String? get activeTabId => _activeTabId; - Future openFile(String filePath) async { - try { - // 检查是否已经打开 - final existingIndex = _openTabs.indexWhere((tab) => tab.path == filePath); - if (existingIndex != -1) { - _activeTabIndex = existingIndex; - notifyListeners(); - return; - } - - // 读取文件内容 - final content = await FileService.readFile(filePath); - final fileName = filePath.split('/').last; - final fileType = SyntaxService.detectFileType(fileName); - - // 创建新标签页 - final newTab = EditorTab( - id: DateTime.now().millisecondsSinceEpoch.toString(), - title: fileName, - path: filePath, - content: content, - fileType: fileType, - ); - - _openTabs.add(newTab); - _activeTabIndex = _openTabs.length - 1; - notifyListeners(); - } catch (e) { - debugPrint('Error opening file: $e'); - } + void addTab(EditorTab tab) { + _tabs.add(tab); + _activeTabId = tab.id; + notifyListeners(); } void closeTab(String tabId) { - final index = _openTabs.indexWhere((tab) => tab.id == tabId); - if (index != -1) { - _openTabs.removeAt(index); - if (_activeTabIndex >= index && _activeTabIndex > 0) { - _activeTabIndex--; - } - notifyListeners(); + _tabs.removeWhere((tab) => tab.id == tabId); + if (_activeTabId == tabId) { + _activeTabId = _tabs.isNotEmpty ? _tabs.last.id : null; } + notifyListeners(); } - void setActiveTab(int index) { - if (index >= 0 && index < _openTabs.length) { - _activeTabIndex = index; - notifyListeners(); - } - } - - Future saveFile(String tabId) async { - final index = _openTabs.indexWhere((tab) => tab.id == tabId); - if (index != -1) { - final tab = _openTabs[index]; - await FileService.writeFile(tab.path, tab.content); - // 可以添加保存成功的提示 - } + void setActiveTab(String tabId) { + _activeTabId = tabId; + notifyListeners(); } - Future copyToClipboard(String tabId) async { - final index = _openTabs.indexWhere((tab) => tab.id == tabId); - if (index != -1) { - final tab = _openTabs[index]; - // 这里需要实现复制到剪贴板的逻辑 - // 可以使用 clipboard package - } - } - - void changeLayout(int layout) { - if (layout >= 0 && layout <= 2) { - _currentLayout = layout; - notifyListeners(); - } - } - - void updateTabContent(String tabId, String newContent) { - final index = _openTabs.indexWhere((tab) => tab.id == tabId); - if (index != -1) { - _openTabs[index].content = newContent; - notifyListeners(); - } + // 添加模板解析标签 + void addTemplateParserTab() { + final tab = EditorTab( + id: 'template-${DateTime.now().millisecondsSinceEpoch}', + title: '模板解析 ${_tabs.where((t) => t.type == EditorTabType.template).length + 1}', + type: EditorTabType.template, + ); + addTab(tab); } } diff --git a/win_text_editor/lib/app/widgets/editor_pane.dart b/win_text_editor/lib/app/widgets/editor_pane.dart index adf607d..635f112 100644 --- a/win_text_editor/lib/app/widgets/editor_pane.dart +++ b/win_text_editor/lib/app/widgets/editor_pane.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:win_text_editor/app/providers/editor_provider.dart'; -import 'package:win_text_editor/app/widgets/tab_bar.dart'; import '../models/editor_tab.dart'; +import '../providers/editor_provider.dart'; +import 'template_parser.dart'; class EditorPane extends StatelessWidget { const EditorPane({super.key}); @@ -12,85 +12,93 @@ class EditorPane extends StatelessWidget { Widget build(BuildContext context) { final editorProvider = Provider.of(context); - return Column( - children: [ - const EditorTabBar(), - Expanded( - child: IndexedStack( - index: editorProvider.currentLayout, - children: [ - // 平铺布局 - GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2), - itemCount: editorProvider.openTabs.length, - itemBuilder: (context, index) { - return EditorTabContent(tab: editorProvider.openTabs[index]); - }, - ), - // 层叠布局 - Stack( - children: - editorProvider.openTabs.map((tab) { - return Positioned( - top: 20.0 * editorProvider.openTabs.indexOf(tab), - left: 20.0 * editorProvider.openTabs.indexOf(tab), - right: 20.0 * editorProvider.openTabs.indexOf(tab), - bottom: 20.0 * editorProvider.openTabs.indexOf(tab), - child: EditorTabContent(tab: tab), - ); - }).toList(), - ), - // 单页布局 - if (editorProvider.openTabs.isNotEmpty) - EditorTabContent(tab: editorProvider.openTabs[editorProvider.activeTabIndex]), - ], + return Container( + color: Colors.white, + child: Column( + children: [ + // 标签栏 + _buildTabBar(context), + // 内容区域 + Expanded( + child: + editorProvider.tabs.isEmpty + ? const Center(child: Text('空白区域', style: TextStyle(color: Colors.grey))) + : _buildCurrentTabContent(context), ), - ), - ], + ], + ), ); } + + Widget _buildTabBar(BuildContext context) { + final editorProvider = Provider.of(context); + + return SizedBox( + height: 40, + child: ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: editorProvider.tabs.length, + itemBuilder: (ctx, index) { + final tab = editorProvider.tabs[index]; + return _TabItem( + tab: tab, + isActive: tab.id == editorProvider.activeTabId, + onClose: () => editorProvider.closeTab(tab.id), + onTap: () => editorProvider.setActiveTab(tab.id), + ); + }, + ), + ); + } + + Widget _buildCurrentTabContent(BuildContext context) { + final editorProvider = Provider.of(context); + final activeTab = editorProvider.tabs.firstWhere( + (tab) => tab.id == editorProvider.activeTabId, + orElse: () => editorProvider.tabs.first, + ); + + switch (activeTab.type) { + case EditorTabType.template: + return const TemplateParser(); // 你的模板解析组件 + default: + return Container(); // 空白或其他默认视图 + } + } } -class EditorTabContent extends StatelessWidget { +class _TabItem extends StatelessWidget { final EditorTab tab; + final bool isActive; + final VoidCallback onClose; + final VoidCallback onTap; - const EditorTabContent({super.key, required this.tab}); + const _TabItem({ + required this.tab, + required this.isActive, + required this.onClose, + required this.onTap, + }); @override Widget build(BuildContext context) { - final editorProvider = Provider.of(context, listen: false); - - return Card( - margin: const EdgeInsets.all(8.0), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Text(tab.title), - const Spacer(), - IconButton( - icon: const Icon(Icons.content_copy), - onPressed: () => editorProvider.copyToClipboard(tab.id), - ), - IconButton( - icon: const Icon(Icons.save), - onPressed: () => editorProvider.saveFile(tab.id), - ), - IconButton( - icon: const Icon(Icons.close), - onPressed: () => editorProvider.closeTab(tab.id), - ), - ], - ), - ), - Expanded( - child: SingleChildScrollView( - child: Container(padding: const EdgeInsets.all(8.0), child: Text(tab.content)), - ), + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + color: isActive ? Colors.blue[100] : Colors.grey[200], + border: Border( + bottom: BorderSide(color: isActive ? Colors.blue : Colors.transparent, width: 2), ), - ], + ), + child: Row( + children: [ + Text(tab.title), + const SizedBox(width: 8), + IconButton(icon: const Icon(Icons.close, size: 16), onPressed: onClose), + ], + ), ), ); } diff --git a/win_text_editor/lib/app/widgets/file_explorer.dart b/win_text_editor/lib/app/widgets/file_explorer.dart index 71cf8a9..7691e96 100644 --- a/win_text_editor/lib/app/widgets/file_explorer.dart +++ b/win_text_editor/lib/app/widgets/file_explorer.dart @@ -130,14 +130,14 @@ class _FileNodeWidget extends StatelessWidget { @override Widget build(BuildContext context) { - final editorProvider = Provider.of(context, listen: false); + //final editorProvider = Provider.of(context, listen: false); return InkWell( onTap: () { if (node.isDirectory) { onTap(); } else { - editorProvider.openFile(node.path); + //editorProvider.openFile(node.path); } }, child: Container( diff --git a/win_text_editor/lib/app/widgets/tab_bar.dart b/win_text_editor/lib/app/widgets/tab_bar.dart deleted file mode 100644 index 79f5372..0000000 --- a/win_text_editor/lib/app/widgets/tab_bar.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:win_text_editor/app/providers/editor_provider.dart'; - -import '../providers/file_provider.dart'; - -class EditorTabBar extends StatelessWidget { - const EditorTabBar({super.key}); - - @override - Widget build(BuildContext context) { - final editorProvider = Provider.of(context); - - return Container( - height: 40, - color: Colors.grey[300], - child: Row( - children: [ - // 布局切换按钮 - PopupMenuButton( - icon: const Icon(Icons.grid_view), - itemBuilder: - (context) => [ - const PopupMenuItem(value: 0, child: Text('平铺布局')), - const PopupMenuItem(value: 1, child: Text('层叠布局')), - const PopupMenuItem(value: 2, child: Text('单页布局')), - ], - onSelected: (value) => editorProvider.changeLayout(value), - ), - // 标签页 - Expanded( - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: editorProvider.openTabs.length, - itemBuilder: (context, index) { - final tab = editorProvider.openTabs[index]; - return InkWell( - onTap: () => editorProvider.setActiveTab(index), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - decoration: BoxDecoration( - color: - editorProvider.activeTabIndex == index ? Colors.white : Colors.grey[200], - border: Border( - bottom: BorderSide( - color: - editorProvider.activeTabIndex == index - ? Colors.blue - : Colors.transparent, - width: 2.0, - ), - ), - ), - child: Center( - child: Row( - children: [ - Text(tab.title), - const SizedBox(width: 8), - IconButton( - icon: const Icon(Icons.close, size: 16), - onPressed: () => editorProvider.closeTab(tab.id), - ), - ], - ), - ), - ), - ); - }, - ), - ), - // 打开文件按钮 - IconButton( - icon: const Icon(Icons.add), - onPressed: () async { - final fileProvider = Provider.of(context, listen: false); - await fileProvider.pickAndOpenFile(); - }, - ), - ], - ), - ); - } -} diff --git a/win_text_editor/lib/app/widgets/template_parser.dart b/win_text_editor/lib/app/widgets/template_parser.dart new file mode 100644 index 0000000..60ad57d --- /dev/null +++ b/win_text_editor/lib/app/widgets/template_parser.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; + +class TemplateParser extends StatelessWidget { + const TemplateParser({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Card( + margin: EdgeInsets.zero, // 移除默认边距以填满整个区域 + color: Colors.grey[100], + child: Column( + children: [ + // 四个编辑框区域 + Expanded( + child: GridView.builder( + padding: const EdgeInsets.all(8.0), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: 1.0, + crossAxisSpacing: 8.0, + mainAxisSpacing: 8.0, + ), + itemCount: 4, + itemBuilder: (context, index) { + return _buildEditorPanel(context, index); + }, + ), + ), + ], + ), + ); + } + + Widget _buildEditorPanel(BuildContext context, int index) { + // 定义每个面板的标题 + final titles = ['输入内容', '输出内容', '参考文件', '模板']; + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // 工具条 + Container( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), + decoration: BoxDecoration( + color: Colors.grey[100], + borderRadius: const BorderRadius.vertical(top: Radius.circular(4.0)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // 左侧标题 + Text( + titles[index], + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14), + ), + // 右侧工具按钮 + Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.folder_open, size: 18), + onPressed: () {}, + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + ), + IconButton( + icon: const Icon(Icons.content_copy, size: 18), + onPressed: () {}, + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + ), + IconButton( + icon: const Icon(Icons.save, size: 18), + onPressed: () {}, + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + ), + IconButton( + icon: const Icon(Icons.close, size: 18), + onPressed: () {}, + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + ), + ], + ), + ], + ), + ), + + // 编辑框 + Expanded( + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: const BorderRadius.vertical(bottom: Radius.circular(4.0)), + ), + child: const TextField( + maxLines: null, + expands: true, + decoration: InputDecoration( + fillColor: Colors.white, + filled: true, + border: InputBorder.none, + contentPadding: EdgeInsets.all(8.0), + hintText: '输入内容...', + ), + ), + ), + ), + ], + ); + } +}