-
Notifications
You must be signed in to change notification settings - Fork 6k
Refactor and migrate FlutterUndoManagerPlugin to ARC #52234
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,8 @@ | |
|
||
#import <Foundation/Foundation.h> | ||
|
||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
typedef NS_ENUM(NSInteger, FlutterUndoRedoDirection) { | ||
|
@@ -19,8 +21,14 @@ typedef NS_ENUM(NSInteger, FlutterUndoRedoDirection) { | |
@class FlutterUndoManagerPlugin; | ||
|
||
@protocol FlutterUndoManagerDelegate <NSObject> | ||
|
||
@property(nonatomic, weak) UIResponder* viewController; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add declaration comments to these explaining their role in the delegation? (Per style guide, all declarations should be commented. I know the engine is awful about this, but it would be good to break the cycle in new code.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Documenting how this delegate works led me to rewrite it.
|
||
|
||
- (FlutterTextInputPlugin*)textInputPlugin; | ||
jmagman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- (void)flutterUndoManagerPlugin:(FlutterUndoManagerPlugin*)undoManagerPlugin | ||
handleUndoWithDirection:(FlutterUndoRedoDirection)direction; | ||
|
||
@end | ||
NS_ASSUME_NONNULL_END | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -3,12 +3,8 @@ | |||
// found in the LICENSE file. | ||||
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPlugin.h" | ||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h" | ||||
|
||||
#import <Foundation/Foundation.h> | ||||
#import <UIKit/UIKit.h> | ||||
|
||||
#include "flutter/fml/logging.h" | ||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" | ||||
|
||||
#pragma mark - UndoManager channel method names. | ||||
static NSString* const kSetUndoStateMethod = @"UndoManager.setUndoState"; | ||||
|
@@ -17,15 +13,16 @@ | |||
static NSString* const kCanUndo = @"canUndo"; | ||||
static NSString* const kCanRedo = @"canRedo"; | ||||
|
||||
@implementation FlutterUndoManagerPlugin { | ||||
id<FlutterUndoManagerDelegate> _undoManagerDelegate; | ||||
} | ||||
@interface FlutterUndoManagerPlugin () | ||||
@property(nonatomic, weak, readonly) id<FlutterUndoManagerDelegate> undoManagerDelegate; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From below:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should also have a declaration comment (maybe a short summary of what kinds of things are delegated). |
||||
@end | ||||
|
||||
@implementation FlutterUndoManagerPlugin | ||||
|
||||
- (instancetype)initWithDelegate:(id<FlutterUndoManagerDelegate>)undoManagerDelegate { | ||||
self = [super init]; | ||||
|
||||
if (self) { | ||||
// `_undoManagerDelegate` is a weak reference because it should retain FlutterUndoManagerPlugin. | ||||
_undoManagerDelegate = undoManagerDelegate; | ||||
} | ||||
|
||||
|
@@ -34,7 +31,6 @@ - (instancetype)initWithDelegate:(id<FlutterUndoManagerDelegate>)undoManagerDele | |||
|
||||
- (void)dealloc { | ||||
[self resetUndoManager]; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional since it's pre-existing: it would be good to inline the chain of calls (which is still only one line) to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, and my refactor of the delegate reduced the chaining. It still calls [_undoManagerDelegate.undoManager removeAllActionsWithTarget:self]; |
||||
[super dealloc]; | ||||
} | ||||
|
||||
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { | ||||
|
@@ -49,45 +45,47 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { | |||
} | ||||
|
||||
- (NSUndoManager*)undoManager { | ||||
return _viewController.undoManager; | ||||
return self.undoManagerDelegate.viewController.undoManager; | ||||
} | ||||
|
||||
- (void)resetUndoManager API_AVAILABLE(ios(9.0)) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed |
||||
[[self undoManager] removeAllActionsWithTarget:self]; | ||||
} | ||||
|
||||
- (void)registerUndoWithDirection:(FlutterUndoRedoDirection)direction API_AVAILABLE(ios(9.0)) { | ||||
[[self undoManager] beginUndoGrouping]; | ||||
[[self undoManager] registerUndoWithTarget:self | ||||
handler:^(id target) { | ||||
// Register undo with opposite direction. | ||||
FlutterUndoRedoDirection newDirection = | ||||
(direction == FlutterUndoRedoDirectionRedo) | ||||
? FlutterUndoRedoDirectionUndo | ||||
: FlutterUndoRedoDirectionRedo; | ||||
[target registerUndoWithDirection:newDirection]; | ||||
// Invoke method on delegate. | ||||
[_undoManagerDelegate flutterUndoManagerPlugin:self | ||||
handleUndoWithDirection:direction]; | ||||
}]; | ||||
[[self undoManager] endUndoGrouping]; | ||||
NSUndoManager* undoManager = [self undoManager]; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional nit: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now it's a method, but I can add the readonly property. I was trying to limit the number of changes to this file, but I've touched everything at this point. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Scratch that. I since removed the |
||||
[undoManager beginUndoGrouping]; | ||||
[undoManager registerUndoWithTarget:self | ||||
handler:^(FlutterUndoManagerPlugin* target) { | ||||
// Register undo with opposite direction. | ||||
FlutterUndoRedoDirection newDirection = | ||||
(direction == FlutterUndoRedoDirectionRedo) | ||||
? FlutterUndoRedoDirectionUndo | ||||
: FlutterUndoRedoDirectionRedo; | ||||
[target registerUndoWithDirection:newDirection]; | ||||
// Invoke method on delegate. | ||||
[target.undoManagerDelegate flutterUndoManagerPlugin:target | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed |
||||
handleUndoWithDirection:direction]; | ||||
}]; | ||||
[undoManager endUndoGrouping]; | ||||
} | ||||
|
||||
- (void)registerRedo API_AVAILABLE(ios(9.0)) { | ||||
[[self undoManager] beginUndoGrouping]; | ||||
[[self undoManager] | ||||
registerUndoWithTarget:self | ||||
handler:^(id target) { | ||||
// Register undo with opposite direction. | ||||
[target registerUndoWithDirection:FlutterUndoRedoDirectionRedo]; | ||||
}]; | ||||
[[self undoManager] endUndoGrouping]; | ||||
[[self undoManager] undo]; | ||||
NSUndoManager* undoManager = [self undoManager]; | ||||
jmagman marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
[undoManager beginUndoGrouping]; | ||||
[undoManager registerUndoWithTarget:self | ||||
handler:^(id target) { | ||||
// Register undo with opposite direction. | ||||
[target registerUndoWithDirection:FlutterUndoRedoDirectionRedo]; | ||||
}]; | ||||
[undoManager endUndoGrouping]; | ||||
[undoManager undo]; | ||||
} | ||||
|
||||
- (void)setUndoState:(NSDictionary*)dictionary API_AVAILABLE(ios(9.0)) { | ||||
BOOL groupsByEvent = [self undoManager].groupsByEvent; | ||||
[self undoManager].groupsByEvent = NO; | ||||
NSUndoManager* undoManager = [self undoManager]; | ||||
jmagman marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
BOOL groupsByEvent = undoManager.groupsByEvent; | ||||
undoManager.groupsByEvent = NO; | ||||
BOOL canUndo = [dictionary[kCanUndo] boolValue]; | ||||
BOOL canRedo = [dictionary[kCanRedo] boolValue]; | ||||
|
||||
|
@@ -99,16 +97,15 @@ - (void)setUndoState:(NSDictionary*)dictionary API_AVAILABLE(ios(9.0)) { | |||
if (canRedo) { | ||||
[self registerRedo]; | ||||
} | ||||
|
||||
if (_viewController.engine.textInputPlugin.textInputView != nil) { | ||||
UIView<UITextInput>* textInputView = [self.undoManagerDelegate.textInputPlugin textInputView]; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No:
At the moment I don't want to tackle changing that in a public header. |
||||
if (textInputView != nil) { | ||||
// This is needed to notify the iPadOS keyboard that it needs to update the | ||||
// state of the UIBarButtons. Otherwise, the state changes to NSUndoManager | ||||
// will not show up until the next keystroke (or other trigger). | ||||
UITextInputAssistantItem* assistantItem = | ||||
_viewController.engine.textInputPlugin.textInputView.inputAssistantItem; | ||||
UITextInputAssistantItem* assistantItem = textInputView.inputAssistantItem; | ||||
assistantItem.leadingBarButtonGroups = assistantItem.leadingBarButtonGroups; | ||||
} | ||||
[self undoManager].groupsByEvent = groupsByEvent; | ||||
undoManager.groupsByEvent = groupsByEvent; | ||||
} | ||||
|
||||
@end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,51 +8,48 @@ | |
#import <XCTest/XCTest.h> | ||
|
||
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" | ||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h" | ||
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" | ||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" | ||
|
||
FLUTTER_ASSERT_ARC | ||
|
||
@interface FlutterEngine () | ||
- (nonnull FlutterUndoManagerPlugin*)undoManagerPlugin; | ||
- (nonnull FlutterTextInputPlugin*)textInputPlugin; | ||
@interface FlutterUndoManagerDelegateForTest : NSObject <FlutterUndoManagerDelegate> | ||
@property(nonatomic, weak) UIResponder* viewController; | ||
@property(nonatomic) FlutterTextInputPlugin* textInputPlugin; | ||
@end | ||
|
||
@interface FlutterUndoManagerPluginForTest : FlutterUndoManagerPlugin | ||
@property(nonatomic, assign) NSUndoManager* undoManager; | ||
@end | ||
@implementation FlutterUndoManagerDelegateForTest | ||
|
||
@implementation FlutterUndoManagerPluginForTest { | ||
- (void)flutterUndoManagerPlugin:(FlutterUndoManagerPlugin*)undoManagerPlugin | ||
handleUndoWithDirection:(FlutterUndoRedoDirection)direction { | ||
} | ||
@end | ||
|
||
@interface FlutterUndoManagerPluginTest : XCTestCase | ||
@property(nonatomic, strong) id engine; | ||
@property(nonatomic, strong) FlutterUndoManagerPluginForTest* undoManagerPlugin; | ||
@property(nonatomic, strong) FlutterViewController* viewController; | ||
@property(nonatomic, strong) NSUndoManager* undoManager; | ||
@property(nonatomic) id undoManagerDelegate; | ||
jmagman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@property(nonatomic) FlutterUndoManagerPlugin* undoManagerPlugin; | ||
@property(nonatomic) UIResponder* viewController; | ||
@property(nonatomic) NSUndoManager* undoManager; | ||
@end | ||
|
||
@implementation FlutterUndoManagerPluginTest { | ||
} | ||
@implementation FlutterUndoManagerPluginTest | ||
|
||
- (void)setUp { | ||
[super setUp]; | ||
self.engine = OCMClassMock([FlutterEngine class]); | ||
|
||
self.undoManagerPlugin = [[FlutterUndoManagerPluginForTest alloc] initWithDelegate:self.engine]; | ||
self.undoManagerDelegate = OCMClassMock([FlutterUndoManagerDelegateForTest class]); | ||
|
||
self.viewController = [[FlutterViewController alloc] init]; | ||
self.undoManagerPlugin.viewController = self.viewController; | ||
self.undoManagerPlugin = | ||
[[FlutterUndoManagerPlugin alloc] initWithDelegate:self.undoManagerDelegate]; | ||
jmagman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
self.undoManager = OCMClassMock([NSUndoManager class]); | ||
self.undoManagerPlugin.undoManager = self.undoManager; | ||
|
||
self.viewController = OCMClassMock([UIResponder class]); | ||
OCMStub([self.viewController undoManager]).andReturn(self.undoManager); | ||
OCMStub([self.undoManagerDelegate viewController]).andReturn(self.viewController); | ||
} | ||
|
||
- (void)tearDown { | ||
[self.undoManager removeAllActionsWithTarget:self.undoManagerPlugin]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was only class mocked, I don't think this would do anything. |
||
self.engine = nil; | ||
self.undoManagerDelegate = nil; | ||
self.viewController = nil; | ||
self.undoManager = nil; | ||
[super tearDown]; | ||
|
@@ -75,14 +72,14 @@ - (void)testSetUndoState { | |
removeAllActionsCount++; | ||
}); | ||
__block int delegateUndoCount = 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is now tracked in the |
||
OCMStub([self.engine flutterUndoManagerPlugin:[OCMArg any] | ||
handleUndoWithDirection:FlutterUndoRedoDirectionUndo]) | ||
OCMStub([self.undoManagerDelegate flutterUndoManagerPlugin:[OCMArg any] | ||
handleUndoWithDirection:FlutterUndoRedoDirectionUndo]) | ||
.andDo(^(NSInvocation* invocation) { | ||
delegateUndoCount++; | ||
}); | ||
__block int delegateRedoCount = 0; | ||
OCMStub([self.engine flutterUndoManagerPlugin:[OCMArg any] | ||
handleUndoWithDirection:FlutterUndoRedoDirectionRedo]) | ||
OCMStub([self.undoManagerDelegate flutterUndoManagerPlugin:[OCMArg any] | ||
handleUndoWithDirection:FlutterUndoRedoDirectionRedo]) | ||
.andDo(^(NSInvocation* invocation) { | ||
delegateRedoCount++; | ||
}); | ||
|
@@ -143,14 +140,10 @@ - (void)testSetUndoState { | |
|
||
- (void)testSetUndoStateDoesInteractWithInputDelegate { | ||
// Regression test for https://github.com/flutter/flutter/issues/133424 | ||
FlutterViewController* viewController = OCMPartialMock(self.viewController); | ||
self.undoManagerPlugin.viewController = self.viewController; | ||
|
||
FlutterTextInputPlugin* textInputPlugin = OCMClassMock([FlutterTextInputPlugin class]); | ||
FlutterTextInputView* textInputView = OCMClassMock([FlutterTextInputView class]); | ||
|
||
OCMStub([viewController engine]).andReturn(self.engine); | ||
OCMStub([self.engine textInputPlugin]).andReturn(textInputPlugin); | ||
OCMStub([self.undoManagerDelegate textInputPlugin]).andReturn(textInputPlugin); | ||
OCMStub([textInputPlugin textInputView]).andReturn(textInputView); | ||
|
||
FlutterMethodCall* setUndoStateCall = | ||
|
Uh oh!
There was an error while loading. Please reload this page.