Browse Source

开始搞业务逻辑

master
hejl 2 months ago
parent
commit
5313a960ad
  1. 73
      win_text_editor/lib/app/modules/content_search/content_search_controller.dart
  2. 21
      win_text_editor/lib/app/modules/content_search/content_search_view.dart
  3. 25
      win_text_editor/lib/app/modules/content_search/directory_settings.dart
  4. 16
      win_text_editor/lib/app/modules/content_search/results_view.dart
  5. 218
      win_text_editor/lib/app/modules/content_search/search_settings.dart

73
win_text_editor/lib/app/modules/content_search/content_search_controller.dart

@ -1,21 +1,74 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
import 'package:win_text_editor/app/core/tab_manager.dart'; import 'package:win_text_editor/app/core/tab_manager.dart';
class ContentSearchController { class ContentSearchController with ChangeNotifier {
final TabManager tabManager; final TabManager tabManager;
String searchQuery = ''; String _searchQuery = '';
String searchDirectory = ''; String _searchDirectory = '';
String fileType = '*.*'; String _fileType = '*.*';
bool caseSensitive = false; bool _caseSensitive = false;
bool wholeWord = false; bool _wholeWord = false;
bool useRegex = false; bool _useRegex = false;
SearchMode searchMode = SearchMode.locate; bool _customRule = false;
final List<SearchResult> results = []; SearchMode _searchMode = SearchMode.locate;
final List<SearchResult> _results = [];
ContentSearchController({required this.tabManager}); ContentSearchController({required this.tabManager});
// Getters
String get searchQuery => _searchQuery;
String get searchDirectory => _searchDirectory;
String get fileType => _fileType;
bool get caseSensitive => _caseSensitive;
bool get wholeWord => _wholeWord;
bool get useRegex => _useRegex;
bool get customRule => _customRule;
SearchMode get searchMode => _searchMode;
List<SearchResult> get results => _results;
set customRule(bool value) {
_customRule = value;
notifyListeners();
}
// Setters with notifyListeners
set searchQuery(String value) {
_searchQuery = value;
notifyListeners();
}
set searchDirectory(String value) {
_searchDirectory = value;
notifyListeners();
}
set fileType(String value) {
_fileType = value;
notifyListeners();
}
set caseSensitive(bool value) {
_caseSensitive = value;
notifyListeners();
}
set wholeWord(bool value) {
_wholeWord = value;
notifyListeners();
}
set useRegex(bool value) {
_useRegex = value;
notifyListeners();
}
set searchMode(SearchMode value) {
_searchMode = value;
notifyListeners();
}
Future<void> startSearch() async { Future<void> startSearch() async {
results.clear(); results.clear();
// //

21
win_text_editor/lib/app/modules/content_search/content_search_view.dart

@ -28,16 +28,17 @@ class ContentSearchViewState extends State<ContentSearchView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return ChangeNotifierProvider.value(
padding: const EdgeInsets.all(8.0), value: _controller,
child: Column( child: Padding(
children: [ padding: const EdgeInsets.all(4.0),
DirectorySettings(controller: _controller), child: Column(
const SizedBox(height: 16), children: [
SearchSettings(controller: _controller), const DirectorySettings(), // controller
const SizedBox(height: 16), SearchSettings(),
Expanded(child: ResultsView(controller: _controller)), const Expanded(child: ResultsView()),
], ],
),
), ),
); );
} }

25
win_text_editor/lib/app/modules/content_search/directory_settings.dart

@ -1,22 +1,23 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/app/modules/content_search/content_search_controller.dart'; import 'package:win_text_editor/app/modules/content_search/content_search_controller.dart';
class DirectorySettings extends StatelessWidget { class DirectorySettings extends StatelessWidget {
final ContentSearchController controller; const DirectorySettings({super.key});
const DirectorySettings({super.key, required this.controller});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final controller = context.watch<ContentSearchController>();
final searchDirectoryController = TextEditingController(text: controller.searchDirectory);
return Card( return Card(
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Row( child: Row(
children: [ children: [
const Icon(Icons.folder, color: Colors.blue),
const SizedBox(width: 8),
Expanded( Expanded(
child: TextField( child: TextField(
controller: searchDirectoryController,
decoration: const InputDecoration(labelText: '搜索目录', border: OutlineInputBorder()), decoration: const InputDecoration(labelText: '搜索目录', border: OutlineInputBorder()),
onChanged: (value) => controller.searchDirectory = value, onChanged: (value) => controller.searchDirectory = value,
), ),
@ -25,17 +26,19 @@ class DirectorySettings extends StatelessWidget {
SizedBox( SizedBox(
width: 100, width: 100,
child: TextField( child: TextField(
decoration: const InputDecoration( decoration: const InputDecoration(labelText: '文件类型', border: OutlineInputBorder()),
labelText: '文件类型',
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 12),
),
controller: TextEditingController(text: controller.fileType), controller: TextEditingController(text: controller.fileType),
onChanged: (value) => controller.fileType = value, onChanged: (value) => controller.fileType = value,
), ),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
IconButton(icon: const Icon(Icons.folder_open), onPressed: controller.pickDirectory), IconButton(
icon: const Icon(Icons.folder_open),
onPressed: () async {
await controller.pickDirectory();
searchDirectoryController.text = controller.searchDirectory;
},
),
], ],
), ),
), ),

16
win_text_editor/lib/app/modules/content_search/results_view.dart

@ -1,13 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/app/modules/content_search/content_search_controller.dart'; import 'package:win_text_editor/app/modules/content_search/content_search_controller.dart';
class ResultsView extends StatelessWidget { class ResultsView extends StatelessWidget {
final ContentSearchController controller; const ResultsView({super.key});
const ResultsView({super.key, required this.controller});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final controller = context.watch<ContentSearchController>();
return Card( return Card(
child: Column( child: Column(
children: [ children: [
@ -21,15 +21,15 @@ class ResultsView extends StatelessWidget {
Expanded( Expanded(
child: child:
controller.searchMode == SearchMode.locate controller.searchMode == SearchMode.locate
? _buildLocateResults() ? _buildLocateResults(controller)
: _buildCountResults(), : _buildCountResults(controller),
), ),
], ],
), ),
); );
} }
Widget _buildLocateResults() { Widget _buildLocateResults(controller) {
return ListView.builder( return ListView.builder(
itemCount: controller.results.length, itemCount: controller.results.length,
itemBuilder: (ctx, index) { itemBuilder: (ctx, index) {
@ -47,10 +47,10 @@ class ResultsView extends StatelessWidget {
); );
} }
Widget _buildCountResults() { Widget _buildCountResults(controller) {
final counts = <String, int>{}; final counts = <String, int>{};
for (var r in controller.results) { for (var r in controller.results) {
counts[r.filePath] = (counts[r.filePath] ?? 0) + r.matches.length; // counts[r.filePath] = (counts[r.filePath] ?? 0) + r.matches.length;
} }
return ListView.builder( return ListView.builder(

218
win_text_editor/lib/app/modules/content_search/search_settings.dart

@ -1,24 +1,26 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/app/components/text_editor.dart'; import 'package:win_text_editor/app/components/text_editor.dart';
import 'package:win_text_editor/app/modules/content_search/content_search_controller.dart'; import 'package:win_text_editor/app/modules/content_search/content_search_controller.dart';
class SearchSettings extends StatelessWidget { class SearchSettings extends StatelessWidget {
final ContentSearchController controller;
final GlobalKey<TextEditorState> _searchEditorKey = GlobalKey(); final GlobalKey<TextEditorState> _searchEditorKey = GlobalKey();
SearchSettings({super.key, required this.controller}); SearchSettings({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final controller = context.watch<ContentSearchController>();
return Card( return Card(
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// // ()
SizedBox( SizedBox(
width: MediaQuery.of(context).size.width * 0.5, // width: MediaQuery.of(context).size.width * 0.5,
height: 300, height: 300,
child: TextEditor( child: TextEditor(
key: _searchEditorKey, key: _searchEditorKey,
@ -31,84 +33,160 @@ class SearchSettings extends StatelessWidget {
// //
Expanded( Expanded(
child: Container( child: Container(
height: 300,
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.grey), // border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(4), // borderRadius: BorderRadius.circular(4),
), ),
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// // -
Column( const Text('搜索方式:', style: TextStyle(fontSize: 12)),
crossAxisAlignment: CrossAxisAlignment.start, const SizedBox(height: 4),
children: [ Row(
const Text('搜索方式:', style: TextStyle(fontSize: 12)), children: [
Row( Expanded(
children: [ child: Row(
Radio<SearchMode>( children: [
value: SearchMode.locate, Radio<SearchMode>(
groupValue: controller.searchMode, value: SearchMode.locate,
onChanged: (value) => controller.searchMode = value!, groupValue: controller.searchMode,
onChanged: (value) => controller.searchMode = value!,
),
const Text('定位', style: TextStyle(fontSize: 12)),
],
), ),
const Text('定位', style: TextStyle(fontSize: 12)), ),
], Expanded(
), child: Row(
Row( children: [
Radio<SearchMode>(
value: SearchMode.count,
groupValue: controller.searchMode,
onChanged: (value) => controller.searchMode = value!,
),
const Text('计数', style: TextStyle(fontSize: 12)),
],
),
),
],
),
const SizedBox(height: 8),
//
const Text('匹配规则:', style: TextStyle(fontSize: 12)),
const SizedBox(height: 4),
//
Row(
children: [
Expanded(
child: CheckboxListTile(
dense: true,
contentPadding: EdgeInsets.zero,
controlAffinity: ListTileControlAffinity.leading,
title: const Text('大小写敏感', style: TextStyle(fontSize: 12)),
value: controller.caseSensitive,
onChanged:
controller.customRule
? null
: (value) {
controller.caseSensitive = value!;
controller.customRule = false;
},
activeColor: controller.customRule ? Colors.grey : null,
),
),
Expanded(
child: CheckboxListTile(
dense: true,
contentPadding: EdgeInsets.zero,
controlAffinity: ListTileControlAffinity.leading,
title: const Text('全字匹配', style: TextStyle(fontSize: 12)),
value: controller.wholeWord,
onChanged:
controller.customRule
? null
: (value) {
controller.wholeWord = value!;
controller.customRule = false;
},
activeColor: controller.customRule ? Colors.grey : null,
),
),
],
),
//
Row(
children: [
Expanded(
child: CheckboxListTile(
dense: true,
contentPadding: EdgeInsets.zero,
controlAffinity: ListTileControlAffinity.leading,
title: const Text('正则匹配', style: TextStyle(fontSize: 12)),
value: controller.useRegex,
onChanged:
controller.customRule
? null
: (value) {
controller.useRegex = value!;
controller.customRule = false;
},
activeColor: controller.customRule ? Colors.grey : null,
),
),
Expanded(
child: CheckboxListTile(
dense: true,
contentPadding: EdgeInsets.zero,
controlAffinity: ListTileControlAffinity.leading,
title: const Text('自定义规则', style: TextStyle(fontSize: 12)),
value: controller.customRule,
onChanged: (value) {
controller.customRule = value!;
if (value) {
controller.caseSensitive = false;
controller.wholeWord = false;
controller.useRegex = false;
}
},
),
),
],
),
const SizedBox(height: 8),
// JS函数说明
Visibility(
visible: controller.customRule,
child: const Column(
children: [ children: [
Radio<SearchMode>( Text.rich(
value: SearchMode.count, TextSpan(
groupValue: controller.searchMode, text:
onChanged: (value) => controller.searchMode = value!, "在搜索内容输入框中输入js函数体:\n"
"function boolean match(content) {//content为单行文本\n"
"}//返回true或者false",
style: TextStyle(fontSize: 12, fontFamily: 'monospace'),
),
), ),
const Text('计数', style: TextStyle(fontSize: 12)), SizedBox(height: 8),
], ],
), ),
], ),
), //
const SizedBox(height: 8), Align(
// alignment: Alignment.bottomCenter,
Column( child: ElevatedButton.icon(
crossAxisAlignment: CrossAxisAlignment.start, icon: const Icon(Icons.search, size: 20),
children: [ label: const Text('开始搜索'),
const Text('匹配规则:', style: TextStyle(fontSize: 12)), onPressed: controller.startSearch,
CheckboxListTile(
contentPadding: EdgeInsets.zero,
controlAffinity: ListTileControlAffinity.leading,
title: const Text('大小写敏感', style: TextStyle(fontSize: 12)),
value: controller.caseSensitive,
onChanged: (value) => controller.caseSensitive = value!,
),
CheckboxListTile(
contentPadding: EdgeInsets.zero,
controlAffinity: ListTileControlAffinity.leading,
title: const Text('全字匹配', style: TextStyle(fontSize: 12)),
value: controller.wholeWord,
onChanged: (value) => controller.wholeWord = value!,
),
CheckboxListTile(
contentPadding: EdgeInsets.zero,
controlAffinity: ListTileControlAffinity.leading,
title: const Text('正则匹配', style: TextStyle(fontSize: 12)),
value: controller.useRegex,
onChanged: (value) => controller.useRegex = value!,
), ),
],
),
const SizedBox(height: 8),
//
Align(
alignment: Alignment.centerLeft,
child: ElevatedButton.icon(
icon: const Icon(Icons.search, size: 20),
label: const Text('开始搜索'),
onPressed: controller.startSearch,
), ),
), ],
], ),
), ),
), ),
),
], ],
), ),
), ),

Loading…
Cancel
Save