Skip to content

Commit 31bbb20

Browse files
yakagamiyuuangzhang
authored andcommitted
add PointerDeviceKind to ScaleStartDetails (flutter#165096)
Adds the `PointerDeviceKind` of the scale start event to `ScaleStartDetails`, which is currently missing. This is a bug according to flutter#115061. Additionally, according to the docs: >Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan. Just use the scale gesture recognizer. See also flutter#135936 for similar issues and the PRs fixing them. If multiple pointers are contacting the screen at scale start, then the `PointerDeviceKind` of the first pointer is used. In practice this should be good enough as I don't know of any UI that expects you to use two different pointer kinds at the same time. However, if this is an issue, we could either give a list of pointers with their kinds or a set of all active `PointerDeviceKind`s. I don't think this is necessary though. Closes flutter#115061. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent 20b2a13 commit 31bbb20

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-0
lines changed

packages/flutter/lib/src/gestures/scale.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ class ScaleStartDetails {
9999
Offset? localFocalPoint,
100100
this.pointerCount = 0,
101101
this.sourceTimeStamp,
102+
this.kind,
102103
}) : localFocalPoint = localFocalPoint ?? focalPoint;
103104

104105
/// The initial focal point of the pointers in contact with the screen.
@@ -134,6 +135,12 @@ class ScaleStartDetails {
134135
/// Could be null if triggered from proxied events such as accessibility.
135136
final Duration? sourceTimeStamp;
136137

138+
/// The kind of the device that initiated the event.
139+
///
140+
/// If multiple pointers are touching the screen, the kind of the pointer
141+
/// device that first initiated the event is used.
142+
final PointerDeviceKind? kind;
143+
137144
@override
138145
String toString() =>
139146
'ScaleStartDetails(focalPoint: $focalPoint, localFocalPoint: $localFocalPoint, pointersCount: $pointerCount)';
@@ -771,6 +778,12 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
771778
localFocalPoint: _localFocalPoint,
772779
pointerCount: pointerCount,
773780
sourceTimeStamp: _initialEventTimestamp,
781+
kind:
782+
_pointerQueue.isNotEmpty
783+
? getKindForPointer(_pointerQueue.first)
784+
: _pointerPanZooms.isNotEmpty
785+
? getKindForPointer(_pointerPanZooms.keys.first)
786+
: null,
774787
),
775788
);
776789
});

packages/flutter/test/gestures/scale_test.dart

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,4 +1830,91 @@ void main() {
18301830
tap.dispose();
18311831
},
18321832
);
1833+
1834+
testGesture('ScaleStartDetails should contain the correct PointerDeviceKind', (
1835+
GestureTester tester,
1836+
) {
1837+
final ScaleGestureRecognizer scale = ScaleGestureRecognizer();
1838+
1839+
bool didStartScale = false;
1840+
PointerDeviceKind? updatedKind;
1841+
scale.onStart = (ScaleStartDetails details) {
1842+
didStartScale = true;
1843+
updatedKind = details.kind;
1844+
};
1845+
scale.onEnd = (ScaleEndDetails details) {
1846+
didStartScale = false;
1847+
};
1848+
1849+
// The default kind is touch.
1850+
// ignore: avoid_redundant_argument_values
1851+
final TestPointer pointer1 = TestPointer(1, PointerDeviceKind.touch);
1852+
final PointerDownEvent down = pointer1.down(Offset.zero);
1853+
scale.addPointer(down);
1854+
tester.closeArena(1);
1855+
// One-finger panning
1856+
tester.route(down);
1857+
tester.route(pointer1.move(const Offset(20.0, 30.0)));
1858+
expect(didStartScale, isTrue);
1859+
expect(updatedKind, PointerDeviceKind.touch);
1860+
tester.route(pointer1.up());
1861+
expect(didStartScale, isFalse);
1862+
1863+
final TestPointer pointer2 = TestPointer(2, PointerDeviceKind.mouse);
1864+
final PointerDownEvent down2 = pointer2.down(const Offset(10.0, 20.0));
1865+
scale.addPointer(down2);
1866+
tester.closeArena(2);
1867+
tester.route(down2);
1868+
tester.route(pointer2.move(const Offset(20.0, 30.0)));
1869+
expect(didStartScale, isTrue);
1870+
expect(updatedKind, PointerDeviceKind.mouse);
1871+
tester.route(pointer2.up());
1872+
expect(didStartScale, isFalse);
1873+
1874+
final TestPointer pointer3 = TestPointer(3, PointerDeviceKind.stylus);
1875+
final PointerDownEvent down3 = pointer3.down(const Offset(10.0, 20.0));
1876+
scale.addPointer(down3);
1877+
tester.closeArena(3);
1878+
tester.route(down3);
1879+
tester.route(pointer3.move(const Offset(20.0, 30.0)));
1880+
expect(didStartScale, isTrue);
1881+
expect(updatedKind, PointerDeviceKind.stylus);
1882+
tester.route(pointer3.up());
1883+
expect(didStartScale, isFalse);
1884+
1885+
final TestPointer pointer4 = TestPointer(4, PointerDeviceKind.invertedStylus);
1886+
final PointerDownEvent down4 = pointer4.down(const Offset(10.0, 20.0));
1887+
scale.addPointer(down4);
1888+
tester.closeArena(4);
1889+
tester.route(down4);
1890+
tester.route(pointer4.move(const Offset(20.0, 30.0)));
1891+
expect(didStartScale, isTrue);
1892+
expect(updatedKind, PointerDeviceKind.invertedStylus);
1893+
tester.route(pointer4.up());
1894+
expect(didStartScale, isFalse);
1895+
1896+
final TestPointer pointer5 = TestPointer(5, PointerDeviceKind.unknown);
1897+
final PointerDownEvent down5 = pointer5.down(const Offset(10.0, 20.0));
1898+
scale.addPointer(down5);
1899+
tester.closeArena(5);
1900+
tester.route(down5);
1901+
tester.route(pointer5.move(const Offset(20.0, 30.0)));
1902+
expect(didStartScale, isTrue);
1903+
expect(updatedKind, PointerDeviceKind.unknown);
1904+
tester.route(pointer5.up());
1905+
expect(didStartScale, isFalse);
1906+
1907+
final TestPointer pointer6 = TestPointer(6, PointerDeviceKind.trackpad);
1908+
final PointerPanZoomStartEvent down6 = pointer6.panZoomStart(const Offset(10.0, 20.0));
1909+
scale.addPointerPanZoom(down6);
1910+
tester.closeArena(6);
1911+
tester.route(down6);
1912+
tester.route(pointer6.panZoomUpdate(const Offset(20.0, 30.0)));
1913+
expect(didStartScale, isTrue);
1914+
expect(updatedKind, PointerDeviceKind.trackpad);
1915+
tester.route(pointer6.panZoomEnd());
1916+
expect(didStartScale, isFalse);
1917+
1918+
scale.dispose();
1919+
});
18331920
}

0 commit comments

Comments
 (0)