fix(Desktop): Fixed some of desktop specific controls (#131)

Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
PartyDonut 2024-11-08 11:58:24 +01:00 committed by GitHub
parent 0b7b73134f
commit 8435a27ed9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 276 additions and 269 deletions

View file

@ -32,10 +32,134 @@ class ItemInfoScreenState extends ConsumerState<ItemInfoScreen> {
AutoDisposeStateNotifierProvider<InformationNotifier, InformationProviderModel> get provider => AutoDisposeStateNotifierProvider<InformationNotifier, InformationProviderModel> get provider =>
informationProvider(widget.item.id); informationProvider(widget.item.id);
FocusNode focusNode = FocusNode();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
Future.microtask(() => ref.read(provider.notifier).getItemInformation(widget.item)); Future.microtask(() {
focusNode.requestFocus();
return ref.read(provider.notifier).getItemInformation(widget.item);
});
}
@override
Widget build(BuildContext context) {
final info = ref.watch(provider);
final videoStreams = (info.model?.videoStreams.map((map) => streamModel("Video", map)) ?? []).toList();
final audioStreams = (info.model?.audioStreams.map((map) => streamModel("Audio", map)) ?? []).toList();
final subStreams = (info.model?.subStreams.map((map) => streamModel("Subtitle", map)) ?? []).toList();
return Dialog(
child: Card(
color: Theme.of(context).colorScheme.surface,
child: Focus(
autofocus: true,
focusNode: focusNode,
onKeyEvent: (node, event) => KeyEventResult.ignored,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
color: Theme.of(context).colorScheme.surface,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Text(
widget.item.name,
style: Theme.of(context).textTheme.titleLarge,
),
),
const Opacity(opacity: 0.3, child: Divider()),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
const Spacer(),
const SizedBox(width: 6),
IconButton(
onPressed: () async {
await Clipboard.setData(ClipboardData(text: info.model.toString()));
if (context.mounted) {
fladderSnackbar(context, title: "Copied to clipboard");
}
},
icon: const Icon(Icons.copy_all_rounded)),
const SizedBox(width: 6),
IconButton(
onPressed: () => ref.read(provider.notifier).getItemInformation(widget.item),
icon: const Icon(IconsaxOutline.refresh),
),
],
),
),
],
),
),
const SizedBox(height: 6),
Flexible(
fit: FlexFit.loose,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
shrinkWrap: true,
children: [
Stack(
alignment: Alignment.center,
children: [
if (info.model != null) ...{
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: double.infinity, child: streamModel("Info", info.model!.baseInformation)),
if ([...videoStreams, ...audioStreams, ...subStreams].isNotEmpty) ...{
const Divider(),
Wrap(
alignment: WrapAlignment.start,
runAlignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.start,
runSpacing: 16,
spacing: 16,
children: [
...videoStreams,
...audioStreams,
...subStreams,
],
),
},
],
),
},
AnimatedOpacity(
opacity: info.loading ? 1 : 0,
duration: const Duration(milliseconds: 250),
child: const Center(child: CircularProgressIndicator.adaptive(strokeCap: StrokeCap.round)),
)
],
),
],
),
),
),
Container(
color: Theme.of(context).colorScheme.surface,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FilledButton(onPressed: () => Navigator.of(context).pop(), child: Text(context.localized.close))
],
),
),
)
],
),
),
),
);
} }
Widget tileRow(String title, String value) { Widget tileRow(String title, String value) {
@ -103,117 +227,4 @@ class ItemInfoScreenState extends ConsumerState<ItemInfoScreen> {
), ),
); );
} }
@override
Widget build(BuildContext context) {
final info = ref.watch(provider);
final videoStreams = (info.model?.videoStreams.map((map) => streamModel("Video", map)) ?? []).toList();
final audioStreams = (info.model?.audioStreams.map((map) => streamModel("Audio", map)) ?? []).toList();
final subStreams = (info.model?.subStreams.map((map) => streamModel("Subtitle", map)) ?? []).toList();
return Dialog(
child: Card(
color: Theme.of(context).colorScheme.surface,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
color: Theme.of(context).colorScheme.surface,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Text(
widget.item.name,
style: Theme.of(context).textTheme.titleLarge,
),
),
const Opacity(opacity: 0.3, child: Divider()),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
const Spacer(),
const SizedBox(width: 6),
IconButton(
onPressed: () async {
await Clipboard.setData(ClipboardData(text: info.model.toString()));
if (context.mounted) {
fladderSnackbar(context, title: "Copied to clipboard");
}
},
icon: const Icon(Icons.copy_all_rounded)),
const SizedBox(width: 6),
IconButton(
onPressed: () => ref.read(provider.notifier).getItemInformation(widget.item),
icon: const Icon(IconsaxOutline.refresh),
),
],
),
),
],
),
),
const SizedBox(height: 6),
Flexible(
fit: FlexFit.loose,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
shrinkWrap: true,
children: [
Stack(
alignment: Alignment.center,
children: [
if (info.model != null) ...{
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(width: double.infinity, child: streamModel("Info", info.model!.baseInformation)),
if ([...videoStreams, ...audioStreams, ...subStreams].isNotEmpty) ...{
const Divider(),
Wrap(
alignment: WrapAlignment.start,
runAlignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.start,
runSpacing: 16,
spacing: 16,
children: [
...videoStreams,
...audioStreams,
...subStreams,
],
),
},
],
),
},
AnimatedOpacity(
opacity: info.loading ? 1 : 0,
duration: const Duration(milliseconds: 250),
child: const Center(child: CircularProgressIndicator.adaptive(strokeCap: StrokeCap.round)),
)
],
),
],
),
),
),
Container(
color: Theme.of(context).colorScheme.surface,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FilledButton(onPressed: () => Navigator.of(context).pop(), child: Text(context.localized.close))
],
),
),
)
],
),
),
);
}
} }

View file

@ -42,28 +42,26 @@ class _DefaultTitleBarState extends ConsumerState<DefaultTitleBar> with WindowLi
return SizedBox( return SizedBox(
height: widget.height, height: widget.height,
child: switch (AdaptiveLayout.of(context).platform) { child: switch (AdaptiveLayout.of(context).platform) {
TargetPlatform.android || TargetPlatform.iOS => SizedBox(height: MediaQuery.paddingOf(context).top),
TargetPlatform.windows || TargetPlatform.linux => Row( TargetPlatform.windows || TargetPlatform.linux => Row(
children: [ children: [
Expanded( Expanded(
child: DragToMoveArea( child: DragToMoveArea(
child: Container( child: Row(
color: Colors.red.withOpacity(0), crossAxisAlignment: CrossAxisAlignment.stretch,
child: Row( mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch, children: [
mainAxisSize: MainAxisSize.max, Container(
children: [ padding: const EdgeInsets.only(left: 16),
Container( child: DefaultTextStyle(
padding: const EdgeInsets.only(left: 16), style: TextStyle(
child: DefaultTextStyle( color: iconColor,
style: TextStyle( fontSize: 14,
color: iconColor,
fontSize: 14,
),
child: Text(widget.label ?? ""),
), ),
child: Text(widget.label ?? ""),
), ),
], ),
), ],
), ),
), ),
), ),
@ -161,13 +159,7 @@ class _DefaultTitleBarState extends ConsumerState<DefaultTitleBar> with WindowLi
), ),
], ],
), ),
TargetPlatform.macOS => const Row( TargetPlatform.macOS => const SizedBox.shrink(),
children: [
Spacer(),
Text("Fladder"),
SizedBox(width: 16),
],
),
_ => Text(widget.label ?? "Fladder"), _ => Text(widget.label ?? "Fladder"),
}, },
); );

View file

@ -283,16 +283,19 @@ class _VideoPlayerNextWrapperState extends ConsumerState<VideoPlayerNextWrapper>
], ],
), ),
), ),
AnimatedFadeSize( IgnorePointer(
duration: animSpeed, ignoring: !show,
child: show child: AnimatedFadeSize(
? Padding( duration: animSpeed,
padding: const EdgeInsets.only(top: 16), child: show
child: _SimpleControls( ? Padding(
skip: nextUp != null ? () => onTimeOut() : null, padding: const EdgeInsets.only(top: 16),
), child: _SimpleControls(
) skip: nextUp != null ? () => onTimeOut() : null,
: const SizedBox.shrink(), ),
)
: const SizedBox.shrink(),
),
), ),
], ],
), ),
@ -300,12 +303,15 @@ class _VideoPlayerNextWrapperState extends ConsumerState<VideoPlayerNextWrapper>
), ),
), ),
if (AdaptiveLayout.of(context).isDesktop) if (AdaptiveLayout.of(context).isDesktop)
AnimatedOpacity( IgnorePointer(
duration: animSpeed, ignoring: !show,
opacity: show ? 1 : 0, child: AnimatedOpacity(
child: const Align( duration: animSpeed,
alignment: Alignment.topRight, opacity: show ? 1 : 0,
child: DefaultTitleBar(), child: const Align(
alignment: Alignment.topRight,
child: DefaultTitleBar(),
),
), ),
), ),
], ],

View file

@ -255,6 +255,7 @@ class _VideoOptionsMobileState extends ConsumerState<VideoOptions> {
ListView itemInfo(ItemBaseModel? currentItem, BuildContext context) { ListView itemInfo(ItemBaseModel? currentItem, BuildContext context) {
return ListView( return ListView(
shrinkWrap: true, shrinkWrap: true,
controller: widget.controller,
children: [ children: [
navTitle(currentItem?.title, currentItem?.subTextShort(context)), navTitle(currentItem?.title, currentItem?.subTextShort(context)),
if (currentItem != null) ...{ if (currentItem != null) ...{

View file

@ -65,12 +65,13 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
if (value.logicalKey == LogicalKeyboardKey.arrowUp) { if (value.logicalKey == LogicalKeyboardKey.arrowUp) {
resetTimer(); resetTimer();
ref.read(videoPlayerSettingsProvider.notifier).steppedVolume(5); ref.read(videoPlayerSettingsProvider.notifier).steppedVolume(5);
return true;
} }
if (value.logicalKey == LogicalKeyboardKey.arrowDown) { if (value.logicalKey == LogicalKeyboardKey.arrowDown) {
resetTimer(); resetTimer();
ref.read(videoPlayerSettingsProvider.notifier).steppedVolume(-5); ref.read(videoPlayerSettingsProvider.notifier).steppedVolume(-5);
return true;
} }
return true;
} }
if (value is KeyDownEvent) { if (value is KeyDownEvent) {
if (value.logicalKey == LogicalKeyboardKey.keyS) { if (value.logicalKey == LogicalKeyboardKey.keyS) {
@ -79,25 +80,30 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
} else if (showCreditSkipButton) { } else if (showCreditSkipButton) {
skipCredits(introSkipModel); skipCredits(introSkipModel);
} }
return true;
} }
if (value.logicalKey == LogicalKeyboardKey.escape) { if (value.logicalKey == LogicalKeyboardKey.escape) {
disableFullscreen(); disableFullscreen();
return true;
} }
if (value.logicalKey == LogicalKeyboardKey.space) { if (value.logicalKey == LogicalKeyboardKey.space) {
ref.read(videoPlayerProvider).playOrPause(); ref.read(videoPlayerProvider).playOrPause();
return true;
} }
if (value.logicalKey == LogicalKeyboardKey.keyF) { if (value.logicalKey == LogicalKeyboardKey.keyF) {
toggleFullScreen(ref); toggleFullScreen(ref);
return true;
} }
if (value.logicalKey == LogicalKeyboardKey.arrowUp) { if (value.logicalKey == LogicalKeyboardKey.arrowUp) {
resetTimer(); resetTimer();
ref.read(videoPlayerSettingsProvider.notifier).steppedVolume(5); ref.read(videoPlayerSettingsProvider.notifier).steppedVolume(5);
return true;
} }
if (value.logicalKey == LogicalKeyboardKey.arrowDown) { if (value.logicalKey == LogicalKeyboardKey.arrowDown) {
resetTimer(); resetTimer();
ref.read(videoPlayerSettingsProvider.notifier).steppedVolume(-5); ref.read(videoPlayerSettingsProvider.notifier).steppedVolume(-5);
return true;
} }
return true;
} }
return false; return false;
} }
@ -115,86 +121,83 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
return InputHandler( return InputHandler(
autoFocus: false, autoFocus: false,
onKeyEvent: (node, event) => _onKey(event) ? KeyEventResult.handled : KeyEventResult.ignored, onKeyEvent: (node, event) => _onKey(event) ? KeyEventResult.handled : KeyEventResult.ignored,
child: Listener( child: PopScope(
onPointerSignal: (event) => resetTimer(), canPop: false,
child: PopScope( onPopInvokedWithResult: (didPop, result) {
canPop: false, if (!didPop) {
onPopInvokedWithResult: (didPop, result) { closePlayer();
if (!didPop) { }
closePlayer(); },
} child: GestureDetector(
}, onTap: () => toggleOverlay(),
child: GestureDetector( child: MouseRegion(
onTap: () => toggleOverlay(), cursor: showOverlay ? SystemMouseCursors.basic : SystemMouseCursors.none,
child: MouseRegion( onExit: (event) => toggleOverlay(value: false),
cursor: showOverlay ? SystemMouseCursors.basic : SystemMouseCursors.none, onEnter: (event) => toggleOverlay(value: true),
onEnter: (event) => toggleOverlay(value: true), onHover: AdaptiveLayout.of(context).isDesktop ? (event) => toggleOverlay(value: true) : null,
onExit: (event) => toggleOverlay(value: false), child: Stack(
onHover: AdaptiveLayout.of(context).isDesktop || kIsWeb ? (event) => toggleOverlay(value: true) : null, children: [
child: Stack( if (player != null)
children: [ VideoSubtitles(
if (player != null) key: const Key('subtitles'),
VideoSubtitles( controller: player,
key: const Key('subtitles'), overLayed: showOverlay,
controller: player, ),
overLayed: showOverlay, if (AdaptiveLayout.of(context).isDesktop)
), Consumer(builder: (context, ref, child) {
if (AdaptiveLayout.of(context).isDesktop) final playing = ref.watch(mediaPlaybackProvider.select((value) => value.playing));
Consumer(builder: (context, ref, child) { final buffering = ref.watch(mediaPlaybackProvider.select((value) => value.buffering));
final playing = ref.watch(mediaPlaybackProvider.select((value) => value.playing)); return playButton(playing, buffering);
final buffering = ref.watch(mediaPlaybackProvider.select((value) => value.buffering)); }),
return playButton(playing, buffering); IgnorePointer(
}), ignoring: !showOverlay,
IgnorePointer( child: AnimatedOpacity(
ignoring: !showOverlay, duration: fadeDuration,
child: AnimatedOpacity( opacity: showOverlay ? 1 : 0,
duration: fadeDuration, child: Column(
opacity: showOverlay ? 1 : 0, children: [
child: Column( topButtons(context),
children: [ const Spacer(),
topButtons(context), bottomButtons(context),
const Spacer(), ],
bottomButtons(context),
],
),
), ),
), ),
const VideoPlayerSeekIndicator(), ),
Consumer( const VideoPlayerSeekIndicator(),
builder: (context, ref, child) { Consumer(
final position = ref.watch(mediaPlaybackProvider.select((value) => value.position)); builder: (context, ref, child) {
bool showIntroSkipButton = introSkipModel?.introInRange(position) ?? false; final position = ref.watch(mediaPlaybackProvider.select((value) => value.position));
bool showCreditSkipButton = introSkipModel?.creditsInRange(position) ?? false; bool showIntroSkipButton = introSkipModel?.introInRange(position) ?? false;
return Stack( bool showCreditSkipButton = introSkipModel?.creditsInRange(position) ?? false;
children: [ return Stack(
if (showIntroSkipButton) children: [
Align( if (showIntroSkipButton)
alignment: Alignment.centerRight, Align(
child: Padding( alignment: Alignment.centerRight,
padding: const EdgeInsets.all(32), child: Padding(
child: IntroSkipButton( padding: const EdgeInsets.all(32),
isOverlayVisible: showOverlay, child: IntroSkipButton(
skipIntro: () => skipIntro(introSkipModel), isOverlayVisible: showOverlay,
), skipIntro: () => skipIntro(introSkipModel),
), ),
), ),
if (showCreditSkipButton) ),
Align( if (showCreditSkipButton)
alignment: Alignment.centerRight, Align(
child: Padding( alignment: Alignment.centerRight,
padding: const EdgeInsets.all(32), child: Padding(
child: CreditsSkipButton( padding: const EdgeInsets.all(32),
isOverlayVisible: showOverlay, child: CreditsSkipButton(
skipCredits: () => skipCredits(introSkipModel), isOverlayVisible: showOverlay,
), skipCredits: () => skipCredits(introSkipModel),
), ),
) ),
], )
); ],
}, );
), },
], ),
), ],
), ),
), ),
), ),
@ -236,47 +239,41 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
Colors.black.withOpacity(0), Colors.black.withOpacity(0),
], ],
)), )),
child: Stack( child: Padding(
children: [ padding: MediaQuery.paddingOf(context).copyWith(bottom: 0, top: 0),
if (AdaptiveLayout.of(context).isDesktop) child: Container(
const Align( alignment: Alignment.topCenter,
alignment: Alignment.topRight, child: Column(
child: DefaultTitleBar(), children: [
), const Align(
Padding( alignment: Alignment.topRight,
padding: MediaQuery.paddingOf(context).copyWith(bottom: 0), child: DefaultTitleBar(),
child: Container(
alignment: Alignment.topCenter,
height: 80,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(
onPressed: () => minimizePlayer(context),
icon: const Icon(
IconsaxOutline.arrow_down_1,
size: 24,
),
),
const SizedBox(width: 16),
Flexible(
child: Text(
currentItem?.title ?? "",
style: Theme.of(context).textTheme.titleLarge,
),
),
],
),
),
],
), ),
), Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(
onPressed: () => minimizePlayer(context),
icon: const Icon(
IconsaxOutline.arrow_down_1,
size: 24,
),
),
const SizedBox(width: 16),
Flexible(
child: Text(
currentItem?.title ?? "",
style: Theme.of(context).textTheme.titleLarge,
),
),
],
),
),
],
), ),
], ),
), ),
); );
} }