diff --git a/Natives/CMakeLists.txt b/Natives/CMakeLists.txt index 57c255f0..0aa7c78c 100644 --- a/Natives/CMakeLists.txt +++ b/Natives/CMakeLists.txt @@ -112,6 +112,7 @@ add_executable(PojavLauncher customcontrols/ControlButton.m customcontrols/ControlDrawer.m + customcontrols/ControlJoystick.m customcontrols/ControlLayout.m customcontrols/ControlSubButton.m customcontrols/CustomControlsUtils.m diff --git a/Natives/CustomControlsViewController+UndoManager.m b/Natives/CustomControlsViewController+UndoManager.m index 5da48cd5..f5a5066f 100644 --- a/Natives/CustomControlsViewController+UndoManager.m +++ b/Natives/CustomControlsViewController+UndoManager.m @@ -1,5 +1,6 @@ #import "CustomControlsViewController.h" #import "customcontrols/ControlDrawer.h" +#import "customcontrols/ControlJoystick.h" #import "customcontrols/ControlLayout.h" #import "utils.h" @@ -8,7 +9,7 @@ - (void)doAddButton:(ControlButton *)button atIndex:(NSNumber *)index { NSUndoManager *undo = self.undoManager; - if ([button isKindOfClass:[ControlSubButton class]]) { + if ([button isKindOfClass:ControlSubButton.class]) { undo.actionName = localize(@"custom_controls.button_menu.add_subbutton", nil); [self.ctrlView addSubview:button]; @@ -16,7 +17,7 @@ [drawer.buttons insertObject:button atIndex:index.intValue]; [drawer.drawerData[@"buttonProperties"] insertObject:button.properties atIndex:index.intValue]; [drawer syncButtons]; - } else if ([button isKindOfClass:[ControlDrawer class]]) { + } else if ([button isKindOfClass:ControlDrawer.class]) { undo.actionName = localize(@"custom_controls.control_menu.add_drawer", nil); for (ControlSubButton *subButton in ((ControlDrawer *)button).buttons) { @@ -24,6 +25,10 @@ } [self.ctrlView.layoutDictionary[@"mDrawerDataList"] insertObject:((ControlDrawer *)button).drawerData atIndex:index.intValue]; [self.ctrlView addSubview:button]; + } else if ([button isKindOfClass:ControlJoystick.class]) { + undo.actionName = localize(@"custom_controls.control_menu.add_joystick", nil); + [self.ctrlView.layoutDictionary[@"mJoystickDataList"] insertObject:button.properties atIndex:index.intValue]; + [self.ctrlView addSubview:button]; } else { undo.actionName = localize(@"custom_controls.control_menu.add_button", nil); [self.ctrlView.layoutDictionary[@"mControlDataList"] insertObject:button.properties atIndex:index.intValue]; @@ -38,17 +43,20 @@ - (void)doRemoveButton:(ControlButton *)button { NSNumber *index; - if ([button isKindOfClass:[ControlSubButton class]]) { + if ([button isKindOfClass:ControlSubButton.class]) { ControlDrawer *parent = ((ControlSubButton *)button).parentDrawer; index = @([parent.drawerData[@"buttonProperties"] indexOfObject:button.properties]); [parent.buttons removeObject:button]; [parent.drawerData[@"buttonProperties"] removeObject:button.properties]; [parent syncButtons]; - } else if ([button isKindOfClass:[ControlDrawer class]]) { + } else if ([button isKindOfClass:ControlDrawer.class]) { ControlDrawer *drawer = (ControlDrawer *)button; index = @([self.ctrlView.layoutDictionary[@"mDrawerDataList"] indexOfObject:drawer.drawerData]); [drawer.buttons makeObjectsPerformSelector:@selector(removeFromSuperview)]; [self.ctrlView.layoutDictionary[@"mDrawerDataList"] removeObject:drawer.drawerData]; + } else if ([button isKindOfClass:ControlJoystick.class]) { + index = @([self.ctrlView.layoutDictionary[@"mJoystickDataList"] indexOfObject:button.properties]); + [self.ctrlView.layoutDictionary[@"mJoystickDataList"] removeObject:button.properties]; } else { index = @([self.ctrlView.layoutDictionary[@"mControlDataList"] indexOfObject:button.properties]); [self.ctrlView.layoutDictionary[@"mControlDataList"] removeObject:button.properties]; diff --git a/Natives/CustomControlsViewController.m b/Natives/CustomControlsViewController.m index a1c95ba7..40e1b28c 100644 --- a/Natives/CustomControlsViewController.m +++ b/Natives/CustomControlsViewController.m @@ -5,6 +5,7 @@ #import "ios_uikit_bridge.h" #import "customcontrols/ControlDrawer.h" +#import "customcontrols/ControlJoystick.h" #import "customcontrols/CustomControlsUtils.h" #include @@ -235,11 +236,11 @@ BOOL shouldDismissPopover = YES; UIMenuItem *actionExit = [[UIMenuItem alloc] initWithTitle:localize(@"custom_controls.control_menu.exit", nil) action:@selector(actionMenuExit)]; UIMenuItem *actionSave = [[UIMenuItem alloc] initWithTitle:localize(@"custom_controls.control_menu.save", nil) action:@selector(actionMenuSave)]; UIMenuItem *actionLoad = [[UIMenuItem alloc] initWithTitle:localize(@"custom_controls.control_menu.load", nil) action:@selector(actionMenuLoad)]; - UIMenuItem *actionSetDef = [[UIMenuItem alloc] initWithTitle:localize(@"custom_controls.control_menu.make_default", nil) action:@selector(actionMenuSetDef)]; UIMenuItem *actionSafeArea = [[UIMenuItem alloc] initWithTitle:localize(@"custom_controls.control_menu.safe_area", nil) action:@selector(actionMenuSafeArea)]; UIMenuItem *actionAddButton = [[UIMenuItem alloc] initWithTitle:localize(@"custom_controls.control_menu.add_button", nil) action:@selector(actionMenuAddButton)]; UIMenuItem *actionAddDrawer = [[UIMenuItem alloc] initWithTitle:localize(@"custom_controls.control_menu.add_drawer", nil) action:@selector(actionMenuAddDrawer)]; - [menuController setMenuItems:@[actionExit, actionSave, actionLoad, actionSetDef, actionSafeArea, actionAddButton, actionAddDrawer]]; + UIMenuItem *actionAddJoystick = [[UIMenuItem alloc] initWithTitle:localize(@"custom_controls.control_menu.add_joystick", nil) action:@selector(actionMenuAddJoystick)]; + [menuController setMenuItems:@[actionExit, actionSave, actionLoad, actionSafeArea, actionAddButton, actionAddDrawer, actionAddJoystick]]; CGPoint point = [sender locationInView:sender.view]; self.selectedPoint = CGRectMake(point.x, point.y, 1.0, 1.0); @@ -334,13 +335,6 @@ BOOL shouldDismissPopover = YES; } - (void)actionMenuLoad { - [self actionOpenFilePicker:^void(NSString* name) { - self.currentFileName = name; - [self loadControlFile:[NSString stringWithFormat:@"%@.json", name]]; - }]; -} - -- (void)actionMenuSetDef { [self actionOpenFilePicker:^void(NSString* name) { self.currentFileName = name; name = [NSString stringWithFormat:@"%@.json", name]; @@ -377,10 +371,7 @@ BOOL shouldDismissPopover = YES; - (void)actionMenuAddButtonWithDrawer:(ControlDrawer *)drawer { NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; dict[@"name"] = @"New"; - dict[@"keycodes"] = [[NSMutableArray alloc] initWithCapacity:4]; - for (int i = 0; i < 4; i++) { - dict[@"keycodes"][i] = @(0); - } + dict[@"keycodes"] = @[@0, @0, @0, @0].mutableCopy; dict[@"dynamicX"] = @"0"; dict[@"dynamicY"] = @"0"; dict[@"width"] = @(50.0); @@ -390,6 +381,7 @@ BOOL shouldDismissPopover = YES; dict[@"bgColor"] = @(0x4d000000); ControlButton *button; if (drawer == nil) { + dict[@"displayInGame"] = dict[@"displayInMenu"] = @YES; button = [ControlButton buttonWithProperties:dict]; [self doAddButton:button atIndex:@([self.ctrlView.layoutDictionary[@"mControlDataList"] count])]; [button snapAndAlignX:self.selectedPoint.origin.x-25.0 Y:self.selectedPoint.origin.y-25.0]; @@ -423,6 +415,7 @@ BOOL shouldDismissPopover = YES; properties[@"opacity"] = @(1); properties[@"cornerRadius"] = @(0); properties[@"bgColor"] = @(0x4d000000); + properties[@"displayInGame"] = properties[@"displayInMenu"] = @YES; NSMutableDictionary *data = [[NSMutableDictionary alloc] init]; data[@"orientation"] = @"FREE"; data[@"properties"] = properties; @@ -443,6 +436,26 @@ BOOL shouldDismissPopover = YES; [self actionMenuAddButtonWithDrawer:(ControlDrawer *)self.currentGesture.view]; } +- (void)actionMenuAddJoystick { + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + dict[@"dynamicX"] = @"0"; + dict[@"dynamicY"] = @"0"; + dict[@"width"] = @(100.0); + dict[@"height"] = @(100.0); + dict[@"opacity"] = @(1); + dict[@"bgColor"] = @(0x4d000000); + dict[@"displayInGame"] = dict[@"displayInMenu"] = @YES; + dict[@"forwardLock"] = @NO; + ControlJoystick *button = [ControlJoystick buttonWithProperties:dict]; + [self doAddButton:button atIndex:@([self.ctrlView.layoutDictionary[@"mJoystickDataList"] count])]; + [button snapAndAlignX:self.selectedPoint.origin.x-25.0 Y:self.selectedPoint.origin.y-25.0]; + [button update]; + [button addGestureRecognizer:[[UITapGestureRecognizer alloc] + initWithTarget:self action:@selector(showControlPopover:)]]; + [button addGestureRecognizer:[[UIPanGestureRecognizer alloc] + initWithTarget:self action:@selector(onTouch:)]]; +} + - (void)actionMenuBtnCopy { // copy } @@ -605,6 +618,10 @@ BOOL shouldDismissPopover = YES; #define TAG_SLIDER_OPACITY 12 #define TAG_SWITCH_DYNAMICPOS 13 +#define VISIBILITY_ALWAYS 0 +#define VISIBILITY_IN_GAME 1 +#define VISIBILITY_IN_MENU 2 + #pragma mark - CCMenuViewController CGFloat currentY; @@ -613,7 +630,7 @@ CGFloat currentY; } @property(nonatomic) NSMutableArray *keyCodeMap, *keyValueMap; -@property(nonatomic) NSArray* arrOrientation; +@property(nonatomic) NSArray *arrOrientation, *arrVisibility; @property(nonatomic) NSMutableDictionary* oldProperties; @property UITextField *activeField; @@ -621,8 +638,8 @@ CGFloat currentY; @property(nonatomic) UITextField *editName, *editSizeWidth, *editSizeHeight; @property(nonatomic) UITextView* editMapping; @property(nonatomic) UIPickerView* pickerMapping; -@property(nonatomic) UISegmentedControl* ctrlOrientation; -@property(nonatomic) UISwitch *switchToggleable, *switchMousePass, *switchSwipeable; +@property(nonatomic) UISegmentedControl *ctrlOrientation, *ctrlVisibility; +@property(nonatomic) UISwitch *switchFwdLock, *switchToggleable, *switchMousePass, *switchSwipeable; @property(nonatomic) UIColorWell *colorWellINTBackground, *colorWellINTStroke; @property(nonatomic) DBNumberedSlider *sliderStrokeWidth, *sliderCornerRadius, *sliderOpacity; @@ -692,18 +709,21 @@ CGFloat currentY; // Property: Name - UILabel *labelName = [self addLabel:localize(@"custom_controls.button_edit.name", nil)]; - self.editName = [[UITextField alloc] initWithFrame:CGRectMake(labelName.frame.size.width + 5.0, currentY, width - labelName.frame.size.width - 5.0, labelName.frame.size.height)]; - [self.editName addTarget:self action:@selector(textFieldEditingChanged) forControlEvents:UIControlEventEditingChanged]; - [self.editName addTarget:self.editName action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; - self.editName.placeholder = localize(@"custom_controls.button_edit.name", nil); - self.editName.returnKeyType = UIReturnKeyDone; - self.editName.text = self.targetButton.properties[@"name"]; - [self.scrollView addSubview:self.editName]; + if (![self.targetButton isKindOfClass:ControlJoystick.class]) { + UILabel *labelName = [self addLabel:localize(@"custom_controls.button_edit.name", nil)]; + self.editName = [[UITextField alloc] initWithFrame:CGRectMake(labelName.frame.size.width + 5.0, currentY, width - labelName.frame.size.width - 5.0, labelName.frame.size.height)]; + [self.editName addTarget:self action:@selector(textFieldEditingChanged) forControlEvents:UIControlEventEditingChanged]; + [self.editName addTarget:self.editName action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; + self.editName.placeholder = localize(@"custom_controls.button_edit.name", nil); + self.editName.returnKeyType = UIReturnKeyDone; + self.editName.text = self.targetButton.properties[@"name"]; + [self.scrollView addSubview:self.editName]; + currentY += labelName.frame.size.height + 15.0; + } - if (![self.targetButton isKindOfClass:[ControlSubButton class]]) { + if (![self.targetButton isKindOfClass:ControlSubButton.class] || + [((ControlSubButton *)self.targetButton).parentDrawer.drawerData[@"orientation"] isEqualToString:@"FREE"]) { // Property: Size - currentY += labelName.frame.size.height + 12.0; UILabel *labelSize = [self addLabel:localize(@"custom_controls.button_edit.size", nil)]; // width / 2.0 + (labelSize.frame.size.width + 4.0) / 2.0 CGFloat editSizeWidthValue = (width - labelSize.frame.size.width) / 2 - labelSize.frame.size.height / 2; @@ -728,11 +748,29 @@ CGFloat currentY; self.editSizeHeight.text = [self.targetButton.properties[@"height"] stringValue]; self.editSizeHeight.textAlignment = NSTextAlignmentCenter; [self.scrollView addSubview:self.editSizeHeight]; + currentY += labelSize.frame.size.height + 15.0; } - currentY += labelName.frame.size.height + 12.0; - if (![self.targetButton isKindOfClass:[ControlDrawer class]]) { + if ([self.targetButton isKindOfClass:ControlDrawer.class]) { + // Property: Orientation + self.arrOrientation = @[@"DOWN", @"LEFT", @"UP", @"RIGHT", @"FREE"]; + UILabel *labelOrientation = [self addLabel:localize(@"custom_controls.button_edit.orientation", nil)]; + self.ctrlOrientation = [[UISegmentedControl alloc] initWithItems:self.arrOrientation]; + [self.ctrlOrientation addTarget:self action:@selector(orientationValueChanged:) forControlEvents:UIControlEventValueChanged]; + self.ctrlOrientation.frame = CGRectMake(labelOrientation.frame.size.width + 5.0, currentY - 5.0, width - labelOrientation.frame.size.width - 5.0, 30.0); + self.ctrlOrientation.selectedSegmentIndex = [self.arrOrientation indexOfObject: + ((ControlDrawer *)self.targetButton).drawerData[@"orientation"]]; + [self.scrollView addSubview:self.ctrlOrientation]; + currentY += labelOrientation.frame.size.height + 15.0; + } else if ([self.targetButton isKindOfClass:ControlJoystick.class]) { + // Property: Forward lock + UILabel *labelFwdLock = [self addLabel:localize(@"custom_controls.button_edit.forward_lock", nil)]; + self.switchFwdLock = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currentY - 5.0, 50.0, 30)]; + [self.switchFwdLock setOn:[self.targetButton.properties[@"forwardLock"] boolValue] animated:NO]; + [self.scrollView addSubview:self.switchFwdLock]; + currentY += labelFwdLock.frame.size.height + 15.0; + } else { // Property: Mapping UILabel *labelMapping = [self addLabel:localize(@"custom_controls.button_edit.mapping", nil)]; @@ -754,87 +792,80 @@ CGFloat currentY; self.editMapping.inputAccessoryView = editPickToolbar; self.editMapping.inputView = self.pickerMapping; [self.scrollView addSubview:self.editMapping]; - currentY += self.editMapping.frame.size.height + 12.0; - } else { - // Property: Orientation - self.arrOrientation = @[@"DOWN", @"LEFT", @"UP", @"RIGHT", @"FREE"]; - UILabel *labelOrientation = [self addLabel:localize(@"custom_controls.button_edit.orientation", nil)]; - self.ctrlOrientation = [[UISegmentedControl alloc] initWithItems:self.arrOrientation]; - [self.ctrlOrientation addTarget:self action:@selector(orientationValueChanged) forControlEvents:UIControlEventValueChanged]; - self.ctrlOrientation.frame = CGRectMake(labelOrientation.frame.size.width + 5.0, currentY - 5.0, width - labelOrientation.frame.size.width - 5.0, 30.0); - self.ctrlOrientation.selectedSegmentIndex = [self.arrOrientation indexOfObject: - ((ControlDrawer *)self.targetButton).drawerData[@"orientation"]]; - [self.scrollView addSubview:self.ctrlOrientation]; - currentY += labelName.frame.size.height + 12.0; + currentY += self.editMapping.frame.size.height + 15.0; } - // Property: Toggleable - UILabel *labelToggleable = [self addLabel:localize(@"custom_controls.button_edit.toggleable", nil)]; - self.switchToggleable = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currentY - 5.0, 50.0, 30)]; - [self.switchToggleable setOn:[self.targetButton.properties[@"isToggle"] boolValue] animated:NO]; - [self.scrollView addSubview:self.switchToggleable]; + if (![self.targetButton isKindOfClass:ControlJoystick.class]) { + // Property: Toggleable + UILabel *labelToggleable = [self addLabel:localize(@"custom_controls.button_edit.toggleable", nil)]; + self.switchToggleable = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currentY - 5.0, 50.0, 30)]; + [self.switchToggleable setOn:[self.targetButton.properties[@"isToggle"] boolValue] animated:NO]; + [self.scrollView addSubview:self.switchToggleable]; + currentY += labelToggleable.frame.size.height + 15.0; - // Property: Mouse pass - currentY += labelName.frame.size.height + 12.0; - UILabel *labelMousePass = [self addLabel:localize(@"custom_controls.button_edit.mouse_pass", nil)]; - self.switchMousePass = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currentY - 5.0, 50.0, 30)]; - [self.switchMousePass setOn:[self.targetButton.properties[@"passThruEnabled"] boolValue]]; - [self.scrollView addSubview:self.switchMousePass]; + // Property: Mouse pass + UILabel *labelMousePass = [self addLabel:localize(@"custom_controls.button_edit.mouse_pass", nil)]; + self.switchMousePass = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currentY - 5.0, 50.0, 30)]; + [self.switchMousePass setOn:[self.targetButton.properties[@"passThruEnabled"] boolValue]]; + [self.scrollView addSubview:self.switchMousePass]; + currentY += labelMousePass.frame.size.height + 15.0; - // Property: Swipeable - currentY += labelName.frame.size.height + 12.0; - UILabel *labelSwipeable = [self addLabel:localize(@"custom_controls.button_edit.swipeable", nil)]; - self.switchSwipeable = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currentY - 5.0, 50.0, 30)]; - [self.switchSwipeable setOn:[self.targetButton.properties[@"isSwipeable"] boolValue]]; - [self.scrollView addSubview:self.switchSwipeable]; + // Property: Swipeable + UILabel *labelSwipeable = [self addLabel:localize(@"custom_controls.button_edit.swipeable", nil)]; + self.switchSwipeable = [[UISwitch alloc] initWithFrame:CGRectMake(width - 62.0, currentY - 5.0, 50.0, 30)]; + [self.switchSwipeable setOn:[self.targetButton.properties[@"isSwipeable"] boolValue]]; + [self.scrollView addSubview:self.switchSwipeable]; + currentY += labelSwipeable.frame.size.height + 15.0; + } // Property: Background color - currentY += labelName.frame.size.height + 12.0; UILabel *labelBGColor = [self addLabel:localize(@"custom_controls.button_edit.bg_color", nil)]; self.colorWellINTBackground = [[UIColorWell alloc] initWithFrame:CGRectMake(width - 42.0, currentY - 5.0, 30.0, 30.0)]; [self.colorWellINTBackground addTarget:self action:@selector(colorWellChanged) forControlEvents:UIControlEventValueChanged]; self.colorWellINTBackground.selectedColor = self.targetButton.backgroundColor; [self.scrollView addSubview:self.colorWellINTBackground]; + currentY += labelBGColor.frame.size.height + 15.0; // Property: Stroke width - currentY += labelName.frame.size.height + 12.0; UILabel *labelStrokeWidth = [self addLabel:localize(@"custom_controls.button_edit.stroke_width", nil)]; self.sliderStrokeWidth = [[DBNumberedSlider alloc] initWithFrame:CGRectMake(labelStrokeWidth.frame.size.width + 5.0, currentY - 5.0, width - labelStrokeWidth.frame.size.width - 5.0, 30.0)]; self.sliderStrokeWidth.continuous = YES; - self.sliderStrokeWidth.maximumValue = 100; + self.sliderStrokeWidth.maximumValue = MAX([self.targetButton.properties[@"width"] intValue], [self.targetButton.properties[@"height"] intValue]) / 2; self.sliderStrokeWidth.tag = TAG_SLIDER_STROKEWIDTH; self.sliderStrokeWidth.value = [self.targetButton.properties[@"strokeWidth"] intValue]; [self.sliderStrokeWidth addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged]; [self.scrollView addSubview:self.sliderStrokeWidth]; + currentY += labelStrokeWidth.frame.size.height + 15.0; // Property: Stroke color - currentY += labelName.frame.size.height + 12.0; UILabel *labelStrokeColor = [self addLabel:localize(@"custom_controls.button_edit.stroke_color", nil)]; self.colorWellINTStroke = [[UIColorWell alloc] initWithFrame:CGRectMake(width - 42.0, currentY - 5.0, 30.0, 30.0)]; [self.colorWellINTStroke addTarget:self action:@selector(colorWellChanged) forControlEvents:UIControlEventValueChanged]; self.colorWellINTStroke.selectedColor = [[UIColor alloc] initWithCGColor:self.targetButton.layer.borderColor]; [self.scrollView addSubview:self.colorWellINTStroke]; + currentY += labelStrokeColor.frame.size.height + 15.0; // Property: Corner radius - currentY += labelName.frame.size.height + 12.0; - UILabel *labelCornerRadius = [self addLabel:localize(@"custom_controls.button_edit.corner_radius", nil)]; - self.sliderCornerRadius = [[DBNumberedSlider alloc] initWithFrame:CGRectMake(labelCornerRadius.frame.size.width + 5.0, currentY - 5.0, width - labelCornerRadius.frame.size.width - 5.0, 30.0)]; - self.sliderCornerRadius.continuous = YES; - self.sliderCornerRadius.maximumValue = 100; - self.sliderCornerRadius.tag = TAG_SLIDER_CORNERRADIUS; - self.sliderCornerRadius.value = [self.targetButton.properties[@"cornerRadius"] intValue]; - [self.sliderCornerRadius addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged]; - [self.scrollView addSubview:self.sliderCornerRadius]; + if (![self.targetButton isKindOfClass:ControlJoystick.class]) { + UILabel *labelCornerRadius = [self addLabel:localize(@"custom_controls.button_edit.corner_radius", nil)]; + self.sliderCornerRadius = [[DBNumberedSlider alloc] initWithFrame:CGRectMake(labelCornerRadius.frame.size.width + 5.0, currentY - 5.0, width - labelCornerRadius.frame.size.width - 5.0, 30.0)]; + self.sliderCornerRadius.continuous = YES; + self.sliderCornerRadius.maximumValue = 100; + self.sliderCornerRadius.tag = TAG_SLIDER_CORNERRADIUS; + self.sliderCornerRadius.value = [self.targetButton.properties[@"cornerRadius"] intValue]; + [self.sliderCornerRadius addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged]; + [self.scrollView addSubview:self.sliderCornerRadius]; + currentY += labelCornerRadius.frame.size.height + 15.0; + } // Property: Button Opacity - currentY += labelName.frame.size.height + 12.0; UILabel *labelOpacity = [self addLabel:localize(@"custom_controls.button_edit.opacity", nil)]; self.sliderOpacity = [[DBNumberedSlider alloc] initWithFrame:CGRectMake(labelOpacity.frame.size.width + 5.0, currentY - 5.0, width - labelOpacity.frame.size.width - 5.0, 30.0)]; self.sliderOpacity.continuous = YES; @@ -844,9 +875,27 @@ CGFloat currentY; self.sliderOpacity.value = [self.targetButton.properties[@"opacity"] floatValue] * 100.0; [self.sliderOpacity addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged]; [self.scrollView addSubview:self.sliderOpacity]; + currentY += labelOpacity.frame.size.height + 15.0; - currentY += labelName.frame.size.height + 12.0; + // Property: Visibility + self.arrVisibility = @[localize(@"Always", nil), localize(@"In game", nil), localize(@"In menu", nil)]; + UILabel *labelVisibility = [self addLabel:localize(@"custom_controls.button_edit.visibility", nil)]; + self.ctrlVisibility = [[UISegmentedControl alloc] initWithItems:self.arrVisibility]; + [self.ctrlVisibility addTarget:self action:@selector(visibilityValueChanged:) forControlEvents:UIControlEventValueChanged]; + self.ctrlVisibility.frame = CGRectMake(labelVisibility.frame.size.width + 5.0, currentY - 5.0, width - labelVisibility.frame.size.width - 5.0, 30.0); + BOOL displayInGame = [self.targetButton.properties[@"displayInGame"] boolValue]; + BOOL displayInMenu = [self.targetButton.properties[@"displayInMenu"] boolValue]; + if (displayInGame && displayInMenu) { + self.ctrlVisibility.selectedSegmentIndex = VISIBILITY_ALWAYS; + } else if (displayInGame) { + self.ctrlVisibility.selectedSegmentIndex = VISIBILITY_IN_GAME; + } else if (displayInMenu) { + self.ctrlVisibility.selectedSegmentIndex = VISIBILITY_IN_MENU; + } // else the segment is not chosen + [self.scrollView addSubview:self.ctrlVisibility]; + currentY += labelVisibility.frame.size.height + 15.0; + self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, currentY); } @@ -887,6 +936,9 @@ CGFloat currentY; } - (void)actionEditFinish { + if (self.switchFwdLock) { + self.targetButton.properties[@"forwardLock"] = @(self.switchFwdLock.isOn); + } self.targetButton.properties[@"isToggle"] = @(self.switchToggleable.isOn); self.targetButton.properties[@"passThruEnabled"] = @(self.switchMousePass.isOn); self.targetButton.properties[@"isSwipeable"] = @(self.switchSwipeable.isOn); @@ -906,24 +958,29 @@ CGFloat currentY; [self dismissViewControllerAnimated:YES completion:nil]; } -- (void)orientationValueChanged { +- (void)orientationValueChanged:(UISegmentedControl *)sender { ((ControlDrawer *)self.targetButton).drawerData[@"orientation"] = - self.arrOrientation[self.ctrlOrientation.selectedSegmentIndex]; + self.arrOrientation[sender.selectedSegmentIndex]; [(ControlDrawer *)self.targetButton syncButtons]; } - (void)sliderValueChanged:(DBNumberedSlider *)sender { - [sender setValue:(NSInteger)sender.value animated:NO]; if (sender.tag == TAG_SLIDER_STROKEWIDTH) { self.colorWellINTStroke.enabled = sender.value != 0; self.targetButton.properties[@"strokeWidth"] = @((NSInteger) self.sliderStrokeWidth.value); } else { + [sender setValue:(NSInteger)sender.value animated:NO]; self.targetButton.properties[@"cornerRadius"] = @((NSInteger) self.sliderCornerRadius.value); self.targetButton.properties[@"opacity"] = @(self.sliderOpacity.value / 100.0); } [self.targetButton update]; } +- (void)visibilityValueChanged:(UISegmentedControl *)sender { + self.targetButton.properties[@"displayInGame"] = @(sender.selectedSegmentIndex != VISIBILITY_IN_MENU); + self.targetButton.properties[@"displayInMenu"] = @(sender.selectedSegmentIndex != VISIBILITY_IN_GAME); +} + #pragma mark - UIPickerView stuff - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view { diff --git a/Natives/SurfaceViewController.h b/Natives/SurfaceViewController.h index 36744889..60ad03d5 100644 --- a/Natives/SurfaceViewController.h +++ b/Natives/SurfaceViewController.h @@ -21,6 +21,7 @@ CGPoint lastVirtualMousePoint; - (void)sendTouchPoint:(CGPoint)location withEvent:(int)event; - (void)updateSavedResolution; +- (void)updateGrabState; + (BOOL)isRunning; diff --git a/Natives/SurfaceViewController.m b/Natives/SurfaceViewController.m index cf7ddbb0..b4e3a823 100644 --- a/Natives/SurfaceViewController.m +++ b/Natives/SurfaceViewController.m @@ -5,10 +5,6 @@ #import "authenticator/BaseAuthenticator.h" #import "customcontrols/ControlButton.h" #import "customcontrols/ControlDrawer.h" -// Incomplete -#ifdef INTERNAL_VIRTUAL_JOYSTICK -#import "customcontrols/ControlJoystick.h" -#endif #import "customcontrols/ControlSubButton.h" #import "customcontrols/CustomControlsUtils.h" @@ -60,7 +56,7 @@ BOOL slideableHotbar; @property(nonatomic) CGFloat screenScale; @property(nonatomic) CGFloat mouseSpeed; @property(nonatomic) CGRect clickRange; -@property(nonatomic) BOOL isMacCatalystApp, shouldTriggerClick, shouldTriggerHaptic; +@property(nonatomic) BOOL isMacCatalystApp, shouldTriggerClick, shouldTriggerHaptic, toggleHidden; @property(nonatomic) BOOL enableMouseGestures, enableHotbarGestures; @@ -265,14 +261,6 @@ BOOL slideableHotbar; [self updateJetsamControl]; [self updatePreferenceChanges]; [self loadCustomControls]; - [self executebtn_special_togglebtn:0]; - -#ifdef INTERNAL_VIRTUAL_JOYSTICK - // just for testing - ControlJoystick *joystick = ControlJoystick.buttonWithDefaultProperties; - [self.ctrlView addSubview:joystick]; - [joystick update]; -#endif if (UIApplication.sharedApplication.connectedScenes.count > 1 && getPrefBool(@"video.fullscreen_airplay")) { @@ -388,6 +376,40 @@ BOOL slideableHotbar; CallbackBridge_nativeSendScreenSize(windowWidth, windowHeight); } +- (void)updateControlHiddenState:(BOOL)hide { + for (UIView *view in self.ctrlView.subviews) { + ControlButton *button = (ControlButton *)view; + if (!button.canBeHidden) continue; + BOOL hidden = hide || !( + (isGrabbing && [button.properties[@"displayInGame"] boolValue]) || + (!isGrabbing && [button.properties[@"displayInMenu"] boolValue])); + if (!hidden && ![button isKindOfClass:ControlSubButton.class]) { + button.hidden = hidden; + if ([button isKindOfClass:ControlDrawer.class]) { + [(ControlDrawer *)button restoreButtonVisibility]; + } + } else if (hidden) { + button.hidden = hidden; + } + } +} + +- (void)updateGrabState { + // Update cursor position + if (isGrabbing == JNI_TRUE) { + CGFloat screenScale = self.surfaceView.layer.contentsScale; + CallbackBridge_nativeSendCursorPos(ACTION_DOWN, lastVirtualMousePoint.x * screenScale, lastVirtualMousePoint.y * screenScale); + virtualMouseFrame.origin.x = self.view.frame.size.width / 2; + virtualMouseFrame.origin.y = self.view.frame.size.height / 2; + self.mousePointerView.frame = virtualMouseFrame; + } + self.scrollPanGesture.enabled = !isGrabbing; + self.mousePointerView.hidden = isGrabbing || !virtualMouseEnabled; + + // Update buttons visibility + [self updateControlHiddenState:NO]; +} + - (void)launchMinecraft { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSDictionary *version = @{@"id": PLProfiles.current.selectedProfile[@"lastVersionId"]}; @@ -412,6 +434,8 @@ BOOL slideableHotbar; [self.swipeableButtons removeAllObjects]; NSString *controlFile = [PLProfiles resolveKeyForCurrentProfile:@"defaultTouchCtrl"]; [self.ctrlView loadControlFile:controlFile]; + + ControlButton *menuButton; for (ControlButton *button in self.ctrlView.subviews) { BOOL isSwipeable = [button.properties[@"isSwipeable"] boolValue]; @@ -419,8 +443,10 @@ BOOL slideableHotbar; BOOL isMenuButton = NO; for (int i = 0; i < 4; i++) { int keycodeInt = [button.properties[@"keycodes"][i] intValue]; - button.canBeHidden &= keycodeInt != SPECIALBTN_TOGGLECTRL; - isMenuButton |= keycodeInt == SPECIALBTN_MENU; + button.canBeHidden &= keycodeInt != SPECIALBTN_TOGGLECTRL && keycodeInt != SPECIALBTN_VIRTUALMOUSE; + if (keycodeInt == SPECIALBTN_MENU) { + menuButton = button; + } } [button addTarget:self action:@selector(executebtn_down:) forControlEvents:UIControlEventTouchDown]; @@ -433,17 +459,20 @@ BOOL slideableHotbar; [button addGestureRecognizer:panRecognizerButton]; [self.swipeableButtons addObject:button]; } + } - if (!isMenuButton) continue; + [self updateControlHiddenState:self.toggleHidden]; + + if (menuButton) { NSMutableArray *items = [NSMutableArray new]; for (int i = 0; i < self.menuArray.count; i++) { UIAction *item = [UIAction actionWithTitle:localize(self.menuArray[i], nil) image:nil identifier:nil handler:^(id action) {[self didSelectMenuItem:i];}]; [items addObject:item]; } - button.menu = [UIMenu menuWithTitle:@"" image:nil identifier:nil + menuButton.menu = [UIMenu menuWithTitle:@"" image:nil identifier:nil options:UIMenuOptionsDisplayInline children:items]; - button.showsMenuAsPrimaryAction = YES; + menuButton.showsMenuAsPrimaryAction = YES; self.edgeGesture.enabled = NO; } } @@ -780,7 +809,6 @@ BOOL slideableHotbar; #pragma mark - On-screen button functions -int currentVisibility = 1; - (void)executebtn:(ControlButton *)sender withAction:(int)action { int held = action == ACTION_DOWN; for (int i = 0; i < 4; i++) { @@ -924,22 +952,9 @@ int currentVisibility = 1; } - (void)executebtn_special_togglebtn:(int)held { - if (held == 0) { - currentVisibility = !currentVisibility; - for (UIView *view in self.ctrlView.subviews) { - ControlButton *button = (ControlButton *)view; - if (button.canBeHidden) { - if (!currentVisibility && ![button isKindOfClass:[ControlSubButton class]]) { - button.hidden = currentVisibility; - if ([button isKindOfClass:[ControlDrawer class]]) { - [(ControlDrawer *)button restoreButtonVisibility]; - } - } else if (currentVisibility) { - button.hidden = currentVisibility; - } - } - } - } + if (held) return; + self.toggleHidden = !self.toggleHidden; + [self updateControlHiddenState:self.toggleHidden]; } #pragma mark - Input: On-screen touch events diff --git a/Natives/customcontrols/ControlButton.h b/Natives/customcontrols/ControlButton.h index a63a6e65..027ecb95 100644 --- a/Natives/customcontrols/ControlButton.h +++ b/Natives/customcontrols/ControlButton.h @@ -11,12 +11,13 @@ @interface ControlButton : UIButton { } -@property BOOL canBeHidden, isToggleOn; +@property BOOL canBeHidden, displayInGame, displayInMenu, isToggleOn; @property(nonatomic) NSMutableDictionary* properties; @property(nonatomic) UIColor* savedBackgroundColor; + (id)buttonWithProperties:(NSMutableDictionary *)propArray; +- (CGFloat)calculateDynamicPos:(NSString *)string; - (BOOL)canSnap:(ControlButton *)button; - (NSString *)generateDynamicX:(CGFloat)x; - (NSString *)generateDynamicY:(CGFloat)y; diff --git a/Natives/customcontrols/ControlButton.m b/Natives/customcontrols/ControlButton.m index 76632d26..40735120 100644 --- a/Natives/customcontrols/ControlButton.m +++ b/Natives/customcontrols/ControlButton.m @@ -29,6 +29,7 @@ //NSLog(@"DBG button prop = %@", propArray); ControlButton *instance = [self buttonWithType:UIButtonTypeSystem]; instance.autoresizingMask = UIViewAutoresizingFlexibleWidth; + instance.clipsToBounds = YES; instance.tintColor = [UIColor whiteColor]; instance.titleLabel.adjustsFontSizeToFitWidth = YES; instance.titleLabel.lineBreakMode = NSLineBreakByCharWrapping; @@ -151,6 +152,9 @@ - (void)update { NSAssert(self.superview != nil, @"should not be nil"); + self.displayInGame = [self.properties[@"displayInGame"] boolValue]; + self.displayInMenu = [self.properties[@"displayInMenu"] boolValue]; + // net/kdt/pojavlaunch/customcontrols/ControlData.update() [self preProcessProperties]; @@ -179,10 +183,12 @@ self.layer.borderColor = [convertARGB2UIColor(propStrokeColor) CGColor]; self.layer.cornerRadius = MIN(self.frame.size.width, self.frame.size.height) / 200.0 * propCornerRadius; - self.layer.borderWidth = MAX(self.frame.size.width, self.frame.size.height) / 200.0 * propStrokeWidth; - self.clipsToBounds = YES; + self.layer.borderWidth = propStrokeWidth; - [self setTitle:self.properties[@"name"] forState:UIControlStateNormal]; + [UIView performWithoutAnimation:^{ + [self setTitle:self.properties[@"name"] forState:UIControlStateNormal]; + [self layoutIfNeeded]; + }]; } // NOTE: Unlike Android's impl, this method uses dp instead of px (no call to dpToPx), "view.center" instead of "view.pos + view.size/2" diff --git a/Natives/customcontrols/ControlDrawer.m b/Natives/customcontrols/ControlDrawer.m index 92b49cfd..3306e8a8 100644 --- a/Natives/customcontrols/ControlDrawer.m +++ b/Natives/customcontrols/ControlDrawer.m @@ -39,13 +39,12 @@ // NOTE: Unlike Android's impl, this method uses dp instead of px (no call to dpToPx) - (void)alignButtons { NSString *orientation = (NSString *)self.drawerData[@"orientation"]; - if ([orientation isEqualToString:@"FREE"]) { - return; - } for (int i = 0; i < self.buttons.count; i++) { ControlButton *button = self.buttons[i]; - if ([orientation isEqualToString:@"RIGHT"]) { + if ([orientation isEqualToString:@"FREE"]) { + // Do nothing but call update + } else if ([orientation isEqualToString:@"RIGHT"]) { button.properties[@"dynamicX"] = [self generateDynamicX:self.frame.origin.x + ([self.properties[@"width"] floatValue] + 2.0) * (i+1)]; button.properties[@"dynamicY"] = [self generateDynamicY:self.frame.origin.y]; } else if ([orientation isEqualToString:@"LEFT"]) { diff --git a/Natives/customcontrols/ControlJoystick.h b/Natives/customcontrols/ControlJoystick.h new file mode 100644 index 00000000..38ef223e --- /dev/null +++ b/Natives/customcontrols/ControlJoystick.h @@ -0,0 +1,8 @@ +#import "ControlButton.h" + +@interface ControlJoystick : ControlButton { +} + ++ (id)buttonWithDefaultProperties; + +@end diff --git a/Natives/customcontrols/ControlJoystick.m b/Natives/customcontrols/ControlJoystick.m new file mode 100644 index 00000000..6cc7e72d --- /dev/null +++ b/Natives/customcontrols/ControlJoystick.m @@ -0,0 +1,240 @@ +#import "ControlJoystick.h" +#import "CustomControlsUtils.h" +#import "../input/ControllerInput.h" +#import "../UIKit+hook.h" +#import "../utils.h" +#include "../glfw_keycodes.h" + +// Left thumbstick directions +#define DIRECTION_EAST 0 +#define DIRECTION_NORTH_EAST 1 +#define DIRECTION_NORTH 2 +#define DIRECTION_NORTH_WEST 3 +//#define DIRECTION_WEST 4 +#define DIRECTION_SOUTH_WEST 5 +//#define DIRECTION_SOUTH 6 +#define DIRECTION_SOUTH_EAST 7 + +extern BOOL leftShiftHeld; + +extern CGFloat lastXValue; // lastHorizontalValue +extern CGFloat lastYValue; // lastVerticalValue + +// From CustomControlsUtils +NSMutableDictionary* createButton(NSString* name, int* keycodes, NSString* dynamicX, NSString* dynamicY, CGFloat width, CGFloat height); + +@interface ControlJoystick() +@property(nonatomic) UIView *background, *thumb; +@property(nonatomic) UIButton *fwdLockView; +@property(nonatomic) CGPoint mCenter; +@end + +@implementation ControlJoystick + ++ (id)buttonWithDefaultProperties { + NSMutableDictionary *properties = createButton(@"", (int[4]){0,0,0,0 /* GLFW_KEY_W, GLFW_KEY_A, GLFW_KEY_S, GLFW_KEY_D */}, @"${margin}", @"${bottom} - ${margin}", 200, 200); + properties[@"cornerRadius"] = @100; + properties[@"bgColor"] = @(0x77111111); + ControlJoystick *instance = [self buttonWithProperties:properties]; + return instance; +} + ++ (id)buttonWithProperties:(NSMutableDictionary *)propArray { + ControlJoystick *instance = [super buttonWithProperties:propArray]; + instance.clipsToBounds = NO; + instance.background = [UIView new]; + instance.thumb = [UIView new]; + instance.thumb.backgroundColor = UIColor.blackColor; + [instance addSubview:instance.background]; + [instance addSubview:instance.thumb]; + + if (!isControlModifiable && [propArray[@"forwardLock"] boolValue]) { + instance.fwdLockView = [UIButton buttonWithType:UIButtonTypeCustom]; + instance.fwdLockView.hidden = YES; + instance.fwdLockView.imageEdgeInsets = UIEdgeInsetsMake(4, 4, 4, 4); + instance.fwdLockView.tintColor = UIColor.whiteColor; + instance.fwdLockView.userInteractionEnabled = NO; + [instance addSubview:instance.fwdLockView]; + } + + return instance; +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + if (isControlModifiable || touches.count != 1) return; + if (touches.anyObject.view == self) { + self.thumb.center = [touches.anyObject locationInView:self]; + } + if (isGrabbing && self.fwdLockView) { + self.fwdLockView.center = CGPointMake(self.mCenter.x, -self.mCenter.y); + } +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + if (isControlModifiable || touches.count != 1) return; + UITouch *touch = touches.anyObject; + + //CGPoint prev = [touches.anyObject previousLocationInView:self]; + //CGPoint loc = [touches.anyObject locationInView:self]; + + CGPoint center = [touches.anyObject locationInView:self]; + //self.thumb.center; + //center.x += loc.x - prev.x; + //center.y += loc.y - prev.y; + + CGFloat dirX = center.x - self.mCenter.x; + CGFloat dirY = center.y - self.mCenter.y; + CGFloat radius = sqrt(dirX*dirX + dirY*dirY); + CGFloat maxRadius = MIN(self.mCenter.x, self.mCenter.y) - self.thumb.frame.size.width/2; + if (radius > maxRadius) { + center.x = dirX*maxRadius/radius + self.mCenter.x; + center.y = dirY*maxRadius/radius + self.mCenter.y; + } + self.thumb.center = center; + + CGFloat deadzone = 0.35; + if (!isGrabbing || radius >= maxRadius*deadzone) { + [self callbackMoveX:((center.x / self.frame.size.width)*2-1) Y:((-center.y / self.frame.size.height)*2+1)]; + } + +#ifdef DEBUG_JOYSTICK + [UIView performWithoutAnimation:^{ + [self setTitle:[NSString stringWithFormat:@"rad=%f\nCX=%f\nCY=%f", radius, self.mCenter.x / radius, self.mCenter.y / radius] forState:UIControlStateNormal]; + [self layoutIfNeeded]; + }]; +#endif +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + if (isControlModifiable) return; + + lastXValue = lastYValue = 0; + if (isGrabbing && self.fwdLockView && CGRectContainsPoint(self.fwdLockView.frame, [touches.anyObject locationInView:self])) { + self.fwdLockView.center = self.thumb.center; + } else { + self.thumb.center = self.mCenter; + [self callbackMoveX:0 Y:0]; + } +} + +- (void)callbackMoveX:(CGFloat)xValue Y:(CGFloat)yValue { +#ifdef DEBUG_JOYSTICK + static UILabel *debugLabel; + if (debugLabel == nil) { + debugLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 200)]; + debugLabel.lineBreakMode = NSLineBreakByWordWrapping; + debugLabel.numberOfLines = 0; + debugLabel.userInteractionEnabled = NO; + [currentWindow() addSubview:debugLabel]; + } +#endif + + if (!isGrabbing) { + // Update virtual mouse position + lastXValue = xValue; + lastYValue = yValue; + return; + } + + static char lastDirection = -2; + char direction = -1; + if (xValue != 0 && yValue != 0) { + CGFloat degree = atan2f(yValue, xValue) * (180.0 / M_PI); + if (degree < 0) { + degree += 360; + } + direction = (int)((degree+22.5)/45.0) % 8; +#ifdef DEBUG_JOYSTICK + debugLabel.text = [NSString stringWithFormat:@"x=%f\ny=%f\natan=%f\ndeg=%f\ndirection=%d, last=%d\npressW=%d A=%d S=%d D=%d", atan2f(yValue, xValue), xValue, yValue, degree, direction, lastDirection, + direction >= DIRECTION_NORTH_EAST && + direction <= DIRECTION_NORTH_WEST, + direction >= DIRECTION_NORTH_WEST && + direction <= DIRECTION_SOUTH_WEST, + direction >= DIRECTION_SOUTH_WEST && + direction <= DIRECTION_SOUTH_EAST, + direction == DIRECTION_SOUTH_EAST || + direction == DIRECTION_EAST || + direction == DIRECTION_NORTH_EAST]; +#endif + } + if (lastDirection == direction) { + return; + } + + // Update WASD states + int mod = leftShiftHeld ? GLFW_MOD_SHIFT : 0; + CallbackBridge_nativeSendKey(GLFW_KEY_W, 0, + direction >= DIRECTION_NORTH_EAST && + direction <= DIRECTION_NORTH_WEST, + mod); + CallbackBridge_nativeSendKey(GLFW_KEY_A, 0, + direction >= DIRECTION_NORTH_WEST && + direction <= DIRECTION_SOUTH_WEST, + mod); + CallbackBridge_nativeSendKey(GLFW_KEY_S, 0, + direction >= DIRECTION_SOUTH_WEST && + direction <= DIRECTION_SOUTH_EAST, + mod); + CallbackBridge_nativeSendKey(GLFW_KEY_D, 0, + direction == DIRECTION_SOUTH_EAST || + direction == DIRECTION_EAST || + direction == DIRECTION_NORTH_EAST, + mod); + + self.fwdLockView.hidden = direction != DIRECTION_NORTH; + lastDirection = direction; +} + +- (void)setFrame:(CGRect)frame { + [super setFrame:frame]; + self.mCenter = CGPointMake(frame.size.width/2, frame.size.height/2); + CGFloat minSize = MIN(frame.size.width, frame.size.height); + + self.fwdLockView.frame = CGRectMake(0, 0, minSize/4, minSize/4); + [self.fwdLockView setImage:[UIImage systemImageNamed:@"lock"] forState:UIControlStateNormal]; + self.fwdLockView.layer.cornerRadius = self.fwdLockView.frame.size.width/2; + + self.thumb.frame = CGRectMake(0, 0, minSize/4, minSize/4); + self.thumb.center = self.mCenter; + self.thumb.layer.cornerRadius = self.thumb.frame.size.width/2; + + CGFloat bgSize = minSize - self.thumb.frame.size.width + self.background.layer.borderWidth/2; + self.background.frame = CGRectMake(0, 0, bgSize, bgSize); + self.background.layer.cornerRadius = bgSize/2; + self.background.center = self.mCenter; +} + +- (void)update { + NSAssert(self.superview != nil, @"should not be nil"); + + self.displayInGame = [self.properties[@"displayInGame"] boolValue]; + self.displayInMenu = [self.properties[@"displayInMenu"] boolValue]; + + // net/kdt/pojavlaunch/customcontrols/ControlData.update() + [self preProcessProperties]; + + NSString *propDynamicX = (NSString *) self.properties[@"dynamicX"]; + NSString *propDynamicY = (NSString *) self.properties[@"dynamicY"]; + + CGFloat propW = [self.properties[@"width"] floatValue]; + CGFloat propH = [self.properties[@"height"] floatValue]; + float propStrokeWidth = [self.properties[@"strokeWidth"] floatValue]; + int propBackgroundColor = [self.properties[@"bgColor"] intValue]; + int propStrokeColor = [self.properties[@"strokeColor"] intValue]; + + self.background.backgroundColor = self.fwdLockView.backgroundColor = convertARGB2UIColor(propBackgroundColor); + self.background.layer.borderColor = [convertARGB2UIColor(propStrokeColor) CGColor]; + self.background.layer.borderWidth = propStrokeWidth + 1; + + // Calculate dynamic position + CGFloat propX = [self calculateDynamicPos:propDynamicX]; + CGFloat propY = [self calculateDynamicPos:propDynamicY]; + + // Update other properties + self.frame = CGRectMake(propX, propY, propW, propH); + self.alpha = [self.properties[@"opacity"] floatValue]; + self.alpha = MAX(self.alpha, isControlModifiable ? 0.1 : 0.01); + self.layer.cornerRadius = MIN(self.frame.size.width, self.frame.size.height) / 2.0; +} + +@end diff --git a/Natives/customcontrols/ControlSubButton.m b/Natives/customcontrols/ControlSubButton.m index 7cd03b29..2ba19981 100644 --- a/Natives/customcontrols/ControlSubButton.m +++ b/Natives/customcontrols/ControlSubButton.m @@ -17,6 +17,11 @@ - (void)setParentDrawer:(ControlDrawer *)drawer { _parentDrawer = drawer; + + // Copy visibility properties + self.displayInGame = drawer.displayInGame; + self.displayInMenu = drawer.displayInMenu; + [self preProcessProperties]; //NSLog(@"DEBUG: SubButton properties %@", self.properties); } diff --git a/Natives/customcontrols/CustomControlsUtils.m b/Natives/customcontrols/CustomControlsUtils.m index ea2d8cf1..4f007113 100644 --- a/Natives/customcontrols/CustomControlsUtils.m +++ b/Natives/customcontrols/CustomControlsUtils.m @@ -1,4 +1,5 @@ #import "ControlDrawer.h" +#import "ControlJoystick.h" #import "ControlSubButton.h" #import "CustomControlsUtils.h" #import "../LauncherPreferences.h" @@ -20,6 +21,8 @@ NSMutableDictionary* createButton(NSString* name, int* keycodes, NSString* dynam dict[@"opacity"] = @(1); dict[@"cornerRadius"] = @(0); dict[@"bgColor"] = @(0x4d000000); + dict[@"displayInGame"] = @YES; + dict[@"displayInMenu"] = @YES; return dict; } @@ -56,6 +59,28 @@ int convertUIColor2RGB(UIColor* color) { return (0xFF << 24) | (r << 16) | (g << 8) | (b << 0); } +static int computeStrokeWidth(float widthInPercent, float width, float height) { + CGFloat maxSize = MAX(width, height); + return (int) ((maxSize / 2) * (widthInPercent / 100)); +} + +void convertV3_4Layout(NSMutableDictionary* dict) { + // Convert the layout stroke width to the V5 form + for (NSMutableDictionary *button in (NSMutableArray *)dict[@"mControlDataList"]) { + button[@"strokeWidth"] = @(computeStrokeWidth([button[@"strokeWidth"] intValue], [button[@"width"] intValue], [button[@"height"] intValue])); + } + + // Add default values + for (NSString *key in @[@"mControlDataList", @"mDrawerDataList"]) { + for (NSMutableDictionary *button in (NSMutableArray *)dict[key]) { + button[@"displayInGame"] = @YES; + button[@"displayInMenu"] = @YES; + } + } + + dict[@"version"] = @(6); +} + void convertV2Layout(NSMutableDictionary* dict) { CGRect screenBounds = [[UIScreen mainScreen] bounds]; CGFloat screenScale = [[UIScreen mainScreen] scale]; @@ -83,6 +108,7 @@ void convertV2Layout(NSMutableDictionary* dict) { } dict[@"version"] = @(5); + convertV3_4Layout(dict); } void convertV1Layout(NSMutableDictionary* dict) { @@ -155,9 +181,12 @@ BOOL convertLayoutIfNecessary(NSMutableDictionary* dict) { case 3: case 4: case 5: + convertV3_4Layout(dict); + break; + case 6: break; default: - showDialog(localize(@"custom_controls.control_menu.save.error.json", nil), [NSString stringWithFormat:localize(@"custom_controls.error.imcompatible", nil), version]); + showDialog(localize(@"custom_controls.control_menu.save.error.json", nil), [NSString stringWithFormat:localize(@"custom_controls.error.incompatible", nil), version]); return NO; } return YES; @@ -169,12 +198,13 @@ void generateAndSaveDefaultControl() { return; } - // Generate a v2.6 control + // Generate a v2.7 control NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; dict[@"version"] = @(5); dict[@"scaledAt"] = @(100); - dict[@"mControlDataList"] = [[NSMutableArray alloc] init]; - dict[@"mDrawerDataList"] = [[NSMutableArray alloc] init]; + dict[@"mControlDataList"] = [NSMutableArray new]; + dict[@"mDrawerDataList"] = [NSMutableArray new]; + dict[@"mJoystickDataList"] = [NSMutableArray new]; [dict[@"mControlDataList"] addObject:createButton(@"Keyboard", (int[]){SPECIALBTN_KEYBOARD,0,0,0}, @"${margin} * 3 + ${width} * 2", @@ -405,6 +435,17 @@ void loadControlObject(UIView* targetView, NSMutableDictionary* controlDictionar [drawer update]; } + NSMutableArray *joystickDataList = controlDictionary[@"mJoystickDataList"]; + for (NSMutableDictionary *joystickDict in joystickDataList) { + @try { + ControlJoystick *button = [ControlJoystick buttonWithProperties:joystickDict]; + [targetView addSubview:button]; + [button update]; + } @catch (NSException *exception) { + [errorString appendFormat:@"%@: %@\n", @"ControlJoystick", exception.reason]; + } + } + controlDictionary[@"scaledAt"] = getPrefObject(@"control.button_scale"); if (errorString.length > 0) { diff --git a/Natives/input_bridge_v3.m b/Natives/input_bridge_v3.m index a9f4ebab..61ee7d6e 100644 --- a/Natives/input_bridge_v3.m +++ b/Natives/input_bridge_v3.m @@ -358,17 +358,7 @@ JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetGrabbing(JNIE dispatch_async(dispatch_get_main_queue(), ^{ SurfaceViewController *vc = ((SurfaceViewController *)currentWindow().rootViewController); - UIView *surfaceView = vc.surfaceView; - if (isGrabbing == JNI_TRUE) { - CGFloat screenScale = [[UIScreen mainScreen] scale] * resolutionScale; - CallbackBridge_nativeSendCursorPos(ACTION_DOWN, lastVirtualMousePoint.x * screenScale, lastVirtualMousePoint.y * screenScale); - CGRect screenBounds = [[UIScreen mainScreen] bounds]; - virtualMouseFrame.origin.x = screenBounds.size.width / 2; - virtualMouseFrame.origin.y = screenBounds.size.height / 2; - vc.mousePointerView.frame = virtualMouseFrame; - } - vc.scrollPanGesture.enabled = !isGrabbing; - vc.mousePointerView.hidden = isGrabbing || !virtualMouseEnabled; + [vc updateGrabState]; }); } diff --git a/Natives/resources/en.lproj/Localizable.strings b/Natives/resources/en.lproj/Localizable.strings index 88fb52a2..d4ef4fef 100644 --- a/Natives/resources/en.lproj/Localizable.strings +++ b/Natives/resources/en.lproj/Localizable.strings @@ -28,7 +28,12 @@ "Move" = "Move"; "Remove" = "Remove"; "Resize" = "Resize"; +// Visibility +"Always" = "Always"; +"In game" = "In game"; +"In menu" = "In menu"; +"custom_controls.button_edit.forward_lock" = "Forward lock"; "custom_controls.button_edit.name" = "Name"; "custom_controls.button_edit.size" = "Size"; "custom_controls.button_edit.mapping" = "Mapping"; @@ -37,17 +42,17 @@ "custom_controls.button_edit.mouse_pass" = "Mouse pass"; "custom_controls.button_edit.swipeable" = "Swipeable"; "custom_controls.button_edit.bg_color" = "Background color"; -"custom_controls.button_edit.stroke_width" = "Stroke width (%)"; +"custom_controls.button_edit.stroke_width" = "Stroke width (dp)"; "custom_controls.button_edit.stroke_color" = "Stroke color"; "custom_controls.button_edit.corner_radius" = "Corner radius (%)"; "custom_controls.button_edit.opacity" = "Opacity (%)"; +"custom_controls.button_edit.visibility" = "Visibility"; "custom_controls.button_menu.add_subbutton" = "Add sub-button"; "custom_controls.control_menu.discard_changes" = "Discard changes"; "custom_controls.control_menu.exit" = "Exit"; "custom_controls.control_menu.exit.warn" = "You are about to exit without saving changes."; "custom_controls.control_menu.save" = "Save"; -"custom_controls.control_menu.save.error.default" = "Control name can not be \"default\" as it will be overwritten."; "custom_controls.control_menu.save.error.json" = "JSON error"; "custom_controls.control_menu.save.error.write" = "Error saving file"; "custom_controls.control_menu.load" = "Load"; @@ -55,6 +60,7 @@ "custom_controls.control_menu.safe_area" = "Safe area"; "custom_controls.control_menu.add_button" = "Add button"; "custom_controls.control_menu.add_drawer" = "Add drawer"; +"custom_controls.control_menu.add_joystick" = "Add joystick"; "custom_controls.error.incompatible" = "This control version (code %d) is not implemented in this launcher build."; "custom_controls.hint" = "Long-press anywhere on screen for more options";