Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-01-20 17:42:40 +08:00
parent 923af32c96
commit 0b1f6c4d0e
26 changed files with 377 additions and 451 deletions

View File

@@ -6,6 +6,7 @@ import 'dart:ui' as ui;
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/cropped_image.dart';
import 'package:PiliPlus/common/widgets/custom_icon.dart';
import 'package:PiliPlus/common/widgets/disabled_icon.dart';
import 'package:PiliPlus/common/widgets/gesture/immediate_tap_gesture_recognizer.dart';
import 'package:PiliPlus/common/widgets/gesture/mouse_interactive_viewer.dart';
import 'package:PiliPlus/common/widgets/loading_widget.dart';
@@ -414,34 +415,20 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
() {
final list = videoDetailController.dmTrend.value?.dataOrNull;
if (list != null && list.isNotEmpty) {
final show = videoDetailController.showDmTrendChart.value;
return ComBtn(
width: widgetWidth,
height: 30,
tooltip: '高能进度条',
icon: videoDetailController.showDmTrendChart.value
? const Icon(
Icons.show_chart,
size: 22,
color: Colors.white,
)
: const Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
Icon(
Icons.show_chart,
size: 22,
color: Colors.white,
),
Icon(
Icons.hide_source,
size: 22,
color: Colors.white,
),
],
),
onTap: () => videoDetailController.showDmTrendChart.value =
!videoDetailController.showDmTrendChart.value,
icon: DisabledIcon(
disable: !show,
child: const Icon(
Icons.show_chart,
size: 22,
color: Colors.white,
),
),
onTap: () => videoDetailController.showDmTrendChart.value = !show,
);
}
return const SizedBox.shrink();
@@ -450,64 +437,76 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
/// 超分辨率
BottomControlType.superResolution => Obx(
() => PopupMenuButton<SuperResolutionType>(
tooltip: '超分辨率',
requestFocus: false,
initialValue: plPlayerController.superResolutionType.value,
color: Colors.black.withValues(alpha: 0.8),
itemBuilder: (context) {
return SuperResolutionType.values
.map(
(type) => PopupMenuItem<SuperResolutionType>(
height: 35,
padding: const EdgeInsets.only(left: 30),
value: type,
onTap: () => plPlayerController.setShader(type),
child: Text(
type.title,
style: const TextStyle(color: Colors.white, fontSize: 13),
() {
final type = plPlayerController.superResolutionType.value;
return PopupMenuButton<SuperResolutionType>(
tooltip: '超分辨率',
requestFocus: false,
initialValue: type,
color: Colors.black.withValues(alpha: 0.8),
itemBuilder: (context) {
return SuperResolutionType.values
.map(
(type) => PopupMenuItem<SuperResolutionType>(
height: 35,
padding: const EdgeInsets.only(left: 30),
value: type,
onTap: () => plPlayerController.setShader(type),
child: Text(
type.title,
style: const TextStyle(
color: Colors.white,
fontSize: 13,
),
),
),
),
)
.toList();
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
plPlayerController.superResolutionType.value.title,
style: const TextStyle(color: Colors.white, fontSize: 13),
)
.toList();
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
type.title,
style: const TextStyle(color: Colors.white, fontSize: 13),
),
),
),
),
);
},
),
/// 分段信息
BottomControlType.viewPoints => Obx(
() => videoDetailController.viewPointList.isEmpty
? const SizedBox.shrink()
: ComBtn(
width: widgetWidth,
height: 30,
tooltip: '分段信息',
icon: Transform.rotate(
() {
if (videoDetailController.viewPointList.isNotEmpty) {
final show = videoDetailController.showVP.value;
return ComBtn(
width: widgetWidth,
height: 30,
tooltip: '分段信息',
icon: DisabledIcon(
iconSize: 22,
disable: !show,
child: Transform.rotate(
angle: math.pi / 2,
child: const Icon(
MdiIcons.viewHeadline,
Icons.reorder,
size: 22,
color: Colors.white,
),
),
onTap: widget.showViewPoints,
onLongPress: () {
Feedback.forLongPress(context);
videoDetailController.showVP.value =
!videoDetailController.showVP.value;
},
onSecondaryTap: PlatformUtils.isMobile
? null
: () => videoDetailController.showVP.value =
!videoDetailController.showVP.value,
),
onTap: widget.showViewPoints,
onLongPress: () {
Feedback.forLongPress(context);
videoDetailController.showVP.value = !show;
},
onSecondaryTap: PlatformUtils.isMobile
? null
: () => videoDetailController.showVP.value = !show,
);
}
return const SizedBox.shrink();
},
),
/// 选集
@@ -567,35 +566,41 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
/// 画面比例
BottomControlType.fit => Obx(
() => PopupMenuButton<VideoFitType>(
tooltip: '画面比例',
requestFocus: false,
initialValue: plPlayerController.videoFit.value,
color: Colors.black.withValues(alpha: 0.8),
itemBuilder: (context) {
return VideoFitType.values
.map(
(boxFit) => PopupMenuItem<VideoFitType>(
height: 35,
padding: const EdgeInsets.only(left: 30),
value: boxFit,
onTap: () => plPlayerController.toggleVideoFit(boxFit),
child: Text(
boxFit.desc,
style: const TextStyle(color: Colors.white, fontSize: 13),
() {
final fit = plPlayerController.videoFit.value;
return PopupMenuButton<VideoFitType>(
tooltip: '画面比例',
requestFocus: false,
initialValue: fit,
color: Colors.black.withValues(alpha: 0.8),
itemBuilder: (context) {
return VideoFitType.values
.map(
(boxFit) => PopupMenuItem<VideoFitType>(
height: 35,
padding: const EdgeInsets.only(left: 30),
value: boxFit,
onTap: () => plPlayerController.toggleVideoFit(boxFit),
child: Text(
boxFit.desc,
style: const TextStyle(
color: Colors.white,
fontSize: 13,
),
),
),
),
)
.toList();
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
plPlayerController.videoFit.value.desc,
style: const TextStyle(color: Colors.white, fontSize: 13),
)
.toList();
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
fit.desc,
style: const TextStyle(color: Colors.white, fontSize: 13),
),
),
),
),
);
},
),
BottomControlType.aiTranslate => Obx(
@@ -654,66 +659,65 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
/// 字幕
BottomControlType.subtitle => Obx(
() => videoDetailController.subtitles.isEmpty
? const SizedBox.shrink()
: PopupMenuButton<int>(
tooltip: '字幕',
requestFocus: false,
initialValue: videoDetailController.vttSubtitlesIndex.value
.clamp(
0,
videoDetailController.subtitles.length,
() {
if (videoDetailController.subtitles.isNotEmpty) {
final val = videoDetailController.vttSubtitlesIndex.value;
return PopupMenuButton<int>(
tooltip: '字幕',
requestFocus: false,
initialValue: val,
color: Colors.black.withValues(alpha: 0.8),
itemBuilder: (context) {
return [
PopupMenuItem<int>(
value: 0,
height: 35,
onTap: () => videoDetailController.setSubtitle(0),
child: const Text(
"关闭字幕",
style: TextStyle(
color: Colors.white,
fontSize: 13,
),
),
color: Colors.black.withValues(alpha: 0.8),
itemBuilder: (context) {
return [
PopupMenuItem<int>(
value: 0,
),
...videoDetailController.subtitles.indexed.map((e) {
return PopupMenuItem<int>(
value: e.$1 + 1,
height: 35,
onTap: () => videoDetailController.setSubtitle(0),
child: const Text(
"关闭字幕",
style: TextStyle(
onTap: () => videoDetailController.setSubtitle(e.$1 + 1),
child: Text(
"${e.$2.lanDoc}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white,
fontSize: 13,
),
),
),
...videoDetailController.subtitles.indexed.map((e) {
return PopupMenuItem<int>(
value: e.$1 + 1,
height: 35,
onTap: () =>
videoDetailController.setSubtitle(e.$1 + 1),
child: Text(
"${e.$2.lanDoc}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white,
fontSize: 13,
),
),
);
}),
];
},
child: SizedBox(
width: widgetWidth,
height: 30,
child: videoDetailController.vttSubtitlesIndex.value == 0
? const Icon(
Icons.closed_caption_off_outlined,
size: 22,
color: Colors.white,
)
: const Icon(
Icons.closed_caption_off_rounded,
size: 22,
color: Colors.white,
),
),
);
}),
];
},
child: SizedBox(
width: widgetWidth,
height: 30,
child: val == 0
? const Icon(
Icons.closed_caption_off_outlined,
size: 22,
color: Colors.white,
)
: const Icon(
Icons.closed_caption_off_rounded,
size: 22,
color: Colors.white,
),
),
);
}
return const SizedBox.shrink();
},
),
/// 播放速度
@@ -1701,15 +1705,12 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
vsync: this,
duration: const Duration(milliseconds: 255),
);
final anim =
Matrix4Tween(
begin: transformationController.value,
end: Matrix4.identity(),
).animate(
CurveTween(
curve: Curves.easeOut,
).animate(animController),
);
final anim = animController.drive(
Matrix4Tween(
begin: transformationController.value,
end: Matrix4.identity(),
).chain(CurveTween(curve: Curves.easeOut)),
);
void listener() {
transformationController.value = anim.value;
}
@@ -2748,17 +2749,20 @@ class _DanmakuTip extends SingleChildRenderObjectWidget {
@override
RenderObject createRenderObject(BuildContext context) {
return RenderDanmakuTip(offset: offset);
return _RenderDanmakuTip(offset: offset);
}
@override
void updateRenderObject(BuildContext context, RenderDanmakuTip renderObject) {
void updateRenderObject(
BuildContext context,
_RenderDanmakuTip renderObject,
) {
renderObject.offset = offset;
}
}
class RenderDanmakuTip extends RenderProxyBox {
RenderDanmakuTip({
class _RenderDanmakuTip extends RenderProxyBox {
_RenderDanmakuTip({
required double offset,
}) : _offset = offset;

View File

@@ -16,7 +16,7 @@ class AppBarAni extends StatelessWidget {
final bool isFullScreen;
static final _topPos = Tween<Offset>(
begin: const Offset(0, -1),
begin: const Offset(0.0, -1.0),
end: Offset.zero,
);
@@ -49,8 +49,8 @@ class AppBarAni extends StatelessWidget {
Widget build(BuildContext context) {
return SlideTransition(
position: isTop
? _topPos.animate(controller)
: _bottomPos.animate(controller),
? controller.drive(_topPos)
: controller.drive(_bottomPos),
child: DecoratedBox(
decoration: BoxDecoration(
gradient: isTop ? _topDecoration : _bottomDecoration,