Make pickerview functional; Rewrite instance manager

This commit is contained in:
Duy Tran Khanh committed 2022-09-03 21:36:40 +07:00
1 parent 9c5bea91c8
commit 2a64640c19
6 files changed
+302 -13

No files matched your search

+1
View File
@@ -118,6 +118,7 @@ add_executable(PojavLauncher
LauncherPreferences.m
LauncherPreferencesViewController.m
LauncherPreferencesViewController2.m
LauncherPrefGameDirViewController.m
LauncherSplitViewController.m
LauncherViewController.m
LoginViewController.m
@@ -0,0 +1,6 @@
#import <UIKit/UIKit.h>
@interface LauncherPrefGameDirViewController : UITableViewController
@end
+227
View File
@@ -0,0 +1,227 @@
#import "LauncherPreferences.h"
#import "LauncherPrefGameDirViewController.h"
#import "TOInsetGroupedTableView.h"
#import "ios_uikit_bridge.h"
@interface LauncherPrefGameDirViewController ()<UITextFieldDelegate>
@property NSMutableArray *array;
@end
@implementation LauncherPrefGameDirViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self setTitle:NSLocalizedString(@"preference.title.game_directory", nil)];
self.array = [[NSMutableArray alloc] init];
[self.array addObject:@"default"];
self.tableView = [[TOInsetGroupedTableView alloc] init];
self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
self.tableView.sectionFooterHeight = 50;
NSString *path = [NSString stringWithFormat:@"%s/instances", getenv("POJAV_HOME")];
NSFileManager *fm = NSFileManager.defaultManager;
NSArray *files = [fm contentsOfDirectoryAtPath:path error:nil];
BOOL isDir;
for (NSString *file in files) {
[fm fileExistsAtPath:path isDirectory:(&isDir)];
if (isDir && ![file isEqualToString:@"default"]) {
[self.array addObject:file];
}
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.array.count;
}
- (UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITextField *view;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
if (indexPath.row == 0) {
cell.textLabel.text = self.array[indexPath.row];
} else {
view = [[UITextField alloc] initWithFrame:CGRectMake(20, 10, cell.bounds.size.width/2, cell.bounds.size.height-20)];
[view addTarget:view action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
view.autocorrectionType = UITextAutocorrectionTypeNo;
view.autocapitalizationType = UITextAutocapitalizationTypeNone;
view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
view.delegate = self;
view.returnKeyType = UIReturnKeyDone;
[cell.contentView addSubview:view];
}
}
if (indexPath.row > 0) {
view = cell.contentView.subviews.lastObject;
view.placeholder = self.array[indexPath.row];
view.text = self.array[indexPath.row];
}
if ([getPreference(@"game_directory") isEqualToString:self.array[indexPath.row]]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
// TODO: display size in the detail label
return cell;
}
- (UIView *)tableView:(UITableView *)tableView
viewForFooterInSection:(NSInteger)section
{
UITextField *view = [[UITextField alloc] init];
[view addTarget:view action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
view.autocorrectionType = UITextAutocorrectionTypeNo;
view.autocapitalizationType = UITextAutocapitalizationTypeNone;
view.delegate = self;
view.placeholder = @"add game directory";
view.returnKeyType = UIReturnKeyDone;
return view;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
setPreference(@"game_directory", self.array[indexPath.row]);
[tableView deselectRowAtIndexPath:indexPath animated:NO];
for (int i = 0; i < self.array.count; i++) {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
if (i == indexPath.row) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
}
- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point API_AVAILABLE(ios(13.0))
{
// You can't delete or rename the default instance, though there will be a reset action (TODO)
if (indexPath.row == 0) {
return nil;
}
if (@available(iOS 13.0, *)) {
UIAction *delete = [UIAction actionWithTitle:@"Delete" image:[UIImage systemImageNamed:@"trash"] identifier:@"deleteAction" handler:^(__kindof UIAction * _Nonnull action) {
[self actionDeleteAtIndexPath:indexPath];
}];
delete.attributes = UIMenuElementAttributesDestructive;
UIAction *rename = [UIAction actionWithTitle:@"Rename" image:[UIImage systemImageNamed:@"pencil"] identifier:@"editAction" handler:^(__kindof UIAction * _Nonnull action) {
UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath];
[view.contentView.subviews.lastObject becomeFirstResponder];
}];
return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:^UIMenu * _Nullable(NSArray<UIMenuElement *> * _Nonnull suggestedActions) {
return [UIMenu menuWithTitle:self.array[indexPath.row] children:@[delete, rename]];
}];
}
return nil;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self actionDeleteAtIndexPath:indexPath];
}
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) {
// TODO: Reset action?
return UITableViewCellEditingStyleNone;
} else {
return UITableViewCellEditingStyleDelete;
}
}
- (void)actionDeleteAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath];
NSString *title = NSLocalizedString(@"preference.title.confirm", nil);
NSString *message = [NSString stringWithFormat:NSLocalizedString(@"preference.title.confirm.delete_game_directory", nil), self.array[indexPath.row]];
UIAlertController *confirmAlert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet];
confirmAlert.popoverPresentationController.sourceView = view;
confirmAlert.popoverPresentationController.sourceRect = view.bounds;
UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
NSString *directory = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), self.array[indexPath.row]];
NSError *error;
if([[NSFileManager defaultManager] removeItemAtPath:directory error:&error]) {
if ([getPreference(@"game_directory") isEqualToString:self.array[indexPath.row]]) {
setPreference(@"game_directory", self.array[0]);
[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]].accessoryType = UITableViewCellAccessoryCheckmark;
}
[self.array removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else {
showDialog(self, NSLocalizedString(@"Error", nil), error.localizedDescription);
}
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil];
[confirmAlert addAction:cancel];
[confirmAlert addAction:ok];
[self presentViewController:confirmAlert animated:YES completion:nil];
}
#pragma mark UITextField
- (void)textFieldDidEndEditing:(UITextField *)sender {
BOOL isFooterView = sender.superview == self.tableView;
if (!sender.hasText || [sender.text isEqualToString:sender.placeholder]) {
if (isFooterView) {
return;
}
sender.text = sender.placeholder;
return;
}
NSError *error;
NSString *dest = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), sender.text];
if (isFooterView) {
[[NSFileManager defaultManager] createDirectoryAtPath:dest withIntermediateDirectories:NO attributes:nil error:&error];
} else {
NSString *source = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), sender.placeholder];
[NSFileManager.defaultManager moveItemAtPath:source toPath:dest error:&error];
}
if (error == nil) {
setPreference(@"game_directory", sender.text);
if (isFooterView) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.array.count inSection:0];
[self.array addObject:sender.text];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
// Clear text
sender.text = @"";
} else {
int index = [self.array indexOfObject:sender.placeholder];
self.array[index] = sender.placeholder = sender.text;
}
} else {
// Restore to the previous name if we encounter an error
if (!isFooterView) {
sender.text = sender.placeholder;
}
showDialog(self, NSLocalizedString(@"Error", nil), error.localizedDescription);
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
CGRect frame = textField.frame;
frame.size.width = MAX(50, textField.intrinsicContentSize.width + 10);
textField.frame = frame;
return YES;
}
@end
+61 -12
View File
@@ -4,6 +4,7 @@
#import "DBNumberedSlider.h"
#import "LauncherPreferences.h"
#import "LauncherPreferencesViewController2.h"
#import "LauncherPrefGameDirViewController.h"
#import "TOInsetGroupedTableView.h"
#include "utils.h"
@@ -14,7 +15,7 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);
@property NSArray<NSString*>* prefSections;
@property NSArray<NSArray<NSDictionary*>*>* prefContents;
@property CreateView typeButton, typePickField, typeTextField, typeSlider, typeSwitch;
@property CreateView typeButton, typeChildPane, typePickField, typeTextField, typeSlider, typeSwitch;
@end
@implementation LauncherPreferencesViewController2
@@ -35,8 +36,8 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);
// General settings
@{@"key": @"game_directory",
@"icon": @"folder",
@"type": self.typePickField,
@"pickList": @[@"default", @"TODO"]
@"type": self.typeChildPane,
@"class": LauncherPrefGameDirViewController.class,
},
@{@"key": @"home_symlink",
@"icon": @"link",
@@ -73,7 +74,18 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);
@{@"key": @"renderer",
@"icon": @"cpu",
@"type": self.typePickField,
@"pickList": @[@"gl4es", @"zink", @"wip"]
@"pickKeys": @[
@"auto",
@ RENDERER_NAME_GL4ES,
@ RENDERER_NAME_MTL_ANGLE,
@ RENDERER_NAME_VK_ZINK
],
@"pickList": @[
NSLocalizedString(@"preference.title.renderer.auto", nil),
NSLocalizedString(@"preference.title.renderer.gl4es", nil),
NSLocalizedString(@"preference.title.renderer.tinygl4angle", nil),
NSLocalizedString(@"preference.title.renderer.zink", nil)
]
},
@{@"key": @"resolution",
@"icon": @"viewfinder",
@@ -171,10 +183,14 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);
- (UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"cell"];
}
// Reset cell properties, as it could be reused
cell.accessoryType = UITableViewCellAccessoryNone;
cell.accessoryView = nil;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.textColor = nil;
cell.detailTextLabel.text = nil;
NSDictionary *item = self.prefContents[indexPath.section][indexPath.row];
NSString *key = item[@"key"];
@@ -206,11 +222,16 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);
self.typeButton = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) {
BOOL destructive = [item[@"destructive"] boolValue];
cell.accessoryView = nil;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.textLabel.textColor = destructive ? UIColor.systemRedColor : UIColor.systemBlueColor;
};
self.typeChildPane = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.detailTextLabel.text = getPreference(key);
};
/*
self.typePickField = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) {
cell.accessoryView = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, cell.bounds.size.width / 2, cell.bounds.size.height)];
@@ -238,9 +259,11 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);
UITextField *view = (UITextField *)cell.accessoryView;
UIPickerView *picker = [[UIPickerView alloc] init];
objc_setAssociatedObject(picker, @"item", item, OBJC_ASSOCIATION_ASSIGN);
objc_setAssociatedObject(picker, @"view", cell.accessoryView, OBJC_ASSOCIATION_ASSIGN);
picker.delegate = weakSelf;
picker.dataSource = weakSelf;
[picker reloadAllComponents];
[picker selectRow:[item[@"pickKeys"] indexOfObject:getPreference(key)] inComponent:0 animated:NO];
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, weakSelf.view.frame.size.width, 44.0)];
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:view action:@selector(resignFirstResponder)];
@@ -296,7 +319,18 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);
#pragma mark - UIPickerView
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
NSDictionary *item = objc_getAssociatedObject(pickerView, @"item");
UITextField *view = objc_getAssociatedObject(pickerView, @"view");
// If there is key, use it
if (item[@"pickKeys"] != nil) {
view.text = item[@"pickKeys"][row];
} else {
view.text = item[@"pickList"][row];
}
// Save the preference
[self textFieldDidEndEditing:view];
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView {
@@ -347,12 +381,25 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath];
if (view.selectionStyle == UITableViewCellSelectionStyleNone) {
// Ignore if selection style is none
return;
}
NSDictionary *item = self.prefContents[indexPath.section][indexPath.row];
if (item[@"type"] == self.typeButton) {
[self tableView:tableView invokeActionWithPromptAtIndexPath:indexPath];
} else if (item[@"type"] == self.typeChildPane) {
[self tableView:tableView openChildPaneAtIndexPath:indexPath];
}
}
#pragma mark External UITableView functions
- (void)tableView:(UITableView *)tableView openChildPaneAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *item = self.prefContents[indexPath.section][indexPath.row];
UIViewController *vc = [[item[@"class"] alloc] init];
[self.navigationController pushViewController:vc animated:YES];
}
- (void)tableView:(UITableView *)tableView invokeActionWithPromptAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath];
NSDictionary *item = self.prefContents[indexPath.section][indexPath.row];
NSString *key = item[@"key"];
@@ -390,6 +437,8 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *);
[self showAlertOnView:view title:title message:nil];
}
#pragma mark UITextField
- (void)textFieldDidEndEditing:(UITextField *)sender {
[self checkWarn:sender];
NSString *key = objc_getAssociatedObject(sender, @"key");
+6
View File
@@ -73,6 +73,7 @@
"preference.title.confirm" = "Are you sure?";
"preference.title.game_directory" = "Minecraft game directory";
"preference.title.confirm.delete_game_directory" = "The instance %@ will be deleted forever and cannot be restored.";
"preference.title.home_symlink" = "Enable compatibility symlink";
"preference.title.check_sha" = "Enable asset verification when launching";
"preference.title.cosmetica" = "Enable Cosmetica integration";
@@ -85,6 +86,11 @@
"preference.title.done.reset_settings" = "All of the default settings have been restored.";
"preference.title.renderer" = "Game renderer";
"preference.title.renderer.auto" = "Auto: gl4es or tinygl4angle";
"preference.title.renderer.gl4es" = "gl4es 1.1.4 - exports OpenGL 2.1";
"preference.title.renderer.tinygl4angle" = "tinygl4angle (1.17+) - exports OpenGL 3.2 (Core Profile, limited)";
"preference.title.renderer.zink" = "Zink (Mesa 21.0) - exports OpenGL 4.1";
"preference.title.resolution" = "Game resolution";
"preference.title.disable_gesture" = "Disable gestures";
"preference.title.press_duration" = "Long-press duration";
+1 -1
View File
@@ -30,7 +30,7 @@
#define RENDERER_NAME_GL4ES "libgl4es_114.dylib"
#define RENDERER_NAME_MTL_ANGLE "libtinygl4angle.dylib"
#define RENDERER_NAME_VK_ZINK "libOSMesaOverride.dylib"
#define RENDERER_NAME_VK_ZINK "libOSMesa.8.dylib"
#define SPECIALBTN_KEYBOARD -1
#define SPECIALBTN_TOGGLECTRL -2