Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit dbb6093

Browse files
authored
Revert "[macOS] performKeyEquivalent cleanup (#45946)" (#46374)
This broke some keyboard shortcut handling (e.g. cmd-a to select all). This reverts commit 0087948. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I signed the [CLA]. - [ ] 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/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent 0087948 commit dbb6093

File tree

2 files changed

+44
-14
lines changed

2 files changed

+44
-14
lines changed

shell/platform/darwin/macos/framework/Source/FlutterViewController.mm

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ @interface FlutterViewWrapper : NSView
164164

165165
- (void)setBackgroundColor:(NSColor*)color;
166166

167+
- (BOOL)performKeyEquivalent:(NSEvent*)event;
168+
167169
@end
168170

169171
/**
@@ -240,6 +242,37 @@ - (void)onKeyboardLayoutChanged;
240242

241243
@end
242244

245+
#pragma mark - NSEvent (KeyEquivalentMarker) protocol
246+
247+
@interface NSEvent (KeyEquivalentMarker)
248+
249+
// Internally marks that the event was received through performKeyEquivalent:.
250+
// When text editing is active, keyboard events that have modifier keys pressed
251+
// are received through performKeyEquivalent: instead of keyDown:. If such event
252+
// is passed to TextInputContext but doesn't result in a text editing action it
253+
// needs to be forwarded by FlutterKeyboardManager to the next responder.
254+
- (void)markAsKeyEquivalent;
255+
256+
// Returns YES if the event is marked as a key equivalent.
257+
- (BOOL)isKeyEquivalent;
258+
259+
@end
260+
261+
@implementation NSEvent (KeyEquivalentMarker)
262+
263+
// This field doesn't need a value because only its address is used as a unique identifier.
264+
static char markerKey;
265+
266+
- (void)markAsKeyEquivalent {
267+
objc_setAssociatedObject(self, &markerKey, @true, OBJC_ASSOCIATION_RETAIN);
268+
}
269+
270+
- (BOOL)isKeyEquivalent {
271+
return [objc_getAssociatedObject(self, &markerKey) boolValue] == YES;
272+
}
273+
274+
@end
275+
243276
#pragma mark - Private dependant functions
244277

245278
namespace {
@@ -279,15 +312,19 @@ - (void)setBackgroundColor:(NSColor*)color {
279312
}
280313

281314
- (BOOL)performKeyEquivalent:(NSEvent*)event {
282-
// Do not intercept the event if flutterView is not first responder, otherwise this would
283-
// interfere with TextInputPlugin, which also handles key equivalents.
284-
//
285-
// Also do not intercept the event if key equivalent is a product of an event being
286-
// redispatched by the TextInputPlugin, in which case it needs to bubble up so that menus
287-
// can handle key equivalents.
288-
if (self.window.firstResponder != _flutterView || [_controller isDispatchingKeyEvent:event]) {
315+
if ([_controller isDispatchingKeyEvent:event]) {
316+
// When NSWindow is nextResponder, keyboard manager will send to it
317+
// unhandled events (through [NSWindow keyDown:]). If event has both
318+
// control and cmd modifiers set (i.e. cmd+control+space - emoji picker)
319+
// NSWindow will then send this event as performKeyEquivalent: to first
320+
// responder, which might be FlutterTextInputPlugin. If that's the case, the
321+
// plugin must not handle the event, otherwise the emoji picker would not
322+
// work (due to first responder returning YES from performKeyEquivalent:)
323+
// and there would be an infinite loop, because FlutterViewController will
324+
// send the event back to [keyboardManager handleEvent:].
289325
return NO;
290326
}
327+
[event markAsKeyEquivalent];
291328
[_flutterView keyDown:event];
292329
return YES;
293330
}

shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -331,13 +331,6 @@ - (bool)testCtrlTabKeyEventIsPropagated {
331331
const uint64_t kPhysicalKeyTab = 0x7002b;
332332

333333
[viewController viewWillAppear]; // Initializes the event channel.
334-
// Creates a NSWindow so that FlutterView view can be first responder.
335-
NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600)
336-
styleMask:NSBorderlessWindowMask
337-
backing:NSBackingStoreBuffered
338-
defer:NO];
339-
window.contentView = viewController.view;
340-
[window makeFirstResponder:viewController.flutterView];
341334
[viewController.view performKeyEquivalent:event];
342335

343336
EXPECT_TRUE(called);

0 commit comments

Comments
 (0)