- Rewrote LauncherPreferences -> PLPreferences + Now the file layout is the same as UI layout + (get/set)Preference are replaced with corresponding get/set types + Getting/setting is now done through keyPath instead of manually iterating over subdictionaries. + Implemented an isolation layer, allowing an instance to have its own settings. This is off by default - Implemented launcher_profiles + Added Profile manager that can be accessed from the sidebar + Moved "Game directory (instances)" to this screen + Replaced version selector with profile selector. Version selector is moved into profile editor. - Other changes + Cleanup code by using NSPredicate instead of huge for loops + Titles in settings are made multilined + Controller config: replaced bar button items with localized ones Due to how large the changes are, this is committed directly to the mainline branch. Bugs are expected to be found (although I did enough tests for this locally)
305 lines
13 KiB
Objective-C
305 lines
13 KiB
Objective-C
#import "LauncherNavigationController.h"
|
|
#import "LauncherPreferences.h"
|
|
#import "LauncherProfileEditorViewController.h"
|
|
#import "MinecraftResourceUtils.h"
|
|
#import "PLProfiles.h"
|
|
#import "ios_uikit_bridge.h"
|
|
#import "utils.h"
|
|
|
|
@interface LauncherProfileEditorViewController()<UIPickerViewDataSource, UIPickerViewDelegate>
|
|
@property(nonatomic) NSString* oldName;
|
|
|
|
@property(nonatomic) NSArray<NSDictionary *> *versionList;
|
|
@property(nonatomic) UITextField* versionTextField;
|
|
@property(nonatomic) UISegmentedControl* versionTypeControl;
|
|
@property(nonatomic) UIPickerView* versionPickerView;
|
|
@property(nonatomic) UIToolbar* versionPickerToolbar;
|
|
@property(nonatomic) int versionSelectedAt;
|
|
@end
|
|
|
|
@implementation LauncherProfileEditorViewController
|
|
|
|
- (void)viewDidLoad {
|
|
// Setup navigation bar
|
|
self.title = localize(@"Edit profile", nil);
|
|
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(actionDone)];
|
|
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemClose target:self action:@selector(actionClose)];
|
|
|
|
// Setup appearance
|
|
self.prefSectionsVisible = YES;
|
|
|
|
// Setup preference getter and setter
|
|
__weak LauncherProfileEditorViewController *weakSelf = self;
|
|
self.getPreference = ^id(NSString *section, NSString *key){
|
|
NSString *value = weakSelf.profile[key];
|
|
if (value.length > 0 || ![weakSelf isPickFieldAtSection:section key:key]) {
|
|
return value;
|
|
} else {
|
|
return @"(default)";
|
|
}
|
|
};
|
|
self.setPreference = ^(NSString *section, NSString *key, NSString *value){
|
|
if ([value isEqualToString:@"(default)"] && [weakSelf isPickFieldAtSection:section key:key]) {
|
|
[weakSelf.profile removeObjectForKey:key];
|
|
} else {
|
|
weakSelf.profile[key] = value;
|
|
}
|
|
};
|
|
|
|
// Obtain all the lists
|
|
self.oldName = self.getPreference(nil, @"name");
|
|
if ([self.oldName length] == 0) {
|
|
self.setPreference(nil, @"name", @"New Profile");
|
|
}
|
|
NSArray *rendererKeys = getRendererKeys(YES);
|
|
NSArray *rendererList = getRendererNames(YES);
|
|
NSArray *touchControlList = [self listFilesAtPath:[NSString stringWithFormat:@"%s/controlmap", getenv("POJAV_HOME")]];
|
|
NSArray *gamepadControlList = [self listFilesAtPath:[NSString stringWithFormat:@"%s/controlmap/gamepads", getenv("POJAV_HOME")]];
|
|
NSMutableArray *javaList = [getPrefObject(@"java.java_homes") allKeys].mutableCopy;
|
|
[javaList sortUsingSelector:@selector(compare:)];
|
|
javaList[0] = @"(default)";
|
|
|
|
// Setup version picker
|
|
[self setupVersionPicker];
|
|
id typeVersionPicker = ^void(UITableViewCell *cell, NSString *section, NSString *key, NSDictionary *item){
|
|
self.typeTextField(cell, section, key, item);
|
|
UITextField *textField = (id)cell.accessoryView;
|
|
weakSelf.versionTextField = textField;
|
|
textField.inputAccessoryView = weakSelf.versionPickerToolbar;
|
|
textField.inputView = weakSelf.versionPickerView;
|
|
// Auto pick version type
|
|
if (self.versionList) return;
|
|
if ([MinecraftResourceUtils findVersion:textField.text inList:localVersionList]) {
|
|
self.versionTypeControl.selectedSegmentIndex = 0;
|
|
} else {
|
|
NSDictionary *selected = (id)[MinecraftResourceUtils findVersion:textField.text inList:remoteVersionList];
|
|
if (selected) {
|
|
NSArray *types = @[@"installed", @"release", @"snapshot", @"old_beta", @"old_alpha"];
|
|
NSString *type = selected[@"type"];
|
|
self.versionTypeControl.selectedSegmentIndex = [types indexOfObject:type];
|
|
} else {
|
|
// Version not found
|
|
self.versionTypeControl.selectedSegmentIndex = 0;
|
|
}
|
|
}
|
|
self.versionSelectedAt = -1;
|
|
[self changeVersionType:nil];
|
|
};
|
|
|
|
self.prefContents = @[
|
|
@[
|
|
// General settings
|
|
@{@"key": @"name",
|
|
@"icon": @"tag",
|
|
@"title": @"preference.profile.title.name",
|
|
@"type": self.typeTextField,
|
|
@"placeholder": self.oldName
|
|
},
|
|
@{@"key": @"lastVersionId",
|
|
@"icon": @"archivebox",
|
|
@"title": @"preference.profile.title.version",
|
|
@"type": typeVersionPicker,
|
|
@"placeholder": self.getPreference(nil, @"lastVersionId")
|
|
},
|
|
@{@"key": @"gameDir",
|
|
@"icon": @"folder",
|
|
@"title": @"preference.title.game_directory",
|
|
@"type": self.typeTextField,
|
|
@"placeholder": [NSString stringWithFormat:@". -> /Documents/instances/%@", getPrefObject(@"general.game_directory")]
|
|
},
|
|
// Video and renderer settings
|
|
@{@"key": @"renderer",
|
|
@"icon": @"cpu",
|
|
@"type": self.typePickField,
|
|
@"pickKeys": rendererKeys,
|
|
@"pickList": rendererList
|
|
},
|
|
// Control settings
|
|
@{@"key": @"defaultTouchCtrl",
|
|
@"icon": @"hand.tap",
|
|
@"title": @"preference.profile.title.default_touch_control",
|
|
@"type": self.typePickField,
|
|
@"pickKeys": touchControlList,
|
|
@"pickList": touchControlList
|
|
},
|
|
@{@"key": @"defaultGamepadCtrl",
|
|
@"icon": @"gamecontroller",
|
|
@"title": @"preference.profile.title.default_gamepad_control",
|
|
@"type": self.typePickField,
|
|
@"pickKeys": gamepadControlList,
|
|
@"pickList": gamepadControlList
|
|
},
|
|
// Java tweaks
|
|
@{@"key": @"javaVersion",
|
|
@"icon": @"cube",
|
|
@"title": @"preference.manage_runtime.header.default",
|
|
@"type": self.typePickField,
|
|
@"pickKeys": javaList,
|
|
@"pickList": javaList
|
|
},
|
|
@{@"key": @"javaArgs",
|
|
@"icon": @"slider.vertical.3",
|
|
@"title": @"preference.title.java_args",
|
|
@"type": self.typeTextField,
|
|
@"placeholder": @"(default)"
|
|
}
|
|
]
|
|
];
|
|
|
|
[super viewDidLoad];
|
|
}
|
|
|
|
- (void)actionClose {
|
|
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
|
}
|
|
|
|
- (void)actionDone {
|
|
// We might be saving without ending editing, so make sure textFieldDidEndEditing is always called
|
|
UITextField *currentTextField = [self performSelector:@selector(_firstResponder)];
|
|
if ([currentTextField isKindOfClass:UITextField.class]) {
|
|
[self textFieldDidEndEditing:currentTextField];
|
|
}
|
|
|
|
if ([self.profile[@"name"] length] == 0 && self.oldName.length > 0) {
|
|
// Return to its old name
|
|
self.profile[@"name"] = self.oldName;
|
|
}
|
|
|
|
if ([self.oldName isEqualToString:self.profile[@"name"]]) {
|
|
// Not a rename, directly create/replace
|
|
PLProfiles.current.profiles[self.oldName] = self.profile;
|
|
} else if (!PLProfiles.current.profiles[self.profile[@"name"]]) {
|
|
// A rename, remove then re-add to update its key name
|
|
if (self.oldName.length > 0) {
|
|
[PLProfiles.current.profiles removeObjectForKey:self.oldName];
|
|
}
|
|
PLProfiles.current.profiles[self.profile[@"name"]] = self.profile;
|
|
// Update selected name
|
|
if ([PLProfiles.current.selectedProfileName isEqualToString:self.oldName]) {
|
|
PLProfiles.current.selectedProfileName = self.profile[@"name"];
|
|
}
|
|
} else {
|
|
// Cancel rename since a profile with the same name already exists
|
|
showDialog(localize(@"Error", nil), localize(@"profile.error.name_exists", nil));
|
|
// Skip dismissing this view controller
|
|
return;
|
|
}
|
|
|
|
[PLProfiles.current save];
|
|
[self actionClose];
|
|
|
|
// Call LauncherProfilesViewController's viewWillAppear
|
|
UINavigationController *navVC = (id) ((UISplitViewController *)self.presentingViewController).viewControllers[1];
|
|
[navVC.viewControllers[0] viewWillAppear:NO];
|
|
}
|
|
|
|
- (BOOL)isPickFieldAtSection:(NSString *)section key:(NSString *)key {
|
|
NSDictionary *pref = [self.prefContents[0] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(key == %@)", key]].firstObject;
|
|
return pref[@"type"] == self.typePickField;
|
|
}
|
|
|
|
- (NSArray *)listFilesAtPath:(NSString *)path {
|
|
NSMutableArray *files = [NSFileManager.defaultManager contentsOfDirectoryAtPath:path error:nil].mutableCopy;
|
|
for (int i = 0; i < files.count;) {
|
|
if ([files[i] hasSuffix:@".json"]) {
|
|
i++;
|
|
} else {
|
|
[files removeObjectAtIndex:i];
|
|
}
|
|
}
|
|
[files insertObject:@"(default)" atIndex:0];
|
|
return files;
|
|
}
|
|
|
|
#pragma mark Version picker
|
|
|
|
- (void)setupVersionPicker {
|
|
self.versionPickerView = [[UIPickerView alloc] init];
|
|
self.versionPickerView.delegate = self;
|
|
self.versionPickerView.dataSource = self;
|
|
self.versionPickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, 44.0)];
|
|
self.versionTypeControl = [[UISegmentedControl alloc] initWithItems:@[
|
|
localize(@"Installed", nil),
|
|
localize(@"Releases", nil),
|
|
localize(@"Snapshot", nil),
|
|
localize(@"Old-beta", nil),
|
|
localize(@"Old-alpha", nil)
|
|
]];
|
|
[self.versionTypeControl addTarget:self action:@selector(changeVersionType:) forControlEvents:UIControlEventValueChanged];
|
|
self.versionPickerToolbar.items = @[
|
|
[[UIBarButtonItem alloc] initWithCustomView:self.versionTypeControl],
|
|
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
|
|
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(versionClosePicker)]
|
|
];
|
|
}
|
|
|
|
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
|
|
if (self.versionList.count == 0) {
|
|
self.versionTextField.text = @"";
|
|
return;
|
|
}
|
|
self.versionSelectedAt = row;
|
|
self.versionTextField.text = [self pickerView:pickerView titleForRow:row forComponent:component];
|
|
}
|
|
|
|
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView {
|
|
return 1;
|
|
}
|
|
|
|
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
|
|
return self.versionList.count;
|
|
}
|
|
|
|
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
|
|
if (self.versionList.count <= row) return nil;
|
|
NSObject *object = self.versionList[row];
|
|
if ([object isKindOfClass:[NSString class]]) {
|
|
return (NSString*) object;
|
|
} else {
|
|
return [object valueForKey:@"id"];
|
|
}
|
|
}
|
|
|
|
- (void)versionClosePicker {
|
|
[self.versionTextField endEditing:YES];
|
|
[self pickerView:self.versionPickerView didSelectRow:[self.versionPickerView selectedRowInComponent:0] inComponent:0];
|
|
}
|
|
|
|
- (void)changeVersionType:(UISegmentedControl *)sender {
|
|
if (sender || !self.versionList) {
|
|
if (self.versionTypeControl.selectedSegmentIndex == 0) {
|
|
// installed
|
|
self.versionList = localVersionList;
|
|
} else {
|
|
NSString *type = @[@"installed", @"release", @"snapshot", @"old_beta", @"old_alpha"][self.versionTypeControl.selectedSegmentIndex];
|
|
self.versionList = [remoteVersionList filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"(type == %@)", type]];
|
|
}
|
|
[self.versionPickerView reloadAllComponents];
|
|
}
|
|
|
|
if (self.versionSelectedAt == -1) {
|
|
NSDictionary *selected = (id)[MinecraftResourceUtils findVersion:self.versionTextField.text inList:self.versionList];
|
|
self.versionSelectedAt = [self.versionList indexOfObject:selected];
|
|
} else {
|
|
// Find the most matching version for this type
|
|
NSObject *lastSelected = nil;
|
|
if (self.versionList.count > 0 && self.versionSelectedAt >= 0) {
|
|
lastSelected = self.versionList[self.versionSelectedAt];
|
|
}
|
|
if (self.versionSelectedAt < 0 && lastSelected != nil) {
|
|
NSObject *nearest = [MinecraftResourceUtils findNearestVersion:lastSelected expectedType:self.versionTypeControl.selectedSegmentIndex];
|
|
if (nearest != nil) {
|
|
self.versionSelectedAt = [self.versionList indexOfObject:(id)nearest];
|
|
}
|
|
}
|
|
lastSelected = nil;
|
|
// Get back the currently selected in case none matching version found
|
|
self.versionSelectedAt = MIN(abs(self.versionSelectedAt), self.versionList.count - 1);
|
|
}
|
|
|
|
[self.versionPickerView selectRow:self.versionSelectedAt inComponent:0 animated:NO];
|
|
[self pickerView:self.versionPickerView didSelectRow:self.versionSelectedAt inComponent:0];
|
|
}
|
|
|
|
@end
|