This commit is contained in:
ashilkn
2024-07-05 12:49:55 +05:30
parent e71f40098f
commit 0d0f4d9b4d
4 changed files with 165 additions and 123 deletions

View File

@@ -4,6 +4,7 @@ import "package:flutter/widgets.dart";
import "package:logging/logging.dart";
import "package:photos/models/file/file.dart";
import "package:photos/models/selected_files.dart";
import "package:photos/ui/viewer/gallery/component/group/lazy_group_gallery.dart";
class LastSelectedFileByDragging extends InheritedWidget {
///Check if this should updates on didUpdateWidget. If so, use a state varaible
@@ -63,6 +64,7 @@ class _PointerProviderState extends State<PointerProvider> {
int prevSelectedFileIndex = -1;
int currentSelectedFileIndex = -1;
final _logger = Logger("PointerProvider");
final _groupGalleryGlobalKey = GlobalKey();
@override
void initState() {
@@ -125,42 +127,47 @@ class _PointerProviderState extends State<PointerProvider> {
child: Builder(
builder: (context) {
pointer = Pointer.of(context);
return GestureDetector(
onTap: () {
pointer.onTapStreamController.add(pointer.pointerPosition);
},
onLongPress: () {
_isFingerOnScreenSinceLongPress = true;
pointer.onLongPressStreamController.add(pointer.pointerPosition);
},
onHorizontalDragUpdate: (details) {
onDragToSelect(details.localPosition);
},
child: Listener(
onPointerMove: (event) {
pointer.pointerPosition = event.localPosition;
return GroupGalleryGlobalKey(
globalKey: _groupGalleryGlobalKey,
child: GestureDetector(
key: _groupGalleryGlobalKey,
onTap: () {
pointer.onTapStreamController.add(pointer.pointerPosition);
},
onLongPress: () {
_isFingerOnScreenSinceLongPress = true;
pointer.onLongPressStreamController
.add(pointer.pointerPosition);
},
onHorizontalDragUpdate: (details) {
onDragToSelect(details.localPosition);
},
child: Listener(
onPointerMove: (event) {
pointer.pointerPosition = event.localPosition;
//onHorizontalDragUpdate is not called when dragging after
//long press without lifting finger. This is for handling only
//this case.
if (_isFingerOnScreenSinceLongPress &&
(event.localDelta.dx.abs() > 0 &&
event.localDelta.dy.abs() > 0)) {
onDragToSelect(event.localPosition);
}
},
onPointerDown: (event) {
pointer.pointerPosition = event.localPosition;
},
onPointerUp: (event) {
_isFingerOnScreenSinceLongPress = false;
_isDragging = false;
pointer.upOffsetStreamController.add(event.localPosition);
//onHorizontalDragUpdate is not called when dragging after
//long press without lifting finger. This is for handling only
//this case.
if (_isFingerOnScreenSinceLongPress &&
(event.localDelta.dx.abs() > 0 &&
event.localDelta.dy.abs() > 0)) {
onDragToSelect(event.localPosition);
}
},
onPointerDown: (event) {
pointer.pointerPosition = event.localPosition;
},
onPointerUp: (event) {
_isFingerOnScreenSinceLongPress = false;
_isDragging = false;
pointer.upOffsetStreamController.add(event.localPosition);
LastSelectedFileByDragging.of(context).index.value = -1;
currentSelectedFileIndex = -1;
},
child: widget.child,
LastSelectedFileByDragging.of(context).index.value = -1;
currentSelectedFileIndex = -1;
},
child: widget.child,
),
),
);
},

View File

@@ -1,6 +1,9 @@
import 'dart:math' show max;
import 'package:flutter/material.dart';
import "package:photos/models/file/file.dart";
import "package:photos/models/selected_files.dart";
import "package:photos/states/pointer_provider.dart";
import 'package:photos/ui/huge_listview/draggable_scrollbar.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
@@ -14,6 +17,10 @@ typedef HugeListViewErrorBuilder = Widget Function(
);
class HugeListView<T> extends StatefulWidget {
final List<EnteFile> files;
final SelectedFiles? selectedFiles;
/// A [ScrollablePositionedList] controller for jumping or scrolling to an item.
final ItemScrollController? controller;
@@ -66,6 +73,8 @@ class HugeListView<T> extends StatefulWidget {
const HugeListView({
Key? key,
required this.files,
required this.selectedFiles,
this.controller,
required this.startIndex,
required this.totalCount,
@@ -167,30 +176,62 @@ class HugeListViewState<T> extends State<HugeListView<T>> {
currentFirstIndex: _currentFirst(),
isEnabled: widget.isDraggableScrollbarEnabled,
padding: widget.thumbPadding,
child: ScrollablePositionedList.builder(
physics: widget.disableScroll
? const NeverScrollableScrollPhysics()
: const BouncingScrollPhysics(),
itemScrollController: widget.controller,
itemPositionsListener: listener,
initialScrollIndex: widget.startIndex,
itemCount: max(widget.totalCount, 0),
itemBuilder: (context, index) {
return ExcludeSemantics(
child: widget.itemBuilder(context, index),
);
},
child: LastSelectedFileByDragging(
filesInGroup: widget.files,
child: Builder(
builder: (context) {
return PointerProvider(
selectedFiles: widget.selectedFiles!,
files: widget.files,
child: ScrollablePositionedList.builder(
physics: widget.disableScroll
? const NeverScrollableScrollPhysics()
: const BouncingScrollPhysics(),
itemScrollController: widget.controller,
itemPositionsListener: listener,
initialScrollIndex: widget.startIndex,
itemCount: max(widget.totalCount, 0),
itemBuilder: (context, index) {
return ExcludeSemantics(
child: widget.itemBuilder(context, index),
);
},
),
);
},
),
),
)
: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: max(widget.totalCount, 0),
itemBuilder: (context, index) {
return ExcludeSemantics(
child: widget.itemBuilder(context, index),
: widget.selectedFiles != null
? LastSelectedFileByDragging(
filesInGroup: widget.files,
child: Builder(
builder: (context) {
return PointerProvider(
selectedFiles: widget.selectedFiles!,
files: widget.files,
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: max(widget.totalCount, 0),
itemBuilder: (context, index) {
return ExcludeSemantics(
child: widget.itemBuilder(context, index),
);
},
),
);
},
),
)
: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: max(widget.totalCount, 0),
itemBuilder: (context, index) {
return ExcludeSemantics(
child: widget.itemBuilder(context, index),
);
},
);
},
);
}
/// Jump to the [position] in the list. [position] is between 0.0 (first item) and 1.0 (last item), practically currentIndex / totalCount.

View File

@@ -7,7 +7,6 @@ import 'package:photos/core/constants.dart';
import 'package:photos/events/files_updated_event.dart';
import 'package:photos/models/file/file.dart';
import 'package:photos/models/selected_files.dart';
import "package:photos/states/pointer_provider.dart";
import 'package:photos/theme/ente_theme.dart';
import "package:photos/ui/viewer/gallery/component/grid/place_holder_grid_view_widget.dart";
import "package:photos/ui/viewer/gallery/component/group/group_gallery.dart";
@@ -62,7 +61,6 @@ class _LazyGroupGalleryState extends State<LazyGroupGallery> {
late StreamSubscription<FilesUpdatedEvent>? _reloadEventSubscription;
late StreamSubscription<int> _currentIndexSubscription;
bool? _shouldRender;
final _groupGalleryGlobalKey = GlobalKey();
@override
void initState() {
@@ -236,39 +234,21 @@ class _LazyGroupGalleryState extends State<LazyGroupGallery> {
],
),
widget.selectedFiles != null
? LastSelectedFileByDragging(
filesInGroup: _filesInGroup,
child: Builder(
builder: (context) {
return PointerProvider(
selectedFiles: widget.selectedFiles!,
files: _filesInGroup,
child: GroupGalleryGlobalKey(
globalKey: _groupGalleryGlobalKey,
child: SizedBox(
key: _groupGalleryGlobalKey,
child: _shouldRender!
? GroupGallery(
photoGridSize: widget.photoGridSize,
files: _filesInGroup,
tag: widget.tag,
asyncLoader: widget.asyncLoader,
selectedFiles: widget.selectedFiles,
limitSelectionToOne:
widget.limitSelectionToOne,
)
// todo: perf eval should we have separate PlaceHolder for Groups
// instead of creating a large cached view
: PlaceHolderGridViewWidget(
_filesInGroup.length,
widget.photoGridSize,
),
),
),
);
},
),
)
? _shouldRender!
? GroupGallery(
photoGridSize: widget.photoGridSize,
files: _filesInGroup,
tag: widget.tag,
asyncLoader: widget.asyncLoader,
selectedFiles: widget.selectedFiles,
limitSelectionToOne: widget.limitSelectionToOne,
)
// todo: perf eval should we have separate PlaceHolder for Groups
// instead of creating a large cached view
: PlaceHolderGridViewWidget(
_filesInGroup.length,
widget.photoGridSize,
)
: _shouldRender!
? GroupGallery(
photoGridSize: widget.photoGridSize,

View File

@@ -24,7 +24,7 @@ particular group of files.
If a group has more than 400 files, LazyGroupGallery internally divides the
group into multiple grid views during rendering.
*/
class MultipleGroupsGalleryView extends StatelessWidget {
class MultipleGroupsGalleryView extends StatefulWidget {
final ItemScrollController itemScroller;
final List<List<EnteFile>> groupedFiles;
final bool disableScroll;
@@ -66,31 +66,42 @@ class MultipleGroupsGalleryView extends StatelessWidget {
super.key,
});
@override
State<MultipleGroupsGalleryView> createState() =>
_MultipleGroupsGalleryViewState();
}
class _MultipleGroupsGalleryViewState extends State<MultipleGroupsGalleryView> {
@override
Widget build(BuildContext context) {
final filesInGroup =
widget.groupedFiles.expand((element) => element).toList();
final gType = GalleryContextState.of(context)!.type;
return HugeListView<List<EnteFile>>(
controller: itemScroller,
files: filesInGroup,
selectedFiles: widget.selectedFiles,
controller: widget.itemScroller,
startIndex: 0,
totalCount: groupedFiles.length,
isDraggableScrollbarEnabled: groupedFiles.length > 10,
disableScroll: disableScroll,
isScrollablePositionedList: isScrollablePositionedList,
totalCount: widget.groupedFiles.length,
isDraggableScrollbarEnabled: widget.groupedFiles.length > 10,
disableScroll: widget.disableScroll,
isScrollablePositionedList: widget.isScrollablePositionedList,
waitBuilder: (_) {
return const EnteLoadingWidget();
},
emptyResultBuilder: (_) {
final List<Widget> children = [];
if (header != null) {
children.add(header!);
if (widget.header != null) {
children.add(widget.header!);
}
children.add(
Expanded(
child: emptyState,
child: widget.emptyState,
),
);
if (footer != null) {
children.add(footer!);
if (widget.footer != null) {
children.add(widget.footer!);
}
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -100,60 +111,63 @@ class MultipleGroupsGalleryView extends StatelessWidget {
itemBuilder: (context, index) {
Widget gallery;
gallery = LazyGroupGallery(
groupedFiles[index],
widget.groupedFiles[index],
index,
reloadEvent,
removalEventTypes,
asyncLoader,
selectedFiles,
tagPrefix,
widget.reloadEvent,
widget.removalEventTypes,
widget.asyncLoader,
widget.selectedFiles,
widget.tagPrefix,
Bus.instance
.on<GalleryIndexUpdatedEvent>()
.where((event) => event.tag == tagPrefix)
.where((event) => event.tag == widget.tagPrefix)
.map((event) => event.index),
enableFileGrouping,
showSelectAllByDefault,
logTag: logTag,
widget.enableFileGrouping,
widget.showSelectAllByDefault,
logTag: widget.logTag,
photoGridSize: LocalSettings.instance.getPhotoGridSize(),
limitSelectionToOne: limitSelectionToOne,
limitSelectionToOne: widget.limitSelectionToOne,
);
if (header != null && index == 0) {
gallery = Column(children: [header!, gallery]);
if (widget.header != null && index == 0) {
gallery = Column(children: [widget.header!, gallery]);
}
if (footer != null && index == groupedFiles.length - 1) {
gallery = Column(children: [gallery, footer!]);
if (widget.footer != null && index == widget.groupedFiles.length - 1) {
gallery = Column(children: [gallery, widget.footer!]);
}
return gallery;
},
labelTextBuilder: (int index) {
try {
final EnteFile file = groupedFiles[index][0];
final EnteFile file = widget.groupedFiles[index][0];
if (gType == GroupType.size) {
return file.fileSize != null
? convertBytesToReadableFormat(file.fileSize!)
: "";
}
return DateFormat.yMMM(Localizations.localeOf(context).languageCode)
.format(
return DateFormat.yMMM(
Localizations.localeOf(context).languageCode,
).format(
DateTime.fromMicrosecondsSinceEpoch(
file.creationTime!,
),
);
} catch (e) {
logger.severe("label text builder failed", e);
widget.logger.severe("label text builder failed", e);
return "";
}
},
thumbBackgroundColor:
Theme.of(context).colorScheme.galleryThumbBackgroundColor,
thumbDrawColor: Theme.of(context).colorScheme.galleryThumbDrawColor,
thumbPadding: header != null
thumbPadding: widget.header != null
? const EdgeInsets.only(top: 60)
: const EdgeInsets.all(0),
bottomSafeArea: scrollBottomSafeArea,
bottomSafeArea: widget.scrollBottomSafeArea,
firstShown: (int firstIndex) {
Bus.instance.fire(GalleryIndexUpdatedEvent(tagPrefix, firstIndex));
Bus.instance.fire(
GalleryIndexUpdatedEvent(widget.tagPrefix, firstIndex),
);
},
);
}