mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-08-06 10:40:09 +08:00
feat: drag subtitle
Closes #588 Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -2098,6 +2098,13 @@ List<SettingsModel> get extraSettings => [
|
|||||||
setKey: SettingBoxKey.showDynActionBar,
|
setKey: SettingBoxKey.showDynActionBar,
|
||||||
defaultVal: true,
|
defaultVal: true,
|
||||||
),
|
),
|
||||||
|
SettingsModel(
|
||||||
|
settingsType: SettingsType.sw1tch,
|
||||||
|
title: '启用拖拽字幕调整底部边距',
|
||||||
|
leading: Icon(MdiIcons.dragVariant),
|
||||||
|
setKey: SettingBoxKey.enableDragSubtitle,
|
||||||
|
defaultVal: false,
|
||||||
|
),
|
||||||
SettingsModel(
|
SettingsModel(
|
||||||
settingsType: SettingsType.sw1tch,
|
settingsType: SettingsType.sw1tch,
|
||||||
enableFeedback: true,
|
enableFeedback: true,
|
||||||
|
|||||||
@@ -1238,9 +1238,9 @@ class HeaderControlState extends State<HeaderControl> {
|
|||||||
data: sliderTheme,
|
data: sliderTheme,
|
||||||
child: Slider(
|
child: Slider(
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 100,
|
max: 200,
|
||||||
value: subtitlePaddingB.toDouble(),
|
value: subtitlePaddingB.toDouble(),
|
||||||
divisions: 100,
|
divisions: 200,
|
||||||
label: '$subtitlePaddingB',
|
label: '$subtitlePaddingB',
|
||||||
onChanged: (double val) {
|
onChanged: (double val) {
|
||||||
updateBottomPadding(val.round());
|
updateBottomPadding(val.round());
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ class PlPlayerController {
|
|||||||
late final darkVideoPage = GStorage.darkVideoPage;
|
late final darkVideoPage = GStorage.darkVideoPage;
|
||||||
late final enableSlideVolumeBrightness = GStorage.enableSlideVolumeBrightness;
|
late final enableSlideVolumeBrightness = GStorage.enableSlideVolumeBrightness;
|
||||||
late final enableSlideFS = GStorage.enableSlideFS;
|
late final enableSlideFS = GStorage.enableSlideFS;
|
||||||
|
late final enableDragSubtitle = GStorage.enableDragSubtitle;
|
||||||
|
|
||||||
/// 弹幕权重
|
/// 弹幕权重
|
||||||
int danmakuWeight = 0;
|
int danmakuWeight = 0;
|
||||||
@@ -324,6 +325,11 @@ class PlPlayerController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onUpdatePadding(EdgeInsets padding) {
|
||||||
|
subtitlePaddingB = padding.bottom.round().clamp(0, 200);
|
||||||
|
putSubtitleSettings();
|
||||||
|
}
|
||||||
|
|
||||||
void updateSliderPositionSecond() {
|
void updateSliderPositionSecond() {
|
||||||
int newSecond = _sliderPosition.value.inSeconds;
|
int newSecond = _sliderPosition.value.inSeconds;
|
||||||
if (sliderPositionSeconds.value != newSecond) {
|
if (sliderPositionSeconds.value != newSecond) {
|
||||||
|
|||||||
@@ -949,6 +949,132 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
},
|
},
|
||||||
flipX: plPlayerController.flipX.value,
|
flipX: plPlayerController.flipX.value,
|
||||||
flipY: plPlayerController.flipY.value,
|
flipY: plPlayerController.flipY.value,
|
||||||
|
onVerticalDragStart: (details) {
|
||||||
|
if (plPlayerController.controlsLock.value) return;
|
||||||
|
if (details.localPosition.dy < 40) return;
|
||||||
|
if (details.localPosition.dx < 40) return;
|
||||||
|
if (details.localPosition.dx > context.width - 40) return;
|
||||||
|
if (details.localPosition.dy > context.height - 40) return;
|
||||||
|
_initialFocalPoint = details.localPosition;
|
||||||
|
_gestureType = null;
|
||||||
|
},
|
||||||
|
onVerticalDragUpdate: (details) {
|
||||||
|
if (plPlayerController.controlsLock.value) return;
|
||||||
|
if (plPlayerController.enableSlideVolumeBrightness.not &&
|
||||||
|
plPlayerController.enableSlideFS.not) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RenderBox renderBox =
|
||||||
|
_playerKey.currentContext!.findRenderObject() as RenderBox;
|
||||||
|
final double totalWidth = renderBox.size.width;
|
||||||
|
final double tapPosition = details.localPosition.dx;
|
||||||
|
final double sectionWidth = totalWidth / 3;
|
||||||
|
late String gestureType;
|
||||||
|
if (tapPosition < sectionWidth) {
|
||||||
|
if (plPlayerController.enableSlideVolumeBrightness.not) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 左边区域
|
||||||
|
gestureType = 'left';
|
||||||
|
} else if (tapPosition < sectionWidth * 2) {
|
||||||
|
if (plPlayerController.enableSlideFS.not) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 全屏
|
||||||
|
gestureType = 'center';
|
||||||
|
} else {
|
||||||
|
if (plPlayerController.enableSlideVolumeBrightness.not) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 右边区域
|
||||||
|
gestureType = 'right';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_gestureType != null && _gestureType != gestureType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_gestureType = gestureType;
|
||||||
|
|
||||||
|
if (_gestureType == 'left') {
|
||||||
|
// 左边区域 👈
|
||||||
|
final double level = renderBox.size.height * 3;
|
||||||
|
final double brightness =
|
||||||
|
_brightnessValue.value - details.delta.dy / level;
|
||||||
|
final double result = brightness.clamp(0.0, 1.0);
|
||||||
|
setBrightness(result);
|
||||||
|
} else if (_gestureType == 'center') {
|
||||||
|
// 全屏
|
||||||
|
const double threshold = 2.5; // 滑动阈值
|
||||||
|
double cumulativeDy =
|
||||||
|
details.localPosition.dy - _initialFocalPoint.dy;
|
||||||
|
|
||||||
|
void fullScreenTrigger(bool status) {
|
||||||
|
plPlayerController.triggerFullScreen(status: status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cumulativeDy > threshold) {
|
||||||
|
_gestureType = 'center_down';
|
||||||
|
if (isFullScreen ^ fullScreenGestureReverse) {
|
||||||
|
fullScreenTrigger(fullScreenGestureReverse);
|
||||||
|
}
|
||||||
|
// debugPrint('center_down:$cumulativeDy');
|
||||||
|
} else if (cumulativeDy < -threshold) {
|
||||||
|
_gestureType = 'center_up';
|
||||||
|
if (!isFullScreen ^ fullScreenGestureReverse) {
|
||||||
|
fullScreenTrigger(!fullScreenGestureReverse);
|
||||||
|
}
|
||||||
|
// debugPrint('center_up:$cumulativeDy');
|
||||||
|
}
|
||||||
|
} else if (_gestureType == 'right') {
|
||||||
|
// 右边区域
|
||||||
|
final double level = renderBox.size.height * 0.5;
|
||||||
|
EasyThrottle.throttle(
|
||||||
|
'setVolume', const Duration(milliseconds: 20), () {
|
||||||
|
final double volume =
|
||||||
|
_volumeValue.value - details.delta.dy / level;
|
||||||
|
final double result = volume.clamp(0.0, 1.0);
|
||||||
|
setVolume(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onVerticalDragEnd: (details) {
|
||||||
|
interacting = false;
|
||||||
|
_initialFocalPoint = Offset.zero;
|
||||||
|
_gestureType = null;
|
||||||
|
},
|
||||||
|
onTap: () {
|
||||||
|
plPlayerController.controls =
|
||||||
|
!plPlayerController.showControls.value;
|
||||||
|
},
|
||||||
|
onDoubleTapDown: (TapDownDetails details) {
|
||||||
|
// live模式下禁用 锁定时🔒禁用
|
||||||
|
if (plPlayerController.videoType.value == 'live' ||
|
||||||
|
plPlayerController.controlsLock.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RenderBox renderBox =
|
||||||
|
_playerKey.currentContext!.findRenderObject() as RenderBox;
|
||||||
|
final double totalWidth = renderBox.size.width;
|
||||||
|
final double tapPosition = details.localPosition.dx;
|
||||||
|
final double sectionWidth = totalWidth / 4;
|
||||||
|
String type = 'left';
|
||||||
|
if (tapPosition < sectionWidth) {
|
||||||
|
type = 'left';
|
||||||
|
} else if (tapPosition < sectionWidth * 3) {
|
||||||
|
type = 'center';
|
||||||
|
} else {
|
||||||
|
type = 'right';
|
||||||
|
}
|
||||||
|
doubleTapFuc(type);
|
||||||
|
},
|
||||||
|
onLongPressStart: (LongPressStartDetails detail) {
|
||||||
|
plPlayerController.setLongPressStatus(true);
|
||||||
|
},
|
||||||
|
onLongPressEnd: (LongPressEndDetails details) {
|
||||||
|
plPlayerController.setLongPressStatus(false);
|
||||||
|
},
|
||||||
|
enableDragSubtitle: plPlayerController.enableDragSubtitle,
|
||||||
|
onUpdatePadding: plPlayerController.onUpdatePadding,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -1155,143 +1281,6 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
|||||||
// }
|
// }
|
||||||
// }),
|
// }),
|
||||||
|
|
||||||
/// 手势
|
|
||||||
Positioned.fill(
|
|
||||||
left: 16,
|
|
||||||
top: 25,
|
|
||||||
right: 15,
|
|
||||||
bottom: 15,
|
|
||||||
child: Semantics(
|
|
||||||
label: '双击开关控件',
|
|
||||||
child: GestureDetector(
|
|
||||||
onVerticalDragStart: (details) {
|
|
||||||
if (plPlayerController.controlsLock.value) return;
|
|
||||||
if (details.localPosition.dy < 40) return;
|
|
||||||
if (details.localPosition.dx < 40) return;
|
|
||||||
if (details.localPosition.dx > context.width - 40) return;
|
|
||||||
if (details.localPosition.dy > context.height - 40) return;
|
|
||||||
_initialFocalPoint = details.localPosition;
|
|
||||||
_gestureType = null;
|
|
||||||
},
|
|
||||||
onVerticalDragUpdate: (details) {
|
|
||||||
if (plPlayerController.controlsLock.value) return;
|
|
||||||
if (plPlayerController.enableSlideVolumeBrightness.not &&
|
|
||||||
plPlayerController.enableSlideFS.not) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
RenderBox renderBox =
|
|
||||||
_playerKey.currentContext!.findRenderObject() as RenderBox;
|
|
||||||
final double totalWidth = renderBox.size.width;
|
|
||||||
final double tapPosition = details.localPosition.dx;
|
|
||||||
final double sectionWidth = totalWidth / 3;
|
|
||||||
late String gestureType;
|
|
||||||
if (tapPosition < sectionWidth) {
|
|
||||||
if (plPlayerController.enableSlideVolumeBrightness.not) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 左边区域
|
|
||||||
gestureType = 'left';
|
|
||||||
} else if (tapPosition < sectionWidth * 2) {
|
|
||||||
if (plPlayerController.enableSlideFS.not) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 全屏
|
|
||||||
gestureType = 'center';
|
|
||||||
} else {
|
|
||||||
if (plPlayerController.enableSlideVolumeBrightness.not) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 右边区域
|
|
||||||
gestureType = 'right';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_gestureType != null && _gestureType != gestureType) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_gestureType = gestureType;
|
|
||||||
|
|
||||||
if (_gestureType == 'left') {
|
|
||||||
// 左边区域 👈
|
|
||||||
final double level = renderBox.size.height * 3;
|
|
||||||
final double brightness =
|
|
||||||
_brightnessValue.value - details.delta.dy / level;
|
|
||||||
final double result = brightness.clamp(0.0, 1.0);
|
|
||||||
setBrightness(result);
|
|
||||||
} else if (_gestureType == 'center') {
|
|
||||||
// 全屏
|
|
||||||
const double threshold = 2.5; // 滑动阈值
|
|
||||||
double cumulativeDy =
|
|
||||||
details.localPosition.dy - _initialFocalPoint.dy;
|
|
||||||
|
|
||||||
void fullScreenTrigger(bool status) {
|
|
||||||
plPlayerController.triggerFullScreen(status: status);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cumulativeDy > threshold) {
|
|
||||||
_gestureType = 'center_down';
|
|
||||||
if (isFullScreen ^ fullScreenGestureReverse) {
|
|
||||||
fullScreenTrigger(fullScreenGestureReverse);
|
|
||||||
}
|
|
||||||
// debugPrint('center_down:$cumulativeDy');
|
|
||||||
} else if (cumulativeDy < -threshold) {
|
|
||||||
_gestureType = 'center_up';
|
|
||||||
if (!isFullScreen ^ fullScreenGestureReverse) {
|
|
||||||
fullScreenTrigger(!fullScreenGestureReverse);
|
|
||||||
}
|
|
||||||
// debugPrint('center_up:$cumulativeDy');
|
|
||||||
}
|
|
||||||
} else if (_gestureType == 'right') {
|
|
||||||
// 右边区域
|
|
||||||
final double level = renderBox.size.height * 0.5;
|
|
||||||
EasyThrottle.throttle(
|
|
||||||
'setVolume', const Duration(milliseconds: 20), () {
|
|
||||||
final double volume =
|
|
||||||
_volumeValue.value - details.delta.dy / level;
|
|
||||||
final double result = volume.clamp(0.0, 1.0);
|
|
||||||
setVolume(result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onVerticalDragEnd: (details) {
|
|
||||||
interacting = false;
|
|
||||||
_initialFocalPoint = Offset.zero;
|
|
||||||
_gestureType = null;
|
|
||||||
},
|
|
||||||
onTap: () {
|
|
||||||
plPlayerController.controls =
|
|
||||||
!plPlayerController.showControls.value;
|
|
||||||
},
|
|
||||||
onDoubleTapDown: (TapDownDetails details) {
|
|
||||||
// live模式下禁用 锁定时🔒禁用
|
|
||||||
if (plPlayerController.videoType.value == 'live' ||
|
|
||||||
plPlayerController.controlsLock.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
RenderBox renderBox =
|
|
||||||
_playerKey.currentContext!.findRenderObject() as RenderBox;
|
|
||||||
final double totalWidth = renderBox.size.width;
|
|
||||||
final double tapPosition = details.localPosition.dx;
|
|
||||||
final double sectionWidth = totalWidth / 4;
|
|
||||||
String type = 'left';
|
|
||||||
if (tapPosition < sectionWidth) {
|
|
||||||
type = 'left';
|
|
||||||
} else if (tapPosition < sectionWidth * 3) {
|
|
||||||
type = 'center';
|
|
||||||
} else {
|
|
||||||
type = 'right';
|
|
||||||
}
|
|
||||||
doubleTapFuc(type);
|
|
||||||
},
|
|
||||||
onLongPressStart: (LongPressStartDetails detail) {
|
|
||||||
plPlayerController.setLongPressStatus(true);
|
|
||||||
},
|
|
||||||
onLongPressEnd: (LongPressEndDetails details) {
|
|
||||||
plPlayerController.setLongPressStatus(false);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// 头部、底部控制条
|
// 头部、底部控制条
|
||||||
Obx(
|
Obx(
|
||||||
() => Positioned.fill(
|
() => Positioned.fill(
|
||||||
|
|||||||
@@ -436,6 +436,9 @@ class GStorage {
|
|||||||
static int get appFontWeight =>
|
static int get appFontWeight =>
|
||||||
GStorage.setting.get(SettingBoxKey.appFontWeight, defaultValue: -1);
|
GStorage.setting.get(SettingBoxKey.appFontWeight, defaultValue: -1);
|
||||||
|
|
||||||
|
static bool get enableDragSubtitle => GStorage.setting
|
||||||
|
.get(SettingBoxKey.enableDragSubtitle, defaultValue: false);
|
||||||
|
|
||||||
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
static List<double> get dynamicDetailRatio => List<double>.from(setting
|
||||||
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
||||||
|
|
||||||
@@ -716,6 +719,7 @@ class SettingBoxKey {
|
|||||||
liveQuality = 'liveQuality',
|
liveQuality = 'liveQuality',
|
||||||
liveQualityCellular = 'liveQualityCellular',
|
liveQualityCellular = 'liveQualityCellular',
|
||||||
appFontWeight = 'appFontWeight',
|
appFontWeight = 'appFontWeight',
|
||||||
|
enableDragSubtitle = 'enableDragSubtitle',
|
||||||
|
|
||||||
// Sponsor Block
|
// Sponsor Block
|
||||||
enableSponsorBlock = 'enableSponsorBlock',
|
enableSponsorBlock = 'enableSponsorBlock',
|
||||||
|
|||||||
@@ -1212,7 +1212,7 @@ packages:
|
|||||||
description:
|
description:
|
||||||
path: media_kit_video
|
path: media_kit_video
|
||||||
ref: "version_1.2.5"
|
ref: "version_1.2.5"
|
||||||
resolved-ref: "134abc2548eea0f8d31a99964aed51ff03a91a5d"
|
resolved-ref: a74d1af2bcc6b5c88b2216c594e7e3eba0c7cee5
|
||||||
url: "https://github.com/bggRGjQaUbCoE/media-kit.git"
|
url: "https://github.com/bggRGjQaUbCoE/media-kit.git"
|
||||||
source: git
|
source: git
|
||||||
version: "1.2.5"
|
version: "1.2.5"
|
||||||
|
|||||||
Reference in New Issue
Block a user