From 30b3256eecea8eb590cb12f97cea317ecf6ebc05 Mon Sep 17 00:00:00 2001 From: nyne Date: Fri, 18 Oct 2024 11:34:31 +0800 Subject: [PATCH] search options in results page --- lib/components/appbar.dart | 33 +++++-- lib/components/message.dart | 26 +++--- lib/pages/search_page.dart | 2 +- lib/pages/search_result_page.dart | 141 +++++++++++++++++++++++++++++- 4 files changed, 178 insertions(+), 24 deletions(-) diff --git a/lib/components/appbar.dart b/lib/components/appbar.dart index 5fb43a8..a58d14f 100644 --- a/lib/components/appbar.dart +++ b/lib/components/appbar.dart @@ -184,12 +184,12 @@ class _MySliverAppBarDelegate extends SliverPersistentHeaderDelegate { leading ?? (Navigator.of(context).canPop() ? Tooltip( - message: "Back".tl, - child: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.maybePop(context), - ), - ) + message: "Back".tl, + child: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.maybePop(context), + ), + ) : const SizedBox()), const SizedBox( width: 24, @@ -197,7 +197,7 @@ class _MySliverAppBarDelegate extends SliverPersistentHeaderDelegate { Expanded( child: DefaultTextStyle( style: - DefaultTextStyle.of(context).style.copyWith(fontSize: 20), + DefaultTextStyle.of(context).style.copyWith(fontSize: 20), maxLines: 1, overflow: TextOverflow.ellipsis, child: title, @@ -562,12 +562,19 @@ abstract mixin class _SearchBarMixin { } class SliverSearchBar extends StatefulWidget { - const SliverSearchBar({super.key, required this.controller, this.onChanged}); + const SliverSearchBar({ + super.key, + required this.controller, + this.onChanged, + this.action, + }); final SearchBarController controller; final void Function(String)? onChanged; + final Widget? action; + @override State createState() => _SliverSearchBarState(); } @@ -605,6 +612,7 @@ class _SliverSearchBarState extends State controller: _controller, topPadding: MediaQuery.of(context).padding.top, onChanged: widget.onChanged, + action: widget.action, ), ); } @@ -619,11 +627,14 @@ class _SliverSearchBarDelegate extends SliverPersistentHeaderDelegate { final void Function(String)? onChanged; + final Widget? action; + const _SliverSearchBarDelegate({ required this.editingController, required this.controller, required this.topPadding, this.onChanged, + this.action, }); static const _kAppBarHeight = 52.0; @@ -677,6 +688,7 @@ class _SliverSearchBarDelegate extends SliverPersistentHeaderDelegate { ); }, ), + if (action != null) action!, const SizedBox(width: 8), ], ), @@ -699,10 +711,12 @@ class _SliverSearchBarDelegate extends SliverPersistentHeaderDelegate { } class AppSearchBar extends StatefulWidget { - const AppSearchBar({super.key, required this.controller}); + const AppSearchBar({super.key, required this.controller, this.action}); final SearchBarController controller; + final Widget? action; + @override State createState() => _SearchBarState(); } @@ -777,6 +791,7 @@ class _SearchBarState extends State with _SearchBarMixin { ); }, ), + if (widget.action != null) widget.action!, const SizedBox(width: 8), ], ), diff --git a/lib/components/message.dart b/lib/components/message.dart index dc4cb42..b430d91 100644 --- a/lib/components/message.dart +++ b/lib/components/message.dart @@ -277,17 +277,21 @@ class ContentDialog extends StatelessWidget { : const EdgeInsets.symmetric(horizontal: 16), elevation: 2, shadowColor: context.colorScheme.shadow, - child: IntrinsicWidth( - child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: 600, - minWidth: math.min(400, context.width - 16), - ), - child: MediaQuery.removePadding( - removeTop: true, - removeBottom: true, - context: context, - child: content, + child: AnimatedSize( + duration: const Duration(milliseconds: 200), + alignment: Alignment.topCenter, + child: IntrinsicWidth( + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: 600, + minWidth: math.min(400, context.width - 16), + ), + child: MediaQuery.removePadding( + removeTop: true, + removeBottom: true, + context: context, + child: content, + ), ), ), ), diff --git a/lib/pages/search_page.dart b/lib/pages/search_page.dart index 8c98b87..9d05738 100644 --- a/lib/pages/search_page.dart +++ b/lib/pages/search_page.dart @@ -174,7 +174,7 @@ class _SearchPageState extends State { children: [ ListTile( contentPadding: EdgeInsets.zero, - title: Text("Search From".tl), + title: Text("Search in".tl), ), Wrap( spacing: 8, diff --git a/lib/pages/search_result_page.dart b/lib/pages/search_result_page.dart index a6e2422..ca558dc 100644 --- a/lib/pages/search_result_page.dart +++ b/lib/pages/search_result_page.dart @@ -49,7 +49,7 @@ class _SearchResultPageState extends State { } void onChanged(String s) { - if(!ComicSource.find(sourceKey)!.enableTagsSuggestions){ + if (!ComicSource.find(sourceKey)!.enableTagsSuggestions) { return; } suggestionsController.findSuggestions(); @@ -96,13 +96,15 @@ class _SearchResultPageState extends State { @override Widget build(BuildContext context) { return ComicList( - key: Key(text + options.toString()), + key: Key(text + options.toString() + sourceKey), errorLeading: AppSearchBar( controller: controller, + action: buildAction(), ), leadingSliver: SliverSearchBar( controller: controller, onChanged: onChanged, + action: buildAction(), ), loadPage: (i) { var source = ComicSource.find(sourceKey); @@ -114,6 +116,25 @@ class _SearchResultPageState extends State { }, ); } + + Widget buildAction() { + return Tooltip( + message: "Settings".tl, + child: IconButton( + icon: const Icon(Icons.tune), + onPressed: () async { + await showDialog( + context: context, + useRootNavigator: true, + builder: (context) { + return _SearchSettingsDialog(state: this); + }, + ); + setState(() {}); + }, + ), + ); + } } class _SuggestionsController { @@ -308,7 +329,7 @@ class _SuggestionsState extends State<_Suggestions> { controller.text = controller.text.replaceLast(words[words.length - 1], ""); } - if(text.contains(' ')) { + if (text.contains(' ')) { text = "'$text'"; } if (type != null) { @@ -320,3 +341,117 @@ class _SuggestionsState extends State<_Suggestions> { widget.controller.remove(); } } + +class _SearchSettingsDialog extends StatefulWidget { + const _SearchSettingsDialog({required this.state}); + + final _SearchResultPageState state; + + @override + State<_SearchSettingsDialog> createState() => _SearchSettingsDialogState(); +} + +class _SearchSettingsDialogState extends State<_SearchSettingsDialog> { + late String searchTarget; + + late List options; + + @override + void initState() { + searchTarget = widget.state.sourceKey; + options = widget.state.options; + super.initState(); + } + + void onChanged() { + widget.state.sourceKey = searchTarget; + widget.state.options = options; + } + + @override + Widget build(BuildContext context) { + return ContentDialog( + title: "Settings".tl, + content: Column( + children: [ + ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + title: Text("Search in".tl), + ), + Wrap( + spacing: 8, + runSpacing: 8, + children: ComicSource.all().map((e) { + return OptionChip( + text: e.name.tl, + isSelected: searchTarget == e.key, + onTap: () { + setState(() { + searchTarget = e.key; + options.clear(); + onChanged(); + }); + }, + ); + }).toList(), + ), + buildSearchOptions(), + const SizedBox(height: 24), + FilledButton( + child: Text("Confirm".tl), + onPressed: () { + context.pop(); + }, + ), + ], + ).fixWidth(400), + ); + } + + Widget buildSearchOptions() { + var children = []; + + final searchOptions = + ComicSource.find(searchTarget)!.searchPageData!.searchOptions ?? + []; + if (searchOptions.length != options.length) { + options = searchOptions.map((e) => e.defaultValue).toList(); + } + if (searchOptions.isEmpty) { + return const SizedBox(); + } + for (int i = 0; i < searchOptions.length; i++) { + final option = searchOptions[i]; + children.add(ListTile( + contentPadding: EdgeInsets.zero, + title: Text(option.label.tl), + )); + children.add(Wrap( + runSpacing: 8, + spacing: 8, + children: option.options.entries.map((e) { + return OptionChip( + text: e.value.ts(searchTarget), + isSelected: options[i] == e.key, + onTap: () { + setState(() { + options[i] = e.key; + }); + onChanged(); + }, + ); + }).toList(), + )); + } + + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: children, + ), + ); + } +}