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

View file

@ -283,16 +283,19 @@ class _VideoPlayerNextWrapperState extends ConsumerState<VideoPlayerNextWrapper>
],
),
),
AnimatedFadeSize(
duration: animSpeed,
child: show
? Padding(
padding: const EdgeInsets.only(top: 16),
child: _SimpleControls(
skip: nextUp != null ? () => onTimeOut() : null,
),
)
: const SizedBox.shrink(),
IgnorePointer(
ignoring: !show,
child: AnimatedFadeSize(
duration: animSpeed,
child: show
? Padding(
padding: const EdgeInsets.only(top: 16),
child: _SimpleControls(
skip: nextUp != null ? () => onTimeOut() : null,
),
)
: const SizedBox.shrink(),
),
),
],
),
@ -300,12 +303,15 @@ class _VideoPlayerNextWrapperState extends ConsumerState<VideoPlayerNextWrapper>
),
),
if (AdaptiveLayout.of(context).isDesktop)
AnimatedOpacity(
duration: animSpeed,
opacity: show ? 1 : 0,
child: const Align(
alignment: Alignment.topRight,
child: DefaultTitleBar(),
IgnorePointer(
ignoring: !show,
child: AnimatedOpacity(
duration: animSpeed,
opacity: show ? 1 : 0,
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) {
return ListView(
shrinkWrap: true,
controller: widget.controller,
children: [
navTitle(currentItem?.title, currentItem?.subTextShort(context)),
if (currentItem != null) ...{

View file

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