import 'package:flutter/material.dart'; import 'package:re_editor/re_editor.dart'; import '../../l10n/strings.dart'; /// Find and replace bar for the editor: case sensitivity, regular /// expressions, previous/next, replace one or all. class FindPanel extends StatelessWidget implements PreferredSizeWidget { final CodeFindController controller; final bool readOnly; const FindPanel({ required this.controller, required this.readOnly, super.key, }); @override Size get preferredSize => Size( double.infinity, controller.value == null ? 0 : (controller.value!.replaceMode ? 104 : 56), ); @override Widget build(BuildContext context) { final value = controller.value; if (value == null) return const SizedBox.shrink(); final scheme = Theme.of(context).colorScheme; final result = value.result; final count = result == null ? (value.option.pattern.isEmpty ? '' : '0/0') : '${result.index + 1}/${result.matches.length}'; final invalidRegex = value.option.regex && value.option.pattern.isNotEmpty && value.option.regExp == null; Widget toggle(String label, String tooltip, bool on, VoidCallback tap) => Tooltip( message: tooltip, child: InkWell( onTap: tap, borderRadius: BorderRadius.circular(6), child: Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), decoration: BoxDecoration( color: on ? scheme.primaryContainer : null, borderRadius: BorderRadius.circular(6), ), child: Text( label, style: TextStyle( fontWeight: FontWeight.w600, color: on ? scheme.onPrimaryContainer : null, ), ), ), ), ); return Material( color: scheme.surfaceContainerHigh, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox( height: 48, child: Row( children: [ Expanded( child: TextField( controller: controller.findInputController, focusNode: controller.findInputFocusNode, autocorrect: false, enableSuggestions: false, decoration: InputDecoration( isDense: true, hintText: context.tr('findHint'), errorText: invalidRegex ? context.tr('invalidRegex') : null, suffixText: count, ), onSubmitted: (_) => controller.nextMatch(), ), ), const SizedBox(width: 4), toggle( 'Aa', context.tr('matchCase'), value.option.caseSensitive, controller.toggleCaseSensitive, ), toggle( '.*', context.tr('regularExpression'), value.option.regex, controller.toggleRegex, ), IconButton( tooltip: context.tr('previousMatch'), icon: const Icon(Icons.keyboard_arrow_up), onPressed: controller.previousMatch, ), IconButton( tooltip: context.tr('nextMatch'), icon: const Icon(Icons.keyboard_arrow_down), onPressed: controller.nextMatch, ), if (!readOnly) IconButton( tooltip: context.tr('replace'), icon: Icon( value.replaceMode ? Icons.unfold_less : Icons.find_replace, ), onPressed: controller.toggleMode, ), IconButton( tooltip: context.tr('close'), icon: const Icon(Icons.close), onPressed: controller.close, ), ], ), ), if (value.replaceMode && !readOnly) SizedBox( height: 48, child: Row( children: [ Expanded( child: TextField( controller: controller.replaceInputController, focusNode: controller.replaceInputFocusNode, autocorrect: false, enableSuggestions: false, decoration: InputDecoration( isDense: true, hintText: context.tr('replaceHint'), ), ), ), TextButton( onPressed: controller.replaceMatch, child: Text(context.tr('replace')), ), TextButton( onPressed: controller.replaceAllMatches, child: Text(context.tr('replaceAll')), ), ], ), ), ], ), ), ); } }