mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +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,
|
||||
defaultVal: true,
|
||||
),
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.sw1tch,
|
||||
title: '启用拖拽字幕调整底部边距',
|
||||
leading: Icon(MdiIcons.dragVariant),
|
||||
setKey: SettingBoxKey.enableDragSubtitle,
|
||||
defaultVal: false,
|
||||
),
|
||||
SettingsModel(
|
||||
settingsType: SettingsType.sw1tch,
|
||||
enableFeedback: true,
|
||||
|
||||
@@ -1238,9 +1238,9 @@ class HeaderControlState extends State<HeaderControl> {
|
||||
data: sliderTheme,
|
||||
child: Slider(
|
||||
min: 0,
|
||||
max: 100,
|
||||
max: 200,
|
||||
value: subtitlePaddingB.toDouble(),
|
||||
divisions: 100,
|
||||
divisions: 200,
|
||||
label: '$subtitlePaddingB',
|
||||
onChanged: (double val) {
|
||||
updateBottomPadding(val.round());
|
||||
|
||||
@@ -256,6 +256,7 @@ class PlPlayerController {
|
||||
late final darkVideoPage = GStorage.darkVideoPage;
|
||||
late final enableSlideVolumeBrightness = GStorage.enableSlideVolumeBrightness;
|
||||
late final enableSlideFS = GStorage.enableSlideFS;
|
||||
late final enableDragSubtitle = GStorage.enableDragSubtitle;
|
||||
|
||||
/// 弹幕权重
|
||||
int danmakuWeight = 0;
|
||||
@@ -324,6 +325,11 @@ class PlPlayerController {
|
||||
);
|
||||
}
|
||||
|
||||
void onUpdatePadding(EdgeInsets padding) {
|
||||
subtitlePaddingB = padding.bottom.round().clamp(0, 200);
|
||||
putSubtitleSettings();
|
||||
}
|
||||
|
||||
void updateSliderPositionSecond() {
|
||||
int newSecond = _sliderPosition.value.inSeconds;
|
||||
if (sliderPositionSeconds.value != newSecond) {
|
||||
|
||||
@@ -949,6 +949,132 @@ class _PLVideoPlayerState extends State<PLVideoPlayer>
|
||||
},
|
||||
flipX: plPlayerController.flipX.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(
|
||||
() => Positioned.fill(
|
||||
|
||||
@@ -436,6 +436,9 @@ class GStorage {
|
||||
static int get appFontWeight =>
|
||||
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
|
||||
.get(SettingBoxKey.dynamicDetailRatio, defaultValue: [60.0, 40.0]));
|
||||
|
||||
@@ -716,6 +719,7 @@ class SettingBoxKey {
|
||||
liveQuality = 'liveQuality',
|
||||
liveQualityCellular = 'liveQualityCellular',
|
||||
appFontWeight = 'appFontWeight',
|
||||
enableDragSubtitle = 'enableDragSubtitle',
|
||||
|
||||
// Sponsor Block
|
||||
enableSponsorBlock = 'enableSponsorBlock',
|
||||
|
||||
Reference in New Issue
Block a user