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: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<LiveRoomPage>
@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(

View File

@@ -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) {

View File

@@ -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<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 bool isPortrait;
late double maxWidth;
@@ -1473,14 +1464,16 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
@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(

View File

@@ -1925,7 +1925,9 @@ class HeaderControlState extends TripleState<HeaderControl> {
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<HeaderControl> {
},
),
),
if (Platform.isAndroid)
if (Platform.isAndroid || Utils.isDesktop)
SizedBox(
width: 42,
height: 34,
@@ -2133,6 +2135,10 @@ class HeaderControlState extends TripleState<HeaderControl> {
padding: WidgetStatePropertyAll(EdgeInsets.zero),
),
onPressed: () async {
if (Utils.isDesktop) {
plPlayerController.toggleDesktopPip();
return;
}
bool canUsePiP = await Floating().isPipAvailable;
plPlayerController.hiddenControls(false);
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/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);

View File

@@ -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<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() {
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: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<PLVideoPlayer>
],
];
final flag = isFullScreen || plPlayerController.isDesktopPip;
List<BottomControlType> 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<PLVideoPlayer>
isTop: true,
controller: animationController,
isFullScreen: isFullScreen,
child: plPlayerController.isDesktopPip
? GestureDetector(
behavior: HitTestBehavior.translucent,
onPanStart: (_) => windowManager.startDragging(),
child: widget.headerControl,
)
: widget.headerControl,
),
AppBarAni(
isTop: false,