feat(video): add custom zoom controls

close #1159
This commit is contained in:
edde746
2026-05-29 00:06:47 +02:00
parent 47276e1bac
commit 2b7ea5fe02
52 changed files with 1123 additions and 106 deletions
@@ -1,6 +1,8 @@
part of '../video_controls.dart';
extension _PlexVideoControlsPlaybackInputMethods on _PlexVideoControlsState {
static const Duration _touchTapSuppressionPadding = Duration(milliseconds: 80);
void _onRateChanged(double newRate) {
if (!mounted) return;
if (_isLongPressing) return;
@@ -95,9 +97,52 @@ extension _PlexVideoControlsPlaybackInputMethods on _PlexVideoControlsState {
unawaited(_seekToPosition(position));
}
bool get _isTouchTapSuppressed {
final until = _suppressTouchTapUntil;
if (until == null) return false;
if (DateTime.now().isAfter(until)) {
_suppressTouchTapUntil = null;
return false;
}
return true;
}
void _suppressTouchTaps() {
_singleTapTimer?.cancel();
_singleTapTimer = null;
_suppressTouchTapUntil = DateTime.now().add(kDoubleTapTimeout + _touchTapSuppressionPadding);
}
void _handleTouchPointerDown(PointerDownEvent event) {
if (event.kind != PointerDeviceKind.touch) return;
_twoFingerDoubleTapTracker.pointerDown(event.pointer, event.position);
if (_twoFingerDoubleTapTracker.isChordActive) _suppressTouchTaps();
}
void _handleTouchPointerMove(PointerMoveEvent event) {
if (event.kind != PointerDeviceKind.touch) return;
_twoFingerDoubleTapTracker.pointerMove(event.pointer, event.position);
if (_twoFingerDoubleTapTracker.isChordActive) _suppressTouchTaps();
}
void _handleTouchPointerUp(PointerUpEvent event) {
if (event.kind != PointerDeviceKind.touch) return;
final isResetGesture = _twoFingerDoubleTapTracker.pointerUp(event.pointer, event.position);
if (_isTouchTapSuppressed || isResetGesture) _suppressTouchTaps();
if (isResetGesture) widget.onResetVideoZoom?.call();
}
void _handleTouchPointerCancel(PointerCancelEvent event) {
if (event.kind != PointerDeviceKind.touch) return;
_twoFingerDoubleTapTracker.pointerCancel(event.pointer);
if (_twoFingerDoubleTapTracker.isChordActive) _suppressTouchTaps();
}
/// Timing-based double-click detection: avoids `onDoubleTap`'s ~300 ms
/// tap-resolution delay and the arena competition it introduces.
void _handleOuterTap() {
if (PlatformDetector.isMobile(context) && _isTouchTapSuppressed) return;
if (widget.canControl && _clickVideoTogglesPlayback) {
_playOrPause();
} else {
@@ -117,6 +162,8 @@ extension _PlexVideoControlsPlaybackInputMethods on _PlexVideoControlsState {
/// Handle tap in skip zone with custom double-tap detection
void _handleTapInSkipZone({required bool isForward}) {
if (_isTouchTapSuppressed) return;
final now = DateTime.now();
// Cancel any pending single-tap action
@@ -253,6 +300,8 @@ extension _PlexVideoControlsPlaybackInputMethods on _PlexVideoControlsState {
return;
}
if (_isTouchTapSuppressed) return;
final skipZone = mobileSkipZoneForTap(position: details.localPosition, size: size);
if (skipZone != null) {
_handleTapInSkipZone(isForward: skipZone);