Files
PojavLauncher_iOS/Natives/LauncherPrefGameDirViewController.m
T
Eilionoir Tunnicliff de2b1843cc Use modals for presenting view controllers from Settings view
Also change "Minecraft game directory" to just "Game directory". Reduce output of reloadVersionList to just localized description.
This does require the modals to have dismissModalViewController() defined in their classes, not really an issue since all view controllers will be custom classes anyway.
2023-03-15 13:14:14 -04:00

316 lines
13 KiB
Objective-C

#import "LauncherNavigationController.h"
#import "LauncherPreferences.h"
#import "LauncherPrefGameDirViewController.h"
#import "TOInsetGroupedTableView.h"
#import "ios_uikit_bridge.h"
#import "utils.h"
#define contentNavigationController (LauncherNavigationController *)UIApplication.sharedApplication.keyWindow.rootViewController.splitViewController.viewControllers[1]
@interface LauncherPrefGameDirViewController ()<UITextFieldDelegate>
@property(nonatomic) NSMutableArray *array;
@end
@implementation LauncherPrefGameDirViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self setTitle:localize(@"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];
}
}
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissModalViewController)];
}
- (void)changeSelectionTo:(NSString *)name {
setPreference(@"game_directory", name);
NSString *multidirPath = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), name];
NSString *lasmPath = [NSString stringWithFormat:@"%s/Library/Application Support/minecraft", getenv("POJAV_HOME")];
[NSFileManager.defaultManager removeItemAtPath:lasmPath error:nil];
[NSFileManager.defaultManager createSymbolicLinkAtPath:lasmPath withDestinationPath:multidirPath error:nil];
if ([getPreference(@"selected_version_type") intValue] == 0) {
[(LauncherNavigationController *)contentNavigationController reloadVersionList:0];
}
}
- (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"];
view = [[UITextField alloc] initWithFrame:CGRectMake(20, 10, (cell.bounds.size.width-40)/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;
view.userInteractionEnabled = indexPath.row != 0;
[cell.contentView addSubview:view];
}
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 = localize(@"preference.multidir.add_directory", nil);
view.returnKeyType = UIReturnKeyDone;
return view;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self changeSelectionTo: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 *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location
{
return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:^UIMenu * _Nullable(NSArray<UIMenuElement *> * _Nonnull suggestedActions) {
return [UIMenu menuWithTitle:self.array[indexPath.row] children:@[files, filza, santander]];
}];
}
*/
- (id)createOpenScheme:(NSString *)scheme at:(NSString *)directory {
if (@available(iOS 13.0, *)) {
return ^(UIAction *action) {
[UIApplication.sharedApplication
openURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", scheme, directory]]
options:@{} completionHandler:nil];
};
}
return nil;
}
- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point API_AVAILABLE(ios(13.0))
{
// suppress warning
if (@available(iOS 13.0, *)) {}
else return nil;
NSArray *menuItems;
NSMutableArray *openItems = [[NSMutableArray alloc] init];
NSString *directory = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), self.array[indexPath.row]];
if ([UIApplication.sharedApplication canOpenURL:[NSURL URLWithString:@"shareddocuments://"]]) {
[openItems addObject:[UIAction
actionWithTitle:@"Files"
image:nil
identifier:nil
handler:[self createOpenScheme:@"shareddocuments" at:directory]]];
}
if ([UIApplication.sharedApplication canOpenURL:[NSURL URLWithString:@"filza://"]]) {
[openItems addObject:[UIAction
actionWithTitle:@"Filza"
image:nil
identifier:nil
handler:[self createOpenScheme:@"filza" at:directory]]];
}
if ([UIApplication.sharedApplication canOpenURL:[NSURL URLWithString:@"santander://"]]) {
[openItems addObject:[UIAction
actionWithTitle:@"Santander"
image:nil
identifier:nil
handler:[self createOpenScheme:@"santander" at:directory]]];
}
UIMenu *open = [UIMenu
menuWithTitle:@""
image:nil
identifier:nil
options:UIMenuOptionsDisplayInline
children:openItems];
if (indexPath.row == 0) {
// You can't delete or rename the default instance, though there will be a reset action (TODO)
menuItems = @[open];
} else {
UIAction *rename = [UIAction
actionWithTitle:localize(@"Rename", nil)
image:[UIImage systemImageNamed:@"pencil"]
identifier:nil
handler:^(UIAction *action) {
UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath];
[view.contentView.subviews.lastObject becomeFirstResponder];
}
];
UIAction *delete = [UIAction
actionWithTitle:localize(@"Delete", nil)
image:[UIImage systemImageNamed:@"trash"]
identifier:nil
handler:^(UIAction *action) {
[self actionDeleteAtIndexPath:indexPath];
}
];
delete.attributes = UIMenuElementAttributesDestructive;
menuItems = @[open, rename, delete];
}
return [UIContextMenuConfiguration
configurationWithIdentifier:nil
previewProvider:nil
actionProvider:^UIMenu *(NSArray<UIMenuElement *> *suggestedActions) {
return [UIMenu menuWithTitle:self.array[indexPath.row] children:menuItems];
}
];
}
- (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 = localize(@"preference.title.confirm", nil);
NSString *message = [NSString stringWithFormat:localize(@"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:localize(@"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]]) {
[self changeSelectionTo: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, localize(@"Error", nil), error.localizedDescription);
}
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil];
[confirmAlert addAction:cancel];
[confirmAlert addAction:ok];
[self presentViewController:confirmAlert animated:YES completion:nil];
}
- (void) dismissModalViewController {
[self.navigationController dismissViewControllerAnimated: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) {
[self changeSelectionTo: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;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
}
} else {
// Restore to the previous name if we encounter an error
if (!isFooterView) {
sender.text = sender.placeholder;
}
showDialog(self, localize(@"Error", nil), error.localizedDescription);
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
[textField invalidateIntrinsicContentSize];
CGRect frame = textField.frame;
frame.size.width = MAX(50, textField.intrinsicContentSize.width + 10);
textField.frame = frame;
return YES;
}
@end