desktop pip

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-09-20 15:06:44 +08:00
parent 0745d83e4b
commit 36de899a35
8 changed files with 97 additions and 28 deletions

View File

@@ -29,7 +29,6 @@ import 'package:PiliPlus/utils/storage_key.dart';
import 'package:PiliPlus/utils/utils.dart'; import 'package:PiliPlus/utils/utils.dart';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:canvas_danmaku/canvas_danmaku.dart'; import 'package:canvas_danmaku/canvas_danmaku.dart';
import 'package:floating/floating.dart';
import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show SystemUiOverlayStyle; import 'package:flutter/services.dart' show SystemUiOverlayStyle;
@@ -181,23 +180,26 @@ class _LiveRoomPageState extends State<LiveRoomPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (Platform.isAndroid && Floating().isPipMode) { Widget child;
return videoPlayerPanel( if (plPlayerController.isPipMode) {
child = videoPlayerPanel(
isFullScreen, isFullScreen,
width: maxWidth, width: maxWidth,
height: maxHeight, height: maxHeight,
isPipMode: true, isPipMode: true,
needDm: !plPlayerController.pipNoDanmaku, needDm: !plPlayerController.pipNoDanmaku,
); );
} else {
child = childWhenDisabled;
} }
if (plPlayerController.keyboardControl) { if (plPlayerController.keyboardControl) {
return PlayerFocus( child = PlayerFocus(
plPlayerController: plPlayerController, plPlayerController: plPlayerController,
onSendDanmaku: _liveRoomController.onSendDanmaku, onSendDanmaku: _liveRoomController.onSendDanmaku,
child: childWhenDisabled, child: child,
); );
} }
return childWhenDisabled; return child;
} }
Widget videoPlayerPanel( Widget videoPlayerPanel(

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart';
import 'package:PiliPlus/plugin/pl_player/widgets/common_btn.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/common_btn.dart';
import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/page_utils.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:floating/floating.dart'; import 'package:floating/floating.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.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( ComBtn(
tooltip: '画中画', tooltip: '画中画',
onTap: () async { onTap: () async {
if (Utils.isDesktop) {
plPlayerController.toggleDesktopPip();
return;
}
try { try {
var floating = Floating(); var floating = Floating();
if ((await floating.isPipAvailable) == true) { if ((await floating.isPipAvailable) == true) {

View File

@@ -429,7 +429,8 @@ class VideoDetailController extends GetxController
bool get horizontalScreen => plPlayerController.horizontalScreen; bool get horizontalScreen => plPlayerController.horizontalScreen;
bool get showVideoSheet => !horizontalScreen && !isPortrait; bool get showVideoSheet =>
(!horizontalScreen && !isPortrait) || plPlayerController.isDesktopPip;
int? _lastPos; int? _lastPos;
late final List<PostSegmentModel> postList = []; late final List<PostSegmentModel> postList = [];

View File

@@ -1456,15 +1456,6 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
), ),
); );
Widget autoChoose(Widget childWhenDisabled) {
if (Platform.isAndroid) {
return Floating().isPipMode
? plPlayer(width: maxWidth, height: maxHeight, isPipMode: true)
: childWhenDisabled;
}
return childWhenDisabled;
}
late ThemeData themeData; late ThemeData themeData;
late bool isPortrait; late bool isPortrait;
late double maxWidth; late double maxWidth;
@@ -1473,14 +1464,16 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget child; Widget child;
if (!videoDetailController.horizontalScreen) { if (videoDetailController.plPlayerController.isPipMode) {
child = autoChoose(childWhenDisabled); child = plPlayer(width: maxWidth, height: maxHeight, isPipMode: true);
} else if (!videoDetailController.horizontalScreen) {
child = childWhenDisabled;
} else if (maxWidth > maxHeight * 1.25) { } else if (maxWidth > maxHeight * 1.25) {
child = autoChoose(childWhenDisabledLandscape); child = childWhenDisabledLandscape;
} else if (maxWidth * (9 / 16) < (2 / 5) * maxHeight) { } else if (maxWidth * (9 / 16) < (2 / 5) * maxHeight) {
child = autoChoose(childWhenDisabled); child = childWhenDisabled;
} else { } else {
child = autoChoose(childWhenDisabledAlmostSquare); child = childWhenDisabledAlmostSquare;
} }
if (videoDetailController.plPlayerController.keyboardControl) { if (videoDetailController.plPlayerController.keyboardControl) {
child = PlayerFocus( child = PlayerFocus(

View File

@@ -1925,7 +1925,9 @@ class HeaderControlState extends TripleState<HeaderControl> {
color: Colors.white, color: Colors.white,
), ),
onPressed: () { onPressed: () {
if (isFullScreen) { if (plPlayerController.isDesktopPip) {
plPlayerController.exitDesktopPip();
} else if (isFullScreen) {
plPlayerController.triggerFullScreen(status: false); plPlayerController.triggerFullScreen(status: false);
} else if (!horizontalScreen && !isPortrait) { } else if (!horizontalScreen && !isPortrait) {
verticalScreenForTwoSeconds(); verticalScreenForTwoSeconds();
@@ -2123,7 +2125,7 @@ class HeaderControlState extends TripleState<HeaderControl> {
}, },
), ),
), ),
if (Platform.isAndroid) if (Platform.isAndroid || Utils.isDesktop)
SizedBox( SizedBox(
width: 42, width: 42,
height: 34, height: 34,
@@ -2133,6 +2135,10 @@ class HeaderControlState extends TripleState<HeaderControl> {
padding: WidgetStatePropertyAll(EdgeInsets.zero), padding: WidgetStatePropertyAll(EdgeInsets.zero),
), ),
onPressed: () async { onPressed: () async {
if (Utils.isDesktop) {
plPlayerController.toggleDesktopPip();
return;
}
bool canUsePiP = await Floating().isPipAvailable; bool canUsePiP = await Floating().isPipAvailable;
plPlayerController.hiddenControls(false); plPlayerController.hiddenControls(false);
if (canUsePiP) { if (canUsePiP) {

View File

@@ -6,6 +6,7 @@ import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart';
import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart';
import 'package:PiliPlus/utils/storage.dart'; import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart'; import 'package:PiliPlus/utils/storage_key.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart' import 'package:flutter/services.dart'
show KeyDownEvent, KeyUpEvent, LogicalKeyboardKey; show KeyDownEvent, KeyUpEvent, LogicalKeyboardKey;
@@ -71,7 +72,9 @@ class PlayerFocus extends StatelessWidget {
return true; return true;
case LogicalKeyboardKey.escape: case LogicalKeyboardKey.escape:
if (isFullScreen) { if (plPlayerController.isDesktopPip) {
plPlayerController.exitDesktopPip();
} else if (isFullScreen) {
plPlayerController.triggerFullScreen(status: false); plPlayerController.triggerFullScreen(status: false);
} else { } else {
Get.back(); Get.back();
@@ -94,6 +97,10 @@ class PlayerFocus extends StatelessWidget {
} }
return true; return true;
case LogicalKeyboardKey.keyP when (Utils.isDesktop && hasPlayer):
plPlayerController.toggleDesktopPip();
return true;
case LogicalKeyboardKey.arrowUp: case LogicalKeyboardKey.arrowUp:
if (hasPlayer) { if (hasPlayer) {
final volume = math.min(1.0, plPlayerController.volume.value + 0.1); final volume = math.min(1.0, plPlayerController.volume.value + 0.1);

View File

@@ -41,6 +41,7 @@ import 'package:canvas_danmaku/canvas_danmaku.dart';
import 'package:crclib/catalog.dart'; import 'package:crclib/catalog.dart';
import 'package:dio/dio.dart' show Options; import 'package:dio/dio.dart' show Options;
import 'package:easy_debounce/easy_throttle.dart'; import 'package:easy_debounce/easy_throttle.dart';
import 'package:floating/floating.dart';
import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.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/path.dart' as path;
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:universal_platform/universal_platform.dart'; import 'package:universal_platform/universal_platform.dart';
import 'package:window_manager/window_manager.dart';
class PlPlayerController { class PlPlayerController {
Player? _videoPlayerController; Player? _videoPlayerController;
@@ -253,6 +255,51 @@ class PlPlayerController {
late final RxBool enableShowLiveDanmaku = Pref.enableShowLiveDanmaku.obs; late final RxBool enableShowLiveDanmaku = Pref.enableShowLiveDanmaku.obs;
late final bool autoPiP = Pref.autoPiP; 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<void> 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() { void enterPip() {
if (Get.currentRoute.startsWith('/video')) { if (Get.currentRoute.startsWith('/video')) {

View File

@@ -59,6 +59,7 @@ import 'package:get/get.dart' hide ContextExtensionss;
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:media_kit_video/media_kit_video.dart'; import 'package:media_kit_video/media_kit_video.dart';
import 'package:screen_brightness_platform_interface/screen_brightness_platform_interface.dart'; import 'package:screen_brightness_platform_interface/screen_brightness_platform_interface.dart';
import 'package:window_manager/window_manager.dart';
class PLVideoPlayer extends StatefulWidget { class PLVideoPlayer extends StatefulWidget {
const PLVideoPlayer({ const PLVideoPlayer({
@@ -749,16 +750,17 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
], ],
]; ];
final flag = isFullScreen || plPlayerController.isDesktopPip;
List<BottomControlType> userSpecifyItemRight = [ List<BottomControlType> userSpecifyItemRight = [
if (plPlayerController.showDmChart) BottomControlType.dmChart, if (plPlayerController.showDmChart) BottomControlType.dmChart,
if (plPlayerController.isAnim) BottomControlType.superResolution, if (plPlayerController.isAnim) BottomControlType.superResolution,
if (plPlayerController.showViewPoints) BottomControlType.viewPoints, if (plPlayerController.showViewPoints) BottomControlType.viewPoints,
if (anySeason) BottomControlType.episode, if (anySeason) BottomControlType.episode,
if (isFullScreen) BottomControlType.fit, if (flag) BottomControlType.fit,
BottomControlType.aiTranslate, BottomControlType.aiTranslate,
BottomControlType.subtitle, BottomControlType.subtitle,
BottomControlType.speed, BottomControlType.speed,
if (isFullScreen) BottomControlType.qa, if (flag) BottomControlType.qa,
BottomControlType.fullscreen, BottomControlType.fullscreen,
]; ];
@@ -1308,7 +1310,13 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
isTop: true, isTop: true,
controller: animationController, controller: animationController,
isFullScreen: isFullScreen, isFullScreen: isFullScreen,
child: widget.headerControl, child: plPlayerController.isDesktopPip
? GestureDetector(
behavior: HitTestBehavior.translucent,
onPanStart: (_) => windowManager.startDragging(),
child: widget.headerControl,
)
: widget.headerControl,
), ),
AppBarAni( AppBarAni(
isTop: false, isTop: false,