mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Support right-clicking on iPadOS (flutter/engine#27019)
This commit is contained in:
parent
07cda5195c
commit
764f2e1137
@ -18,3 +18,4 @@ shoryukenn <naifu.guan@gmail.com>
|
||||
SOTEC GmbH & Co. KG <sotec-contributors@sotec.eu>
|
||||
Hidenori Matsubayashi <Hidenori.Matsubayashi@sony.com>
|
||||
Sarbagya Dhaubanjar <mail@sarbagyastha.com.np>
|
||||
Callum Moffat <callum@moffatman.com>
|
||||
|
||||
@ -814,7 +814,8 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
// in the status bar area are available to framework code. The change type (optional) of the faked
|
||||
// touch is specified in the second argument.
|
||||
- (void)dispatchTouches:(NSSet*)touches
|
||||
pointerDataChangeOverride:(flutter::PointerData::Change*)overridden_change {
|
||||
pointerDataChangeOverride:(flutter::PointerData::Change*)overridden_change
|
||||
event:(UIEvent*)event {
|
||||
if (!_engine) {
|
||||
return;
|
||||
}
|
||||
@ -925,6 +926,17 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
pointer_data.orientation = [touch azimuthAngleInView:nil] - M_PI_2;
|
||||
}
|
||||
|
||||
if (@available(iOS 13.4, *)) {
|
||||
if (event != nullptr) {
|
||||
pointer_data.buttons = (((event.buttonMask & UIEventButtonMaskPrimary) > 0)
|
||||
? flutter::PointerButtonMouse::kPointerButtonMousePrimary
|
||||
: 0) |
|
||||
(((event.buttonMask & UIEventButtonMaskSecondary) > 0)
|
||||
? flutter::PointerButtonMouse::kPointerButtonMouseSecondary
|
||||
: 0);
|
||||
}
|
||||
}
|
||||
|
||||
packet->SetPointerData(pointer_index++, pointer_data);
|
||||
}
|
||||
|
||||
@ -932,24 +944,24 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[self dispatchTouches:touches pointerDataChangeOverride:nullptr];
|
||||
[self dispatchTouches:touches pointerDataChangeOverride:nullptr event:event];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[self dispatchTouches:touches pointerDataChangeOverride:nullptr];
|
||||
[self dispatchTouches:touches pointerDataChangeOverride:nullptr event:event];
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[self dispatchTouches:touches pointerDataChangeOverride:nullptr];
|
||||
[self dispatchTouches:touches pointerDataChangeOverride:nullptr event:event];
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[self dispatchTouches:touches pointerDataChangeOverride:nullptr];
|
||||
[self dispatchTouches:touches pointerDataChangeOverride:nullptr event:event];
|
||||
}
|
||||
|
||||
- (void)forceTouchesCancelled:(NSSet*)touches {
|
||||
flutter::PointerData::Change cancel = flutter::PointerData::Change::kCancel;
|
||||
[self dispatchTouches:touches pointerDataChangeOverride:&cancel];
|
||||
[self dispatchTouches:touches pointerDataChangeOverride:&cancel event:nullptr];
|
||||
}
|
||||
|
||||
#pragma mark - Handle view resizing
|
||||
|
||||
@ -56,6 +56,7 @@
|
||||
6816DBA42318358200A51400 /* GoldenTestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA32318358200A51400 /* GoldenTestManager.m */; };
|
||||
68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */; };
|
||||
68D4017D2564859300ECD91A /* ContinuousTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 68D4017C2564859300ECD91A /* ContinuousTexture.m */; };
|
||||
F26F15B8268B6B5600EC54D3 /* iPadGestureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F26F15B7268B6B5500EC54D3 /* iPadGestureTests.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -172,6 +173,7 @@
|
||||
68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGestureRecognizerTests.m; sourceTree = "<group>"; };
|
||||
68D4017B2564859300ECD91A /* ContinuousTexture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContinuousTexture.h; sourceTree = "<group>"; };
|
||||
68D4017C2564859300ECD91A /* ContinuousTexture.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContinuousTexture.m; sourceTree = "<group>"; };
|
||||
F26F15B7268B6B5500EC54D3 /* iPadGestureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iPadGestureTests.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -295,6 +297,7 @@
|
||||
246A6610252E693A00EAB0F3 /* RenderingSelectionTest.m */,
|
||||
0DDEBC87258830B40065D0E8 /* SpawnEngineTest.h */,
|
||||
0DDEBC88258830B40065D0E8 /* SpawnEngineTest.m */,
|
||||
F26F15B7268B6B5500EC54D3 /* iPadGestureTests.m */,
|
||||
);
|
||||
path = ScenariosUITests;
|
||||
sourceTree = "<group>";
|
||||
@ -490,6 +493,7 @@
|
||||
6816DBA42318358200A51400 /* GoldenTestManager.m in Sources */,
|
||||
248D76EF22E388380012F0C1 /* PlatformViewUITests.m in Sources */,
|
||||
0D8470A4240F0B1F0030B565 /* StatusBarTest.m in Sources */,
|
||||
F26F15B8268B6B5600EC54D3 /* iPadGestureTests.m in Sources */,
|
||||
246A6611252E693A00EAB0F3 /* RenderingSelectionTest.m in Sources */,
|
||||
4F06F1B32473296E000AF246 /* LocalizationInitializationTest.m in Sources */,
|
||||
0A42BFB42447E179007E212E /* TextSemanticsFocusTest.m in Sources */,
|
||||
|
||||
@ -57,6 +57,7 @@
|
||||
@"--platform-view-with-continuous-texture" : @"platform_view_with_continuous_texture",
|
||||
@"--bogus-font-text" : @"bogus_font_text",
|
||||
@"--spawn-engine-works" : @"spawn_engine_works",
|
||||
@"--pointer-events" : @"pointer_events",
|
||||
};
|
||||
__block NSString* flutterViewControllerTestName = nil;
|
||||
[launchArgsMap
|
||||
@ -109,6 +110,7 @@
|
||||
FlutterEngine* engine = [self engineForTest:scenarioIdentifier];
|
||||
FlutterViewController* flutterViewController =
|
||||
[self flutterViewControllerForTest:scenarioIdentifier withEngine:engine];
|
||||
flutterViewController.view.accessibilityIdentifier = @"flutter_view";
|
||||
|
||||
[engine.binaryMessenger
|
||||
setMessageHandlerOnChannel:@"waiting_for_status"
|
||||
|
||||
@ -30,10 +30,13 @@
|
||||
[[self.application.statusBars firstMatch] tap];
|
||||
}
|
||||
|
||||
XCUIElement* addTextField = self.application.textFields[@"PointerChange.add"];
|
||||
XCUIElement* addTextField = self.application.textFields[@"PointerChange.add:0"];
|
||||
BOOL exists = [addTextField waitForExistenceWithTimeout:1];
|
||||
XCTAssertTrue(exists, @"");
|
||||
XCUIElement* upTextField = self.application.textFields[@"PointerChange.up"];
|
||||
XCUIElement* downTextField = self.application.textFields[@"PointerChange.down:0"];
|
||||
exists = [downTextField waitForExistenceWithTimeout:1];
|
||||
XCTAssertTrue(exists, @"");
|
||||
XCUIElement* upTextField = self.application.textFields[@"PointerChange.up:0"];
|
||||
exists = [upTextField waitForExistenceWithTimeout:1];
|
||||
XCTAssertTrue(exists, @"");
|
||||
}
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
// Copyright 2020 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
static const NSInteger kSecondsToWaitForFlutterView = 30;
|
||||
|
||||
@interface iPadGestureTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation iPadGestureTests
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
self.continueAfterFailure = NO;
|
||||
}
|
||||
|
||||
#ifdef __IPHONE_15_0
|
||||
- (void)testPointerButtons {
|
||||
if (@available(iOS 15, *)) {
|
||||
XCTSkipUnless([XCUIDevice.sharedDevice supportsPointerInteraction],
|
||||
"Device does not support pointer interaction");
|
||||
XCUIApplication* app = [[XCUIApplication alloc] init];
|
||||
app.launchArguments = @[ @"--pointer-events" ];
|
||||
[app launch];
|
||||
|
||||
NSPredicate* predicateToFindFlutterView =
|
||||
[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject,
|
||||
NSDictionary<NSString*, id>* _Nullable bindings) {
|
||||
XCUIElement* element = evaluatedObject;
|
||||
return [element.identifier hasPrefix:@"flutter_view"];
|
||||
}];
|
||||
XCUIElement* flutterView = [[app descendantsMatchingType:XCUIElementTypeAny]
|
||||
elementMatchingPredicate:predicateToFindFlutterView];
|
||||
if (![flutterView waitForExistenceWithTimeout:kSecondsToWaitForFlutterView]) {
|
||||
NSLog(@"%@", app.debugDescription);
|
||||
XCTFail(@"Failed due to not able to find any flutterView with %@ seconds",
|
||||
@(kSecondsToWaitForFlutterView));
|
||||
}
|
||||
|
||||
XCTAssertNotNil(flutterView);
|
||||
|
||||
[flutterView tap];
|
||||
// Initial add event should have buttons = 0
|
||||
XCTAssertTrue([app.textFields[@"PointerChange.add:0"] waitForExistenceWithTimeout:1],
|
||||
@"PointerChange.add event did not occur");
|
||||
// Normal tap should have buttons = 0, the flutter framework will ensure it has buttons = 1
|
||||
XCTAssertTrue([app.textFields[@"PointerChange.down:0"] waitForExistenceWithTimeout:1],
|
||||
@"PointerChange.down event did not occur for a normal tap");
|
||||
XCTAssertTrue([app.textFields[@"PointerChange.up:0"] waitForExistenceWithTimeout:1],
|
||||
@"PointerChange.up event did not occur for a normal tap");
|
||||
[flutterView rightClick];
|
||||
// Since each touch is its own device, we can't distinguish the other add event(s)
|
||||
// Right click should have buttons = 2
|
||||
XCTAssertTrue([app.textFields[@"PointerChange.down:2"] waitForExistenceWithTimeout:1],
|
||||
@"PointerChange.down event did not occur for a right-click");
|
||||
XCTAssertTrue([app.textFields[@"PointerChange.up:2"] waitForExistenceWithTimeout:1],
|
||||
@"PointerChange.up event did not occur for a right-click");
|
||||
NSLog(@"DebugDescriptionX: %@", app.debugDescription);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@end
|
||||
@ -48,6 +48,7 @@ Map<String, ScenarioFactory> _scenarios = <String, ScenarioFactory>{
|
||||
'platform_view_with_continuous_texture': () => PlatformViewWithContinuousTexture(PlatformDispatcher.instance, 'Platform View', id: _viewId++),
|
||||
'bogus_font_text': () => BogusFontText(PlatformDispatcher.instance),
|
||||
'spawn_engine_works' : () => BogusFontText(PlatformDispatcher.instance),
|
||||
'pointer_events': () => TouchesScenario(PlatformDispatcher.instance),
|
||||
};
|
||||
|
||||
Map<String, dynamic> _currentScenarioParams = <String, dynamic>{};
|
||||
|
||||
@ -14,14 +14,24 @@ class TouchesScenario extends Scenario {
|
||||
/// Constructor for `TouchesScenario`.
|
||||
TouchesScenario(PlatformDispatcher dispatcher) : super(dispatcher);
|
||||
|
||||
@override
|
||||
void onBeginFrame(Duration duration) {
|
||||
// It is necessary to render frames for touch events to work properly on iOS
|
||||
final Scene scene = SceneBuilder().build();
|
||||
window.render(scene);
|
||||
scene.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void onPointerDataPacket(PointerDataPacket packet) {
|
||||
sendJsonMessage(
|
||||
dispatcher: dispatcher,
|
||||
channel: 'display_data',
|
||||
json: <String, dynamic>{
|
||||
'data': packet.data[0].change.toString(),
|
||||
},
|
||||
);
|
||||
for (final PointerData datum in packet.data) {
|
||||
sendJsonMessage(
|
||||
dispatcher: dispatcher,
|
||||
channel: 'display_data',
|
||||
json: <String, dynamic>{
|
||||
'data': datum.change.toString() + ':' + datum.buttons.toString(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user