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

Commit caf81dd

Browse files
authored
Refactor and migrate FlutterUndoManagerPlugin to ARC (#52234)
Smart pointers support ARC as of #47612, and the unit tests were migrated in #48162. Migrate `FlutterUndoManagerPlugin` from MRC to ARC. 1. Refactor so the plugin and its tests don't need to understand the details of `FlutterViewController` or `FlutterEngine` (its delegate). This decouples the plugin, and means it doesn't depend on any MRC classes. 2. Change the delegate so conforming only requires the objects the undo plugin actually needs. Part of flutter/flutter#137801.
1 parent 6c8f8ef commit caf81dd

File tree

6 files changed

+134
-124
lines changed

6 files changed

+134
-124
lines changed

shell/platform/darwin/ios/BUILD.gn

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ source_set("flutter_framework_source_arc") {
8484
"framework/Source/FlutterTextureRegistryRelay.mm",
8585
"framework/Source/FlutterUIPressProxy.h",
8686
"framework/Source/FlutterUIPressProxy.mm",
87+
"framework/Source/FlutterUndoManagerDelegate.h",
88+
"framework/Source/FlutterUndoManagerPlugin.h",
89+
"framework/Source/FlutterUndoManagerPlugin.mm",
8790
"framework/Source/KeyCodeMap.g.mm",
8891
"framework/Source/KeyCodeMap_Internal.h",
8992
"framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h",
@@ -157,9 +160,6 @@ source_set("flutter_framework_source") {
157160
"framework/Source/FlutterPluginAppLifeCycleDelegate.mm",
158161
"framework/Source/FlutterSemanticsScrollView.h",
159162
"framework/Source/FlutterSemanticsScrollView.mm",
160-
"framework/Source/FlutterUndoManagerDelegate.h",
161-
"framework/Source/FlutterUndoManagerPlugin.h",
162-
"framework/Source/FlutterUndoManagerPlugin.mm",
163163
"framework/Source/FlutterView.h",
164164
"framework/Source/FlutterView.mm",
165165
"framework/Source/FlutterViewController.mm",

shell/platform/darwin/ios/framework/Source/FlutterEngine.mm

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,6 @@ - (void)setViewController:(FlutterViewController*)viewController {
430430
[self maybeSetupPlatformViewChannels];
431431
[self updateDisplays];
432432
_textInputPlugin.get().viewController = viewController;
433-
_undoManagerPlugin.get().viewController = viewController;
434433

435434
if (viewController) {
436435
__block FlutterEngine* blockSelf = self;
@@ -465,7 +464,6 @@ - (void)setFlutterViewControllerWillDeallocObserver:(id<NSObject>)observer {
465464
- (void)notifyViewControllerDeallocated {
466465
[[self lifecycleChannel] sendMessage:@"AppLifecycleState.detached"];
467466
_textInputPlugin.get().viewController = nil;
468-
_undoManagerPlugin.get().viewController = nil;
469467
if (!_allowHeadlessExecution) {
470468
[self destroyContext];
471469
} else if (_shell) {
@@ -1189,12 +1187,19 @@ - (void)flutterTextInputView:(FlutterTextInputView*)textInputView
11891187

11901188
#pragma mark - Undo Manager Delegate
11911189

1192-
- (void)flutterUndoManagerPlugin:(FlutterUndoManagerPlugin*)undoManagerPlugin
1193-
handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
1190+
- (void)handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
11941191
NSString* action = (direction == FlutterUndoRedoDirectionUndo) ? @"undo" : @"redo";
11951192
[_undoManagerChannel.get() invokeMethod:@"UndoManagerClient.handleUndo" arguments:@[ action ]];
11961193
}
11971194

1195+
- (UIView<UITextInput>*)activeTextInputView {
1196+
return [[self textInputPlugin] textInputView];
1197+
}
1198+
1199+
- (NSUndoManager*)undoManager {
1200+
return self.viewController.undoManager;
1201+
}
1202+
11981203
#pragma mark - Screenshot Delegate
11991204

12001205
- (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type

shell/platform/darwin/ios/framework/Source/FlutterUndoManagerDelegate.h

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,29 @@ typedef NS_ENUM(NSInteger, FlutterUndoRedoDirection) {
1616
// NOLINTEND(readability-identifier-naming)
1717
};
1818

19-
@class FlutterUndoManagerPlugin;
20-
19+
/**
20+
* Protocol for undo manager changes from the `FlutterUndoManagerPlugin`, typically a
21+
* `FlutterEngine`.
22+
*/
2123
@protocol FlutterUndoManagerDelegate <NSObject>
22-
- (void)flutterUndoManagerPlugin:(FlutterUndoManagerPlugin*)undoManagerPlugin
23-
handleUndoWithDirection:(FlutterUndoRedoDirection)direction;
24+
25+
/**
26+
* The `NSUndoManager` that should be managed by the `FlutterUndoManagerPlugin`.
27+
* When the delegate is `FlutterEngine` this will be the `FlutterViewController`'s undo manager.
28+
*/
29+
@property(nonatomic, readonly, nullable) NSUndoManager* undoManager;
30+
31+
/**
32+
* Used to notify the active view when undo manager state (can redo/can undo)
33+
* changes, in order to force keyboards to update undo/redo buttons.
34+
*/
35+
@property(nonatomic, readonly, nullable) UIView<UITextInput>* activeTextInputView;
36+
37+
/**
38+
* Pass changes to the framework through the undo manager channel.
39+
*/
40+
- (void)handleUndoWithDirection:(FlutterUndoRedoDirection)direction;
41+
2442
@end
2543
NS_ASSUME_NONNULL_END
2644

shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPlugin.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,11 @@
77

88
#import <UIKit/UIKit.h>
99

10-
#import "flutter/fml/memory/weak_ptr.h"
1110
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
12-
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
1311
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerDelegate.h"
1412

1513
@interface FlutterUndoManagerPlugin : NSObject
1614

17-
@property(nonatomic, assign) FlutterViewController* viewController;
18-
1915
- (instancetype)init NS_UNAVAILABLE;
2016
+ (instancetype)new NS_UNAVAILABLE;
2117

shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPlugin.mm

Lines changed: 41 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@
33
// found in the LICENSE file.
44

55
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPlugin.h"
6-
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h"
7-
8-
#import <Foundation/Foundation.h>
9-
#import <UIKit/UIKit.h>
10-
11-
#include "flutter/fml/logging.h"
126

137
#pragma mark - UndoManager channel method names.
148
static NSString* const kSetUndoStateMethod = @"UndoManager.setUndoState";
@@ -17,24 +11,24 @@
1711
static NSString* const kCanUndo = @"canUndo";
1812
static NSString* const kCanRedo = @"canRedo";
1913

20-
@implementation FlutterUndoManagerPlugin {
21-
id<FlutterUndoManagerDelegate> _undoManagerDelegate;
22-
}
14+
@interface FlutterUndoManagerPlugin ()
15+
@property(nonatomic, weak, readonly) id<FlutterUndoManagerDelegate> undoManagerDelegate;
16+
@end
17+
18+
@implementation FlutterUndoManagerPlugin
2319

2420
- (instancetype)initWithDelegate:(id<FlutterUndoManagerDelegate>)undoManagerDelegate {
2521
self = [super init];
2622

2723
if (self) {
28-
// `_undoManagerDelegate` is a weak reference because it should retain FlutterUndoManagerPlugin.
2924
_undoManagerDelegate = undoManagerDelegate;
3025
}
3126

3227
return self;
3328
}
3429

3530
- (void)dealloc {
36-
[self resetUndoManager];
37-
[super dealloc];
31+
[_undoManagerDelegate.undoManager removeAllActionsWithTarget:self];
3832
}
3933

4034
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
@@ -48,46 +42,43 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
4842
}
4943
}
5044

51-
- (NSUndoManager*)undoManager {
52-
return _viewController.undoManager;
53-
}
54-
55-
- (void)resetUndoManager API_AVAILABLE(ios(9.0)) {
56-
[[self undoManager] removeAllActionsWithTarget:self];
45+
- (void)resetUndoManager {
46+
[self.undoManagerDelegate.undoManager removeAllActionsWithTarget:self];
5747
}
5848

59-
- (void)registerUndoWithDirection:(FlutterUndoRedoDirection)direction API_AVAILABLE(ios(9.0)) {
60-
[[self undoManager] beginUndoGrouping];
61-
[[self undoManager] registerUndoWithTarget:self
62-
handler:^(id target) {
63-
// Register undo with opposite direction.
64-
FlutterUndoRedoDirection newDirection =
65-
(direction == FlutterUndoRedoDirectionRedo)
66-
? FlutterUndoRedoDirectionUndo
67-
: FlutterUndoRedoDirectionRedo;
68-
[target registerUndoWithDirection:newDirection];
69-
// Invoke method on delegate.
70-
[_undoManagerDelegate flutterUndoManagerPlugin:self
71-
handleUndoWithDirection:direction];
72-
}];
73-
[[self undoManager] endUndoGrouping];
49+
- (void)registerUndoWithDirection:(FlutterUndoRedoDirection)direction {
50+
NSUndoManager* undoManager = self.undoManagerDelegate.undoManager;
51+
[undoManager beginUndoGrouping];
52+
[undoManager registerUndoWithTarget:self
53+
handler:^(FlutterUndoManagerPlugin* target) {
54+
// Register undo with opposite direction.
55+
FlutterUndoRedoDirection newDirection =
56+
(direction == FlutterUndoRedoDirectionRedo)
57+
? FlutterUndoRedoDirectionUndo
58+
: FlutterUndoRedoDirectionRedo;
59+
[target registerUndoWithDirection:newDirection];
60+
// Invoke method on delegate.
61+
[target.undoManagerDelegate handleUndoWithDirection:direction];
62+
}];
63+
[undoManager endUndoGrouping];
7464
}
7565

76-
- (void)registerRedo API_AVAILABLE(ios(9.0)) {
77-
[[self undoManager] beginUndoGrouping];
78-
[[self undoManager]
79-
registerUndoWithTarget:self
80-
handler:^(id target) {
81-
// Register undo with opposite direction.
82-
[target registerUndoWithDirection:FlutterUndoRedoDirectionRedo];
83-
}];
84-
[[self undoManager] endUndoGrouping];
85-
[[self undoManager] undo];
66+
- (void)registerRedo {
67+
NSUndoManager* undoManager = self.undoManagerDelegate.undoManager;
68+
[undoManager beginUndoGrouping];
69+
[undoManager registerUndoWithTarget:self
70+
handler:^(id target) {
71+
// Register undo with opposite direction.
72+
[target registerUndoWithDirection:FlutterUndoRedoDirectionRedo];
73+
}];
74+
[undoManager endUndoGrouping];
75+
[undoManager undo];
8676
}
8777

88-
- (void)setUndoState:(NSDictionary*)dictionary API_AVAILABLE(ios(9.0)) {
89-
BOOL groupsByEvent = [self undoManager].groupsByEvent;
90-
[self undoManager].groupsByEvent = NO;
78+
- (void)setUndoState:(NSDictionary*)dictionary {
79+
NSUndoManager* undoManager = self.undoManagerDelegate.undoManager;
80+
BOOL groupsByEvent = undoManager.groupsByEvent;
81+
undoManager.groupsByEvent = NO;
9182
BOOL canUndo = [dictionary[kCanUndo] boolValue];
9283
BOOL canRedo = [dictionary[kCanRedo] boolValue];
9384

@@ -99,16 +90,15 @@ - (void)setUndoState:(NSDictionary*)dictionary API_AVAILABLE(ios(9.0)) {
9990
if (canRedo) {
10091
[self registerRedo];
10192
}
102-
103-
if (_viewController.engine.textInputPlugin.textInputView != nil) {
93+
UIView<UITextInput>* textInputView = self.undoManagerDelegate.activeTextInputView;
94+
if (textInputView != nil) {
10495
// This is needed to notify the iPadOS keyboard that it needs to update the
10596
// state of the UIBarButtons. Otherwise, the state changes to NSUndoManager
10697
// will not show up until the next keystroke (or other trigger).
107-
UITextInputAssistantItem* assistantItem =
108-
_viewController.engine.textInputPlugin.textInputView.inputAssistantItem;
98+
UITextInputAssistantItem* assistantItem = textInputView.inputAssistantItem;
10999
assistantItem.leadingBarButtonGroups = assistantItem.leadingBarButtonGroups;
110100
}
111-
[self undoManager].groupsByEvent = groupsByEvent;
101+
undoManager.groupsByEvent = groupsByEvent;
112102
}
113103

114104
@end

0 commit comments

Comments
 (0)