mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
desktop pip
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -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(
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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 = [];
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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')) {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user