mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
feat: searching in editor page (#756)
* feat: searching in editor page Fixes #734 * opt.: editor searching ui
This commit is contained in:
@@ -14,6 +14,7 @@ import 'package:server_box/data/store/setting.dart';
|
||||
|
||||
import 'package:server_box/view/widget/two_line_text.dart';
|
||||
import 'package:re_editor/re_editor.dart';
|
||||
import 'package:server_box/view/widget/code_find_panel_view.dart';
|
||||
|
||||
enum EditorPageRetType { path, text }
|
||||
|
||||
@@ -67,6 +68,7 @@ class _EditorPageState extends State<EditorPage> {
|
||||
final _focusNode = FocusNode();
|
||||
|
||||
late CodeLineEditingController _controller;
|
||||
late CodeFindController _findController;
|
||||
late Map<String, TextStyle> _codeTheme;
|
||||
|
||||
String? _langCode;
|
||||
@@ -74,14 +76,17 @@ class _EditorPageState extends State<EditorPage> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_controller.dispose();
|
||||
_findController.dispose();
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = CodeLineEditingController();
|
||||
_findController = CodeFindController(_controller);
|
||||
_init();
|
||||
}
|
||||
|
||||
@@ -120,6 +125,11 @@ class _EditorPageState extends State<EditorPage> {
|
||||
down: l10n.editor,
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.search),
|
||||
tooltip: libL10n.search,
|
||||
onPressed: () => _findController.findMode(),
|
||||
),
|
||||
PopupMenuButton<String>(
|
||||
icon: const Icon(Icons.language),
|
||||
tooltip: libL10n.language,
|
||||
@@ -150,6 +160,7 @@ class _EditorPageState extends State<EditorPage> {
|
||||
color: _EditorReTheme.getBackground(_codeTheme),
|
||||
child: CodeEditor(
|
||||
controller: _controller,
|
||||
findController: _findController,
|
||||
wordWrap: Stores.setting.editorSoftWrap.fetch(),
|
||||
focusNode: _focusNode,
|
||||
indicatorBuilder: (context, editingController, chunkController, notifier) {
|
||||
@@ -162,7 +173,8 @@ class _EditorPageState extends State<EditorPage> {
|
||||
],
|
||||
);
|
||||
},
|
||||
// findBuilder: (context, controller, readOnly) => CodeFindPanelView(controller: controller, readOnly: readOnly),
|
||||
findBuilder: (context, controller, readOnly) =>
|
||||
CodeFindPanelView(controller: controller, readOnly: readOnly),
|
||||
// toolbarController: const ContextMenuControllerImpl(),
|
||||
),
|
||||
);
|
||||
@@ -175,7 +187,6 @@ extension on _EditorPageState {
|
||||
if (Stores.setting.editorHighlight.fetch()) {
|
||||
_langCode = widget.args?.langCode ?? Highlights.getCode(widget.args?.path);
|
||||
}
|
||||
_controller = CodeLineEditingController();
|
||||
if (_langCode == null) {
|
||||
_setupCtrl();
|
||||
} else {
|
||||
|
||||
239
lib/view/widget/code_find_panel_view.dart
Normal file
239
lib/view/widget/code_find_panel_view.dart
Normal file
@@ -0,0 +1,239 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:fl_lib/fl_lib.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:re_editor/re_editor.dart';
|
||||
|
||||
const EdgeInsetsGeometry _kDefaultFindMargin = EdgeInsets.only(right: 10);
|
||||
const double _kDefaultFindPanelWidth = 360;
|
||||
const double _kDefaultFindPanelHeight = 36;
|
||||
const double _kDefaultReplacePanelHeight = _kDefaultFindPanelHeight * 2;
|
||||
const double _kDefaultFindIconSize = 16;
|
||||
const double _kDefaultFindIconWidth = 30;
|
||||
const double _kDefaultFindIconHeight = 30;
|
||||
const double _kDefaultFindInputFontSize = 13;
|
||||
const double _kDefaultFindResultFontSize = 12;
|
||||
const EdgeInsetsGeometry _kDefaultFindPadding = EdgeInsets.only(left: 5, right: 5, top: 2.5, bottom: 2.5);
|
||||
const EdgeInsetsGeometry _kDefaultFindInputContentPadding = EdgeInsets.only(left: 5, right: 5);
|
||||
|
||||
class CodeFindPanelView extends StatelessWidget implements PreferredSizeWidget {
|
||||
final CodeFindController controller;
|
||||
final EdgeInsetsGeometry margin;
|
||||
final bool readOnly;
|
||||
final Color? iconColor;
|
||||
final Color? iconSelectedColor;
|
||||
final double iconSize;
|
||||
final double inputFontSize;
|
||||
final double resultFontSize;
|
||||
final Color? inputTextColor;
|
||||
final Color? resultFontColor;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final InputDecoration decoration;
|
||||
|
||||
const CodeFindPanelView({
|
||||
super.key,
|
||||
required this.controller,
|
||||
this.margin = _kDefaultFindMargin,
|
||||
required this.readOnly,
|
||||
this.iconSelectedColor,
|
||||
this.iconColor,
|
||||
this.iconSize = _kDefaultFindIconSize,
|
||||
this.inputFontSize = _kDefaultFindInputFontSize,
|
||||
this.resultFontSize = _kDefaultFindResultFontSize,
|
||||
this.inputTextColor,
|
||||
this.resultFontColor,
|
||||
this.padding = _kDefaultFindPadding,
|
||||
this.decoration = const InputDecoration(
|
||||
filled: true,
|
||||
contentPadding: _kDefaultFindInputContentPadding,
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(0)), gapPadding: 0),
|
||||
),
|
||||
});
|
||||
|
||||
@override
|
||||
Size get preferredSize {
|
||||
final value = controller.value;
|
||||
final height = value == null
|
||||
? 0.0
|
||||
: (value.replaceMode ? _kDefaultReplacePanelHeight : _kDefaultFindPanelHeight) + margin.vertical;
|
||||
return Size(double.infinity, height);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (controller.value == null) return UIs.placeholder;
|
||||
|
||||
return Container(
|
||||
margin: margin,
|
||||
alignment: Alignment.topRight,
|
||||
height: preferredSize.height,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: _kDefaultFindPanelWidth,
|
||||
child: Column(
|
||||
children: [
|
||||
_buildFindInputView(context),
|
||||
if (controller.value!.replaceMode) _buildReplaceInputView(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFindInputView(BuildContext context) {
|
||||
final CodeFindValue value = controller.value!;
|
||||
final String result;
|
||||
if (value.result == null) {
|
||||
result = libL10n.empty;
|
||||
} else {
|
||||
final index = value.result?.index;
|
||||
final count = value.result?.matches.length;
|
||||
result = '$index/$count';
|
||||
}
|
||||
return Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: _kDefaultFindPanelWidth / 1.75,
|
||||
height: _kDefaultFindPanelHeight,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
_buildTextField(
|
||||
context: context,
|
||||
controller: controller.findInputController,
|
||||
focusNode: controller.findInputFocusNode,
|
||||
iconsWidth: _kDefaultFindIconWidth * 1.5,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
_buildCheckText(
|
||||
context: context,
|
||||
text: 'Aa',
|
||||
checked: value.option.caseSensitive,
|
||||
onPressed: controller.toggleCaseSensitive,
|
||||
),
|
||||
_buildCheckText(
|
||||
context: context,
|
||||
text: '.*',
|
||||
checked: value.option.regex,
|
||||
onPressed: controller.toggleRegex,
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)),
|
||||
Text(result, style: TextStyle(color: resultFontColor, fontSize: resultFontSize)),
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
_buildIconButton(
|
||||
onPressed: value.result == null ? null : controller.previousMatch,
|
||||
icon: Icons.arrow_upward,
|
||||
tooltip: libL10n.previous,
|
||||
),
|
||||
_buildIconButton(
|
||||
onPressed: value.result == null ? null : controller.nextMatch,
|
||||
icon: Icons.arrow_downward,
|
||||
tooltip: libL10n.next,
|
||||
),
|
||||
_buildIconButton(
|
||||
onPressed: controller.close,
|
||||
icon: Icons.close,
|
||||
tooltip: libL10n.close,
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildReplaceInputView(BuildContext context) {
|
||||
final CodeFindValue value = controller.value!;
|
||||
return Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: _kDefaultFindPanelWidth / 1.75,
|
||||
height: _kDefaultFindPanelHeight,
|
||||
child: _buildTextField(
|
||||
context: context,
|
||||
controller: controller.replaceInputController,
|
||||
focusNode: controller.replaceInputFocusNode,
|
||||
),
|
||||
),
|
||||
_buildIconButton(
|
||||
onPressed: value.result == null ? null : controller.replaceMatch,
|
||||
icon: Icons.done,
|
||||
tooltip: libL10n.replace,
|
||||
),
|
||||
_buildIconButton(
|
||||
onPressed: value.result == null ? null : controller.replaceAllMatches,
|
||||
icon: Icons.done_all,
|
||||
tooltip: libL10n.replaceAll,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTextField({
|
||||
required BuildContext context,
|
||||
required TextEditingController controller,
|
||||
required FocusNode focusNode,
|
||||
double iconsWidth = 0,
|
||||
}) {
|
||||
return Padding(
|
||||
padding: padding,
|
||||
child: TextField(
|
||||
maxLines: 1,
|
||||
focusNode: focusNode,
|
||||
style: TextStyle(color: inputTextColor, fontSize: inputFontSize),
|
||||
decoration: decoration.copyWith(
|
||||
contentPadding:
|
||||
(decoration.contentPadding ?? EdgeInsets.zero).add(EdgeInsets.only(right: iconsWidth))),
|
||||
controller: controller,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCheckText({
|
||||
required BuildContext context,
|
||||
required String text,
|
||||
required bool checked,
|
||||
required VoidCallback onPressed,
|
||||
}) {
|
||||
final Color selectedColor = iconSelectedColor ?? Theme.of(context).primaryColor;
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: SizedBox(
|
||||
width: _kDefaultFindIconWidth * 0.75,
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: checked ? selectedColor : iconColor,
|
||||
fontSize: inputFontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIconButton({required IconData icon, VoidCallback? onPressed, String? tooltip}) {
|
||||
return IconButton(
|
||||
onPressed: onPressed,
|
||||
icon: Icon(
|
||||
icon,
|
||||
size: iconSize,
|
||||
),
|
||||
constraints: const BoxConstraints(maxWidth: _kDefaultFindIconWidth, maxHeight: _kDefaultFindIconHeight),
|
||||
tooltip: tooltip,
|
||||
splashRadius: max(_kDefaultFindIconWidth, _kDefaultFindIconHeight) / 2,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -534,8 +534,8 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: "v1.0.283"
|
||||
resolved-ref: "6e25f9f00bbf5c2e2900009f9f2e28454050c4ec"
|
||||
ref: "v1.0.285"
|
||||
resolved-ref: "5bcde3c934290093bc468d24c6f308da8fb8dd4e"
|
||||
url: "https://github.com/lppcg/fl_lib"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
|
||||
@@ -63,7 +63,7 @@ dependencies:
|
||||
fl_lib:
|
||||
git:
|
||||
url: https://github.com/lppcg/fl_lib
|
||||
ref: v1.0.283
|
||||
ref: v1.0.285
|
||||
|
||||
dependency_overrides:
|
||||
# webdav_client_plus:
|
||||
|
||||
Reference in New Issue
Block a user