search options in results page

This commit is contained in:
nyne
2024-10-18 11:34:31 +08:00
parent e251355d66
commit 30b3256eec
4 changed files with 178 additions and 24 deletions

View File

@@ -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<SliverSearchBar> createState() => _SliverSearchBarState();
}
@@ -605,6 +612,7 @@ class _SliverSearchBarState extends State<SliverSearchBar>
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<AppSearchBar> createState() => _SearchBarState();
}
@@ -777,6 +791,7 @@ class _SearchBarState extends State<AppSearchBar> with _SearchBarMixin {
);
},
),
if (widget.action != null) widget.action!,
const SizedBox(width: 8),
],
),

View File

@@ -277,6 +277,9 @@ class ContentDialog extends StatelessWidget {
: const EdgeInsets.symmetric(horizontal: 16),
elevation: 2,
shadowColor: context.colorScheme.shadow,
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
alignment: Alignment.topCenter,
child: IntrinsicWidth(
child: ConstrainedBox(
constraints: BoxConstraints(
@@ -291,6 +294,7 @@ class ContentDialog extends StatelessWidget {
),
),
),
),
);
}
}

View File

@@ -174,7 +174,7 @@ class _SearchPageState extends State<SearchPage> {
children: [
ListTile(
contentPadding: EdgeInsets.zero,
title: Text("Search From".tl),
title: Text("Search in".tl),
),
Wrap(
spacing: 8,

View File

@@ -96,13 +96,15 @@ class _SearchResultPageState extends State<SearchResultPage> {
@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<SearchResultPage> {
},
);
}
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 {
@@ -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<String> 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 = <Widget>[];
final searchOptions =
ComicSource.find(searchTarget)!.searchPageData!.searchOptions ??
<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,
),
);
}
}