| // Copyright 2013 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 "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" |
| |
| #pragma mark - UndoManager channel method names. |
| static NSString* const kSetUndoStateMethod = @"UndoManager.setUndoState"; |
| |
| #pragma mark - Undo State field names |
| static NSString* const kCanUndo = @"canUndo"; |
| static NSString* const kCanRedo = @"canRedo"; |
| |
| @implementation FlutterUndoManagerPlugin { |
| id<FlutterUndoManagerDelegate> _undoManagerDelegate; |
| } |
| |
| - (instancetype)initWithDelegate:(id<FlutterUndoManagerDelegate>)undoManagerDelegate { |
| self = [super init]; |
| |
| if (self) { |
| // `_undoManagerDelegate` is a weak reference because it should retain FlutterUndoManagerPlugin. |
| _undoManagerDelegate = undoManagerDelegate; |
| } |
| |
| return self; |
| } |
| |
| - (void)dealloc { |
| [self resetUndoManager]; |
| [super dealloc]; |
| } |
| |
| - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { |
| NSString* method = call.method; |
| id args = call.arguments; |
| if ([method isEqualToString:kSetUndoStateMethod]) { |
| [self setUndoState:args]; |
| result(nil); |
| } else { |
| result(FlutterMethodNotImplemented); |
| } |
| } |
| |
| - (NSUndoManager*)undoManager { |
| return _viewController.undoManager; |
| } |
| |
| - (void)resetUndoManager API_AVAILABLE(ios(9.0)) { |
| [[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]; |
| } |
| |
| - (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]; |
| } |
| |
| - (void)setUndoState:(NSDictionary*)dictionary API_AVAILABLE(ios(9.0)) { |
| BOOL groupsByEvent = [self undoManager].groupsByEvent; |
| [self undoManager].groupsByEvent = NO; |
| BOOL canUndo = [dictionary[kCanUndo] boolValue]; |
| BOOL canRedo = [dictionary[kCanRedo] boolValue]; |
| |
| [self resetUndoManager]; |
| |
| if (canUndo) { |
| [self registerUndoWithDirection:FlutterUndoRedoDirectionUndo]; |
| } |
| if (canRedo) { |
| [self registerRedo]; |
| } |
| |
| if (_viewController.engine.textInputPlugin.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). |
| id<UITextInputDelegate> inputDelegate = |
| _viewController.engine.textInputPlugin.textInputView.inputDelegate; |
| [inputDelegate selectionDidChange:_viewController.engine.textInputPlugin.textInputView]; |
| } |
| [self undoManager].groupsByEvent = groupsByEvent; |
| } |
| |
| @end |