mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-06 07:37:40 +00:00
context simplification, legacy app determination integrated into TextCompatibilityCheck
This commit is contained in:
parent
cc1c7605df
commit
682d99c2cb
18 changed files with 249 additions and 231 deletions
|
|
@ -116,7 +116,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
if (type == NX_KEYDOWN) {
|
||||
// Key down event
|
||||
NSEvent *mEvent = [NSEvent eventWithCGEvent:event];
|
||||
KMEngine *kme = [[KMEngine alloc] initWithKMX:kmx contextBuffer:contextBuffer];
|
||||
KMEngine *kme = [[KMEngine alloc] initWithKMX:kmx context:contextBuffer];
|
||||
NSArray *actions = [kme processEvent:mEvent];
|
||||
//if (debugMode)
|
||||
NSLog(@"%@", actions);
|
||||
|
|
|
|||
|
|
@ -38,13 +38,13 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
|
|||
-(instancetype)initWithString:(NSString*)initialContext {
|
||||
self = [self init];
|
||||
if (self) {
|
||||
[_context setString:[self trimToMaximumContext:initialContext]];
|
||||
[_context setString:[KMContext trimToMaximumContext:initialContext]];
|
||||
_invalid = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(NSString*)trimToMaximumContext:(NSString*)initialContext {
|
||||
+(NSString*)trimToMaximumContext:(NSString*)initialContext {
|
||||
NSString *maximumContext = nil;
|
||||
|
||||
// copy up to MAXIMUM_CONTEXT_LENGTH from the end of the context
|
||||
|
|
@ -59,8 +59,9 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
|
|||
}
|
||||
|
||||
-(void)resetContext:(NSString*)newContext {
|
||||
[_context setString:[self trimToMaximumContext:newContext]];
|
||||
[_context setString:[KMContext trimToMaximumContext:newContext]];
|
||||
self.invalid = NO;
|
||||
NSLog(@" ***CachedContext resetContext, resulting context = %@", _context);
|
||||
}
|
||||
|
||||
-(NSString*)currentContext {
|
||||
|
|
@ -78,12 +79,14 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
|
|||
|
||||
-(void)clearContext {
|
||||
_context.string = @"";
|
||||
NSLog(@" ***CachedContext clearContext, resulting context = %@", _context);
|
||||
}
|
||||
|
||||
-(void)appendMarker {
|
||||
#define UC_NONCHARACTER 0xFFFF
|
||||
NSString *markerString = [NSString stringWithFormat:@"%C", UC_NONCHARACTER];
|
||||
[self.context appendString:markerString];
|
||||
NSLog(@" ***CachedContext appendMarker, resulting context = %@", _context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -100,6 +103,7 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
|
|||
NSLog(@"KMContext deleteLastUnicodeCharacter, index = %lu, rangeOfLastCharacter = %@\n", characterIndex, NSStringFromRange(characterRange));
|
||||
[self.context deleteCharactersInRange:characterRange];
|
||||
}
|
||||
NSLog(@" ***CachedContext deleteLastCodePoint, resulting context = %@", _context);
|
||||
}
|
||||
|
||||
-(void)replaceSubstring:(NSString*)newText count:(int)count {
|
||||
|
|
@ -113,10 +117,12 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
|
|||
if (newText.length > 0) {
|
||||
[self addSubtring:newText];
|
||||
}
|
||||
NSLog(@" ***CachedContext replaceSubstring, resulting context = %@", _context);
|
||||
}
|
||||
|
||||
-(void)addSubtring:(NSString*)string {
|
||||
[self.context appendString:string];
|
||||
NSLog(@" ***CachedContext addSubtring, resulting context = %@", _context);
|
||||
}
|
||||
|
||||
-(void)applyAction:(CoreAction*)action keyDownEvent:(nonnull NSEvent *)event {
|
||||
|
|
@ -145,6 +151,7 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
|
|||
NSLog(@"CachedContext applyAction, action not applied to context %@\n", action.typeName.description);
|
||||
break;
|
||||
}
|
||||
NSLog(@" ***CachedContext applyAction, resulting context = %@", _context);
|
||||
}
|
||||
|
||||
// TODO: see handleDefaultKeymanEngineActions
|
||||
|
|
@ -189,6 +196,7 @@ NSUInteger const MAXIMUM_CONTEXT_LENGTH = 80; // KM_KBP_IT_CHAR
|
|||
}
|
||||
break;
|
||||
}
|
||||
NSLog(@" ***CachedContext applyUnhandledEvent, resulting context = %@", _context);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -129,42 +129,24 @@ typedef enum {BackspacesOnly,
|
|||
switch (self.pattern) {
|
||||
case CharactersOnly:
|
||||
result = [self buildResultForCharactersOnly];
|
||||
//handledEvent = [self applyCharacterActions:0];
|
||||
break;
|
||||
// TODO: create separate pattern for single backspace? and for EmitKeystroke action?
|
||||
case BackspacesOnly:
|
||||
if ([self isSinglePassThroughBackspace]) {
|
||||
result = [self buildResultForSinglePassThroughBackspaceNoText];
|
||||
} else {
|
||||
result = [self buildResultForMultipleBackspacesNoText];
|
||||
//handledEvent = [self applyIsolatedBackspaceActions:self.backspaceCount];
|
||||
}
|
||||
break;
|
||||
case BackspaceBeforeCharacter:
|
||||
result = [self buildResultForBackspacesBeforeText];
|
||||
//handledEvent = [self applyCharacterActions:self.backspaceCount];
|
||||
break;
|
||||
case None:
|
||||
result = [self buildResultForNoCharactersOrBackspaces];
|
||||
break;
|
||||
default:
|
||||
NSLog(@"Unimplemented pattern***");
|
||||
NSLog(@"KMCoreActionHandler handleActions(), Unimplemented pattern***");
|
||||
}
|
||||
|
||||
// TODO: delete
|
||||
/**
|
||||
* this event is passed through and will be sent to the client
|
||||
* but we need to update the context for it -- no, actually we don't:
|
||||
* the context is updated for every action, and we should still get an action
|
||||
* for a backspace, even if the event is unhandled by Core
|
||||
* if there is a need for this, it would be while processing the Emit Keystroke action
|
||||
*/
|
||||
/*
|
||||
if(!result.handledEvent) {
|
||||
[self.context applyUnhandledEvent:self.event];
|
||||
}
|
||||
*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -185,7 +167,7 @@ typedef enum {BackspacesOnly,
|
|||
}
|
||||
|
||||
/**
|
||||
* For multiple backspaces with no text, new events must be generated for each backspace
|
||||
* Multiple backspaces with no text will be handled by generating events
|
||||
*/
|
||||
-(KMActionHandlerResult*)buildResultForMultipleBackspacesNoText {
|
||||
NSLog(@"buildResultForMultipleBackspacesNoText");
|
||||
|
|
@ -196,7 +178,6 @@ typedef enum {BackspacesOnly,
|
|||
* For backspaces needed before text, insert with replace is possible, but some clients do not support replace
|
||||
*/
|
||||
-(KMActionHandlerResult*)buildResultForBackspacesBeforeText {
|
||||
// TODO: make backspaces events if replacement does not work with insertText
|
||||
NSLog(@"buildResultForBackspacesBeforeText");
|
||||
return [[KMActionHandlerResult alloc] initForActions:self.actions handledEvent:YES backspaceCount:self.backspaceCount textToInsert:[self collectOutputText]];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,6 @@ typedef struct {
|
|||
- (void)postKeyboardEventWithSource: (CGEventSourceRef)source code:(CGKeyCode) virtualKey postCallback:(PostEventCallback)postEvent;
|
||||
- (KeymanVersionInfo)versionInfo;
|
||||
- (NSString *)keymanDataPath;
|
||||
- (NSArray *)legacyAppsUserDefaults;
|
||||
- (void)registerConfigurationWindow:(NSWindowController *)window;
|
||||
@end
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ NSString *const kKMDeprecatedPersistedOptionsKey = @"KMSavedStoresKey";
|
|||
NSString *const kKMPersistedOptionsKey = @"KMPersistedOptionsKey";
|
||||
NSString *const kKMAlwaysShowOSKKey = @"KMAlwaysShowOSKKey";
|
||||
NSString *const kKMUseVerboseLogging = @"KMUseVerboseLogging";
|
||||
NSString *const kKMLegacyApps = @"KMLegacyApps";
|
||||
|
||||
NSString *const kKeymanKeyboardDownloadCompletedNotification = @"kKeymanKeyboardDownloadCompletedNotification";
|
||||
|
||||
|
|
@ -393,7 +392,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
- (KMEngine *)kme {
|
||||
if (_kme == nil) {
|
||||
//TODO: verify that initial context is correct; this is passed to core
|
||||
_kme = [[KMEngine alloc] initWithKMX:nil contextBuffer:self.contextBuffer];
|
||||
_kme = [[KMEngine alloc] initWithKMX:nil context:self.contextBuffer];
|
||||
[_kme setDebugMode:self.debugMode];
|
||||
}
|
||||
|
||||
|
|
@ -530,14 +529,6 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
return _keymanDataPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of user-default legacy apps
|
||||
*/
|
||||
- (NSArray *)legacyAppsUserDefaults {
|
||||
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
|
||||
return [userData arrayForKey:kKMLegacyApps];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root folder where keyboards are stored; currently the same
|
||||
* as the keymanDataPath, but may diverge in future versions (possibly a sub-folder)
|
||||
|
|
@ -773,7 +764,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
_contextBuffer = [contextBuffer mutableCopy];
|
||||
if (_contextBuffer.length)
|
||||
[_contextBuffer replaceOccurrencesOfString:@"\0" withString:[NSString nullChar] options:0 range:NSMakeRange(0, 1)];
|
||||
[self.kme setContextBuffer:self.contextBuffer];
|
||||
[self.kme setCoreContext:self.contextBuffer];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
|
|
|
|||
|
|
@ -59,72 +59,6 @@ NSRange _previousSelRange;
|
|||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the client app requires legacy input mode, first by checking the user defaults, if they exist,
|
||||
* then, by our hard-coded list.
|
||||
*/
|
||||
- (BOOL)isClientAppLegacy:(NSString *)clientAppId {
|
||||
NSArray *legacyAppsUserDefaults = [self.AppDelegate legacyAppsUserDefaults];
|
||||
|
||||
BOOL result = NO;
|
||||
|
||||
if(legacyAppsUserDefaults != nil) {
|
||||
result = [self isClientAppLegacy:clientAppId fromArray:legacyAppsUserDefaults];
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
// TODO: Pages and Keynote (and possibly lots of other undiscovered apps that are otherwise compliant
|
||||
// with Apple's IM framework) have a problem in that if the user selects a different font (or other
|
||||
// formatting) and then types a sequence that causes characters to be added to the document and then
|
||||
// subsequently replaced, the replacement causes the formatting decision to be forgotten. This can be
|
||||
// "fixed" by treating them as legacy apps, but it causes other problems.
|
||||
result = ([clientAppId isEqual: @"com.github.atom"] ||
|
||||
[clientAppId isEqual: @"com.collabora.libreoffice-free"] ||
|
||||
[clientAppId isEqual: @"org.libreoffice.script"] ||
|
||||
[clientAppId isEqual: @"com.axosoft.gitkraken"] ||
|
||||
[clientAppId isEqual: @"org.sil.app.builder.scripture.ScriptureAppBuilder"] ||
|
||||
[clientAppId isEqual: @"org.sil.app.builder.reading.ReadingAppBuilder"] ||
|
||||
[clientAppId isEqual: @"org.sil.app.builder.dictionary.DictionaryAppBuilder"] ||
|
||||
//[clientAppId isEqual: @"com.microsoft.Word"] || // 2020-11-24[mcd]: Appears to work well in Word 16.43, disable legacy by default
|
||||
[clientAppId isEqual: @"org.openoffice.script"] ||
|
||||
[clientAppId isEqual: @"com.adobe.illustrator"] ||
|
||||
[clientAppId isEqual: @"com.adobe.InDesign"] ||
|
||||
[clientAppId isEqual: @"com.adobe.Photoshop"] ||
|
||||
[clientAppId isEqual: @"com.adobe.AfterEffects"] ||
|
||||
[clientAppId isEqual: @"com.microsoft.VSCode"] ||
|
||||
[clientAppId isEqual: @"com.google.Chrome"] ||
|
||||
[clientAppId hasPrefix: @"net.java"] ||
|
||||
[clientAppId isEqual: @"com.Keyman.test.legacyInput"]
|
||||
/*||[clientAppId isEqual: @"ro.sync.exml.Oxygen"] - Oxygen has worse problems */
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks user defaults array for a list of possible regexes to match a client app id
|
||||
*/
|
||||
- (BOOL)isClientAppLegacy:(NSString *)clientAppId fromArray:(NSArray *)legacyApps {
|
||||
for(id legacyApp in legacyApps) {
|
||||
if(![legacyApp isKindOfClass:[NSString class]]) {
|
||||
NSLog(@"isClientAppLegacy:fromArray: LegacyApps user defaults array should contain only strings");
|
||||
} else {
|
||||
NSError *error = nil;
|
||||
NSRange range = NSMakeRange(0, clientAppId.length);
|
||||
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: (NSString *) legacyApp options: 0 error: &error];
|
||||
NSArray *matchesArray = [regex matchesInString:clientAppId options:0 range:range];
|
||||
if(matchesArray.count>0) {
|
||||
NSLog(@"isClientAppLegacy: found match for legacy app %@: ", clientAppId);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
// This is the public initializer.
|
||||
- (instancetype)initWithClient:(NSString *)clientAppId client:(id) sender {
|
||||
_textCompatibility = [[TextCompatibilityCheck alloc]initWithClient:sender applicationId:clientAppId];
|
||||
|
|
@ -136,7 +70,7 @@ NSRange _previousSelRange;
|
|||
_generatedBackspaceCount = 0;
|
||||
|
||||
|
||||
BOOL legacy = [self isClientAppLegacy:clientAppId];
|
||||
//BOOL legacy = [self isClientAppLegacy:clientAppId];
|
||||
|
||||
// In Xcode, if Keyman is the active IM and is in "debugMode" and "English plus Spanish" is
|
||||
// the current keyboard and you type "Sentry force now", it will force a simulated crash to
|
||||
|
|
@ -148,10 +82,12 @@ NSRange _previousSelRange;
|
|||
else
|
||||
_easterEggForSentry = nil;
|
||||
|
||||
//TODO: move to TextCompatibilityCheck
|
||||
// For the Atom editor, this isn't really true (the context CAN change unexpectedly), but we can't get
|
||||
// the context, so we pretend/hope it won't.
|
||||
BOOL selectionCanChangeUnexpectedly = (![clientAppId isEqual: @"com.github.atom"]);
|
||||
return [self initWithLegacyMode:legacy clientSelectionCanChangeUnexpectedly:selectionCanChangeUnexpectedly];
|
||||
// TODO: this equates 'legacyMode' with being forced to send events
|
||||
return [self initWithLegacyMode:self.textCompatibility.mustBackspaceUsingEvents clientSelectionCanChangeUnexpectedly:selectionCanChangeUnexpectedly];
|
||||
}
|
||||
|
||||
- (void)switchToLegacyMode {
|
||||
|
|
@ -967,7 +903,7 @@ NSRange _previousSelRange;
|
|||
//MARK: Core-related key processing
|
||||
// replacement handleEvent implementation for core event processing
|
||||
- (BOOL)handleEvent:(NSEvent *)event client:(id)sender {
|
||||
NSLog(@"SGS handleEvent event = %@", event);
|
||||
NSLog(@"handleEvent event = %@", event);
|
||||
|
||||
// mouse movement requires that the context be invalidated
|
||||
|
||||
|
|
@ -1053,13 +989,14 @@ NSRange _previousSelRange;
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
-(void)checkClientTextCompatibility:(id) client {
|
||||
if(!self.textCompatibility) {
|
||||
_textCompatibility = [[TextCompatibilityCheck alloc]initWithClient:client applicationId:self.clientApplicationId];
|
||||
NSLog(@"KMInputMethodHandler checkClientTextCompatibility: %@", self.textCompatibility);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
-(BOOL)containsKeymanCoreActions:(NSArray*)actions {
|
||||
BOOL containsCoreActions = NO;
|
||||
|
|
@ -1110,7 +1047,7 @@ NSRange _previousSelRange;
|
|||
NSUInteger realLengthAfter =
|
||||
[_cachedContext.currentContext lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4;
|
||||
|
||||
NSLog(@"applyActions, contextBefore = '%@', length = %lu, real length = %lu; contextAfter = '%@', length = %lu, real length = %lu", contextBefore, lengthBefore, realLengthBefore, contextAfter, lengthAfter, realLengthAfter);
|
||||
NSLog(@"applyKeymanCoreActions, contextBefore = '%@', length = %lu, real length = %lu; contextAfter = '%@', length = %lu, real length = %lu", contextBefore, lengthBefore, realLengthBefore, contextAfter, lengthAfter, realLengthAfter);
|
||||
|
||||
return result.handledEvent;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Keyman is copyright (C) SIL International. MIT License.
|
||||
*
|
||||
* KeySender.h
|
||||
* KeyTest
|
||||
* Keyman
|
||||
*
|
||||
* Created by Shawn Schantz on 2023-04-17.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Keyman is copyright (C) SIL International. MIT License.
|
||||
*
|
||||
* KeySender.m
|
||||
* KeyTest
|
||||
* Keyman
|
||||
*
|
||||
* Created by Shawn Schantz on 2023-04-17.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Keyman is copyright (C) SIL International. MIT License.
|
||||
*
|
||||
* TextCompatibilityCheck.h
|
||||
* KeyTest
|
||||
* Keyman
|
||||
*
|
||||
* Created by Shawn Schantz on 2023-05-05.
|
||||
*
|
||||
|
|
@ -21,7 +21,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
-(BOOL)canInsertText;
|
||||
-(BOOL)canReplaceText;
|
||||
-(BOOL)mustBackspaceUsingEvents;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* Keyman is copyright (C) SIL International. MIT License.
|
||||
*
|
||||
* TextCompatibilityCheck.m
|
||||
* KeyTest
|
||||
* Keyman
|
||||
*
|
||||
* Created by Shawn Schantz on 2023-05-05.
|
||||
*
|
||||
|
|
@ -12,6 +12,8 @@
|
|||
#import "TextCompatibilityCheck.h"
|
||||
#import <InputMethodKit/InputMethodKit.h>
|
||||
|
||||
NSString *const kKMLegacyApps = @"KMLegacyApps";
|
||||
|
||||
@interface TextCompatibilityCheck()
|
||||
|
||||
@property (readonly) id client;
|
||||
|
|
@ -22,7 +24,6 @@
|
|||
|
||||
@end
|
||||
|
||||
|
||||
@implementation TextCompatibilityCheck
|
||||
|
||||
-(instancetype)initWithClient:(id) client applicationId:(NSString *)appId {
|
||||
|
|
@ -47,20 +48,16 @@ return [NSString stringWithFormat:@"hasSelectionAPI: %d, hasReadAPI: %d, hasInse
|
|||
-(BOOL) checkSelectionApi:(id) client applicationId:(NSString *)clientAppId {
|
||||
BOOL workingSelectionApi = NO;
|
||||
|
||||
// if the selector exists, then call the API and see if it returns a valid value
|
||||
workingSelectionApi = [client respondsToSelector:@selector(selectedRange)];
|
||||
|
||||
if ([client respondsToSelector:@selector(selectedRange)]) {
|
||||
/*
|
||||
// if the selector exists, then call the API and see if it returns a valid value
|
||||
if (workingSelectionApi) {
|
||||
NSRange selectionRange = [self.client selectedRange];
|
||||
NSLog(@"TextCompatibilityCheck checkSelectionApi, location = %lu, length = %lu", selectionRange.location, selectionRange.length);
|
||||
|
||||
NSRange notFoundRange = NSMakeRange(NSNotFound, NSNotFound);
|
||||
|
||||
/*
|
||||
if (selectionRange.location == 0) {
|
||||
canGetSelection = NO;
|
||||
NSLog(@"checkCanGetSelection, selectionRange.location == 0 ");
|
||||
} else
|
||||
*/
|
||||
if (NSEqualRanges(selectionRange, notFoundRange)) {
|
||||
workingSelectionApi = YES;
|
||||
// no current selection, but API is working
|
||||
|
|
@ -69,12 +66,39 @@ return [NSString stringWithFormat:@"hasSelectionAPI: %d, hasReadAPI: %d, hasInse
|
|||
workingSelectionApi = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: integrate with existing legacy code
|
||||
*/
|
||||
|
||||
|
||||
// if the selection API appears to work, it may still be broken
|
||||
// getting the selection does not work for anything in the legacy app list
|
||||
// getting the selection does not work for anything in the noncompliant app lists
|
||||
if (workingSelectionApi) {
|
||||
BOOL isLegacy = ([clientAppId isEqual: @"com.github.atom"] ||
|
||||
workingSelectionApi = ![self containedInNoncompliantAppLists:clientAppId];
|
||||
}
|
||||
|
||||
NSLog(@"hasWorkingSelectionApi for app %@: set to %@", clientAppId, workingSelectionApi?@"yes":@"no");
|
||||
return workingSelectionApi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the client app is known to be non-complian, first by checking the hard-coded non-compliant app list
|
||||
* and then by checking the user-managed (via user defaults) non-compliant app list
|
||||
*/
|
||||
- (BOOL)containedInNoncompliantAppLists:(NSString *)clientAppId {
|
||||
BOOL isAppNonCompliant = [self containedInHardCodedNoncompliantAppList:clientAppId];
|
||||
if (!isAppNonCompliant) {
|
||||
isAppNonCompliant = [self containedInUserManagedNoncompliantAppList:clientAppId];
|
||||
}
|
||||
|
||||
NSLog(@"containedInNoncompliantAppLists: for app %@: %@", clientAppId, isAppNonCompliant?@"yes":@"no");
|
||||
return isAppNonCompliant;
|
||||
}
|
||||
|
||||
/** Check this hard-coded list to see if the application ID is among those
|
||||
* that are known to not implement selectionRange correctly.
|
||||
* This was formerly called the legacy app list, renamed to improve clarity.
|
||||
*/
|
||||
- (BOOL)containedInHardCodedNoncompliantAppList:(NSString *)clientAppId {
|
||||
BOOL isAppNonCompliant = ([clientAppId isEqual: @"com.github.atom"] ||
|
||||
[clientAppId isEqual: @"com.collabora.libreoffice-free"] ||
|
||||
[clientAppId isEqual: @"org.libreoffice.script"] ||
|
||||
[clientAppId isEqual: @"com.axosoft.gitkraken"] ||
|
||||
|
|
@ -93,11 +117,105 @@ return [NSString stringWithFormat:@"hasSelectionAPI: %d, hasReadAPI: %d, hasInse
|
|||
[clientAppId isEqual: @"com.Keyman.test.legacyInput"]
|
||||
/*||[clientAppId isEqual: @"ro.sync.exml.Oxygen"] - Oxygen has worse problems */
|
||||
);
|
||||
workingSelectionApi = !isLegacy;
|
||||
NSLog(@"containedInHardCodedNoncompliantAppList: for app %@: %@", clientAppId, isAppNonCompliant?@"yes":@"no");
|
||||
return isAppNonCompliant;
|
||||
}
|
||||
return workingSelectionApi;
|
||||
|
||||
/**
|
||||
* Returns the list of user-default legacy apps
|
||||
*/
|
||||
- (NSArray *)legacyAppsUserDefaults {
|
||||
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
|
||||
return [userData arrayForKey:kKMLegacyApps];
|
||||
}
|
||||
|
||||
/** Check this user-managed list to see if the application ID is among those
|
||||
* that are known to not implement selectionRange correctly.
|
||||
* This was formerly called the legacy app list, renamed to improve clarity.
|
||||
*/
|
||||
- (BOOL)containedInUserManagedNoncompliantAppList:(NSString *)clientAppId {
|
||||
BOOL isAppNonCompliant = false;
|
||||
NSArray *legacyAppsUserDefaults = self.legacyAppsUserDefaults;
|
||||
|
||||
if(legacyAppsUserDefaults != nil) {
|
||||
isAppNonCompliant = [self isClientAppLegacy:clientAppId fromArray:legacyAppsUserDefaults];
|
||||
}
|
||||
NSLog(@"containedInUserManagedNoncompliantAppList: for app %@: %@", clientAppId, isAppNonCompliant?@"yes":@"no");
|
||||
|
||||
return isAppNonCompliant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the client app requires legacy input mode, first by checking the user defaults, if they exist,
|
||||
* then, by our hard-coded list.
|
||||
*/
|
||||
/*
|
||||
- (BOOL)isClientAppLegacy:(NSString *)clientAppId {
|
||||
BOOL isAppNonCompliant = false;
|
||||
|
||||
NSArray *legacyAppsUserDefaults = self.legacyAppsUserDefaults;
|
||||
|
||||
BOOL result = NO;
|
||||
|
||||
if(legacyAppsUserDefaults != nil) {
|
||||
result = [self isClientAppLegacy:clientAppId fromArray:legacyAppsUserDefaults];
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
// TODO: Pages and Keynote (and possibly lots of other undiscovered apps that are otherwise compliant
|
||||
// with Apple's IM framework) have a problem in that if the user selects a different font (or other
|
||||
// formatting) and then types a sequence that causes characters to be added to the document and then
|
||||
// subsequently replaced, the replacement causes the formatting decision to be forgotten. This can be
|
||||
// "fixed" by treating them as legacy apps, but it causes other problems.
|
||||
result = ([clientAppId isEqual: @"com.github.atom"] ||
|
||||
[clientAppId isEqual: @"com.collabora.libreoffice-free"] ||
|
||||
[clientAppId isEqual: @"org.libreoffice.script"] ||
|
||||
[clientAppId isEqual: @"com.axosoft.gitkraken"] ||
|
||||
[clientAppId isEqual: @"org.sil.app.builder.scripture.ScriptureAppBuilder"] ||
|
||||
[clientAppId isEqual: @"org.sil.app.builder.reading.ReadingAppBuilder"] ||
|
||||
[clientAppId isEqual: @"org.sil.app.builder.dictionary.DictionaryAppBuilder"] ||
|
||||
//[clientAppId isEqual: @"com.microsoft.Word"] || // 2020-11-24[mcd]: Appears to work well in Word 16.43, disable legacy by default
|
||||
[clientAppId isEqual: @"org.openoffice.script"] ||
|
||||
[clientAppId isEqual: @"com.adobe.illustrator"] ||
|
||||
[clientAppId isEqual: @"com.adobe.InDesign"] ||
|
||||
[clientAppId isEqual: @"com.adobe.Photoshop"] ||
|
||||
[clientAppId isEqual: @"com.adobe.AfterEffects"] ||
|
||||
[clientAppId isEqual: @"com.microsoft.VSCode"] ||
|
||||
[clientAppId isEqual: @"com.google.Chrome"] ||
|
||||
[clientAppId hasPrefix: @"net.java"] ||
|
||||
[clientAppId isEqual: @"com.Keyman.test.legacyInput"]
|
||||
//||[clientAppId isEqual: @"ro.sync.exml.Oxygen"] - Oxygen has worse problems
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks array for a list of possible regexes to match a client app id
|
||||
*/
|
||||
- (BOOL)isClientAppLegacy:(NSString *)clientAppId fromArray:(NSArray *)legacyApps {
|
||||
for(id legacyApp in legacyApps) {
|
||||
if(![legacyApp isKindOfClass:[NSString class]]) {
|
||||
NSLog(@"isClientAppLegacy:fromArray: LegacyApps user defaults array should contain only strings");
|
||||
} else {
|
||||
NSError *error = nil;
|
||||
NSRange range = NSMakeRange(0, clientAppId.length);
|
||||
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: (NSString *) legacyApp options: 0 error: &error];
|
||||
NSArray *matchesArray = [regex matchesInString:clientAppId options:0 range:range];
|
||||
if(matchesArray.count>0) {
|
||||
NSLog(@"isClientAppLegacy: found match for legacy app %@: ", clientAppId);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
-(BOOL) canGetSelection {
|
||||
return self.hasWorkingSelectionApi;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,19 @@
|
|||
#import "KMInputMethodEventHandlerProtected.h"
|
||||
#import "LegacyTestClient.h"
|
||||
#import "AppleCompliantTestClient.h"
|
||||
#import "TextCompatibilityCheck.h"
|
||||
|
||||
@interface InputMethodTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
// included following interface that we can see and test private methods of TextCompatibilityCheck
|
||||
@interface TextCompatibilityCheck (Testing)
|
||||
|
||||
- (BOOL)isClientAppLegacy:(NSString *)clientAppId fromArray:(NSArray *)legacyApps;
|
||||
|
||||
@end
|
||||
|
||||
@implementation InputMethodTests
|
||||
|
||||
- (void)setUp {
|
||||
|
|
@ -30,25 +38,25 @@
|
|||
}
|
||||
|
||||
- (void)testIsClientAppLegacy_unlistedClientAppId_returnsNo {
|
||||
KMInputMethodEventHandler * inputMethod = [[KMInputMethodEventHandler alloc] initWithLegacyMode:NO clientSelectionCanChangeUnexpectedly:NO];
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextCompatibilityCheck *textCompatibility = [[TextCompatibilityCheck alloc]initWithClient:client applicationId:clientAppId];
|
||||
|
||||
NSArray *legacyAppsArray = [NSArray arrayWithObjects:@"com.microsoft.VSCode",@"com.adobe.Photoshop",nil];
|
||||
|
||||
BOOL isLegacy = [inputMethod isClientAppLegacy:clientAppId fromArray:legacyAppsArray];
|
||||
BOOL isLegacy = [textCompatibility isClientAppLegacy:clientAppId fromArray:legacyAppsArray];
|
||||
NSLog(@"isLegacy = %@", isLegacy?@"yes":@"no");
|
||||
XCTAssert(isLegacy == NO, @"App not expected to be in legacy list");
|
||||
}
|
||||
|
||||
- (void)testIsClientAppLegacy_listedClientAppId_returnsYes {
|
||||
KMInputMethodEventHandler * inputMethod = [[KMInputMethodEventHandler alloc] initWithLegacyMode:NO clientSelectionCanChangeUnexpectedly:NO];
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
|
||||
NSString *clientAppId = @"com.microsoft.VSCode";
|
||||
TextCompatibilityCheck *textCompatibility = [[TextCompatibilityCheck alloc]initWithClient:client applicationId:clientAppId];
|
||||
|
||||
NSArray *legacyAppsArray = [NSArray arrayWithObjects:@"com.adobe.Photoshop",@"com.microsoft.VSCode",nil];
|
||||
|
||||
BOOL isLegacy = [inputMethod isClientAppLegacy:clientAppId fromArray:legacyAppsArray];
|
||||
BOOL isLegacy = [textCompatibility isClientAppLegacy:clientAppId fromArray:legacyAppsArray];
|
||||
NSLog(@"isLegacy = %@", isLegacy?@"yes":@"no");
|
||||
XCTAssert(isLegacy == YES, @"App expected to be in legacy list");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
- (KMEngine *)kme {
|
||||
if (_kme == nil) {
|
||||
_kme = [[KMEngine alloc] initWithKMX:nil contextBuffer:self.contextBuffer];
|
||||
_kme = [[KMEngine alloc] initWithKMX:nil context:self.contextBuffer];
|
||||
[_kme setDebugMode:self.debugMode];
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
_contextBuffer = [contextBuffer mutableCopy];
|
||||
if (_contextBuffer.length)
|
||||
[_contextBuffer replaceOccurrencesOfString:@"\0" withString:[NSString nullChar] options:0 range:NSMakeRange(0, 1)];
|
||||
[self.kme setContextBuffer:self.contextBuffer];
|
||||
[self.kme setCoreContext:self.contextBuffer];
|
||||
}
|
||||
|
||||
- (BOOL)debugMode {
|
||||
|
|
|
|||
|
|
@ -81,34 +81,26 @@ UInt32 VirtualKeyMap[VIRTUAL_KEY_ARRAY_SIZE];
|
|||
return optimizedActionArray;
|
||||
}
|
||||
|
||||
// TODO: remove excessive debug statements above and below
|
||||
-(UInt32)macToKeymanModifier:(NSEventModifierFlags)modifiers {
|
||||
UInt32 keymanModifiers = 0;
|
||||
if ([self isShiftKey:modifiers]) {
|
||||
keymanModifiers |= KM_KBP_MODIFIER_SHIFT;
|
||||
NSLog(@"macToKeymanModifier setting shift bit");
|
||||
}
|
||||
|
||||
if([self isLeftOptionKey:modifiers]) {
|
||||
keymanModifiers |= KM_KBP_MODIFIER_LALT;
|
||||
NSLog(@"macToKeymanModifier setting left alt bit");
|
||||
} else if([self isRightOptionKey:modifiers]) {
|
||||
keymanModifiers |= KM_KBP_MODIFIER_RALT;
|
||||
NSLog(@"macToKeymanModifier setting right alt bit");
|
||||
} else if ([self isOptionKey:modifiers]) {
|
||||
keymanModifiers |= KM_KBP_MODIFIER_ALT;
|
||||
NSLog(@"macToKeymanModifier setting alt bit");
|
||||
}
|
||||
|
||||
if ([self isLeftControlKey:modifiers]) {
|
||||
keymanModifiers |= KM_KBP_MODIFIER_LCTRL;
|
||||
NSLog(@"macToKeymanModifier setting left control bit");
|
||||
} else if ([self isRightControlKey:modifiers]) {
|
||||
keymanModifiers |= KM_KBP_MODIFIER_RCTRL;
|
||||
NSLog(@"macToKeymanModifier setting right control bit");
|
||||
} else if ([self isControlKey:modifiers]) {
|
||||
keymanModifiers |= KM_KBP_MODIFIER_CTRL;
|
||||
NSLog(@"macToKeymanModifier setting control bit");
|
||||
}
|
||||
|
||||
if ([self isCapsLockKey:modifiers]) {
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@interface CoreWrapper : NSObject
|
||||
|
||||
@property (strong, nonatomic, readonly) NSString * keyboardId;
|
||||
@property (strong, nonatomic, readonly) NSString * keyboardVersion;
|
||||
@property (strong, nonatomic) NSString * context;
|
||||
@property (strong, nonatomic, readonly) NSString *keyboardId;
|
||||
@property (strong, nonatomic, readonly) NSString *keyboardVersion;
|
||||
@property (strong, nonatomic) NSString *context;
|
||||
|
||||
-(instancetype)initWithHelper:(CoreHelper*)helper kmxFilePath:(nullable NSString*)path;
|
||||
-(BOOL)setOptionsForCore: (NSString *) key value:(NSString *) value;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@
|
|||
[self setContextUsingCore:newValue];
|
||||
}
|
||||
|
||||
-(void)clearContext {
|
||||
[self clearContextUsingCore];
|
||||
}
|
||||
-(void)changeKeyboardWithKmxFilePath:(NSString*) path {
|
||||
if (path != nil) {
|
||||
@try {
|
||||
|
|
@ -299,19 +302,28 @@
|
|||
return immutableString;
|
||||
}
|
||||
|
||||
-(void)setContextUsingCore:(NSString*)context {
|
||||
char const *coreString = [context cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
km_kbp_context_item *contextItemArray;
|
||||
|
||||
// create array of context items
|
||||
km_kbp_status result = km_kbp_context_items_from_utf8(coreString, &contextItemArray);
|
||||
NSLog(@"km_kbp_context_items_from_utf8, result=%i", result);
|
||||
|
||||
// set the context in core using the array
|
||||
-(void)clearContextUsingCore {
|
||||
km_kbp_context * coreContext = km_kbp_state_context(self.keyboardState);
|
||||
km_kbp_context_set(coreContext, contextItemArray);
|
||||
// dispose
|
||||
km_kbp_context_items_dispose(contextItemArray);
|
||||
km_kbp_context_clear(coreContext);
|
||||
}
|
||||
|
||||
-(void)setContextUsingCore:(NSString*)context {
|
||||
if (context.length == 0) {
|
||||
[self clearContextUsingCore];
|
||||
} else {
|
||||
char const *coreString = [context cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
km_kbp_context_item *contextItemArray;
|
||||
|
||||
// create array of context items
|
||||
km_kbp_status result = km_kbp_context_items_from_utf8(coreString, &contextItemArray);
|
||||
NSLog(@"km_kbp_context_items_from_utf8, result=%i", result);
|
||||
|
||||
// set the context in core using the array
|
||||
km_kbp_context * coreContext = km_kbp_state_context(self.keyboardState);
|
||||
km_kbp_context_set(coreContext, contextItemArray);
|
||||
// dispose
|
||||
km_kbp_context_items_dispose(contextItemArray);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: create and save as static
|
||||
|
|
|
|||
|
|
@ -19,11 +19,16 @@
|
|||
@property (weak, nonatomic) KMXFile *kmx;
|
||||
@property (assign, nonatomic) BOOL debugMode;
|
||||
|
||||
- (id)initWithKMX:(KMXFile *)kmx contextBuffer:(NSString *)ctxBuf;
|
||||
- (id)initWithKMX:(KMXFile *)kmx context:(NSString *)ctxBuf;
|
||||
- (NSString *)getCoreContext;
|
||||
- (void)clearCoreContext;
|
||||
- (void)setCoreContext:(NSString *)context;
|
||||
|
||||
//TODO: remove additional context methods
|
||||
/*
|
||||
- (void)setContextBuffer:(NSString *)ctxBuf;
|
||||
- (NSString *)contextBuffer;
|
||||
*/
|
||||
- (void)setCoreOptions:(NSString *)key withValue:(NSString *)value;
|
||||
- (NSArray *)processEvent:(NSEvent *)event;
|
||||
- (void)setUseVerboseLogging:(BOOL)useVerboseLogging;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
//DWORD VKMap[0x80];
|
||||
|
||||
@interface KMEngine ()
|
||||
@property (strong, nonatomic) NSMutableString *tmpCtxBuf;
|
||||
@property (readonly) CoreHelper *coreHelper;
|
||||
@property (nonatomic, retain) CoreWrapper * keymanCore;
|
||||
@end
|
||||
|
|
@ -31,7 +30,7 @@ NSMutableString* _easterEggForSentry = nil;
|
|||
const NSString* kEasterEggText = @"Sentrycrash#KME";
|
||||
const NSString* kEasterEggKmxName = @"EnglishSpanish.kmx";
|
||||
|
||||
- (id)initWithKMX:(KMXFile *)kmx contextBuffer:(NSString *)ctxBuf {
|
||||
- (id)initWithKMX:(KMXFile *)kmx context:(NSString *)contextString {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
#ifdef DEBUG
|
||||
|
|
@ -44,14 +43,8 @@ const NSString* kEasterEggKmxName = @"EnglishSpanish.kmx";
|
|||
|
||||
if (kmx) {
|
||||
[self loadCoreWrapperFromKmxFile:self.kmx.filePath];
|
||||
[self.keymanCore setContext:ctxBuf];
|
||||
[self.keymanCore setContext:contextString];
|
||||
}
|
||||
|
||||
_tmpCtxBuf = [[NSMutableString alloc] initWithString:ctxBuf];
|
||||
[_tmpCtxBuf removeAllNullChars];
|
||||
if (_tmpCtxBuf.length)
|
||||
[_tmpCtxBuf replaceOccurrencesOfString:@"\0" withString:@"" options:0 range:NSMakeRange(0, 1)];
|
||||
//[self setVKMapping];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
|
@ -97,36 +90,15 @@ const NSString* kEasterEggKmxName = @"EnglishSpanish.kmx";
|
|||
NSLog(@"KME - Turning verbose logging off");
|
||||
}
|
||||
|
||||
- (NSString *)getCoreContext {
|
||||
return self.keymanCore.context;
|
||||
}
|
||||
|
||||
- (void)setCoreContext:(NSString *)context {
|
||||
[self.keymanCore setContext:context];
|
||||
}
|
||||
|
||||
- (void)setContextBuffer:(NSString *)ctxBuf {
|
||||
_tmpCtxBuf = [[NSMutableString alloc] initWithString:ctxBuf];
|
||||
[_tmpCtxBuf removeAllNullChars];
|
||||
if (_tmpCtxBuf.length)
|
||||
[_tmpCtxBuf replaceOccurrencesOfString:@"\0" withString:@"" options:0 range:NSMakeRange(0, 1)];
|
||||
}
|
||||
|
||||
- (NSString *)contextBuffer {
|
||||
return [NSString stringWithString:self.tmpCtxBuf];
|
||||
}
|
||||
|
||||
- (NSMutableString *)tmpCtxBuf {
|
||||
if (_tmpCtxBuf == nil) {
|
||||
_tmpCtxBuf = [[NSMutableString alloc] initWithString:@""];
|
||||
}
|
||||
|
||||
return _tmpCtxBuf;
|
||||
}
|
||||
|
||||
- (void)setCoreOptions:(NSString *)key withValue:(NSString *)value {
|
||||
/*
|
||||
KMCompStore *store = [self.kmx.store objectAtIndex:storeID];
|
||||
KMCompStore *storeSaved = [self.kmx.storeSaved objectAtIndex:storeID];
|
||||
store.string = [[NSString alloc] initWithString:value];
|
||||
storeSaved.string = [[NSString alloc] initWithString:value];
|
||||
*/
|
||||
BOOL success = [self.keymanCore setOptionsForCore:key value:value];
|
||||
NSLog(@"setCoreOptions for key: %@, value: %@ succeeded = %@", key, value, success ? @"YES" : @"NO");
|
||||
}
|
||||
|
|
@ -144,11 +116,7 @@ const NSString* kEasterEggKmxName = @"EnglishSpanish.kmx";
|
|||
if ([coreActions count] == 0) {
|
||||
return nil;
|
||||
} else {
|
||||
// update context from core
|
||||
[self.tmpCtxBuf setString:self.keymanCore.context];
|
||||
return coreActions;
|
||||
// TODO: integrate the different contexts
|
||||
// -- probably ignore/trash tmpCtxBuf because we update local context using actions
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ NSString * names[nCombinations];
|
|||
}
|
||||
|
||||
- (void)testinitWithKMX_NilKmx_ProcessEventReturnsNil {
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:nil contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:nil context:@""];
|
||||
NSEvent *event = [[NSEvent alloc] init];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions == nil, @"Expected processEvent to return nil for nil kmx");
|
||||
|
|
@ -39,28 +39,28 @@ NSString * names[nCombinations];
|
|||
|
||||
- (void)testinitWithKMX_ValidKmxEmptyContext_InitializedWithEmptyContext {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
XCTAssert(engine != nil, @"Expected non-nil engine");
|
||||
XCTAssert(engine.contextBuffer.length == 0, @"Expected empty context buffer");
|
||||
XCTAssert(engine.getCoreContext.length == 0, @"Expected empty context buffer");
|
||||
}
|
||||
|
||||
- (void)testinitWithKMX_ValidKmxNonEmptyContext_InitializedWithContext {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@"abc"];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@"abc"];
|
||||
XCTAssert(engine != nil, @"Expected non-nil engine");
|
||||
XCTAssert([engine.contextBuffer isEqualToString:@"abc"], @"Expected 'abc' in context buffer");
|
||||
XCTAssert([engine.getCoreContext isEqualToString:@"abc"], @"Expected 'abc' in context buffer");
|
||||
}
|
||||
|
||||
- (void)testsetContextBuffer_NonEmptyContext_InitializedWithContext {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
[engine setContextBuffer:@"xyz"];
|
||||
XCTAssert([engine.contextBuffer isEqualToString:@"xyz"], @"Expected 'xyz' in context buffer");
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
[engine setCoreContext:@"xyz"];
|
||||
XCTAssert([engine.getCoreContext isEqualToString:@"xyz"], @"Expected 'xyz' in context buffer");
|
||||
}
|
||||
|
||||
- (void)testprocessEvent_eventForCommandKey_ReturnsNilActionsArray {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:NSEventModifierFlagCommand timestamp:0 windowNumber:0 context:nil characters:@"a" charactersIgnoringModifiers:@"a" isARepeat:NO keyCode:kVK_ANSI_A];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions == nil, @"Expected nil array of actions");
|
||||
|
|
@ -68,7 +68,7 @@ NSString * names[nCombinations];
|
|||
|
||||
- (void)testprocessEvent_eventWithoutKeycode_ReturnsNilActionsArray {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
NSEvent *event = [NSEvent mouseEventWithType:NSEventTypeMouseMoved location:NSMakePoint(29, 21) modifierFlags:NSEventModifierFlagShift timestamp:0 windowNumber:0 context:nil eventNumber:23 clickCount:0 pressure:0];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions == nil, @"Expected nil array of actions");
|
||||
|
|
@ -77,7 +77,7 @@ NSString * names[nCombinations];
|
|||
// TODO:
|
||||
- (void)testprocessEvent_eventForUnmappedKey_ReturnsNoActions {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"z" charactersIgnoringModifiers:@"z" isARepeat:NO keyCode:kVK_ANSI_Z];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions == nil, @"Expected nil array of actions");
|
||||
|
|
@ -85,7 +85,7 @@ NSString * names[nCombinations];
|
|||
|
||||
- (void)testprocessEvent_eventForLowercaseA_ReturnsCharacterActionWithExpectedCharacterBasedOnKmx {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"a" charactersIgnoringModifiers:@"a" isARepeat:NO keyCode:kVK_ANSI_A];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions.count == 1, @"Expected 1 action");
|
||||
|
|
@ -127,14 +127,14 @@ NSString * names[nCombinations];
|
|||
XCTAssert([actionType isEqualToString:Q_STR], @"Expected Q_STR action");
|
||||
NSString *output = [action objectForKey:actionType];
|
||||
XCTAssert([output isEqualToString:expectedOutput], @"Output incorrect");
|
||||
[engine setContextBuffer:@""];
|
||||
[engine clearContext];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
- (void)testprocessEvent_eventsForOpenCurlyBraceWithCipherMusicKmx_ReturnsCharacterActionForStartSlide {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForCipherMusicTests];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
UTF32Char expectedUtf32Char = 0x1D177;
|
||||
NSString * expectedStartSlideSurrogatePair = [[NSString alloc] initWithBytes:&expectedUtf32Char length:4 encoding:NSUTF32LittleEndianStringEncoding];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:NSEventModifierFlagShift timestamp:0 windowNumber:0 context:nil characters:@"{" charactersIgnoringModifiers:@"[" isARepeat:NO keyCode:kVK_ANSI_LeftBracket];
|
||||
|
|
@ -161,7 +161,7 @@ NSString * names[nCombinations];
|
|||
CoreAction *action = actions[0];
|
||||
XCTAssert([action isCharacter], @"Expected CharacterAction");
|
||||
XCTAssert([action.content isEqualToString:numeral], @"Output incorrect");
|
||||
[engine setContextBuffer:@""];
|
||||
[engine clearContext];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -169,7 +169,7 @@ NSString * names[nCombinations];
|
|||
// TODO: fails with core, returns CharacterAction
|
||||
- (void)testprocessEvent_eventForShiftNumeralsWithoutRulesInCipherMusicKmx_ReturnsNoAction {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForCipherMusicTests];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
for (int i = 0; i <= 9; i++)
|
||||
{
|
||||
if (i == 6)
|
||||
|
|
@ -212,7 +212,7 @@ NSString * names[nCombinations];
|
|||
// TODO: fails with core, returns CharacterAction
|
||||
- (void)testprocessEvent_eventForPeriodWithCipherMusicKmx_ReturnsNoAction {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForCipherMusicTests];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"." charactersIgnoringModifiers:@"." isARepeat:NO keyCode:kVK_ANSI_Period];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions.count == 0, @"Expected no actions");
|
||||
|
|
@ -221,7 +221,7 @@ NSString * names[nCombinations];
|
|||
// TODO: fails with core, returns CharacterAction
|
||||
- (void)testprocessEvent_eventForCtrl8WithCipherMusicKmx_ReturnsNoAction {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForCipherMusicTests];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:NSEventModifierFlagControl timestamp:0 windowNumber:0 context:nil characters:@"8" charactersIgnoringModifiers:@"8" isARepeat:NO keyCode:kVK_ANSI_8];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions.count == 0, @"Expected no actions");
|
||||
|
|
@ -264,10 +264,10 @@ NSString * names[nCombinations];
|
|||
[KMEngineTests fillInNamesAndModifiersForAllChiralCombinations];
|
||||
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
|
||||
for (i = 0; i < nCombinations; i++) {
|
||||
[engine setContextBuffer:@""];
|
||||
[engine clearCoreContext];
|
||||
NSString *charactersIgnoringModifiers = (modifiers[i] & (LEFT_SHIFT_FLAG | RIGHT_SHIFT_FLAG)) ? @"A" : @"a";
|
||||
NSString * characters = charactersIgnoringModifiers;
|
||||
if (modifiers[i] & (LEFT_ALT_FLAG | RIGHT_ALT_FLAG))
|
||||
|
|
@ -333,10 +333,10 @@ NSString * names[nCombinations];
|
|||
[KMEngineTests fillInNamesAndModifiersForAllChiralCombinations];
|
||||
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileTestMacEngine];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
|
||||
for (i = 0; i < nCombinations; i++) {
|
||||
[engine setContextBuffer:@""];
|
||||
[engine clearCoreContext];
|
||||
NSString *charactersIgnoringModifiers = (modifiers[i] & (LEFT_SHIFT_FLAG | RIGHT_SHIFT_FLAG)) ? @"S" : @"s";
|
||||
NSString * characters = charactersIgnoringModifiers;
|
||||
if (modifiers[i] & (LEFT_ALT_FLAG | RIGHT_ALT_FLAG)) {
|
||||
|
|
@ -409,7 +409,7 @@ NSString * names[nCombinations];
|
|||
|
||||
- (NSString *)checkPlatform_getOutputForKeystroke: (NSString*) character modifierFlags: (NSEventModifierFlags) flag keyCode:(unsigned short)code {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForPlatformTest];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
NSString *lcChar = [character lowercaseString];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:flag timestamp:0 windowNumber:0 context:nil characters:character charactersIgnoringModifiers:lcChar isARepeat:NO keyCode:code];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
|
|
@ -512,7 +512,7 @@ NSString * names[nCombinations];
|
|||
// KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@"a"];
|
||||
// NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"=" charactersIgnoringModifiers:@"=" isARepeat:NO keyCode:kVK_ANSI_Equal];
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForIndexOffsetTests];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@"z"];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@"z"];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"a" charactersIgnoringModifiers:@"a" isARepeat:NO keyCode:kVK_ANSI_A];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions.count == 2, @"Expected 2 actions");
|
||||
|
|
@ -541,7 +541,7 @@ NSString * names[nCombinations];
|
|||
|
||||
- (void)testCoreProcessEvent_eventForFWithElNuerKmx_ReturnsCorrectCharacter {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForElNuerTests];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@""];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"f" charactersIgnoringModifiers:@"f" isARepeat:NO keyCode:kVK_ANSI_F];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions.count == 1, @"Expected one action");
|
||||
|
|
@ -552,13 +552,13 @@ NSString * names[nCombinations];
|
|||
|
||||
- (void)testCoreProcessEvent_eventDeleteWithElNuerKmx_EmptiesContextReturnsDelete {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForElNuerTests];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@"ɣ"];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@"ɣ"];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"\b" charactersIgnoringModifiers:@"\b" isARepeat:NO keyCode:kVK_Delete];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions.count == 1, @"Expected one action");
|
||||
CoreAction *action = actions[0];
|
||||
XCTAssert([action isCharacterBackspace], @"Expected CharacterBackspace action");
|
||||
NSString *context = engine.contextBuffer;
|
||||
NSString *context = engine.getCoreContext;
|
||||
XCTAssert([context isEqualToString:@""], @"Context should be empty.");
|
||||
}
|
||||
|
||||
|
|
@ -577,13 +577,13 @@ NSString * names[nCombinations];
|
|||
|
||||
- (void)testCoreProcessEvent_eventReturnWithElNuerKmx_ContextUnchangedReturnsReturn {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForElNuerTests];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@"ɣ"];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@"ɣ"];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"\n" charactersIgnoringModifiers:@"\n" isARepeat:NO keyCode:kVK_Return];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions.count == 2, @"Expected one action");
|
||||
CoreAction *action = actions[1];
|
||||
XCTAssert(action.actionType == EmitKeystrokeAction, @"Expected EmitKeystrokeAction");
|
||||
NSString *context = engine.contextBuffer;
|
||||
NSString *context = engine.getCoreContext;
|
||||
XCTAssert([context isEqualToString:@"ɣ"], @"Context should be unchanged.");
|
||||
}
|
||||
|
||||
|
|
@ -601,13 +601,13 @@ NSString * names[nCombinations];
|
|||
|
||||
- (void)testCoreProcessEvent_eventTabWithElNuerKmx_ContextUnchangedReturnsTab {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForElNuerTests];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@"ɣ"];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:@"ɣ"];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"\t" charactersIgnoringModifiers:@"\t" isARepeat:NO keyCode:kVK_Tab];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions.count == 2, @"Expected two actions");
|
||||
CoreAction *action = actions[1];
|
||||
XCTAssert(action.actionType == EmitKeystrokeAction, @"Expected EmitKeystrokeAction");
|
||||
NSString *context = engine.contextBuffer;
|
||||
NSString *context = engine.getCoreContext;
|
||||
XCTAssert([context isEqualToString:@"ɣ"], @"Context should be unchanged.");
|
||||
}
|
||||
|
||||
|
|
@ -626,13 +626,13 @@ NSString * names[nCombinations];
|
|||
- (void)testCoreProcessEvent_eventSingleQuoteWithElNuerKmx_ReturnsDiacritic {
|
||||
KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForElNuerTests];
|
||||
NSString *context = @"ɛ";
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:context];
|
||||
KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile context:context];
|
||||
NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"'" charactersIgnoringModifiers:@"'" isARepeat:NO keyCode:kVK_ANSI_Quote];
|
||||
NSArray *actions = [engine processEvent:event];
|
||||
XCTAssert(actions.count == 1, @"Expected one action");
|
||||
CoreAction *action = actions[0];
|
||||
XCTAssert([action isCharacter], @"Expected CharacterAction");
|
||||
context = engine.contextBuffer;
|
||||
context = engine.getCoreContext;
|
||||
XCTAssert([context isEqualToString:@"\u025B\u0308"], @"Context updated with diacritic.");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue