Files
keyboard/CustomKeyboard/Utils/KBBackspaceUndoManager.m

123 lines
3.9 KiB
Mathematica
Raw Normal View History

2025-12-19 19:21:08 +08:00
//
// KBBackspaceUndoManager.m
// CustomKeyboard
//
#import "KBBackspaceUndoManager.h"
#import "KBResponderUtils.h"
NSNotificationName const KBBackspaceUndoStateDidChangeNotification = @"KBBackspaceUndoStateDidChangeNotification";
@interface KBBackspaceUndoManager ()
@property (nonatomic, copy) NSString *undoText;
@property (nonatomic, assign) NSInteger undoAfterLength;
2025-12-19 19:21:08 +08:00
@property (nonatomic, assign) BOOL hasUndo;
@end
@implementation KBBackspaceUndoManager
+ (instancetype)shared {
static KBBackspaceUndoManager *mgr = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mgr = [[KBBackspaceUndoManager alloc] init];
});
return mgr;
}
- (instancetype)init {
if (self = [super init]) {
_undoText = @"";
_undoAfterLength = 0;
2025-12-19 19:21:08 +08:00
}
return self;
}
- (void)recordDeletionSnapshotBefore:(NSString *)before after:(NSString *)after {
if (self.undoText.length > 0) { return; }
NSString *safeBefore = before ?: @"";
NSString *safeAfter = after ?: @"";
NSString *full = [safeBefore stringByAppendingString:safeAfter];
if (full.length == 0) { return; }
self.undoText = full;
self.undoAfterLength = (NSInteger)safeAfter.length;
}
2025-12-19 19:21:08 +08:00
- (void)recordClearWithContextBefore:(NSString *)before after:(NSString *)after {
if (self.undoText.length == 0) {
NSString *safeBefore = before ?: @"";
NSString *safeAfter = after ?: @"";
NSString *full = [safeBefore stringByAppendingString:safeAfter];
if (full.length > 0) {
self.undoText = full;
self.undoAfterLength = (NSInteger)safeAfter.length;
}
2025-12-19 19:21:08 +08:00
}
if (self.undoText.length == 0) { return; }
2025-12-19 19:21:08 +08:00
[self kb_updateHasUndo:YES];
}
- (void)performUndoFromResponder:(UIResponder *)responder {
if (self.undoText.length == 0) { return; }
2025-12-19 19:21:08 +08:00
UIInputViewController *ivc = KBFindInputViewController(responder);
if (!ivc) { return; }
id<UITextDocumentProxy> proxy = ivc.textDocumentProxy;
[self kb_clearAllTextForProxy:proxy];
[proxy insertText:self.undoText];
if (self.undoAfterLength > 0 &&
[proxy respondsToSelector:@selector(adjustTextPositionByCharacterOffset:)]) {
[proxy adjustTextPositionByCharacterOffset:-self.undoAfterLength];
}
2025-12-19 19:21:08 +08:00
self.undoText = @"";
self.undoAfterLength = 0;
2025-12-19 19:21:08 +08:00
[self kb_updateHasUndo:NO];
}
- (void)registerNonClearAction {
if (self.undoText.length == 0) { return; }
self.undoText = @"";
self.undoAfterLength = 0;
2025-12-19 19:21:08 +08:00
[self kb_updateHasUndo:NO];
}
#pragma mark - Helpers
- (void)kb_updateHasUndo:(BOOL)hasUndo {
if (self.hasUndo == hasUndo) { return; }
self.hasUndo = hasUndo;
[[NSNotificationCenter defaultCenter] postNotificationName:KBBackspaceUndoStateDidChangeNotification object:self];
}
static const NSInteger kKBUndoClearMaxRounds = 200;
- (void)kb_clearAllTextForProxy:(id<UITextDocumentProxy>)proxy {
if (!proxy) { return; }
if ([proxy respondsToSelector:@selector(adjustTextPositionByCharacterOffset:)]) {
NSInteger guard = 0;
NSString *contextAfter = proxy.documentContextAfterInput ?: @"";
while (contextAfter.length > 0 && guard < kKBUndoClearMaxRounds) {
NSInteger offset = (NSInteger)contextAfter.length;
[proxy adjustTextPositionByCharacterOffset:offset];
for (NSUInteger i = 0; i < contextAfter.length; i++) {
[proxy deleteBackward];
}
guard += 1;
contextAfter = proxy.documentContextAfterInput ?: @"";
2025-12-19 19:21:08 +08:00
}
}
NSInteger guard = 0;
NSString *contextBefore = proxy.documentContextBeforeInput ?: @"";
while (contextBefore.length > 0 && guard < kKBUndoClearMaxRounds) {
for (NSUInteger i = 0; i < contextBefore.length; i++) {
[proxy deleteBackward];
2025-12-19 19:21:08 +08:00
}
guard += 1;
contextBefore = proxy.documentContextBeforeInput ?: @"";
2025-12-19 19:21:08 +08:00
}
}
@end