5 changed files with 240 additions and 5 deletions
@ -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<TemplateNode> _treeNodes = []; |
||||||
|
List<TemplateItem> _templateItems = []; |
||||||
|
|
||||||
|
// Getters |
||||||
|
String get filePath => _filePath; |
||||||
|
List<TemplateNode> get treeNodes => _treeNodes; |
||||||
|
List<TemplateItem> get templateItems => _templateItems; |
||||||
|
|
||||||
|
Future<void> 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<TemplateNode> children; |
||||||
|
|
||||||
|
TemplateNode(this.name, this.children); |
||||||
|
} |
||||||
|
|
||||||
|
class TemplateItem { |
||||||
|
final int id; |
||||||
|
final String content; |
||||||
|
|
||||||
|
TemplateItem({required this.id, required this.content}); |
||||||
|
} |
@ -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<TemplateParserView> createState() => _TemplateParserViewState(); |
||||||
|
} |
||||||
|
|
||||||
|
class _TemplateParserViewState extends State<TemplateParserView> { |
||||||
|
late final TemplateParserController _controller; |
||||||
|
|
||||||
|
get tabManager => Provider.of<TabItemsController>(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<TemplateParserController>( |
||||||
|
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<TemplateParserController>( |
||||||
|
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<TemplateNode> 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<TemplateParserController>(context, listen: false).selectTreeNode(node); |
||||||
|
} |
||||||
|
}, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
Widget _buildGridView(List<TemplateItem> 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}')], |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
}, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue