diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 61dbcfb0..d0700c16 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -29,7 +29,6 @@ import 'package:PiliPlus/utils/storage_key.dart'; import 'package:PiliPlus/utils/utils.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:canvas_danmaku/canvas_danmaku.dart'; -import 'package:floating/floating.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show SystemUiOverlayStyle; @@ -181,23 +180,26 @@ class _LiveRoomPageState extends State @override Widget build(BuildContext context) { - if (Platform.isAndroid && Floating().isPipMode) { - return videoPlayerPanel( + Widget child; + if (plPlayerController.isPipMode) { + child = videoPlayerPanel( isFullScreen, width: maxWidth, height: maxHeight, isPipMode: true, needDm: !plPlayerController.pipNoDanmaku, ); + } else { + child = childWhenDisabled; } if (plPlayerController.keyboardControl) { - return PlayerFocus( + child = PlayerFocus( plPlayerController: plPlayerController, onSendDanmaku: _liveRoomController.onSendDanmaku, - child: childWhenDisabled, + child: child, ); } - return childWhenDisabled; + return child; } Widget videoPlayerPanel( diff --git a/lib/pages/live_room/widgets/header_control.dart b/lib/pages/live_room/widgets/header_control.dart index 77c64b11..8bfa9682 100644 --- a/lib/pages/live_room/widgets/header_control.dart +++ b/lib/pages/live_room/widgets/header_control.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/common_btn.dart'; import 'package:PiliPlus/utils/page_utils.dart'; +import 'package:PiliPlus/utils/utils.dart'; import 'package:floating/floating.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -109,10 +110,14 @@ class LiveHeaderControl extends StatelessWidget { ); }, ), - if (Platform.isAndroid) + if (Platform.isAndroid || Utils.isDesktop) ComBtn( tooltip: '画中画', onTap: () async { + if (Utils.isDesktop) { + plPlayerController.toggleDesktopPip(); + return; + } try { var floating = Floating(); if ((await floating.isPipAvailable) == true) { diff --git a/lib/pages/video/controller.dart b/lib/pages/video/controller.dart index 6dfb8072..4a72fd5e 100644 --- a/lib/pages/video/controller.dart +++ b/lib/pages/video/controller.dart @@ -429,7 +429,8 @@ class VideoDetailController extends GetxController bool get horizontalScreen => plPlayerController.horizontalScreen; - bool get showVideoSheet => !horizontalScreen && !isPortrait; + bool get showVideoSheet => + (!horizontalScreen && !isPortrait) || plPlayerController.isDesktopPip; int? _lastPos; late final List postList = []; diff --git a/lib/pages/video/view.dart b/lib/pages/video/view.dart index d1a50609..d94c8546 100644 --- a/lib/pages/video/view.dart +++ b/lib/pages/video/view.dart @@ -1456,15 +1456,6 @@ class _VideoDetailPageVState extends State ), ); - Widget autoChoose(Widget childWhenDisabled) { - if (Platform.isAndroid) { - return Floating().isPipMode - ? plPlayer(width: maxWidth, height: maxHeight, isPipMode: true) - : childWhenDisabled; - } - return childWhenDisabled; - } - late ThemeData themeData; late bool isPortrait; late double maxWidth; @@ -1473,14 +1464,16 @@ class _VideoDetailPageVState extends State @override Widget build(BuildContext context) { Widget child; - if (!videoDetailController.horizontalScreen) { - child = autoChoose(childWhenDisabled); + if (videoDetailController.plPlayerController.isPipMode) { + child = plPlayer(width: maxWidth, height: maxHeight, isPipMode: true); + } else if (!videoDetailController.horizontalScreen) { + child = childWhenDisabled; } else if (maxWidth > maxHeight * 1.25) { - child = autoChoose(childWhenDisabledLandscape); + child = childWhenDisabledLandscape; } else if (maxWidth * (9 / 16) < (2 / 5) * maxHeight) { - child = autoChoose(childWhenDisabled); + child = childWhenDisabled; } else { - child = autoChoose(childWhenDisabledAlmostSquare); + child = childWhenDisabledAlmostSquare; } if (videoDetailController.plPlayerController.keyboardControl) { child = PlayerFocus( diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index aa7c6da0..cd275de4 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -1925,7 +1925,9 @@ class HeaderControlState extends TripleState { color: Colors.white, ), onPressed: () { - if (isFullScreen) { + if (plPlayerController.isDesktopPip) { + plPlayerController.exitDesktopPip(); + } else if (isFullScreen) { plPlayerController.triggerFullScreen(status: false); } else if (!horizontalScreen && !isPortrait) { verticalScreenForTwoSeconds(); @@ -2123,7 +2125,7 @@ class HeaderControlState extends TripleState { }, ), ), - if (Platform.isAndroid) + if (Platform.isAndroid || Utils.isDesktop) SizedBox( width: 42, height: 34, @@ -2133,6 +2135,10 @@ class HeaderControlState extends TripleState { padding: WidgetStatePropertyAll(EdgeInsets.zero), ), onPressed: () async { + if (Utils.isDesktop) { + plPlayerController.toggleDesktopPip(); + return; + } bool canUsePiP = await Floating().isPipAvailable; plPlayerController.hiddenControls(false); if (canUsePiP) { diff --git a/lib/pages/video/widgets/player_focus.dart b/lib/pages/video/widgets/player_focus.dart index 53a49d43..accc4e4d 100644 --- a/lib/pages/video/widgets/player_focus.dart +++ b/lib/pages/video/widgets/player_focus.dart @@ -6,6 +6,7 @@ import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show KeyDownEvent, KeyUpEvent, LogicalKeyboardKey; @@ -71,7 +72,9 @@ class PlayerFocus extends StatelessWidget { return true; case LogicalKeyboardKey.escape: - if (isFullScreen) { + if (plPlayerController.isDesktopPip) { + plPlayerController.exitDesktopPip(); + } else if (isFullScreen) { plPlayerController.triggerFullScreen(status: false); } else { Get.back(); @@ -94,6 +97,10 @@ class PlayerFocus extends StatelessWidget { } return true; + case LogicalKeyboardKey.keyP when (Utils.isDesktop && hasPlayer): + plPlayerController.toggleDesktopPip(); + return true; + case LogicalKeyboardKey.arrowUp: if (hasPlayer) { final volume = math.min(1.0, plPlayerController.volume.value + 0.1); diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index cd01f30c..cf4b98b8 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -41,6 +41,7 @@ import 'package:canvas_danmaku/canvas_danmaku.dart'; import 'package:crclib/catalog.dart'; import 'package:dio/dio.dart' show Options; import 'package:easy_debounce/easy_throttle.dart'; +import 'package:floating/floating.dart'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -53,6 +54,7 @@ import 'package:media_kit_video/media_kit_video.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; import 'package:universal_platform/universal_platform.dart'; +import 'package:window_manager/window_manager.dart'; class PlPlayerController { Player? _videoPlayerController; @@ -253,6 +255,51 @@ class PlPlayerController { late final RxBool enableShowLiveDanmaku = Pref.enableShowLiveDanmaku.obs; late final bool autoPiP = Pref.autoPiP; + bool get isPipMode => + (Platform.isAndroid && Floating().isPipMode) || + (Utils.isDesktop && isDesktopPip); + late bool isDesktopPip = false; + late Rect _lastWindowBounds; + + void exitDesktopPip() { + isDesktopPip = false; + + windowManager + ..setTitleBarStyle(TitleBarStyle.normal) + ..setMinimumSize(const Size(400, 700)) + ..setBounds(_lastWindowBounds) + ..setAlwaysOnTop(false); + } + + Future enterDesktopPip() async { + isDesktopPip = true; + + _lastWindowBounds = await windowManager.getBounds(); + + windowManager.setTitleBarStyle(TitleBarStyle.hidden); + + late final Size size; + final width = this.width ?? 16; + final height = this.height ?? 9; + if (height > width) { + size = Size(400.0, 400.0 * height / width); + } else { + size = Size(280.0 * width / height, 280.0); + } + + await windowManager.setMinimumSize(size); + windowManager + ..setSize(size) + ..setAlwaysOnTop(true); + } + + void toggleDesktopPip() { + if (isDesktopPip) { + exitDesktopPip(); + } else { + enterDesktopPip(); + } + } void enterPip() { if (Get.currentRoute.startsWith('/video')) { diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index c2e7e21b..c0b49ff8 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -59,6 +59,7 @@ import 'package:get/get.dart' hide ContextExtensionss; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:media_kit_video/media_kit_video.dart'; import 'package:screen_brightness_platform_interface/screen_brightness_platform_interface.dart'; +import 'package:window_manager/window_manager.dart'; class PLVideoPlayer extends StatefulWidget { const PLVideoPlayer({ @@ -749,16 +750,17 @@ class _PLVideoPlayerState extends State ], ]; + final flag = isFullScreen || plPlayerController.isDesktopPip; List userSpecifyItemRight = [ if (plPlayerController.showDmChart) BottomControlType.dmChart, if (plPlayerController.isAnim) BottomControlType.superResolution, if (plPlayerController.showViewPoints) BottomControlType.viewPoints, if (anySeason) BottomControlType.episode, - if (isFullScreen) BottomControlType.fit, + if (flag) BottomControlType.fit, BottomControlType.aiTranslate, BottomControlType.subtitle, BottomControlType.speed, - if (isFullScreen) BottomControlType.qa, + if (flag) BottomControlType.qa, BottomControlType.fullscreen, ]; @@ -1308,7 +1310,13 @@ class _PLVideoPlayerState extends State isTop: true, controller: animationController, isFullScreen: isFullScreen, - child: widget.headerControl, + child: plPlayerController.isDesktopPip + ? GestureDetector( + behavior: HitTestBehavior.translucent, + onPanStart: (_) => windowManager.startDragging(), + child: widget.headerControl, + ) + : widget.headerControl, ), AppBarAni( isTop: false,