Path: blob/main/Natives/LauncherPrefContCfgViewController.m
589 views
#import <Foundation/Foundation.h>1#import <objc/runtime.h>23#import "input/ControllerInput.h"4#import "customcontrols/CustomControlsUtils.h"5#import "FileListViewController.h"6#import "LauncherNavigationController.h"7#import "LauncherPreferences.h"8#import "LauncherPrefContCfgViewController.h"9#import "PickTextField.h"10#import "ios_uikit_bridge.h"11#import "utils.h"1213#include "glfw_keycodes.h"1415#define contentNavigationController (LauncherNavigationController *)UIApplication.sharedApplication.keyWindow.rootViewController.splitViewController.viewControllers[1]1617typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);1819@interface LauncherPrefContCfgViewController ()<UITextFieldDelegate, UIPopoverPresentationControllerDelegate, UIPickerViewDataSource, UIPickerViewDelegate>20@property(nonatomic) NSString *currentFileName;21@property(nonatomic) NSMutableDictionary *currentMappings;22@property(nonatomic) NSDictionary *keycodePlist;23@property(nonatomic) UIPickerView *editPickMapping;24@property(nonatomic) UIToolbar *editPickToolbar;25@property(nonatomic) UITextField *activeTextField;26@property(nonatomic) NSArray<NSString*>* prefSections;27@property(nonatomic) NSMutableArray<NSNumber*>* prefSectionsVisibility;28@property(nonatomic) NSArray<NSDictionary*> *prefControllerTypes;29@property(nonatomic) NSMutableArray *keyCodeMap, *keyValueMap;3031@end3233@implementation LauncherPrefContCfgViewController3435- (void)viewDidLoad36{37[super viewDidLoad];38[self setTitle:localize(@"preference.title.default_gamepad_ctrl", nil)];3940self.keycodePlist = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"glfw_keycodes" ofType:@"plist"]];4142self.keyCodeMap = [[NSMutableArray alloc] init];43self.keyValueMap = [[NSMutableArray alloc] init];44initKeycodeTable(self.keyCodeMap, self.keyValueMap);4546self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];47self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;48self.tableView.sectionFooterHeight = 50;4950[self loadGamepadConfigurationFile];51self.prefControllerTypes = @[@{@"name": @"xbox"}, @{@"name": @"playstation"}];5253self.prefSections = @[@"config_files", @"game_mappings", @"menu_mappings", @"controller_style"];5455self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(actionMenuSave)];56self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemClose target:self action:@selector(exitButtonSelector)];5758self.editPickMapping = [[UIPickerView alloc] init];59self.editPickMapping.delegate = self;60self.editPickMapping.dataSource = self;61self.editPickToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, 44.0)];62UIBarButtonItem *btnFlexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];63UIBarButtonItem *editDoneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(closeTextField:)];64self.editPickToolbar.items = @[btnFlexibleSpace, editDoneButton];65}6667- (void)loadGamepadConfigurationFile {68NSString *gamepadPath = [NSString stringWithFormat:@"%s/controlmap/gamepads/%@", getenv("POJAV_HOME"), getPrefObject(@"control.default_gamepad_ctrl")];69self.currentMappings = parseJSONFromFile(gamepadPath);70self.currentFileName = [getPrefObject(@"control.default_ctrl") stringByDeletingPathExtension];71NSPredicate *filterPredicate = [NSPredicate predicateWithBlock:^BOOL(id obj, NSDictionary *dict) {72return ![obj[@"name"] hasPrefix:@"mouse_"];73}];74[self.currentMappings[@"mGameMappingList"] filterUsingPredicate:filterPredicate];75[self.currentMappings[@"mMenuMappingList"] filterUsingPredicate:filterPredicate];76}7778- (void)viewWillAppear:(BOOL)animated {79[super viewWillAppear:animated];80}8182#pragma mark External UITableView functions8384- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {85return self.prefSections.count;86}8788- (NSArray *)prefContentForIndex:(NSInteger)index {89switch (index) {90case 0: return nil; // one single cell is defined in cellForRowAtIndexPath91case 1: return self.currentMappings[@"mGameMappingList"];92case 2: return self.currentMappings[@"mMenuMappingList"];93case 3: return self.prefControllerTypes;94default: return nil;95}96}9798- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {99if (section == 0) {100return 1;101} else {102return [self prefContentForIndex:section].count;103}104}105106- (UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {107NSMutableDictionary *item = [self prefContentForIndex:indexPath.section][indexPath.row];108NSString *cellID = [NSString stringWithFormat:@"cellValue%ld", indexPath.section];109UITableViewCellStyle cellStyle = UITableViewCellStyleValue1;110UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];111if (cell == nil) {112cell = [[UITableViewCell alloc] initWithStyle:cellStyle reuseIdentifier:cellID];113cell.detailTextLabel.numberOfLines = 0;114cell.detailTextLabel.lineBreakMode = NSLineBreakByWordWrapping;115cell.selectionStyle = UITableViewCellSelectionStyleGray;116}117118if(indexPath.section == 0) {119cell.textLabel.text = localize(@"controller_configurator.title.current", nil);120cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;121cell.detailTextLabel.text = getPrefObject(@"control.default_gamepad_ctrl");122} else if(indexPath.section == 1 || indexPath.section == 2) {123NSNumber *keycode = (NSNumber *)item[@"keycode"];124cell.textLabel.text = localize(([NSString stringWithFormat:@"controller_configurator.%@.title.%@", getPrefObject(@"control.controller_type"), item[@"name"]]), nil);125UITextField *view = (id)cell.accessoryView;126if (view == nil) {127view = [[PickTextField alloc] initWithFrame:CGRectMake(0, 0, cell.bounds.size.width / 2.1, cell.bounds.size.height)];128[view addTarget:view action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];129view.autocorrectionType = UITextAutocorrectionTypeNo;130view.autocapitalizationType = UITextAutocapitalizationTypeNone;131view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;132view.delegate = self;133view.returnKeyType = UIReturnKeyDone;134view.tag = indexPath.section;135view.textAlignment = NSTextAlignmentRight;136view.tintColor = UIColor.clearColor;137view.adjustsFontSizeToFitWidth = YES;138view.inputAccessoryView = self.editPickToolbar;139view.inputView = self.editPickMapping;140cell.accessoryView = view;141}142view.text = self.keyCodeMap[[self.keyValueMap indexOfObject:keycode]];143objc_setAssociatedObject(view, @"gamepad_button", item[@"name"], OBJC_ASSOCIATION_ASSIGN);144objc_setAssociatedObject(view, @"item", item, OBJC_ASSOCIATION_ASSIGN);145} else if(indexPath.section == 3) {146cell.textLabel.text = localize([NSString stringWithFormat:@"controller_configurator.title.type.%@", item[@"name"]], nil);147if ([getPrefObject(@"control.controller_type") isEqualToString:item[@"name"]]) {148cell.accessoryType = UITableViewCellAccessoryCheckmark;149} else {150cell.accessoryType = UITableViewCellAccessoryNone;151}152}153return cell;154}155156- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {157return localize([NSString stringWithFormat:@"controller_configurator.section.%@", self.prefSections[section]], nil);158}159160- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {161NSString *key = [NSString stringWithFormat:@"controller_configurator.section.footer.%@", self.prefSections[section]];162NSString *footer = localize(key, nil);163return [footer isEqualToString:key] ? nil : footer;164}165166- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {167[tableView deselectRowAtIndexPath:indexPath animated:NO];168169NSDictionary *item = [self prefContentForIndex:indexPath.section][indexPath.row];170UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];171if(indexPath.section == 0) {172[self actionMenuLoad];173} else if(indexPath.section == 3) {174setPrefObject(@"control.controller_type", self.prefControllerTypes[indexPath.row][@"name"]);175NSIndexSet *section = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 3)];176[self.tableView reloadSections:section withRowAnimation:UITableViewRowAnimationNone];177}178// 1 and 2 handle themselves with picker views.179}180181#pragma mark UITextField + UIPickerView182183- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view184{185UILabel *label = (UILabel *)view;186if (label == nil) {187label = [UILabel new];188label.adjustsFontSizeToFitWidth = YES;189label.minimumScaleFactor = 0.5;190label.textAlignment = NSTextAlignmentCenter;191}192label.text = [self pickerView:pickerView titleForRow:row forComponent:component];193194return label;195}196197- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {198self.activeTextField.text = [self.keyCodeMap objectAtIndex:row];199}200201- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView {202return 1;203}204205- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {206return self.keyCodeMap.count;207}208209- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {210return [self.keyCodeMap objectAtIndex:row];211}212213- (void)textFieldDidBeginEditing:(UITextField *)textField214{215self.activeTextField = textField;216[self.editPickMapping selectRow:[self.keyCodeMap indexOfObject:textField.text] inComponent:0 animated:NO];217}218- (void)textFieldDidEndEditing:(UITextField *)textField219{220UITableViewCell *cell = (UITableViewCell *)textField.superview;221NSMutableDictionary *item = objc_getAssociatedObject(cell.accessoryView, @"item");222if(![textField.text hasPrefix:@"SPECIALBTN"]) {223item[@"keycode"] = self.keycodePlist[[@"GLFW_KEY_" stringByAppendingString:textField.text]];224} else {225item[@"keycode"] = self.keycodePlist[textField.text];226}227self.activeTextField = nil;228}229230- (void)closeTextField:(UIBarButtonItem *)sender {231[self.activeTextField endEditing:YES];232}233234#pragma mark UI235236- (void) dismissModalViewController {237[self.navigationController dismissViewControllerAnimated:YES completion:nil];238}239240- (void)actionOpenFilePicker:(void (^)(NSString *name))handler {241FileListViewController *vc = [[FileListViewController alloc] init];242vc.listPath = [NSString stringWithFormat:@"%s/controlmap/gamepads", getenv("POJAV_HOME")];243244vc.whenItemSelected = handler;245vc.modalPresentationStyle = UIModalPresentationPopover;246vc.preferredContentSize = CGSizeMake(350, 250);247248UIPopoverPresentationController *popoverController = [vc popoverPresentationController];249popoverController.sourceView = self.tableView;250popoverController.sourceRect = [self.tableView rectForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];251popoverController.delegate = self;252[self presentViewController:vc animated:YES completion:nil];253}254255- (void)actionMenuLoad {256[self actionOpenFilePicker:^void(NSString* name) {257NSString *currentFile = [NSString stringWithFormat:@"%@.json", getPrefObject(@"control.default_gamepad_ctrl")];258if(![currentFile isEqualToString:name]) {259setPrefObject(@"control.default_gamepad_ctrl", [NSString stringWithFormat:@"%@.json", name]);260[self loadGamepadConfigurationFile];261[self.tableView reloadData];262}263}];264}265266- (void)actionMenuSaveWithExit:(BOOL)exit {267UIAlertController *controller = [UIAlertController alertControllerWithTitle:localize(@"custom_controls.control_menu.save", nil)268message:exit?localize(@"custom_controls.control_menu.exit.warn", nil):@""269preferredStyle:UIAlertControllerStyleAlert];270[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {271textField.placeholder = @"Name";272textField.text = self.currentFileName;273textField.clearButtonMode = UITextFieldViewModeWhileEditing;274textField.borderStyle = UITextBorderStyleRoundedRect;275}];276[controller addAction:[UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {277NSArray *textFields = controller.textFields;278UITextField *field = textFields[0];279NSError *error;280NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self.currentMappings options:NSJSONWritingPrettyPrinted error:&error];281if (jsonData == nil) {282showDialog(localize(@"custom_controls.control_menu.save.error.json", nil), error.localizedDescription);283return;284}285BOOL success = [jsonData writeToFile:[NSString stringWithFormat:@"%s/controlmap/gamepads/%@.json", getenv("POJAV_HOME"), field.text] options:NSDataWritingAtomic error:&error];286if (!success) {287showDialog(localize(@"custom_controls.control_menu.save.error.write", nil), error.localizedDescription);288return;289}290291if (exit) {292[self dismissModalViewController];293}294295setPrefObject(@"control.default_gamepad_ctrl", [NSString stringWithFormat:@"%@.json", field.text]);296}]];297if (exit) {298[controller addAction:[UIAlertAction actionWithTitle:localize(@"custom_controls.control_menu.discard_changes", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {299[self dismissModalViewController];300}]];301}302[controller addAction:[UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]];303[self presentViewController:controller animated:YES completion:nil];304}305306- (void)actionMenuSave {307[self actionMenuSaveWithExit:NO];308}309310- (void)exitButtonSelector {311NSString *gamepadPath = [NSString stringWithFormat:@"%s/controlmap/gamepads/%@", getenv("POJAV_HOME"), getPrefObject(@"control.default_gamepad_ctrl")];312if([self.currentMappings isEqualToDictionary:parseJSONFromFile(gamepadPath)]) {313[self dismissModalViewController];314} else {315[self actionMenuSaveWithExit:YES];316}317}318319@end320321322