Files
keyboard/CustomKeyboard/View/KBKeyboardView/KBKeyboardKeyFactory.m

187 lines
6.2 KiB
Mathematica
Raw Normal View History

2026-03-04 12:54:57 +08:00
//
// KBKeyboardKeyFactory.m
// CustomKeyboard
//
#import "KBKeyboardKeyFactory.h"
#import "KBKeyboardLayoutConfig.h"
#import "KBKey.h"
@interface KBKeyboardKeyFactory ()
@property (nonatomic, strong) KBKeyboardLayoutConfig *layoutConfig;
@end
@implementation KBKeyboardKeyFactory
- (instancetype)initWithLayoutConfig:(KBKeyboardLayoutConfig *)layoutConfig {
self = [super init];
if (self) {
_layoutConfig = layoutConfig;
}
return self;
}
- (KBKey *)keyForItemId:(NSString *)itemId shiftOn:(BOOL)shiftOn {
if (itemId.length == 0) { return nil; }
KBKeyboardKeyDef *def = [self.layoutConfig keyDefForIdentifier:itemId];
if (def) {
return [self keyFromDef:def identifier:itemId shiftOn:shiftOn];
}
NSRange range = [itemId rangeOfString:@":"];
if (range.location != NSNotFound) {
NSString *prefix = [itemId substringToIndex:range.location];
NSString *value = [itemId substringFromIndex:range.location + 1];
if ([prefix isEqualToString:@"letter"]) {
if (value.length >= 1) {
return [self letterKeyWithChar:value shiftOn:shiftOn];
}
return nil;
}
if ([prefix isEqualToString:@"digit"]) {
NSString *identifier = [NSString stringWithFormat:@"digit_%@", value];
KBKey *k = [KBKey keyWithIdentifier:identifier title:value output:value type:KBKeyTypeCharacter];
k.caseVariant = KBKeyCaseVariantNone;
return k;
}
if ([prefix isEqualToString:@"sym"]) {
NSString *identifier = [self kb_identifierForSymbol:value];
KBKey *k = [KBKey keyWithIdentifier:identifier title:value output:value type:KBKeyTypeCharacter];
k.caseVariant = KBKeyCaseVariantNone;
return k;
}
}
return nil;
}
- (KBKey *)keyFromDef:(KBKeyboardKeyDef *)def
identifier:(NSString *)identifier
shiftOn:(BOOL)shiftOn {
KBKeyType type = [self kb_keyTypeForDef:def];
NSString *title = def.title ?: @"";
if (type == KBKeyTypeShift && shiftOn && def.selectedTitle.length > 0) {
title = def.selectedTitle;
}
NSString *output = @"";
switch (type) {
case KBKeyTypeSpace:
output = @" ";
break;
case KBKeyTypeReturn:
output = @"\n";
break;
default:
output = @"";
break;
}
NSString *finalId = identifier;
if ([identifier isEqualToString:@"emoji"]) {
finalId = KBKeyIdentifierEmojiPanel;
} else if ([identifier isEqualToString:@"send"]) {
finalId = @"return";
}
KBKey *k = [KBKey keyWithIdentifier:finalId title:title output:output type:type];
if (type == KBKeyTypeShift) {
k.caseVariant = shiftOn ? KBKeyCaseVariantUpper : KBKeyCaseVariantLower;
} else {
k.caseVariant = KBKeyCaseVariantNone;
}
return k;
}
- (KBKey *)letterKeyWithChar:(NSString *)charString shiftOn:(BOOL)shiftOn {
if (charString.length == 0) { return nil; }
NSString *lower = charString.lowercaseString;
NSString *upper = charString.uppercaseString;
NSString *shown = shiftOn ? upper : lower;
NSString *identifier = [NSString stringWithFormat:@"letter_%@", lower];
KBKey *k = [KBKey keyWithIdentifier:identifier
title:shown
output:shown
type:KBKeyTypeCharacter];
k.caseVariant = shiftOn ? KBKeyCaseVariantUpper : KBKeyCaseVariantLower;
return k;
}
#pragma mark - Private
- (KBKeyType)kb_keyTypeForDef:(KBKeyboardKeyDef *)def {
NSString *type = def.type.lowercaseString;
if ([type isEqualToString:@"shift"]) return KBKeyTypeShift;
if ([type isEqualToString:@"backspace"]) return KBKeyTypeBackspace;
if ([type isEqualToString:@"mode"]) return KBKeyTypeModeChange;
if ([type isEqualToString:@"symbolstoggle"]) return KBKeyTypeSymbolsToggle;
if ([type isEqualToString:@"space"]) return KBKeyTypeSpace;
if ([type isEqualToString:@"return"]) return KBKeyTypeReturn;
if ([type isEqualToString:@"globe"]) return KBKeyTypeGlobe;
return KBKeyTypeCustom;
}
- (NSString *)kb_identifierForSymbol:(NSString *)symbol {
if (symbol.length == 0) { return nil; }
static NSDictionary<NSString *, NSString *> *map = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
map = @{
@"-": @"sym_minus",
@"/": @"sym_slash",
@":": @"sym_colon",
@";": @"sym_semicolon",
@"(": @"sym_paren_l",
@")": @"sym_paren_r",
@"¥": @"sym_money",
@"¥": @"sym_money",
@"&": @"sym_amp",
@"@": @"sym_at",
@"\"": @"sym_quote_double",
@"“": @"sym_quote_double",
@"”": @"sym_quote_double",
@".": @"sym_dot",
@"。": @"sym_chinese_dot",
@",": @"sym_comma",
@"、": @"sym_dun",
@"?": @"sym_question",
@"!": @"sym_exclam",
@"'": @"sym_quote_single",
@"": @"sym_quote_single",
@"": @"sym_quote_single",
@"[": @"sym_bracket_l",
@"]": @"sym_bracket_r",
@"{": @"sym_brace_l",
@"}": @"sym_brace_r",
@"「": @"sym_corner_l",
@"」": @"sym_corner_r",
@"#": @"sym_hash",
@"%": @"sym_percent",
@"^": @"sym_caret",
@"*": @"sym_asterisk",
@"+": @"sym_plus",
@"=": @"sym_equal",
@"_": @"sym_underscore",
@"\\": @"sym_backslash",
@"|": @"sym_pipe",
@"~": @"sym_tilde",
@"<": @"sym_lt",
@">": @"sym_gt",
@"€": @"sym_euro",
@"$": @"sym_dollar",
@"£": @"sym_pound",
@"·": @"sym_bullet",
@"^_^": @"sym_face",
@"—": @"sym_emdash",
@"«": @"sym_guillemet_l",
@"»": @"sym_guillemet_r",
@"《": @"sym_book_title_l",
@"》": @"sym_book_title_r",
@"...": @"sym_ellipsis"
};
});
return map[symbol];
}
@end