diff --git a/plugins/aves_magnifier/lib/src/core/scale_gesture_recognizer.dart b/plugins/aves_magnifier/lib/src/core/scale_gesture_recognizer.dart index 72d0c04b2..85b7cfcae 100644 --- a/plugins/aves_magnifier/lib/src/core/scale_gesture_recognizer.dart +++ b/plugins/aves_magnifier/lib/src/core/scale_gesture_recognizer.dart @@ -68,13 +68,38 @@ class MagnifierGestureRecognizer extends ScaleGestureRecognizer { _initialSpan = _currentSpan; } - if (event is PointerMoveEvent && (_areMultiPointers() || (_shouldMove() && _isOverSlop(event.kind)))) { - acceptGesture(pointer); + if (event is PointerMoveEvent) { + if (_areMultiPointers()) { + acceptGesture(pointer); + } else if (_isPriorityGesture() && _isOverSlop(event.kind)) { + acceptGesture(pointer); + } else { + final axis = scope.axis; + final isPriorityMove = (axis.contains(Axis.horizontal) && _canPanX()) || (axis.contains(Axis.vertical) && _canPanY()); + if (isPriorityMove && _isOverSlop(event.kind)) { + acceptGesture(pointer); + } + } } } super.handleEvent(event); } + @override + void resolve(GestureDisposition disposition) { + switch (disposition) { + case GestureDisposition.accepted: + // do not let super `ScaleGestureRecognizer` accept gestures + // when it should yield to other recognizers + final canAccept = _areMultiPointers() || _isPriorityGesture() || _canPanX() || _canPanY(); + super.resolve(canAccept ? GestureDisposition.accepted : GestureDisposition.rejected); + break; + case GestureDisposition.rejected: + super.resolve(disposition); + break; + } + } + void _updateDistances() { // cf super._update final count = _pointerLocations.keys.length; @@ -96,42 +121,29 @@ class MagnifierGestureRecognizer extends ScaleGestureRecognizer { _currentSpan = count > 0 ? totalDeviation / count : 0.0; } + Offset get move => _initialFocalPoint! - _currentFocalPoint!; + // when there are multiple pointers, we always accept the gesture to scale // as this is not competing with single taps or other drag gestures bool _areMultiPointers() => _pointerLocations.keys.length >= 2; - bool _shouldMove() { - final move = _initialFocalPoint! - _currentFocalPoint!; - + bool _isPriorityGesture() { // e.g. vertical drag to adjust brightness instead of panning if (scope.acceptPointerEvent?.call(move) ?? false) return true; // e.g. double tap & drag for one finger zoom if (doubleTapDetails.value != null) return true; - final validateAxis = scope.axis; - final canFling = scope.escapeByFling; - if (validateAxis.length == 2) { - // the image is the descendant of gesture detector(s) handling drag in both directions - final shouldMoveX = validateAxis.contains(Axis.horizontal) && hitDetector.shouldMoveX(move, canFling); - final shouldMoveY = validateAxis.contains(Axis.vertical) && hitDetector.shouldMoveY(move, canFling); - if (shouldMoveX == shouldMoveY) { - // consistently can/cannot pan the image in both direction the same way - return shouldMoveX; - } else { - // can pan the image in one direction, but should yield to an ascendant gesture detector in the other one - // the gesture direction angle is in ]-pi, pi], cf `Offset` doc for details - return (isXPan(move) && shouldMoveX) || (isYPan(move) && shouldMoveY); - } - } else { - // the image is the descendant of a gesture detector handling drag in one direction - return validateAxis.contains(Axis.vertical) ? hitDetector.shouldMoveY(move, canFling) : hitDetector.shouldMoveX(move, canFling); - } + return false; } + bool _canPanX() => hitDetector.shouldMoveX(move, scope.escapeByFling) && isXPan(move); + + bool _canPanY() => hitDetector.shouldMoveY(move, scope.escapeByFling) && isYPan(move); + bool _isOverSlop(PointerDeviceKind kind) { final spanDelta = (_currentSpan! - _initialSpan!).abs(); - final focalPointDelta = (_currentFocalPoint! - _initialFocalPoint!).distance; + final focalPointDelta = move.distance; // warning: do not compare `focalPointDelta` to `kPanSlop` // `ScaleGestureRecognizer` uses `kPanSlop` (or platform settings, cf gestures/events.dart `computePanSlop`), // but `HorizontalDragGestureRecognizer` uses `kTouchSlop` (or platform settings, cf gestures/events.dart `computeHitSlop`) @@ -141,27 +153,14 @@ class MagnifierGestureRecognizer extends ScaleGestureRecognizer { return spanDelta > computeScaleSlop(kind) || focalPointDelta > computeHitSlop(kind, gestureSettings) * scope.touchSlopFactor; } - @override - void resolve(GestureDisposition disposition) { - switch (disposition) { - case GestureDisposition.accepted: - // do not let super `ScaleGestureRecognizer` accept gestures - // when it should yield to other recognizers - final canAccept = _areMultiPointers() || _shouldMove(); - super.resolve(canAccept ? GestureDisposition.accepted : GestureDisposition.rejected); - break; - case GestureDisposition.rejected: - super.resolve(disposition); - break; - } - } - static bool isXPan(Offset move) { + // the gesture direction angle is in ]-pi, pi], cf `Offset` doc for details final d = move.direction; return (-pi / 4 < d && d < pi / 4) || (3 / 4 * pi < d && d <= pi) || (-pi < d && d < -3 / 4 * pi); } static bool isYPan(Offset move) { + // the gesture direction angle is in ]-pi, pi], cf `Offset` doc for details final d = move.direction; return (pi / 4 < d && d < 3 / 4 * pi) || (-3 / 4 * pi < d && d < -pi / 4); } diff --git a/untranslated.json b/untranslated.json index 1150b6e72..dd9a2d560 100644 --- a/untranslated.json +++ b/untranslated.json @@ -1258,14 +1258,6 @@ "tagEditorDiscardDialogMessage" ], - "es": [ - "maxBrightnessNever", - "maxBrightnessAlways", - "videoResumptionModeNever", - "videoResumptionModeAlways", - "exportEntryDialogQuality" - ], - "eu": [ "maxBrightnessNever", "maxBrightnessAlways", @@ -1775,14 +1767,6 @@ "filePickerUseThisFolder" ], - "fr": [ - "maxBrightnessNever", - "maxBrightnessAlways", - "videoResumptionModeNever", - "videoResumptionModeAlways", - "exportEntryDialogQuality" - ], - "gl": [ "columnCount", "chipActionGoToPlacePage", @@ -3602,27 +3586,8 @@ "filePickerUseThisFolder" ], - "hu": [ - "maxBrightnessNever", - "maxBrightnessAlways", - "videoResumptionModeNever", - "videoResumptionModeAlways", - "exportEntryDialogQuality", - "settingsAskEverytime", - "settingsVideoPlaybackTile", - "settingsVideoPlaybackPageTitle", - "settingsVideoResumptionModeTile", - "settingsVideoResumptionModeDialogTitle", - "tagEditorDiscardDialogMessage" - ], - "id": [ - "maxBrightnessNever", - "maxBrightnessAlways", - "videoResumptionModeNever", - "videoResumptionModeAlways", "exportEntryDialogQuality", - "settingsAskEverytime", "settingsVideoPlaybackTile", "settingsVideoPlaybackPageTitle", "settingsVideoResumptionModeTile", @@ -3687,14 +3652,6 @@ "tagEditorDiscardDialogMessage" ], - "ko": [ - "maxBrightnessNever", - "maxBrightnessAlways", - "videoResumptionModeNever", - "videoResumptionModeAlways", - "exportEntryDialogQuality" - ], - "lt": [ "columnCount", "chipActionGoToPlacePage", @@ -3797,32 +3754,19 @@ "chipActionGoToPlacePage", "chipActionLock", "chipActionShowCountryStates", - "chipActionCreateVault", - "chipActionConfigureVault", "entryActionShareImageOnly", "entryActionShareVideoOnly", "viewerActionLock", "viewerActionUnlock", - "entryInfoActionRemoveLocation", - "filterAspectRatioLandscapeLabel", - "filterAspectRatioPortraitLabel", - "filterNoAddressLabel", "filterLocatedLabel", - "filterTaggedLabel", "albumTierVaults", - "keepScreenOnVideoPlayback", - "lengthUnitPixel", "maxBrightnessNever", "maxBrightnessAlways", "subtitlePositionTop", "subtitlePositionBottom", "vaultLockTypePattern", - "vaultLockTypePassword", - "settingsVideoEnablePip", "videoResumptionModeNever", "videoResumptionModeAlways", - "widgetDisplayedItemRandom", - "widgetDisplayedItemMostRecent", "newVaultWarningDialogMessage", "newVaultDialogTitle", "configureVaultDialogTitle", @@ -4802,20 +4746,6 @@ "filePickerUseThisFolder" ], - "pl": [ - "maxBrightnessNever", - "maxBrightnessAlways", - "videoResumptionModeNever", - "videoResumptionModeAlways", - "exportEntryDialogQuality", - "settingsAskEverytime", - "settingsVideoPlaybackTile", - "settingsVideoPlaybackPageTitle", - "settingsVideoResumptionModeTile", - "settingsVideoResumptionModeDialogTitle", - "tagEditorDiscardDialogMessage" - ], - "pt": [ "maxBrightnessNever", "maxBrightnessAlways",