#553 viewer: fix for single axis outer scope

This commit is contained in:
Thibault Deckers 2023-04-30 17:03:37 +02:00
parent ef74ef341e
commit 424ef96353
2 changed files with 38 additions and 109 deletions

View file

@ -68,13 +68,38 @@ class MagnifierGestureRecognizer extends ScaleGestureRecognizer {
_initialSpan = _currentSpan; _initialSpan = _currentSpan;
} }
if (event is PointerMoveEvent && (_areMultiPointers() || (_shouldMove() && _isOverSlop(event.kind)))) { if (event is PointerMoveEvent) {
acceptGesture(pointer); 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); 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() { void _updateDistances() {
// cf super._update // cf super._update
final count = _pointerLocations.keys.length; final count = _pointerLocations.keys.length;
@ -96,42 +121,29 @@ class MagnifierGestureRecognizer extends ScaleGestureRecognizer {
_currentSpan = count > 0 ? totalDeviation / count : 0.0; _currentSpan = count > 0 ? totalDeviation / count : 0.0;
} }
Offset get move => _initialFocalPoint! - _currentFocalPoint!;
// when there are multiple pointers, we always accept the gesture to scale // when there are multiple pointers, we always accept the gesture to scale
// as this is not competing with single taps or other drag gestures // as this is not competing with single taps or other drag gestures
bool _areMultiPointers() => _pointerLocations.keys.length >= 2; bool _areMultiPointers() => _pointerLocations.keys.length >= 2;
bool _shouldMove() { bool _isPriorityGesture() {
final move = _initialFocalPoint! - _currentFocalPoint!;
// e.g. vertical drag to adjust brightness instead of panning // e.g. vertical drag to adjust brightness instead of panning
if (scope.acceptPointerEvent?.call(move) ?? false) return true; if (scope.acceptPointerEvent?.call(move) ?? false) return true;
// e.g. double tap & drag for one finger zoom // e.g. double tap & drag for one finger zoom
if (doubleTapDetails.value != null) return true; if (doubleTapDetails.value != null) return true;
final validateAxis = scope.axis; return false;
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);
}
} }
bool _canPanX() => hitDetector.shouldMoveX(move, scope.escapeByFling) && isXPan(move);
bool _canPanY() => hitDetector.shouldMoveY(move, scope.escapeByFling) && isYPan(move);
bool _isOverSlop(PointerDeviceKind kind) { bool _isOverSlop(PointerDeviceKind kind) {
final spanDelta = (_currentSpan! - _initialSpan!).abs(); final spanDelta = (_currentSpan! - _initialSpan!).abs();
final focalPointDelta = (_currentFocalPoint! - _initialFocalPoint!).distance; final focalPointDelta = move.distance;
// warning: do not compare `focalPointDelta` to `kPanSlop` // warning: do not compare `focalPointDelta` to `kPanSlop`
// `ScaleGestureRecognizer` uses `kPanSlop` (or platform settings, cf gestures/events.dart `computePanSlop`), // `ScaleGestureRecognizer` uses `kPanSlop` (or platform settings, cf gestures/events.dart `computePanSlop`),
// but `HorizontalDragGestureRecognizer` uses `kTouchSlop` (or platform settings, cf gestures/events.dart `computeHitSlop`) // 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; 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) { static bool isXPan(Offset move) {
// the gesture direction angle is in ]-pi, pi], cf `Offset` doc for details
final d = move.direction; final d = move.direction;
return (-pi / 4 < d && d < pi / 4) || (3 / 4 * pi < d && d <= pi) || (-pi < d && d < -3 / 4 * pi); return (-pi / 4 < d && d < pi / 4) || (3 / 4 * pi < d && d <= pi) || (-pi < d && d < -3 / 4 * pi);
} }
static bool isYPan(Offset move) { static bool isYPan(Offset move) {
// the gesture direction angle is in ]-pi, pi], cf `Offset` doc for details
final d = move.direction; final d = move.direction;
return (pi / 4 < d && d < 3 / 4 * pi) || (-3 / 4 * pi < d && d < -pi / 4); return (pi / 4 < d && d < 3 / 4 * pi) || (-3 / 4 * pi < d && d < -pi / 4);
} }

View file

@ -1258,14 +1258,6 @@
"tagEditorDiscardDialogMessage" "tagEditorDiscardDialogMessage"
], ],
"es": [
"maxBrightnessNever",
"maxBrightnessAlways",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"exportEntryDialogQuality"
],
"eu": [ "eu": [
"maxBrightnessNever", "maxBrightnessNever",
"maxBrightnessAlways", "maxBrightnessAlways",
@ -1775,14 +1767,6 @@
"filePickerUseThisFolder" "filePickerUseThisFolder"
], ],
"fr": [
"maxBrightnessNever",
"maxBrightnessAlways",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"exportEntryDialogQuality"
],
"gl": [ "gl": [
"columnCount", "columnCount",
"chipActionGoToPlacePage", "chipActionGoToPlacePage",
@ -3602,27 +3586,8 @@
"filePickerUseThisFolder" "filePickerUseThisFolder"
], ],
"hu": [
"maxBrightnessNever",
"maxBrightnessAlways",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"exportEntryDialogQuality",
"settingsAskEverytime",
"settingsVideoPlaybackTile",
"settingsVideoPlaybackPageTitle",
"settingsVideoResumptionModeTile",
"settingsVideoResumptionModeDialogTitle",
"tagEditorDiscardDialogMessage"
],
"id": [ "id": [
"maxBrightnessNever",
"maxBrightnessAlways",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"exportEntryDialogQuality", "exportEntryDialogQuality",
"settingsAskEverytime",
"settingsVideoPlaybackTile", "settingsVideoPlaybackTile",
"settingsVideoPlaybackPageTitle", "settingsVideoPlaybackPageTitle",
"settingsVideoResumptionModeTile", "settingsVideoResumptionModeTile",
@ -3687,14 +3652,6 @@
"tagEditorDiscardDialogMessage" "tagEditorDiscardDialogMessage"
], ],
"ko": [
"maxBrightnessNever",
"maxBrightnessAlways",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"exportEntryDialogQuality"
],
"lt": [ "lt": [
"columnCount", "columnCount",
"chipActionGoToPlacePage", "chipActionGoToPlacePage",
@ -3797,32 +3754,19 @@
"chipActionGoToPlacePage", "chipActionGoToPlacePage",
"chipActionLock", "chipActionLock",
"chipActionShowCountryStates", "chipActionShowCountryStates",
"chipActionCreateVault",
"chipActionConfigureVault",
"entryActionShareImageOnly", "entryActionShareImageOnly",
"entryActionShareVideoOnly", "entryActionShareVideoOnly",
"viewerActionLock", "viewerActionLock",
"viewerActionUnlock", "viewerActionUnlock",
"entryInfoActionRemoveLocation",
"filterAspectRatioLandscapeLabel",
"filterAspectRatioPortraitLabel",
"filterNoAddressLabel",
"filterLocatedLabel", "filterLocatedLabel",
"filterTaggedLabel",
"albumTierVaults", "albumTierVaults",
"keepScreenOnVideoPlayback",
"lengthUnitPixel",
"maxBrightnessNever", "maxBrightnessNever",
"maxBrightnessAlways", "maxBrightnessAlways",
"subtitlePositionTop", "subtitlePositionTop",
"subtitlePositionBottom", "subtitlePositionBottom",
"vaultLockTypePattern", "vaultLockTypePattern",
"vaultLockTypePassword",
"settingsVideoEnablePip",
"videoResumptionModeNever", "videoResumptionModeNever",
"videoResumptionModeAlways", "videoResumptionModeAlways",
"widgetDisplayedItemRandom",
"widgetDisplayedItemMostRecent",
"newVaultWarningDialogMessage", "newVaultWarningDialogMessage",
"newVaultDialogTitle", "newVaultDialogTitle",
"configureVaultDialogTitle", "configureVaultDialogTitle",
@ -4802,20 +4746,6 @@
"filePickerUseThisFolder" "filePickerUseThisFolder"
], ],
"pl": [
"maxBrightnessNever",
"maxBrightnessAlways",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"exportEntryDialogQuality",
"settingsAskEverytime",
"settingsVideoPlaybackTile",
"settingsVideoPlaybackPageTitle",
"settingsVideoResumptionModeTile",
"settingsVideoResumptionModeDialogTitle",
"tagEditorDiscardDialogMessage"
],
"pt": [ "pt": [
"maxBrightnessNever", "maxBrightnessNever",
"maxBrightnessAlways", "maxBrightnessAlways",