Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/PojavLauncher_iOS
Path: blob/main/Natives/LauncherPrefContCfgViewController.m
589 views
1
#import <Foundation/Foundation.h>
2
#import <objc/runtime.h>
3
4
#import "input/ControllerInput.h"
5
#import "customcontrols/CustomControlsUtils.h"
6
#import "FileListViewController.h"
7
#import "LauncherNavigationController.h"
8
#import "LauncherPreferences.h"
9
#import "LauncherPrefContCfgViewController.h"
10
#import "PickTextField.h"
11
#import "ios_uikit_bridge.h"
12
#import "utils.h"
13
14
#include "glfw_keycodes.h"
15
16
#define contentNavigationController (LauncherNavigationController *)UIApplication.sharedApplication.keyWindow.rootViewController.splitViewController.viewControllers[1]
17
18
typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);
19
20
@interface LauncherPrefContCfgViewController ()<UITextFieldDelegate, UIPopoverPresentationControllerDelegate, UIPickerViewDataSource, UIPickerViewDelegate>
21
@property(nonatomic) NSString *currentFileName;
22
@property(nonatomic) NSMutableDictionary *currentMappings;
23
@property(nonatomic) NSDictionary *keycodePlist;
24
@property(nonatomic) UIPickerView *editPickMapping;
25
@property(nonatomic) UIToolbar *editPickToolbar;
26
@property(nonatomic) UITextField *activeTextField;
27
@property(nonatomic) NSArray<NSString*>* prefSections;
28
@property(nonatomic) NSMutableArray<NSNumber*>* prefSectionsVisibility;
29
@property(nonatomic) NSArray<NSDictionary*> *prefControllerTypes;
30
@property(nonatomic) NSMutableArray *keyCodeMap, *keyValueMap;
31
32
@end
33
34
@implementation LauncherPrefContCfgViewController
35
36
- (void)viewDidLoad
37
{
38
[super viewDidLoad];
39
[self setTitle:localize(@"preference.title.default_gamepad_ctrl", nil)];
40
41
self.keycodePlist = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"glfw_keycodes" ofType:@"plist"]];
42
43
self.keyCodeMap = [[NSMutableArray alloc] init];
44
self.keyValueMap = [[NSMutableArray alloc] init];
45
initKeycodeTable(self.keyCodeMap, self.keyValueMap);
46
47
self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
48
self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
49
self.tableView.sectionFooterHeight = 50;
50
51
[self loadGamepadConfigurationFile];
52
self.prefControllerTypes = @[@{@"name": @"xbox"}, @{@"name": @"playstation"}];
53
54
self.prefSections = @[@"config_files", @"game_mappings", @"menu_mappings", @"controller_style"];
55
56
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(actionMenuSave)];
57
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemClose target:self action:@selector(exitButtonSelector)];
58
59
self.editPickMapping = [[UIPickerView alloc] init];
60
self.editPickMapping.delegate = self;
61
self.editPickMapping.dataSource = self;
62
self.editPickToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, 44.0)];
63
UIBarButtonItem *btnFlexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
64
UIBarButtonItem *editDoneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(closeTextField:)];
65
self.editPickToolbar.items = @[btnFlexibleSpace, editDoneButton];
66
}
67
68
- (void)loadGamepadConfigurationFile {
69
NSString *gamepadPath = [NSString stringWithFormat:@"%s/controlmap/gamepads/%@", getenv("POJAV_HOME"), getPrefObject(@"control.default_gamepad_ctrl")];
70
self.currentMappings = parseJSONFromFile(gamepadPath);
71
self.currentFileName = [getPrefObject(@"control.default_ctrl") stringByDeletingPathExtension];
72
NSPredicate *filterPredicate = [NSPredicate predicateWithBlock:^BOOL(id obj, NSDictionary *dict) {
73
return ![obj[@"name"] hasPrefix:@"mouse_"];
74
}];
75
[self.currentMappings[@"mGameMappingList"] filterUsingPredicate:filterPredicate];
76
[self.currentMappings[@"mMenuMappingList"] filterUsingPredicate:filterPredicate];
77
}
78
79
- (void)viewWillAppear:(BOOL)animated {
80
[super viewWillAppear:animated];
81
}
82
83
#pragma mark External UITableView functions
84
85
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
86
return self.prefSections.count;
87
}
88
89
- (NSArray *)prefContentForIndex:(NSInteger)index {
90
switch (index) {
91
case 0: return nil; // one single cell is defined in cellForRowAtIndexPath
92
case 1: return self.currentMappings[@"mGameMappingList"];
93
case 2: return self.currentMappings[@"mMenuMappingList"];
94
case 3: return self.prefControllerTypes;
95
default: return nil;
96
}
97
}
98
99
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
100
if (section == 0) {
101
return 1;
102
} else {
103
return [self prefContentForIndex:section].count;
104
}
105
}
106
107
- (UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
108
NSMutableDictionary *item = [self prefContentForIndex:indexPath.section][indexPath.row];
109
NSString *cellID = [NSString stringWithFormat:@"cellValue%ld", indexPath.section];
110
UITableViewCellStyle cellStyle = UITableViewCellStyleValue1;
111
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
112
if (cell == nil) {
113
cell = [[UITableViewCell alloc] initWithStyle:cellStyle reuseIdentifier:cellID];
114
cell.detailTextLabel.numberOfLines = 0;
115
cell.detailTextLabel.lineBreakMode = NSLineBreakByWordWrapping;
116
cell.selectionStyle = UITableViewCellSelectionStyleGray;
117
}
118
119
if(indexPath.section == 0) {
120
cell.textLabel.text = localize(@"controller_configurator.title.current", nil);
121
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
122
cell.detailTextLabel.text = getPrefObject(@"control.default_gamepad_ctrl");
123
} else if(indexPath.section == 1 || indexPath.section == 2) {
124
NSNumber *keycode = (NSNumber *)item[@"keycode"];
125
cell.textLabel.text = localize(([NSString stringWithFormat:@"controller_configurator.%@.title.%@", getPrefObject(@"control.controller_type"), item[@"name"]]), nil);
126
UITextField *view = (id)cell.accessoryView;
127
if (view == nil) {
128
view = [[PickTextField alloc] initWithFrame:CGRectMake(0, 0, cell.bounds.size.width / 2.1, cell.bounds.size.height)];
129
[view addTarget:view action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
130
view.autocorrectionType = UITextAutocorrectionTypeNo;
131
view.autocapitalizationType = UITextAutocapitalizationTypeNone;
132
view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;
133
view.delegate = self;
134
view.returnKeyType = UIReturnKeyDone;
135
view.tag = indexPath.section;
136
view.textAlignment = NSTextAlignmentRight;
137
view.tintColor = UIColor.clearColor;
138
view.adjustsFontSizeToFitWidth = YES;
139
view.inputAccessoryView = self.editPickToolbar;
140
view.inputView = self.editPickMapping;
141
cell.accessoryView = view;
142
}
143
view.text = self.keyCodeMap[[self.keyValueMap indexOfObject:keycode]];
144
objc_setAssociatedObject(view, @"gamepad_button", item[@"name"], OBJC_ASSOCIATION_ASSIGN);
145
objc_setAssociatedObject(view, @"item", item, OBJC_ASSOCIATION_ASSIGN);
146
} else if(indexPath.section == 3) {
147
cell.textLabel.text = localize([NSString stringWithFormat:@"controller_configurator.title.type.%@", item[@"name"]], nil);
148
if ([getPrefObject(@"control.controller_type") isEqualToString:item[@"name"]]) {
149
cell.accessoryType = UITableViewCellAccessoryCheckmark;
150
} else {
151
cell.accessoryType = UITableViewCellAccessoryNone;
152
}
153
}
154
return cell;
155
}
156
157
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
158
return localize([NSString stringWithFormat:@"controller_configurator.section.%@", self.prefSections[section]], nil);
159
}
160
161
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
162
NSString *key = [NSString stringWithFormat:@"controller_configurator.section.footer.%@", self.prefSections[section]];
163
NSString *footer = localize(key, nil);
164
return [footer isEqualToString:key] ? nil : footer;
165
}
166
167
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
168
[tableView deselectRowAtIndexPath:indexPath animated:NO];
169
170
NSDictionary *item = [self prefContentForIndex:indexPath.section][indexPath.row];
171
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
172
if(indexPath.section == 0) {
173
[self actionMenuLoad];
174
} else if(indexPath.section == 3) {
175
setPrefObject(@"control.controller_type", self.prefControllerTypes[indexPath.row][@"name"]);
176
NSIndexSet *section = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 3)];
177
[self.tableView reloadSections:section withRowAnimation:UITableViewRowAnimationNone];
178
}
179
// 1 and 2 handle themselves with picker views.
180
}
181
182
#pragma mark UITextField + UIPickerView
183
184
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
185
{
186
UILabel *label = (UILabel *)view;
187
if (label == nil) {
188
label = [UILabel new];
189
label.adjustsFontSizeToFitWidth = YES;
190
label.minimumScaleFactor = 0.5;
191
label.textAlignment = NSTextAlignmentCenter;
192
}
193
label.text = [self pickerView:pickerView titleForRow:row forComponent:component];
194
195
return label;
196
}
197
198
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
199
self.activeTextField.text = [self.keyCodeMap objectAtIndex:row];
200
}
201
202
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView {
203
return 1;
204
}
205
206
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
207
return self.keyCodeMap.count;
208
}
209
210
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
211
return [self.keyCodeMap objectAtIndex:row];
212
}
213
214
- (void)textFieldDidBeginEditing:(UITextField *)textField
215
{
216
self.activeTextField = textField;
217
[self.editPickMapping selectRow:[self.keyCodeMap indexOfObject:textField.text] inComponent:0 animated:NO];
218
}
219
- (void)textFieldDidEndEditing:(UITextField *)textField
220
{
221
UITableViewCell *cell = (UITableViewCell *)textField.superview;
222
NSMutableDictionary *item = objc_getAssociatedObject(cell.accessoryView, @"item");
223
if(![textField.text hasPrefix:@"SPECIALBTN"]) {
224
item[@"keycode"] = self.keycodePlist[[@"GLFW_KEY_" stringByAppendingString:textField.text]];
225
} else {
226
item[@"keycode"] = self.keycodePlist[textField.text];
227
}
228
self.activeTextField = nil;
229
}
230
231
- (void)closeTextField:(UIBarButtonItem *)sender {
232
[self.activeTextField endEditing:YES];
233
}
234
235
#pragma mark UI
236
237
- (void) dismissModalViewController {
238
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
239
}
240
241
- (void)actionOpenFilePicker:(void (^)(NSString *name))handler {
242
FileListViewController *vc = [[FileListViewController alloc] init];
243
vc.listPath = [NSString stringWithFormat:@"%s/controlmap/gamepads", getenv("POJAV_HOME")];
244
245
vc.whenItemSelected = handler;
246
vc.modalPresentationStyle = UIModalPresentationPopover;
247
vc.preferredContentSize = CGSizeMake(350, 250);
248
249
UIPopoverPresentationController *popoverController = [vc popoverPresentationController];
250
popoverController.sourceView = self.tableView;
251
popoverController.sourceRect = [self.tableView rectForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
252
popoverController.delegate = self;
253
[self presentViewController:vc animated:YES completion:nil];
254
}
255
256
- (void)actionMenuLoad {
257
[self actionOpenFilePicker:^void(NSString* name) {
258
NSString *currentFile = [NSString stringWithFormat:@"%@.json", getPrefObject(@"control.default_gamepad_ctrl")];
259
if(![currentFile isEqualToString:name]) {
260
setPrefObject(@"control.default_gamepad_ctrl", [NSString stringWithFormat:@"%@.json", name]);
261
[self loadGamepadConfigurationFile];
262
[self.tableView reloadData];
263
}
264
}];
265
}
266
267
- (void)actionMenuSaveWithExit:(BOOL)exit {
268
UIAlertController *controller = [UIAlertController alertControllerWithTitle:localize(@"custom_controls.control_menu.save", nil)
269
message:exit?localize(@"custom_controls.control_menu.exit.warn", nil):@""
270
preferredStyle:UIAlertControllerStyleAlert];
271
[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {
272
textField.placeholder = @"Name";
273
textField.text = self.currentFileName;
274
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
275
textField.borderStyle = UITextBorderStyleRoundedRect;
276
}];
277
[controller addAction:[UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
278
NSArray *textFields = controller.textFields;
279
UITextField *field = textFields[0];
280
NSError *error;
281
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self.currentMappings options:NSJSONWritingPrettyPrinted error:&error];
282
if (jsonData == nil) {
283
showDialog(localize(@"custom_controls.control_menu.save.error.json", nil), error.localizedDescription);
284
return;
285
}
286
BOOL success = [jsonData writeToFile:[NSString stringWithFormat:@"%s/controlmap/gamepads/%@.json", getenv("POJAV_HOME"), field.text] options:NSDataWritingAtomic error:&error];
287
if (!success) {
288
showDialog(localize(@"custom_controls.control_menu.save.error.write", nil), error.localizedDescription);
289
return;
290
}
291
292
if (exit) {
293
[self dismissModalViewController];
294
}
295
296
setPrefObject(@"control.default_gamepad_ctrl", [NSString stringWithFormat:@"%@.json", field.text]);
297
}]];
298
if (exit) {
299
[controller addAction:[UIAlertAction actionWithTitle:localize(@"custom_controls.control_menu.discard_changes", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
300
[self dismissModalViewController];
301
}]];
302
}
303
[controller addAction:[UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]];
304
[self presentViewController:controller animated:YES completion:nil];
305
}
306
307
- (void)actionMenuSave {
308
[self actionMenuSaveWithExit:NO];
309
}
310
311
- (void)exitButtonSelector {
312
NSString *gamepadPath = [NSString stringWithFormat:@"%s/controlmap/gamepads/%@", getenv("POJAV_HOME"), getPrefObject(@"control.default_gamepad_ctrl")];
313
if([self.currentMappings isEqualToDictionary:parseJSONFromFile(gamepadPath)]) {
314
[self dismissModalViewController];
315
} else {
316
[self actionMenuSaveWithExit:YES];
317
}
318
}
319
320
@end
321
322