feat: right click to open track option

This commit is contained in:
Kingkor Roy Tirtho 2023-09-10 22:50:44 +06:00
parent d4f99ec899
commit 1540999f50
3 changed files with 413 additions and 363 deletions

View File

@ -78,6 +78,31 @@ class AdaptivePopSheetList<T> extends StatelessWidget {
'Either icon or child must be provided', 'Either icon or child must be provided',
); );
Future<T?> showPopupMenu(BuildContext context, RelativeRect position) {
final mediaQuery = MediaQuery.of(context);
return showMenu<T>(
context: context,
useRootNavigator: useRootNavigator,
constraints: BoxConstraints(
maxHeight: mediaQuery.size.height * 0.6,
),
position: position,
items: children
.map(
(item) => PopupMenuItem<T>(
padding: EdgeInsets.zero,
enabled: false,
child: _AdaptivePopSheetListItem<T>(
item: item,
onSelected: onSelected,
),
),
)
.toList(),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context); final mediaQuery = MediaQuery.of(context);

View File

@ -14,7 +14,6 @@ import 'package:spotube/components/shared/heart_button.dart';
import 'package:spotube/components/shared/image/universal_image.dart'; import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/local_track.dart'; import 'package:spotube/models/local_track.dart';
import 'package:spotube/models/spotube_track.dart';
import 'package:spotube/provider/authentication_provider.dart'; import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/blacklist_provider.dart'; import 'package:spotube/provider/blacklist_provider.dart';
import 'package:spotube/provider/download_manager_provider.dart'; import 'package:spotube/provider/download_manager_provider.dart';
@ -40,9 +39,11 @@ class TrackOptions extends HookConsumerWidget {
final Track track; final Track track;
final bool userPlaylist; final bool userPlaylist;
final String? playlistId; final String? playlistId;
final ObjectRef<ValueChanged<RelativeRect>?>? showMenuCbRef;
const TrackOptions({ const TrackOptions({
Key? key, Key? key,
required this.track, required this.track,
this.showMenuCbRef,
this.userPlaylist = false, this.userPlaylist = false,
this.playlistId, this.playlistId,
}) : super(key: key); }) : super(key: key);
@ -114,11 +115,7 @@ class TrackOptions extends HookConsumerWidget {
return downloadManager.getProgressNotifier(spotubeTrack); return downloadManager.getProgressNotifier(spotubeTrack);
}); });
return ListTileTheme( final adaptivePopSheetList = AdaptivePopSheetList<TrackOptionValue>(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: AdaptivePopSheetList<TrackOptionValue>(
onSelected: (value) async { onSelected: (value) async {
switch (value) { switch (value) {
case TrackOptionValue.delete: case TrackOptionValue.delete:
@ -203,8 +200,7 @@ class TrackOptions extends HookConsumerWidget {
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
child: UniversalImage( child: UniversalImage(
path: TypeConversionUtils.image_X_UrlString( path: TypeConversionUtils.image_X_UrlString(track.album!.images,
track.album!.images,
placeholder: ImagePlaceholder.albumArt), placeholder: ImagePlaceholder.albumArt),
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
@ -317,7 +313,18 @@ class TrackOptions extends HookConsumerWidget {
), ),
] ]
}, },
);
//! This is the most ANTI pattern I've ever done, but it works
showMenuCbRef?.value = (relativeRect) {
adaptivePopSheetList.showPopupMenu(context, relativeRect);
};
return ListTileTheme(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
), ),
child: adaptivePopSheetList,
); );
} }
} }

View File

@ -1,3 +1,4 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -57,10 +58,24 @@ class TrackTile extends HookConsumerWidget {
[blacklist, track], [blacklist, track],
); );
final showOptionCbRef = useRef<ValueChanged<RelativeRect>?>(null);
final isPlaying = track.id == playlist.activeTrack?.id; final isPlaying = track.id == playlist.activeTrack?.id;
return LayoutBuilder(builder: (context, constrains) { return LayoutBuilder(builder: (context, constrains) {
return HoverBuilder( return Listener(
onPointerDown: (event) {
if (event.buttons != kSecondaryMouseButton) return;
showOptionCbRef.value?.call(
RelativeRect.fromLTRB(
event.position.dx,
event.position.dy,
constrains.maxWidth - event.position.dx,
constrains.maxHeight - event.position.dy,
),
);
},
child: HoverBuilder(
permanentState: isPlaying || constrains.smAndDown ? true : null, permanentState: isPlaying || constrains.smAndDown ? true : null,
builder: (context, isHovering) { builder: (context, isHovering) {
return ListTile( return ListTile(
@ -69,7 +84,8 @@ class TrackTile extends HookConsumerWidget {
onLongPress: onLongPress, onLongPress: onLongPress,
enabled: !isBlackListed, enabled: !isBlackListed,
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
tileColor: isBlackListed ? theme.colorScheme.errorContainer : null, tileColor:
isBlackListed ? theme.colorScheme.errorContainer : null,
horizontalTitleGap: 12, horizontalTitleGap: 12,
leadingAndTrailingTextStyle: theme.textTheme.bodyMedium, leadingAndTrailingTextStyle: theme.textTheme.bodyMedium,
leading: Row( leading: Row(
@ -220,11 +236,13 @@ class TrackTile extends HookConsumerWidget {
track: track, track: track,
playlistId: playlistId, playlistId: playlistId,
userPlaylist: userPlaylist, userPlaylist: userPlaylist,
showMenuCbRef: showOptionCbRef,
), ),
], ],
), ),
); );
}, },
),
); );
}); });
} }