Moved search popup to the bottom not to override the editor, added wrap around, other enhancements

pull/1341/head
Alspb 2 years ago
parent 3a69551191
commit ac3eda3686
  1. 216
      lib/src/widgets/toolbar/search_dialog.dart

@ -23,6 +23,8 @@ class _SearchDialogState extends State<SearchDialog> {
late TextEditingController _controller;
late List<int>? _offsets;
late int _index;
bool _case_sensitive = false;
bool _whole_word = false;
@override
void initState() {
@ -35,87 +37,113 @@ class _SearchDialogState extends State<SearchDialog> {
@override
Widget build(BuildContext context) {
return StatefulBuilder(builder: (context, setState) {
var label = '';
if (_offsets != null) {
label = '${_offsets!.length} ${'matches'.i18n}';
if (_offsets!.isNotEmpty) {
label += ', ${'showing match'.i18n} ${_index + 1}';
}
var matchShown = '';
if (_offsets != null) {
if (_offsets!.isEmpty) {
matchShown = '0/0';
} else {
matchShown = '${_index + 1}/${_offsets!.length}';
}
return AlertDialog(
backgroundColor: widget.dialogTheme?.dialogBackgroundColor,
content: Container(
height: 100,
child: Column(
children: [
TextField(
keyboardType: TextInputType.multiline,
style: widget.dialogTheme?.inputTextStyle,
decoration: InputDecoration(
labelText: 'Search'.i18n,
labelStyle: widget.dialogTheme?.labelTextStyle,
floatingLabelStyle: widget.dialogTheme?.labelTextStyle),
autofocus: true,
onChanged: _textChanged,
controller: _controller,
}
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
backgroundColor: widget.dialogTheme?.dialogBackgroundColor,
alignment: Alignment.bottomCenter,
insetPadding: EdgeInsets.zero,
child: SizedBox(
height: 45,
child: Row(
children: [
Tooltip(
message: 'Case sensitivity and whole word search',
child: ToggleButtons(
onPressed: (int index) {
if (index == 0) {
_changeCaseSensitivity();
} else if (index == 1) {
_changeWholeWord();
}
},
borderRadius: const BorderRadius.all(Radius.circular(2)),
isSelected: [_case_sensitive, _whole_word],
children: const [
Text(
'\u0391\u03b1',
style: TextStyle(
fontFamily: 'MaterialIcons',
fontSize: 24,
),
),
Text(
'\u201c\u2026\u201d',
style: TextStyle(
fontFamily: 'MaterialIcons',
fontSize: 24,
),
),
],
),
if (_offsets != null)
Padding(
padding: const EdgeInsets.all(8),
child: Text(label, textAlign: TextAlign.left),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(bottom: 12, left: 5),
child: TextField(
style: widget.dialogTheme?.inputTextStyle,
decoration: InputDecoration(
isDense: true,
suffixText: (_offsets != null) ? matchShown : '',
suffixStyle: widget.dialogTheme?.labelTextStyle,
),
autofocus: true,
onChanged: _textChanged,
textInputAction: TextInputAction.done,
onEditingComplete: _findText,
controller: _controller,
),
],
),
),
actions: [
if (_offsets != null && _offsets!.isNotEmpty && _index > 0)
TextButton(
onPressed: () {
setState(() {
_index -= 1;
});
_moveToPosition();
},
child: Text(
'Prev'.i18n,
style: widget.dialogTheme?.labelTextStyle,
),
),
if (_offsets != null &&
_offsets!.isNotEmpty &&
_index < _offsets!.length - 1)
TextButton(
onPressed: () {
setState(() {
_index += 1;
});
_moveToPosition();
},
child: Text(
'Next'.i18n,
style: widget.dialogTheme?.labelTextStyle,
if (_offsets == null)
IconButton(
icon: const Icon(Icons.search),
tooltip: 'Find text',
onPressed: _findText,
),
),
if (_offsets == null && _text.isNotEmpty)
TextButton(
onPressed: () {
setState(() {
_offsets = widget.controller.document.search(_text);
_index = 0;
});
if (_offsets!.isNotEmpty) {
_moveToPosition();
}
},
child: Text(
'Ok'.i18n,
style: widget.dialogTheme?.labelTextStyle,
if (_offsets != null)
IconButton(
icon: const Icon(Icons.keyboard_arrow_up),
tooltip: 'Move to previous occurrence',
onPressed: (_offsets!.isNotEmpty) ? _moveToPrevious : null,
),
),
],
if (_offsets != null)
IconButton(
icon: const Icon(Icons.keyboard_arrow_down),
tooltip: 'Move to next occurrence',
onPressed: (_offsets!.isNotEmpty) ? _moveToNext : null,
),
],
),
),
);
}
void _findText() {
if (_text.isEmpty) {
return;
}
setState(() {
_offsets = widget.controller.document.search(
_text,
caseSensitive: _case_sensitive,
wholeWord: _whole_word,
);
_index = 0;
});
if (_offsets!.isNotEmpty) {
_moveToPosition();
}
}
void _moveToPosition() {
@ -126,6 +154,34 @@ class _SearchDialogState extends State<SearchDialog> {
ChangeSource.LOCAL);
}
void _moveToPrevious() {
if (_offsets!.isEmpty) {
return;
}
setState(() {
if (_index > 0) {
_index -= 1;
} else {
_index = _offsets!.length - 1;
}
});
_moveToPosition();
}
void _moveToNext() {
if (_offsets!.isEmpty) {
return;
}
setState(() {
if (_index < _offsets!.length - 1) {
_index += 1;
} else {
_index = 0;
}
});
_moveToPosition();
}
void _textChanged(String value) {
setState(() {
_text = value;
@ -133,4 +189,20 @@ class _SearchDialogState extends State<SearchDialog> {
_index = 0;
});
}
void _changeCaseSensitivity() {
setState(() {
_case_sensitive = !_case_sensitive;
_offsets = null;
_index = 0;
});
}
void _changeWholeWord() {
setState(() {
_whole_word = !_whole_word;
_offsets = null;
_index = 0;
});
}
}

Loading…
Cancel
Save