onscreen keyboard profile test

This commit is contained in:
True Zhuanjia
2024-08-14 22:49:35 +08:00
parent d90bf747fc
commit fd768db70b
14 changed files with 277 additions and 65 deletions
+34 -16
View File
@@ -669,7 +669,7 @@ ICAgICAgICAgICAgICA
<view alpha="0.75" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fZU-G0-fgM" userLabel="Toolbar Root View" customClass="ToolBarContainerView">
<rect key="frame" x="0.0" y="24" width="834" height="50"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" spacing="70" translatesAutoresizingMaskIntoConstraints="NO" id="YLK-IE-ajS">
<stackView autoresizesSubviews="NO" opaque="NO" contentMode="scaleToFill" spacing="70" translatesAutoresizingMaskIntoConstraints="NO" id="YLK-IE-ajS">
<rect key="frame" x="152" y="0.0" width="530" height="50"/>
<subviews>
<button opaque="NO" tag="20" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qJs-n8-eV7" userLabel="Trash">
@@ -685,7 +685,7 @@ ICAgICAgICAgICAgICA
<action selector="trashCanTapped:" destination="xSZ-i2-Jts" eventType="touchUpInside" id="6s3-Wc-6us"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="F7X-l4-cl3" userLabel="Undo">
<button autoresizesSubviews="NO" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="F7X-l4-cl3" userLabel="Undo">
<rect key="frame" x="120" y="0.0" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="9OY-7z-dfc"/>
@@ -700,25 +700,27 @@ ICAgICAgICAgICAgICA
</button>
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="EPu-dl-Tv8">
<rect key="frame" x="240" y="0.0" width="50" height="50"/>
<subviews>
<button autoresizesSubviews="NO" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uda-zt-xrG" userLabel="Save">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="TEB-dw-YGo"/>
<constraint firstAttribute="height" constant="50" id="z50-CE-W0m"/>
</constraints>
<color key="tintColor" red="0.6705882353" green="0.61568627450000002" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" image="SquareAndArrowDown"/>
<connections>
<action selector="saveTapped:" destination="xSZ-i2-Jts" eventType="touchUpInside" id="bUV-Ja-Htd"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstAttribute="width" constant="50" id="G6V-W8-Ech"/>
</constraints>
</stackView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uda-zt-xrG" userLabel="Save">
<rect key="frame" x="360" y="0.0" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="TEB-dw-YGo"/>
<constraint firstAttribute="height" constant="50" id="z50-CE-W0m"/>
</constraints>
<color key="tintColor" red="0.6705882353" green="0.61568627450000002" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" image="SquareAndArrowDown"/>
<connections>
<action selector="saveTapped:" destination="xSZ-i2-Jts" eventType="touchUpInside" id="bUV-Ja-Htd"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1EQ-sU-0bc" userLabel="Load">
<rect key="frame" x="480" y="0.0" width="50" height="50"/>
<rect key="frame" x="360" y="0.0" width="50" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="0Up-sg-4RI"/>
<constraint firstAttribute="width" constant="50" id="EBU-5l-gEs"/>
@@ -730,6 +732,20 @@ ICAgICAgICAgICAgICA
<action selector="loadTapped:" destination="xSZ-i2-Jts" eventType="touchUpInside" id="84k-1w-sd2"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bMH-Yb-sb6" userLabel="Add">
<rect key="frame" x="480" y="0.0" width="50" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="hGO-XD-eLY"/>
<constraint firstAttribute="width" constant="50" id="rWU-3E-f4u"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="53"/>
<color key="tintColor" red="0.6705882353" green="0.61568627450000002" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="+"/>
<connections>
<action selector="addTapped:" destination="xSZ-i2-Jts" eventType="touchUpInside" id="7dP-Lk-Mdj"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="EPu-dl-Tv8" firstAttribute="centerY" secondItem="YLK-IE-ajS" secondAttribute="centerY" id="9Du-ZM-F8k"/>
@@ -797,6 +813,8 @@ ICAgICAgICAgICAgICA
<constraint firstItem="fZU-G0-fgM" firstAttribute="top" secondItem="O1f-cu-KVo" secondAttribute="bottom" id="Ppb-J1-lXg" userLabel="Toolbar Top Constraint iPad"/>
</constraints>
</view>
<toolbarItems/>
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
<connections>
<outlet property="chevronImageView" destination="iVZ-Fd-dY7" id="403-Nj-wRx"/>
<outlet property="chevronView" destination="hKp-ds-86I" id="Og9-aT-zNH"/>
@@ -162,7 +162,10 @@
}
/* save the name, position, and visibility of button being touched in array in case user wants to undo the change later */
OnScreenButtonState *onScreenButtonState = [[OnScreenButtonState alloc] initWithButtonName:layerBeingDragged.name isKeyboardButton:NO isHidden:layerBeingDragged.isHidden andPosition:layerBeingDragged.position];
OnScreenButtonState *onScreenButtonState = [[OnScreenButtonState alloc] initWithButtonName:layerBeingDragged.name buttonType:GameControllerButton andPosition:layerBeingDragged.position];
// add hidden attr here
onScreenButtonState.isHidden = layerBeingDragged.isHidden;
[layoutChanges addObject:onScreenButtonState];
[[NSNotificationCenter defaultCenter] postNotificationName:@"OSCLayoutChanged" object:self]; // lets the view controller know whether to fade the undo button in or out depending on whether there are any further OSC layout changes the user is allowed to undo
@@ -19,6 +19,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (OSCProfilesManager *) sharedManager;
+ (void) setOnScreenKeyViewsDict:(NSMutableDictionary* )dict;
#pragma mark - Getters
/**
@@ -50,7 +51,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void) saveProfileWithName:(NSString*)name andButtonLayers:(NSMutableArray *)buttonLayers;
- (bool) updateSelectedProfileSucceedWithButtonLayers:(NSMutableArray *)buttonLayers;
- (bool) updateSelectedProfile:(NSMutableArray *) oscButtonLayers;
/**
* Delete current selected profile.
*/
+31 -6
View File
@@ -7,10 +7,13 @@
//
#import "OSCProfilesManager.h"
#import "OnScreenButtonState.h"
#import "Moonlight-Swift.h"
#import "LayoutOnScreenControlsViewController.h"
@implementation OSCProfilesManager
static NSMutableDictionary *onScreenKeyViewsDict;
#pragma mark - Initializer
@@ -27,6 +30,11 @@
#pragma mark - Class Helper Methods
+ (void) setOnScreenKeyViewsDict:(NSMutableDictionary* )dict{
onScreenKeyViewsDict = dict;
}
/**
* Returns the profile whose 'name' property has a value that is equal to the 'name' passed into the method
*/
@@ -94,7 +102,6 @@
NSMutableArray *profiles = [self getAllProfiles];
for (OSCProfile *profile in profiles) { // set all profiles' 'isSelected' property to NO since the new profile we're saving over the old profile with will be the 'selected' profile
profile.isSelected = NO;
}
@@ -192,7 +199,7 @@
}
- (bool) updateSelectedProfileSucceedWithButtonLayers:(NSMutableArray *)buttonLayers {
- (bool) updateSelectedProfile:(NSMutableArray *) oscButtonLayers {
/* iterate through each OSC button the user sees on screen, create an 'OnScreenButtonState' object from each button, encode each object, and then add each object to an array */
/*
NSSet *validPositionButtonNames = [NSSet setWithObjects:
@@ -211,9 +218,12 @@
@"startButton",
nil]; */
NSMutableArray *buttonStatesEncoded = [[NSMutableArray alloc] init];
for (CALayer *buttonLayer in buttonLayers) {
for (CALayer *oscButtonLayer in oscButtonLayers) {
OnScreenButtonState *buttonState = [[OnScreenButtonState alloc] initWithButtonName:oscButtonLayer.name buttonType:GameControllerButton andPosition:oscButtonLayer.position];
// add hidden attr here
buttonState.isHidden = oscButtonLayer.isHidden;
OnScreenButtonState *buttonState = [[OnScreenButtonState alloc] initWithButtonName:buttonLayer.name isKeyboardButton:NO isHidden:buttonLayer.isHidden andPosition:buttonLayer.position];
/* //-------------------------------------
NSLog(@"buttonName to be saved: %@", buttonState.name);
if([validPositionButtonNames containsObject:buttonState.name]) buttonState.hasValidPosition = true; // this is for setting up DZ for custom OSC buttons.
@@ -222,6 +232,19 @@
NSData *buttonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:buttonState requiringSecureCoding:YES error:nil];
[buttonStatesEncoded addObject: buttonStateEncoded];
}
// save keyboard button as buttonstate
[onScreenKeyViewsDict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { // this dict is passed from the layout tool VC, as a static obj in this class.
OnScreenKeyView *keyView = value;
OnScreenButtonState *buttonState = [[OnScreenButtonState alloc] initWithButtonName:keyView.keyString buttonType:KeyboardButton andPosition:keyView.frame.origin];
buttonState.alias = keyView.keyLabel;
buttonState.timestamp = keyView.timestamp;
NSData *buttonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:buttonState requiringSecureCoding:YES error:nil];
[buttonStatesEncoded addObject: buttonStateEncoded];
}];
if([self getIndexOfSelectedProfile]==0) return false;
OSCProfile *newProfile = [[OSCProfile alloc] initWithName:[self getSelectedProfile].name
buttonStates:buttonStatesEncoded isSelected:YES]; // create a new 'OSCProfile'. Set the array of encoded button states created above to the 'buttonStates' property of the new profile, along with a 'name'. Set 'isSelected' argument to YES which will set this saved profile as the one that will show up in the game stream view
@@ -258,7 +281,9 @@
nil]; */
NSMutableArray *buttonStatesEncoded = [[NSMutableArray alloc] init];
for (CALayer *buttonLayer in buttonLayers) {
OnScreenButtonState *buttonState = [[OnScreenButtonState alloc] initWithButtonName:buttonLayer.name isKeyboardButton:NO isHidden:buttonLayer.isHidden andPosition:buttonLayer.position];
OnScreenButtonState *buttonState = [[OnScreenButtonState alloc] initWithButtonName:buttonLayer.name buttonType:GameControllerButton andPosition:buttonLayer.position];
// add hidden attr here
buttonState.isHidden = buttonLayer.isHidden;
/* //-------------------------------------
NSLog(@"buttonName to be saved: %@", buttonState.name);
if([validPositionButtonNames containsObject:buttonState.name]) buttonState.hasValidPosition = true; // this is for setting up DZ for custom OSC buttons.
@@ -20,12 +20,19 @@ NS_ASSUME_NONNULL_BEGIN
@interface OnScreenButtonState : NSObject <NSCoding, NSSecureCoding>
@property NSString *name;
@property NSString *alias;
@property CGPoint position;
@property (nonatomic, assign) BOOL isHidden;
@property (nonatomic, assign) BOOL isKeyboardButton;
@property (nonatomic, assign) uint8_t buttonType;
@property (nonatomic, assign) NSTimeInterval timestamp;
// @property (nonatomic, assign) BOOL hasValidPosition;
- (id) initWithButtonName:(NSString*)name isKeyboardButton:(BOOL)isKeyboardButton isHidden:(BOOL)isHidden andPosition:(CGPoint)position;
typedef NS_ENUM(NSInteger, OnScreenButtonType) {
GameControllerButton,
KeyboardButton
};
- (id) initWithButtonName:(NSString*)name buttonType:(uint8_t)buttonType andPosition:(CGPoint)position;
+ (BOOL) supportsSecureCoding;
- (void) encodeWithCoder:(NSCoder*)encoder;
@@ -10,12 +10,13 @@
@implementation OnScreenButtonState
- (id) initWithButtonName:(NSString*)name isKeyboardButton:(BOOL)isKeyboardButton isHidden:(BOOL)isHidden andPosition:(CGPoint)position {
- (id) initWithButtonName:(NSString*)name buttonType:(uint8_t)buttonType andPosition:(CGPoint)position {
if ((self = [self init])) {
self.name = name;
self.isHidden = isHidden;
//self.isHidden = isHidden;
self.position = position;
self.isKeyboardButton = isKeyboardButton;
self.buttonType = buttonType;
}
return self;
@@ -27,19 +28,21 @@
- (void) encodeWithCoder:(NSCoder*)encoder {
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeBool:self.isKeyboardButton forKey:@"isKeyboardButton"];
[encoder encodeBool:self.isHidden forKey:@"isHidden"];
// [encoder encodeBool:self.hasValidPosition forKey:@"hasValidPosition"];
[encoder encodeObject:self.alias forKey:@"alias"];
[encoder encodeDouble:self.timestamp forKey:@"timestamp"];
[encoder encodeInt:self.buttonType forKey:@"buttonType"];
[encoder encodeCGPoint:self.position forKey:@"position"];
[encoder encodeBool:self.isHidden forKey:@"isHidden"];
}
- (id) initWithCoder:(NSCoder*)decoder {
if (self = [super init]) {
self.name = [decoder decodeObjectForKey:@"name"];
self.isKeyboardButton = [decoder decodeBoolForKey:@"isKeyboardButton"];
self.isHidden = [decoder decodeBoolForKey:@"isHidden"];
// self.hasValidPosition = [decoder decodeBoolForKey:@"hasValidPosition"];
self.alias = [decoder decodeObjectForKey:@"alias"];
self.timestamp = [decoder decodeDoubleForKey:@"timestamp"];
self.buttonType = [decoder decodeIntForKey:@"buttonType"];
self.position = [decoder decodeCGPointForKey:@"position"];
self.isHidden = [decoder decodeBoolForKey:@"isHidden"];
}
return self;
+2 -1
View File
@@ -272,7 +272,8 @@ static float L3_Y;
NSLog(@"_activeCustomOscButtonPositionDict update: STARTOVER");
for (NSData *buttonStateEncoded in oscProfile.buttonStates) {
OnScreenButtonState *buttonState = [NSKeyedUnarchiver unarchivedObjectOfClass:[OnScreenButtonState class] fromData:buttonStateEncoded error:nil];
if(!buttonState.isHidden && [validPositionButtonNames containsObject:buttonState.name] ){
if(!buttonState.isHidden && [validPositionButtonNames containsObject:buttonState.name] && (buttonState.buttonType == GameControllerButton || [profilesManager getIndexOfSelectedProfile] == 0 ) ){
[_activeCustomOscButtonPositionDict setObject:[NSValue valueWithCGPoint:buttonState.position] forKey:buttonState.name]; // we got a buttonname -> position dict here
NSLog(@"_activeCustomOscButtonPositionDict update, button name:%@, position: %f, %f", buttonState.name, buttonState.position.x, buttonState.position.y);
}
+46 -14
View File
@@ -8,10 +8,13 @@
import UIKit
@objc class OnScreenKeyView: UIView {
@objc static var relocationMode: Bool = false
@objc var keyLabel: String
@objc var keyString: String
@objc static public var editMode: Bool = false
@objc static public var timestampOfButtonBeingDragged: TimeInterval = 0
@objc public var keyLabel: String
@objc public var keyString: String
@objc public var timestamp: TimeInterval
private let label: UILabel
private let originalBackgroundColor: UIColor
private var storedLocation: CGPoint = .zero
@@ -22,7 +25,7 @@ import UIKit
self.keyLabel = keyLabel
self.label = UILabel()
self.originalBackgroundColor = UIColor(white: 0.2, alpha: 0.7)
self.timestamp = 0;
super.init(frame: .zero)
setupView()
}
@@ -39,16 +42,17 @@ import UIKit
}
@objc public func enableRelocationMode(enabled: Bool){
OnScreenKeyView.relocationMode = enabled
OnScreenKeyView.editMode = enabled
}
private func setupView() {
label.text = self.keyLabel
label.font = UIFont.systemFont(ofSize: 30)
label.font = UIFont.systemFont(ofSize: 22)
label.textColor = UIColor(white: 1.0, alpha: 0.8)
label.shadowColor = .black
label.translatesAutoresizingMaskIntoConstraints = false
self.translatesAutoresizingMaskIntoConstraints = true // this is mandatory to prevent unexpected key view location change
self.layer.borderColor = UIColor.black.cgColor
self.layer.borderWidth = 2
self.layer.cornerRadius = 8
@@ -64,21 +68,24 @@ import UIKit
])
NSLayoutConstraint.activate([
self.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width * 0.088),
self.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height * 0.1),
])
//self.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width * 0.088),
//self.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height * 0.1),
self.widthAnchor.constraint(equalToConstant: 85),
self.heightAnchor.constraint(equalToConstant: 65),
])
}
// Touch event handling
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
//self.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 1.0, alpha: 0.7)
self.backgroundColor = UIColor.systemBlue.withAlphaComponent(0.7)
if !OnScreenKeyView.relocationMode { LiSendKeyboardEvent(CommandManager.keyMappings[self.keyString]!,Int8(KEY_ACTION_DOWN), 0) }
if !OnScreenKeyView.editMode {
self.backgroundColor = UIColor.systemBlue.withAlphaComponent(0.7)
LiSendKeyboardEvent(CommandManager.keyMappings[self.keyString]!,Int8(KEY_ACTION_DOWN), 0)
}
else {
// Store the initial center point of the key
if let touch = touches.first {
let touchLocation = touch.location(in: superview)
storedLocation = touchLocation
@@ -89,15 +96,17 @@ import UIKit
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
OnScreenKeyView.timestampOfButtonBeingDragged = self.timestamp
// Move the KeyView based on touch movement in relocation mode
if OnScreenKeyView.relocationMode {
if OnScreenKeyView.editMode {
if let touch = touches.first {
let currentLocation = touch.location(in: superview)
let offsetX = currentLocation.x - storedLocation.x
let offsetY = currentLocation.y - storedLocation.y
center = CGPoint(x: center.x + offsetX, y: center.y + offsetY)
//NSLog("x coord: %f, y coord: %f", self.frame.origin.x, self.frame.origin.y)
storedLocation = currentLocation // Update initial center for next movement
}
}
@@ -106,7 +115,30 @@ import UIKit
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
if !OnScreenKeyView.editMode {
LiSendKeyboardEvent(CommandManager.keyMappings[self.keyString]!,Int8(KEY_ACTION_UP), 0)
return
}
self.backgroundColor = self.originalBackgroundColor
if !OnScreenKeyView.relocationMode { LiSendKeyboardEvent(CommandManager.keyMappings[self.keyString]!,Int8(KEY_ACTION_UP), 0) }
guard let superview = superview else { return }
// Deactivate existing constraints if necessary
NSLayoutConstraint.deactivate(self.constraints)
// Add new constraints based on the current center position
translatesAutoresizingMaskIntoConstraints = true
// Create new constraints
let newLeadingConstraint = self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: self.frame.origin.x)
let newTopConstraint = self.topAnchor.constraint(equalTo: superview.topAnchor, constant: self.frame.origin.y)
// Activate the new location constraints
NSLayoutConstraint.activate([newLeadingConstraint, newTopConstraint])
// Trigger layout update
superview.layoutIfNeeded()
setupView(); //re-setup keyview style
}
}
@@ -11,6 +11,7 @@
#import "ToolBarContainerView.h"
#import "OSCProfilesManager.h"
#import "OSCProfilesTableViewController.h"
#import "Moonlight-Swift.h"
NS_ASSUME_NONNULL_BEGIN
@@ -19,8 +20,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@interface LayoutOnScreenControlsViewController : UIViewController
- (void)profileRefresh;
- (void)reloadOnScreenKeyboardButtons;
@property LayoutOnScreenControls *layoutOSC; // object that contains a view which contains the on screen controller buttons that allows the user to drag and positions each button on the screen using touch
@property (nonatomic) NSMutableDictionary* onScreenKeyViewsDict;
@property int OSCSegmentSelected;
@property (weak, nonatomic) IBOutlet UIButton *trashCanButton;
@@ -12,6 +12,7 @@
#import "OnScreenControls.h"
#import "OSCProfilesManager.h"
#import "LocalizationHelper.h"
#import "Moonlight-Swift.h"
@interface LayoutOnScreenControlsViewController ()
@@ -34,14 +35,59 @@
- (void) viewWillDisappear:(BOOL)animated{
OnScreenKeyView.editMode = false;
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] postNotificationName:@"OscLayoutCloseNotification" object:self];
}
- (void) reloadOnScreenKeyboardButtons {
/*
[self.onScreenKeyViewsDict enumerateKeysAndObjectsUsingBlock:^(id timeIntervalKey, id keyView, BOOL *stop) {
[keyView removeFromSuperview];
}];*/
for (UIView *subview in self.view.subviews) {
// 检查子视图是否是特定类型的实例
if ([subview isKindOfClass:[OnScreenKeyView class]]) {
// 如果是,就添加到将要被移除的数组中
[subview removeFromSuperview];
}
}
[self.onScreenKeyViewsDict removeAllObjects];
NSLog(@"reload os Key here");
// _activeCustomOscButtonPositionDict will be updated every time when the osc profile is reloaded
OSCProfile *oscProfile = [profilesManager getSelectedProfile]; //returns the currently selected OSCProfile
for (NSData *buttonStateEncoded in oscProfile.buttonStates) {
OnScreenButtonState* buttonState = [NSKeyedUnarchiver unarchivedObjectOfClass:[OnScreenButtonState class] fromData:buttonStateEncoded error:nil];
if(buttonState.buttonType == KeyboardButton){
OnScreenKeyView* keyView = [[OnScreenKeyView alloc] initWithKeyString:buttonState.name keyLabel:buttonState.alias]; //reconstruct keyView
keyView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
keyView.timestamp = buttonState.timestamp; // will be set as key in in the dict.
// Add the KeyView to the view controller's view
[self.view addSubview:keyView];
[keyView setKeyLocationWithXOffset:buttonState.position.x yOffset:buttonState.position.y];
[self.onScreenKeyViewsDict setObject:keyView forKey:@(keyView.timestamp)];
}
}
//////////////////
/*
[self.onScreenKeyViewsDict enumerateKeysAndObjectsUsingBlock:^(id timeIntervalKey, id keyView, BOOL *stop) {
[self.view addSubview:keyView];
}];*/
}
- (void) viewDidLoad {
[super viewDidLoad];
profilesManager = [OSCProfilesManager sharedManager];
self.onScreenKeyViewsDict = [[NSMutableDictionary alloc] init]; // will be revised to read persisted data , somewhere else
[OSCProfilesManager setOnScreenKeyViewsDict:self.onScreenKeyViewsDict]; // pass the keyboard button dict to profiles manager
isToolbarHidden = NO; // keeps track if the toolbar is hidden up above the screen so that we know whether to hide or show it when the user taps the toolbar's hide/show button
@@ -80,6 +126,14 @@
name:@"OscLayoutTableViewCloseNotification"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reloadOnScreenKeyboardButtons)
name:@"OscLayoutProfileSelctedInTableView" // This is a special notification for reloading the on screen keyboard buttons. which can't be executed by _oscProfilesTableViewController.needToUpdateOscLayoutTVC code block, and has to be triggered by a notification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(OSCLayoutChanged) name:@"OSCLayoutChanged" object:nil]; // used to notifiy this view controller that the user made a change to the OSC layout so that the VC can either fade in or out its 'Undo button' which will signify to the user whether there are any OSC layout changes to undo
/* This will animate the toolbar with a subtle up and down motion intended to telegraph to the user that they can hide the toolbar if they wish*/
@@ -108,7 +162,10 @@
[self profileRefresh];
}
- (void) viewDidAppear:(BOOL)animated {
OnScreenKeyView.editMode = true;
[super viewWillAppear:animated];
[self profileRefresh];
}
@@ -224,10 +281,23 @@
}
}
- (IBAction) addTapped:(id)sender{
OnScreenKeyView* keyView = [[OnScreenKeyView alloc] initWithKeyString:@"ALT" keyLabel:@"test"];
keyView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
keyView.timestamp = CACurrentMediaTime(); // will be set as key in in the dict.
[self.onScreenKeyViewsDict setObject:keyView forKey:@(keyView.timestamp)];
// Add the KeyView to the view controller's view
[self.view addSubview:keyView];
[keyView setKeyLocationWithXOffset:50 yOffset:50];
}
/* show pop up notification that lets users choose to save the current OSC layout configuration as a profile they can load when they want. User can also choose to cancel out of this pop up */
- (IBAction) saveTapped:(id)sender {
if([self->profilesManager updateSelectedProfileSucceedWithButtonLayers:self.layoutOSC.OSCButtonLayers]){
if([self->profilesManager updateSelectedProfile:self.layoutOSC.OSCButtonLayers]){
UIAlertController * savedAlertController = [UIAlertController alertControllerWithTitle: [NSString stringWithFormat:@""] message: [LocalizationHelper localizedStringForKey:@"Current profile updated successfully"] preferredStyle:UIAlertControllerStyleAlert];
[savedAlertController addAction:[UIAlertAction actionWithTitle:[LocalizationHelper localizedStringForKey:@"Ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
}]];
@@ -298,17 +368,17 @@
self->_oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"];
//this part is just for registration, will not be immediately executed.
self->_oscProfilesTableViewController.didDismissOSCProfilesTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC profile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the profile and then hide/show and move each OSC button to their appropriate position
self->_oscProfilesTableViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC profile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the profile and then hide/show and move each OSC button to their appropriate position
[self.layoutOSC updateControls]; // creates and saves a 'Default' OSC profile or loads the one the user selected on the previous screen
[self addInnerAnalogSticksToOuterAnalogLayers];
[self.layoutOSC.layoutChanges removeAllObjects]; // since a new OSC profile is being loaded, this will remove all previous layout changes made from the array
[self OSCLayoutChanged]; // fades the 'Undo Button' out
self->_oscProfilesTableViewController.currentOSCButtonLayers = self.layoutOSC.OSCButtonLayers; //pass updated OSCLayout to OSCProfileTableView again
//[self reloadOnScreenKeyboardButtons];
};
[self.oscProfilesTableViewController profileViewRefresh]; // execute this will make sure OSCLayout is updated from persisted profile, not any cache.
[self reloadOnScreenKeyboardButtons];
// [self presentViewController:vc animated:YES completion:nil];
}
@@ -327,7 +397,7 @@
_oscProfilesTableViewController = [storyboard instantiateViewControllerWithIdentifier:@"OSCProfilesTableViewController"] ;
_oscProfilesTableViewController.didDismissOSCProfilesTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC ofile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the profile and then hide/show and move each OSC button to their appropriate position
_oscProfilesTableViewController.needToUpdateOscLayoutTVC = ^() { // a block that will be called when the modally presented 'OSCProfilesTableViewController' VC is dismissed. By the time the 'OSCProfilesTableViewController' VC is dismissed the user would have potentially selected a different OSC ofile with a different layout and they want to see this layout on this 'LayoutOnScreenControlsViewController.' This block of code will load the profile and then hide/show and move each OSC button to their appropriate position
[self.layoutOSC updateControls]; // creates and saves a 'Default' OSC profile or loads the one the user selected on the previous screen
[self addInnerAnalogSticksToOuterAnalogLayers];
@@ -363,8 +433,9 @@
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
// -------- for OSC buttons
[self.layoutOSC touchesMoved:touches withEvent:event];
if ([self.layoutOSC isLayer:self.layoutOSC.layerBeingDragged
hoveringOverButton:trashCanButton]) { // check if user is dragging around a button and hovering it over the trash can button
trashCanButton.tintColor = [UIColor redColor];
@@ -372,11 +443,44 @@
else {
trashCanButton.tintColor = [UIColor colorWithRed:171.0/255.0 green:157.0/255.0 blue:255.0/255.0 alpha:1];
}
// -------- for keyboard Buttons
UITouch *touch = [touches anyObject]; // Get the first touch in the set
if([self touchWithinTashcanButton:touch]){
trashCanButton.tintColor = [UIColor redColor];
}
else trashCanButton.tintColor = [UIColor colorWithRed:171.0/255.0 green:157.0/255.0 blue:255.0/255.0 alpha:1];
}
- (bool)touchWithinTashcanButton:(UITouch* )touch {
CGPoint locationInView = [touch locationInView:self.view];
// Convert the location to the button's coordinate system
CGPoint locationInButton = [self.view convertPoint:locationInView toView:trashCanButton];
bool ret = CGRectContainsPoint(trashCanButton.bounds, locationInButton);
NSLog(@"within button: %d", ret);
// Check if the location is within the button's bounds
return ret;
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// removing keyboard buttons objs
UITouch *touch = [touches anyObject]; // Get the first touch in the set
if([self touchWithinTashcanButton:touch]){
[self.onScreenKeyViewsDict[@(OnScreenKeyView.timestampOfButtonBeingDragged)] removeFromSuperview];
[self.onScreenKeyViewsDict removeObjectForKey:@(OnScreenKeyView.timestampOfButtonBeingDragged)];
OnScreenKeyView.timestampOfButtonBeingDragged = 0; //reset thie timestamp
}
//removing OSC buttons
if (self.layoutOSC.layerBeingDragged != nil &&
[self.layoutOSC isLayer:self.layoutOSC.layerBeingDragged hoveringOverButton:trashCanButton]) { // check if user wants to throw OSC button into the trash can
// here we're going to delete something
self.layoutOSC.layerBeingDragged.hidden = YES;
@@ -394,8 +498,6 @@
if ([self.layoutOSC.layerBeingDragged.name isEqualToString:@"rightStickBackground"]) {
self.layoutOSC._rightStick.hidden = YES;
}
trashCanButton.tintColor = [UIColor colorWithRed:171.0/255.0 green:157.0/255.0 blue:255.0/255.0 alpha:1];
}
[self.layoutOSC touchesEnded:touches withEvent:event];
@@ -407,6 +509,9 @@
}]];
[self presentViewController:movedAlertController animated:YES completion:nil];
}
trashCanButton.tintColor = [UIColor colorWithRed:171.0/255.0 green:157.0/255.0 blue:255.0/255.0 alpha:1];
}
@end
@@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic, copy) void (^didDismissOSCProfilesTVC)(void);
@property (nonatomic, copy) void (^needToUpdateOscLayoutTVC)(void);
@property (nonatomic, assign) NSMutableArray *currentOSCButtonLayers;
@end
@@ -22,7 +22,6 @@ const double NAV_BAR_HEIGHT = 50;
@implementation OSCProfilesTableViewController {
OSCProfilesManager *profilesManager;
LayoutOnScreenControlsViewController *parentLayoutOSCViewController;
}
@synthesize tableView;
@@ -139,9 +138,11 @@ const double NAV_BAR_HEIGHT = 50;
//[self dismissViewControllerAnimated:YES completion:nil];
//[selfparentLayoutOSCViewController]
[self.tableView reloadData]; // table view will be refreshed by calling reloadData
if (self.didDismissOSCProfilesTVC) { // tells the presenting view controller to lay out the on screen buttons according to the selected profile's instructions
self.didDismissOSCProfilesTVC();
if (self.needToUpdateOscLayoutTVC) { // tells the presenting view controller to lay out the on screen buttons according to the selected profile's instructions
self.needToUpdateOscLayoutTVC();
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"OscLayoutProfileSelctedInTableView" object:self]; // notify other view that oscLayoutManager is closing
}
- (IBAction) deleteTapped:(id)sender{// delete can be executed simply by calling this 2 methods.
@@ -179,8 +179,8 @@
CGFloat spacing = 10.0;
[keyView setKeyLocationWithXOffset:500 yOffset:(CGFloat)550];
*/
*/
+12
View File
@@ -385,6 +385,18 @@
}
}
},
"bMH-Yb-sb6.normalTitle" : {
"comment" : "Class = \"UIButton\"; normalTitle = \"+\"; ObjectID = \"bMH-Yb-sb6\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "+"
}
}
}
},
"byH-Qz-Nbw.segmentTitles[0]" : {
"comment" : "Class = \"UISegmentedControl\"; byH-Qz-Nbw.segmentTitles[0] = \"No\"; ObjectID = \"byH-Qz-Nbw\";",
"extractionState" : "extracted_with_value",