diff --git a/lib/components/gallery_info_desktop.dart b/lib/components/gallery_info_desktop.dart index eb9b51c..19cb246 100644 --- a/lib/components/gallery_info_desktop.dart +++ b/lib/components/gallery_info_desktop.dart @@ -165,7 +165,9 @@ class GalleryInfoDesktop extends StatelessWidget { ) ])), const VerticalDivider(indent: 10, endIndent: 10), - Expanded(child: TagsPanel(gData.tags)), + Expanded( + child: + TagsPanel(gData.tags, controller: controller)), const VerticalDivider(indent: 10, endIndent: 10), SizedBox( width: 150, diff --git a/lib/components/gallery_list_normal_card.dart b/lib/components/gallery_list_normal_card.dart index 8f24714..a0b2c88 100644 --- a/lib/components/gallery_list_normal_card.dart +++ b/lib/components/gallery_list_normal_card.dart @@ -1,3 +1,4 @@ +import 'dart:math'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -6,12 +7,17 @@ import '../api/file.dart'; import '../api/gallery.dart'; import '../globals.dart'; import '../main.dart'; +import '../platform/ua.dart' as ua; +import '../utils.dart'; import 'rate.dart'; +import 'scroll_parent.dart'; import 'thumbnail.dart'; class GalleryListNormalCard extends StatefulWidget { - const GalleryListNormalCard(this.gMeta, {super.key, this.files, this.pMeta}); + const GalleryListNormalCard(this.gMeta, + {super.key, this.controller, this.files, this.pMeta}); final GMeta gMeta; + final ScrollController? controller; final ExtendedPMeta? pMeta; final EhFiles? files; @@ -52,42 +58,66 @@ class _GalleryListNormalCard extends State { ? EdgeInsets.symmetric(vertical: 2 / dpr, horizontal: 4 / dpr) : EdgeInsets.all(8 / dpr), child: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ - SelectableText( - useMobile ? widget.gMeta.preferredTitle : widget.gMeta.title, - style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 16, color: cs.primary), - textAlign: TextAlign.center, - minLines: useMobile ? 1 : 1, - maxLines: useMobile - ? 3 - : widget.gMeta.titleJpn.isEmpty - ? 4 - : 2), + ScrollParent( + controller: widget.controller, + child: SelectableText( + useMobile ? widget.gMeta.preferredTitle : widget.gMeta.title, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + color: cs.primary), + textAlign: TextAlign.center, + minLines: useMobile ? 1 : 1, + maxLines: useMobile + ? 3 + : widget.gMeta.titleJpn.isEmpty + ? 4 + : 2, + scrollPhysics: isIOS || ua.isSafari + ? const ClampingScrollPhysics() + : null)), useMobile || widget.gMeta.titleJpn.isEmpty ? Container() - : SelectableText( - widget.gMeta.titleJpn, - style: TextStyle(color: cs.secondary), - textAlign: TextAlign.center, - maxLines: 2, - minLines: 1, - ), + : ScrollParent( + controller: widget.controller, + child: SelectableText(widget.gMeta.titleJpn, + style: TextStyle(color: cs.secondary), + textAlign: TextAlign.center, + maxLines: 2, + minLines: 1, + scrollPhysics: isIOS || ua.isSafari + ? const ClampingScrollPhysics() + : null)), Expanded(child: Container()), + Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ + SelectableText.rich(TextSpan( + text: widget.gMeta.uploader, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: cs.primary), + mouseCursor: SystemMouseCursors.click, + recognizer: TapGestureRecognizer() + ..onTap = () { + context.pushNamed("/galleries", + queryParameters: {"uploader": widget.gMeta.uploader}); + })), + ]), Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - SelectableText.rich(TextSpan( - text: widget.gMeta.uploader, + Rate(widget.gMeta.rating, fontSize: 14, selectable: true), + ]), + Expanded(child: Container()), + Column(crossAxisAlignment: CrossAxisAlignment.end, children: [ + SelectableText("${widget.gMeta.filecount}P", style: TextStyle( fontSize: 16, - fontWeight: FontWeight.bold, - color: cs.primary), - mouseCursor: SystemMouseCursors.click, - recognizer: TapGestureRecognizer() - ..onTap = () { - context.pushNamed("/galleries", - queryParameters: {"uploader": widget.gMeta.uploader}); - })), - Rate(widget.gMeta.rating, fontSize: 14, selectable: true), + color: cs.primary, + fontWeight: FontWeight.bold)), + ]), + ]), + Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ SelectableText.rich(TextSpan( text: widget.gMeta.category, style: TextStyle( @@ -103,11 +133,6 @@ class _GalleryListNormalCard extends State { ]), Expanded(child: Container()), Column(crossAxisAlignment: CrossAxisAlignment.end, children: [ - SelectableText("${widget.gMeta.filecount}P", - style: TextStyle( - fontSize: 16, - color: cs.primary, - fontWeight: FontWeight.bold)), SelectableText( DateFormat.yMd(locale) .add_jms() @@ -127,7 +152,8 @@ class _GalleryListNormalCard extends State { child: Row(children: [ useMobile ? ConstrainedBox( - constraints: BoxConstraints(maxWidth: maxWidth / 2), + constraints: BoxConstraints( + maxWidth: min(maxWidth / 2, maxWidth - 260)), child: thumbnailWidget) : thumbnailWidget, Expanded(child: mainWidget), diff --git a/lib/components/scroll_parent.dart b/lib/components/scroll_parent.dart index 0007e4d..530d4ef 100644 --- a/lib/components/scroll_parent.dart +++ b/lib/components/scroll_parent.dart @@ -1,27 +1,29 @@ import 'package:flutter/material.dart'; class ScrollParent extends StatelessWidget { - final ScrollController controller; + final ScrollController? controller; final Widget child; - const ScrollParent({super.key, required this.controller, required this.child}); + const ScrollParent({super.key, this.controller, required this.child}); @override Widget build(BuildContext context) { + if (controller == null) return child; return NotificationListener( onNotification: (OverscrollNotification value) { - if (value.overscroll < 0 && controller.offset + value.overscroll <= 0) { - if (controller.offset != 0) controller.jumpTo(0); + if (value.overscroll < 0 && + controller!.offset + value.overscroll <= 0) { + if (controller!.offset != 0) controller!.jumpTo(0); return true; } - if (controller.offset + value.overscroll >= - controller.position.maxScrollExtent) { - if (controller.offset != controller.position.maxScrollExtent) { - controller.jumpTo(controller.position.maxScrollExtent); + if (controller!.offset + value.overscroll >= + controller!.position.maxScrollExtent) { + if (controller!.offset != controller!.position.maxScrollExtent) { + controller!.jumpTo(controller!.position.maxScrollExtent); } return true; } - controller.jumpTo(controller.offset + value.overscroll); + controller!.jumpTo(controller!.offset + value.overscroll); return true; }, child: child, diff --git a/lib/pages/galleries.dart b/lib/pages/galleries.dart index 6f63095..c196d35 100644 --- a/lib/pages/galleries.dart +++ b/lib/pages/galleries.dart @@ -1,3 +1,4 @@ +import 'dart:ui'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -45,6 +46,7 @@ class GalleriesPage extends StatefulWidget { class _GalleriesPage extends State with ThemeModeWidget, IsTopWidget2 { + final ScrollController controller = ScrollController(); static const int _pageSize = 20; bool? _sortByGid; SortByGid _sortByGid2 = SortByGid.none; @@ -229,33 +231,45 @@ class _GalleriesPage extends State buildThemeModeIcon(context), buildMoreVertSettingsButon(context), ]), - body: PagedListView( - physics: const AlwaysScrollableScrollPhysics(), - pagingController: _pagingController, - builderDelegate: PagedChildBuilderDelegate( - itemBuilder: (context, item, index) { - final displayMode = GalleryListDisplayMode - .values[prefs.getInt("galleryListDisplayMode") ?? 1]; - if (displayMode == GalleryListDisplayMode.normal) { - return InkWell( + body: ScrollConfiguration( + behavior: ScrollConfiguration.of(context).copyWith( + dragDevices: { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + PointerDeviceKind.trackpad, + }, + ), + child: PagedListView( + physics: const AlwaysScrollableScrollPhysics(), + pagingController: _pagingController, + scrollController: controller, + builderDelegate: PagedChildBuilderDelegate( + itemBuilder: (context, item, index) { + final displayMode = GalleryListDisplayMode + .values[prefs.getInt("galleryListDisplayMode") ?? 1]; + if (displayMode == GalleryListDisplayMode.normal) { + return InkWell( + onTap: () { + context.push("/gallery/${item.gid}", + extra: + GalleryPageExtra(title: item.preferredTitle)); + }, + mouseCursor: SystemMouseCursors.basic, + child: GalleryListNormalCard(item, + controller: controller, + files: _files, + pMeta: _thumbnails.thumbnails[item.gid] + ?.unwrapOrNull())); + } + return ListTile( + title: Text(item.preferredTitle), onTap: () { context.push("/gallery/${item.gid}", extra: GalleryPageExtra(title: item.preferredTitle)); }, - mouseCursor: SystemMouseCursors.basic, - child: GalleryListNormalCard(item, - files: _files, - pMeta: _thumbnails.thumbnails[item.gid]?.unwrapOrNull())); - } - return ListTile( - title: Text(item.preferredTitle), - onTap: () { - context.push("/gallery/${item.gid}", - extra: GalleryPageExtra(title: item.preferredTitle)); - }, - ); - }), - )); + ); + }), + ))); } @override