// // KBKeyboardLayoutConfig.m // CustomKeyboard // #import "KBKeyboardLayoutConfig.h" #import #import "KBConfig.h" static NSString * const kKBKeyboardLayoutConfigFileName = @"kb_keyboard_layout_config"; @implementation KBKeyboardLayoutMetrics @end @implementation KBKeyboardLayoutFonts @end @implementation KBKeyboardKeyDef @end @implementation KBKeyboardRowItem + (NSDictionary *)mj_replacedKeyFromPropertyName { return @{ @"itemId": @"id" }; } + (NSArray *)itemsFromRawArray:(NSArray *)raw { if (![raw isKindOfClass:[NSArray class]] || raw.count == 0) { return @[]; } NSMutableArray *items = [NSMutableArray arrayWithCapacity:raw.count]; for (id obj in raw) { if ([obj isKindOfClass:[NSString class]]) { KBKeyboardRowItem *item = [KBKeyboardRowItem new]; item.itemId = (NSString *)obj; [items addObject:item]; continue; } if ([obj isKindOfClass:[NSDictionary class]]) { KBKeyboardRowItem *item = [KBKeyboardRowItem mj_objectWithKeyValues:obj]; if (item.itemId.length == 0) { NSString *fallback = ((NSDictionary *)obj)[@"id"]; if ([fallback isKindOfClass:[NSString class]]) { item.itemId = fallback; } } if (item.itemId.length > 0) { [items addObject:item]; } } } return items.copy; } @end @implementation KBKeyboardRowSegments - (NSArray *)leftItems { return [KBKeyboardRowItem itemsFromRawArray:self.left ?: @[]]; } - (NSArray *)centerItems { return [KBKeyboardRowItem itemsFromRawArray:self.center ?: @[]]; } - (NSArray *)rightItems { return [KBKeyboardRowItem itemsFromRawArray:self.right ?: @[]]; } @end @implementation KBKeyboardRowConfig - (NSArray *)resolvedItems { return [KBKeyboardRowItem itemsFromRawArray:self.items ?: @[]]; } @end @implementation KBKeyboardLayout + (NSDictionary *)mj_objectClassInArray { return @{ @"rows": [KBKeyboardRowConfig class] }; } @end @implementation KBKeyboardLayoutConfig + (instancetype)sharedConfig { static KBKeyboardLayoutConfig *config = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *path = [[NSBundle mainBundle] pathForResource:kKBKeyboardLayoutConfigFileName ofType:@"json"]; NSData *data = path.length ? [NSData dataWithContentsOfFile:path] : nil; config = data ? [KBKeyboardLayoutConfig configFromJSONData:data] : nil; }); return config; } + (instancetype)configFromJSONData:(NSData *)data { if (data.length == 0) { return nil; } NSError *error = nil; id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; if (error || ![json isKindOfClass:[NSDictionary class]]) { return nil; } NSDictionary *dict = (NSDictionary *)json; KBKeyboardLayoutConfig *config = [KBKeyboardLayoutConfig mj_objectWithKeyValues:dict]; NSDictionary *keyDefsRaw = dict[@"keyDefs"]; if ([keyDefsRaw isKindOfClass:[NSDictionary class]]) { NSMutableDictionary *defs = [NSMutableDictionary dictionaryWithCapacity:keyDefsRaw.count]; [keyDefsRaw enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { if (![key isKindOfClass:[NSString class]] || ![obj isKindOfClass:[NSDictionary class]]) { return; } KBKeyboardKeyDef *def = [KBKeyboardKeyDef mj_objectWithKeyValues:obj]; if (def) { defs[key] = def; } }]; config.keyDefs = defs.copy; } NSDictionary *layoutsRaw = dict[@"layouts"]; if ([layoutsRaw isKindOfClass:[NSDictionary class]]) { NSMutableDictionary *layouts = [NSMutableDictionary dictionaryWithCapacity:layoutsRaw.count]; [layoutsRaw enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { if (![key isKindOfClass:[NSString class]] || ![obj isKindOfClass:[NSDictionary class]]) { return; } KBKeyboardLayout *layout = [KBKeyboardLayout mj_objectWithKeyValues:obj]; if (layout) { layouts[key] = layout; } }]; config.layouts = layouts.copy; } return config; } - (CGFloat)scaledValue:(CGFloat)designValue { CGFloat baseWidth = (self.designWidth > 0.0) ? self.designWidth : KB_DESIGN_WIDTH; CGFloat scale = KBScreenWidth() / baseWidth; return designValue * scale; } - (CGFloat)keyboardAreaDesignHeight { KBKeyboardLayout *layout = [self layoutForName:@"letters"] ?: self.layouts.allValues.firstObject; NSUInteger rowCount = layout.rows.count; if (rowCount == 0) { return 0.0; } CGFloat rowSpacing = self.metrics.rowSpacing.doubleValue; CGFloat topInset = self.metrics.topInset.doubleValue; CGFloat bottomInset = self.metrics.bottomInset.doubleValue; CGFloat total = topInset + bottomInset + rowSpacing * (rowCount - 1); for (KBKeyboardRowConfig *row in layout.rows) { CGFloat height = row.height.doubleValue; if (height <= 0.0) { height = self.metrics.keyHeight.doubleValue; } if (height <= 0.0) { height = 40.0; } total += height; } return total; } - (CGFloat)keyboardAreaScaledHeight { CGFloat designHeight = [self keyboardAreaDesignHeight]; return designHeight > 0.0 ? [self scaledValue:designHeight] : 0.0; } - (KBKeyboardLayout *)layoutForName:(NSString *)name { if (name.length == 0) { return nil; } return self.layouts[name]; } - (KBKeyboardKeyDef *)keyDefForIdentifier:(NSString *)identifier { if (identifier.length == 0) { return nil; } return self.keyDefs[identifier]; } @end