Improve local comics selection logic.

This commit is contained in:
pkuislm
2024-11-12 19:54:34 +08:00
parent c94438d7c4
commit 601ef68ad3
2 changed files with 149 additions and 26 deletions

View File

@@ -581,10 +581,13 @@ class SliverGridComics extends StatefulWidget {
this.badgeBuilder,
this.menuBuilder,
this.onTap,
this.selections
});
final List<Comic> comics;
final Map<Comic, bool>? selections;
final void Function()? onLastItemBuild;
final String? Function(Comic)? badgeBuilder;
@@ -638,6 +641,7 @@ class _SliverGridComicsState extends State<SliverGridComics> {
Widget build(BuildContext context) {
return _SliverGridComics(
comics: comics,
selection: widget.selections,
onLastItemBuild: widget.onLastItemBuild,
badgeBuilder: widget.badgeBuilder,
menuBuilder: widget.menuBuilder,
@@ -653,10 +657,13 @@ class _SliverGridComics extends StatelessWidget {
this.badgeBuilder,
this.menuBuilder,
this.onTap,
this.selection,
});
final List<Comic> comics;
final Map<Comic, bool>? selection;
final void Function()? onLastItemBuild;
final String? Function(Comic)? badgeBuilder;
@@ -674,11 +681,37 @@ class _SliverGridComics extends StatelessWidget {
onLastItemBuild?.call();
}
var badge = badgeBuilder?.call(comics[index]);
return ComicTile(
return Stack(
children: [
ComicTile(
comic: comics[index],
badge: badge,
menuOptions: menuBuilder?.call(comics[index]),
onTap: onTap != null ? () => onTap!(comics[index]) : null,
),
Positioned(
bottom: 10,
right: 8,
child: Visibility(
visible: selection == null ? false : selection![comics[index]] ?? false,
child: Stack(
children: [
Transform.scale(
scale: 0.9,
child: const Icon(
Icons.circle_rounded,
color: Colors.white,
)
),
const Icon(
Icons.check_circle_rounded,
color: Colors.green,
)
],
)
)
)
],
);
},
childCount: comics.length,

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:venera/components/components.dart';
import 'package:venera/foundation/app.dart';
import 'package:venera/foundation/appdata.dart';
import 'package:venera/foundation/comic_source/comic_source.dart';
import 'package:venera/foundation/local.dart';
import 'package:venera/pages/downloading_page.dart';
import 'package:venera/utils/cbz.dart';
@@ -26,7 +27,7 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
bool multiSelectMode = false;
Map<LocalComic, bool> selectedComics = {};
Map<Comic, bool> selectedComics = {};
void update() {
if (keyword.isEmpty) {
@@ -166,10 +167,66 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
)
else if (multiSelectMode)
SliverAppbar(
title: Text("Selected ${selectedComics.length} comics"),
title: Text("Selected @c comics".tlParams({"c": selectedComics.length})),
actions: [
IconButton(
icon: const Icon(Icons.check_box_rounded),
tooltip: "Select All".tl,
onPressed: () {
setState(() {
selectedComics = comics.asMap().map((k, v) => MapEntry(v, true));
});
},
),
IconButton(
icon: const Icon(Icons.check_box_outline_blank_outlined),
tooltip: "Deselect".tl,
onPressed: () {
setState(() {
selectedComics.clear();
});
},
),
IconButton(
icon: const Icon(Icons.check_box_outlined),
tooltip: "Invert Selection".tl,
onPressed: () {
setState(() {
comics.asMap().forEach((k, v) {
selectedComics[v] = !selectedComics.putIfAbsent(v, () => false);
});
selectedComics.removeWhere((k, v) => !v);
});
},
),
IconButton(
icon: const Icon(Icons.indeterminate_check_box_rounded),
tooltip: "Select in range".tl,
onPressed: () {
setState(() {
List<int> l = [];
selectedComics.forEach((k, v) {
l.add(comics.indexOf(k as LocalComic));
});
if(l.isEmpty) {
return;
}
l.sort();
int start = l.first;
int end = l.last;
selectedComics.clear();
selectedComics.addEntries(
List.generate(end - start + 1, (i) {
return MapEntry(comics[start + i], true);
})
);
});
},
),
IconButton(
icon: const Icon(Icons.close),
tooltip: "Exit Multi-Select".tl,
onPressed: () {
setState(() {
multiSelectMode = false;
@@ -177,6 +234,7 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
});
},
),
],
)
else if (searchMode)
@@ -207,13 +265,14 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
),
SliverGridComics(
comics: comics,
selections: selectedComics,
onTap: multiSelectMode
? (c) {
setState(() {
if (selectedComics.containsKey(c as LocalComic)) {
selectedComics.remove(c as LocalComic);
selectedComics.remove(c);
} else {
selectedComics[c as LocalComic] = true;
selectedComics[c] = true;
}
});
}
@@ -226,23 +285,54 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
icon: Icons.delete,
text: "Delete".tl,
onClick: () {
if (multiSelectMode) {
showConfirmDialog(
showDialog(
context: context,
builder: (context) {
bool removeComicFile = true;
return StatefulBuilder(
builder: (context, state) {
return ContentDialog(
title: "Delete".tl,
content: "Delete selected comics?".tl,
onConfirm: () {
content: Column(
children: [
Text("Delete selected comics?".tl).paddingVertical(8),
Transform.scale(
scale: 0.9,
child: CheckboxListTile(
title: Text("Also remove files on disk".tl),
value: removeComicFile,
onChanged: (v) {
state(() {
removeComicFile = !removeComicFile;
});
}
)
),
],
).paddingHorizontal(16).paddingVertical(8),
actions: [
FilledButton(
onPressed: () {
context.pop();
if(multiSelectMode) {
for (var comic in selectedComics.keys) {
LocalManager().deleteComic(comic);
LocalManager().deleteComic(comic as LocalComic, removeComicFile);
}
setState(() {
selectedComics.clear();
});
},
);
} else {
LocalManager().deleteComic(c as LocalComic);
LocalManager().deleteComic(c as LocalComic, removeComicFile);
}
},
child: Text("Confirm".tl),
),
],
);
}
);
}
);
}),
MenuEntry(
icon: Icons.outbox_outlined,
@@ -255,7 +345,7 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
try {
if (multiSelectMode) {
for (var comic in selectedComics.keys) {
var file = await CBZ.export(comic);
var file = await CBZ.export(comic as LocalComic);
await saveFile(filename: file.name, file: file);
await file.delete();
}