diff --git a/win_text_editor/lib/framework/controllers/tab_items_controller.dart b/win_text_editor/lib/framework/controllers/tab_items_controller.dart index 670680d..1b5ec0b 100644 --- a/win_text_editor/lib/framework/controllers/tab_items_controller.dart +++ b/win_text_editor/lib/framework/controllers/tab_items_controller.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:win_text_editor/framework/models/tab_model.dart'; import 'package:win_text_editor/modules/content_search/controllers/content_search_controller.dart'; +import 'package:win_text_editor/modules/template_parser/controllers/template_parser_controller.dart'; import 'package:win_text_editor/shared/base/base_content_controller.dart'; import 'package:win_text_editor/framework/controllers/logger.dart'; @@ -56,15 +57,28 @@ class TabItemsController with ChangeNotifier { switch (tab.type) { case 'content_search': return ContentSearchController(); + case 'template_parser': + return TemplateParserController(); default: return null; } } BaseContentController? get activeContentController { - if (_activeTabId == null) return null; + if (_activeTabId == null) { + Logger().error("没有活动的选项卡ID", source: 'TabItemsController'); + return null; + } final controller = _contentControllers[_activeTabId]; - return controller is BaseContentController ? controller : null; + if (controller == null) { + Logger().error("没有活动的内容控制器,activeTabId:$_activeTabId", source: 'TabItemsController'); + return null; + } + if (controller is! BaseContentController) { + Logger().error("活动内容控制器不是BaseContentController类型", source: 'TabItemsController'); + return null; + } + return controller; } void closeTab(String tabId) { @@ -103,6 +117,10 @@ class TabItemsController with ChangeNotifier { } void handleFileDoubleTap(String filePath) { + if (activeContentController == null) { + Logger().error("没有活动的内容控制器", source: 'TabItemsController'); + return; + } activeContentController?.onOpenFile(filePath); } } diff --git a/win_text_editor/lib/framework/widgets/tab_view.dart b/win_text_editor/lib/framework/widgets/tab_view.dart index c88b53c..4ed0a5b 100644 --- a/win_text_editor/lib/framework/widgets/tab_view.dart +++ b/win_text_editor/lib/framework/widgets/tab_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:win_text_editor/modules/template_parser/widgets/template_parser_view.dart'; import 'package:win_text_editor/shared/components/text_editor.dart'; import 'package:win_text_editor/framework/controllers/tab_items_controller.dart'; import 'package:win_text_editor/framework/models/tab_model.dart'; @@ -58,7 +59,9 @@ class _TabViewState extends State { Widget _buildTabItem(AppTab tab, ChangeNotifier? controller) { switch (tab.type) { case 'content_search': - return ContentSearchView(tabId: tab.id, controller: controller); + return ContentSearchView(tabId: tab.id); + case 'template_parser': + return TemplateParserView(tabId: tab.id); default: return TextEditor(tabId: tab.id, initialContent: tab.content); } diff --git a/win_text_editor/lib/modules/content_search/widgets/content_search_view.dart b/win_text_editor/lib/modules/content_search/widgets/content_search_view.dart index fe22b60..b02a52e 100644 --- a/win_text_editor/lib/modules/content_search/widgets/content_search_view.dart +++ b/win_text_editor/lib/modules/content_search/widgets/content_search_view.dart @@ -7,9 +7,8 @@ import 'search_settings.dart'; import 'results_view.dart'; class ContentSearchView extends StatefulWidget { - final ChangeNotifier? controller; final String tabId; - const ContentSearchView({super.key, required this.tabId, this.controller}); + const ContentSearchView({super.key, required this.tabId}); @override ContentSearchViewState createState() => ContentSearchViewState(); diff --git a/win_text_editor/lib/modules/template_parser/controllers/template_parser_controller.dart b/win_text_editor/lib/modules/template_parser/controllers/template_parser_controller.dart new file mode 100644 index 0000000..08caec5 --- /dev/null +++ b/win_text_editor/lib/modules/template_parser/controllers/template_parser_controller.dart @@ -0,0 +1,85 @@ +// template_parser_controller.dart + +import 'package:file_picker/file_picker.dart'; +import 'package:win_text_editor/framework/controllers/logger.dart'; +import 'package:win_text_editor/shared/base/base_content_controller.dart'; + +class TemplateParserController extends BaseContentController { + String _filePath = ''; + List _treeNodes = []; + List _templateItems = []; + + // Getters + String get filePath => _filePath; + List get treeNodes => _treeNodes; + List get templateItems => _templateItems; + + Future pickFile() async { + final result = await FilePicker.platform.pickFiles(); + if (result != null) { + _filePath = result.files.single.path!; + _loadTemplateData(); // Simulate loading data + notifyListeners(); + } + } + + void setFilePath(String path) { + _filePath = path; + _loadTemplateData(); // Simulate loading data + notifyListeners(); + } + + void selectTreeNode(TemplateNode node) { + // Simulate loading items when a tree node is selected + _templateItems = List.generate( + 10, + (index) => TemplateItem(id: index + 1, content: 'Content for ${node.name} item ${index + 1}'), + ); + notifyListeners(); + } + + void _loadTemplateData() { + // Simulate loading tree data + _treeNodes = [ + TemplateNode('Section 1', [ + TemplateNode('Subsection 1.1', []), + TemplateNode('Subsection 1.2', []), + ]), + TemplateNode('Section 2', [ + TemplateNode('Subsection 2.1', []), + TemplateNode('Subsection 2.2', []), + ]), + ]; + + // Simulate loading initial items + _templateItems = List.generate( + 15, + (index) => TemplateItem(id: index + 1, content: 'Initial template content ${index + 1}'), + ); + } + + @override + void onOpenFile(String filePath) { + Logger().info('File selected: $filePath'); + setFilePath(filePath); + } + + @override + void onOpenFolder(String folderPath) { + // TODO: implement onOpenFolder + } +} + +class TemplateNode { + final String name; + final List children; + + TemplateNode(this.name, this.children); +} + +class TemplateItem { + final int id; + final String content; + + TemplateItem({required this.id, required this.content}); +} diff --git a/win_text_editor/lib/modules/template_parser/widgets/template_parser_view.dart b/win_text_editor/lib/modules/template_parser/widgets/template_parser_view.dart new file mode 100644 index 0000000..e1b0aaf --- /dev/null +++ b/win_text_editor/lib/modules/template_parser/widgets/template_parser_view.dart @@ -0,0 +1,130 @@ +// template_parser_view.dart +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:win_text_editor/framework/controllers/tab_items_controller.dart'; +import 'package:win_text_editor/modules/template_parser/controllers/template_parser_controller.dart'; + +class TemplateParserView extends StatefulWidget { + final String tabId; + const TemplateParserView({super.key, required this.tabId}); + + @override + State createState() => _TemplateParserViewState(); +} + +class _TemplateParserViewState extends State { + late final TemplateParserController _controller; + + get tabManager => Provider.of(context, listen: false); + + @override + void initState() { + super.initState(); + _controller = tabManager.getController(widget.tabId) ?? TemplateParserController(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider.value( + value: _controller, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + _buildFilePathInput(), + const SizedBox(height: 8), + Expanded(child: _buildMainContent()), + ], + ), + ), + ); + } + + Widget _buildFilePathInput() { + return Consumer( + builder: (context, controller, _) { + return TextField( + decoration: InputDecoration( + labelText: 'Template File', + hintText: 'Select a template file', + suffixIcon: IconButton( + icon: const Icon(Icons.folder_open), + onPressed: controller.pickFile, + ), + border: const OutlineInputBorder(), + ), + controller: TextEditingController(text: controller.filePath), + readOnly: true, + ); + }, + ); + } + + Widget _buildMainContent() { + return Consumer( + builder: (context, controller, _) { + return Row( + children: [ + // Tree view (30% width) + SizedBox( + width: MediaQuery.of(context).size.width * 0.3, + child: Card(child: _buildTreeView(controller.treeNodes)), + ), + const SizedBox(width: 8), + // Grid view (70% width) + Expanded(child: Card(child: _buildGridView(controller.templateItems))), + ], + ); + }, + ); + } + + Widget _buildTreeView(List nodes) { + return ListView.builder( + itemCount: nodes.length, + itemBuilder: (context, index) { + return _buildTreeNode(nodes[index]); + }, + ); + } + + Widget _buildTreeNode(TemplateNode node) { + return ExpansionTile( + title: Text(node.name), + children: node.children.map((child) => _buildTreeNode(child)).toList(), + onExpansionChanged: (expanded) { + if (expanded) { + Provider.of(context, listen: false).selectTreeNode(node); + } + }, + ); + } + + Widget _buildGridView(List items) { + return GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: 5, + ), + itemCount: items.length, + itemBuilder: (context, index) { + final item = items[index]; + return Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [Text('ID: ${item.id}'), Text('Content: ${item.content}')], + ), + ), + ); + }, + ); + } +}