inputAccessoryBar for soft keyboard
This commit is contained in:
1 parent
7e21292a62
commit
e78a351c28
15 files changed
+1640
-46
No files matched your search
@@ -100,6 +100,7 @@
|
|||||||
98CFB82F1CAD481B0048EF74 /* libmoonlight-common.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 98AB2E841CAD46840089BB98 /* libmoonlight-common.a */; };
|
98CFB82F1CAD481B0048EF74 /* libmoonlight-common.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 98AB2E841CAD46840089BB98 /* libmoonlight-common.a */; };
|
||||||
98D5856D1C0EA79600F6CC00 /* TemporaryHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D5856C1C0EA79600F6CC00 /* TemporaryHost.m */; };
|
98D5856D1C0EA79600F6CC00 /* TemporaryHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D5856C1C0EA79600F6CC00 /* TemporaryHost.m */; };
|
||||||
98D585701C0ED0E800F6CC00 /* TemporarySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D5856F1C0ED0E800F6CC00 /* TemporarySettings.m */; };
|
98D585701C0ED0E800F6CC00 /* TemporarySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D5856F1C0ED0E800F6CC00 /* TemporarySettings.m */; };
|
||||||
|
A1900E012F9A100100BA853A /* InputAccessoryBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1900E002F9A100100BA853A /* InputAccessoryBar.swift */; };
|
||||||
A1B2C3D42F90000100ABCDEF /* AbstractGamepadOverlayWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D32F90000100ABCDEF /* AbstractGamepadOverlayWidgetView.swift */; };
|
A1B2C3D42F90000100ABCDEF /* AbstractGamepadOverlayWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D32F90000100ABCDEF /* AbstractGamepadOverlayWidgetView.swift */; };
|
||||||
DC1F5A07206436B20037755F /* ConnectionHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1F5A06206436B20037755F /* ConnectionHelper.m */; };
|
DC1F5A07206436B20037755F /* ConnectionHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1F5A06206436B20037755F /* ConnectionHelper.m */; };
|
||||||
F74BEF9C2C1A705600224667 /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = F74BEF9B2C1A705600224667 /* OpenSSL */; };
|
F74BEF9C2C1A705600224667 /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = F74BEF9B2C1A705600224667 /* OpenSSL */; };
|
||||||
@@ -318,6 +319,7 @@
|
|||||||
98D5856C1C0EA79600F6CC00 /* TemporaryHost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryHost.m; path = Database/TemporaryHost.m; sourceTree = "<group>"; };
|
98D5856C1C0EA79600F6CC00 /* TemporaryHost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryHost.m; path = Database/TemporaryHost.m; sourceTree = "<group>"; };
|
||||||
98D5856E1C0ED0E800F6CC00 /* TemporarySettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporarySettings.h; path = Database/TemporarySettings.h; sourceTree = "<group>"; };
|
98D5856E1C0ED0E800F6CC00 /* TemporarySettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporarySettings.h; path = Database/TemporarySettings.h; sourceTree = "<group>"; };
|
||||||
98D5856F1C0ED0E800F6CC00 /* TemporarySettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporarySettings.m; path = Database/TemporarySettings.m; sourceTree = "<group>"; };
|
98D5856F1C0ED0E800F6CC00 /* TemporarySettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporarySettings.m; path = Database/TemporarySettings.m; sourceTree = "<group>"; };
|
||||||
|
A1900E002F9A100100BA853A /* InputAccessoryBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputAccessoryBar.swift; sourceTree = "<group>"; };
|
||||||
A1B2C3D32F90000100ABCDEF /* AbstractGamepadOverlayWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbstractGamepadOverlayWidgetView.swift; sourceTree = "<group>"; };
|
A1B2C3D32F90000100ABCDEF /* AbstractGamepadOverlayWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbstractGamepadOverlayWidgetView.swift; sourceTree = "<group>"; };
|
||||||
DC1F5A05206436B10037755F /* ConnectionHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConnectionHelper.h; sourceTree = "<group>"; };
|
DC1F5A05206436B10037755F /* ConnectionHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConnectionHelper.h; sourceTree = "<group>"; };
|
||||||
DC1F5A06206436B20037755F /* ConnectionHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConnectionHelper.m; sourceTree = "<group>"; };
|
DC1F5A06206436B20037755F /* ConnectionHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConnectionHelper.m; sourceTree = "<group>"; };
|
||||||
@@ -775,6 +777,7 @@
|
|||||||
18E4BCE92E891895000A8657 /* MotionHandler.swift */,
|
18E4BCE92E891895000A8657 /* MotionHandler.swift */,
|
||||||
84E1CDEB2C4F67D300547904 /* CommandManager.swift */,
|
84E1CDEB2C4F67D300547904 /* CommandManager.swift */,
|
||||||
84203F222C5F24C00032EEED /* OnScreenWidgetView.swift */,
|
84203F222C5F24C00032EEED /* OnScreenWidgetView.swift */,
|
||||||
|
A1900E002F9A100100BA853A /* InputAccessoryBar.swift */,
|
||||||
A1B2C3D32F90000100ABCDEF /* AbstractGamepadOverlayWidgetView.swift */,
|
A1B2C3D32F90000100ABCDEF /* AbstractGamepadOverlayWidgetView.swift */,
|
||||||
1817B61D2E6FD04D000D938D /* SafeTimer.swift */,
|
1817B61D2E6FD04D000D938D /* SafeTimer.swift */,
|
||||||
);
|
);
|
||||||
@@ -1163,6 +1166,7 @@
|
|||||||
1850C82A2E667E4000845308 /* MicHandler.swift in Sources */,
|
1850C82A2E667E4000845308 /* MicHandler.swift in Sources */,
|
||||||
FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */,
|
FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */,
|
||||||
84203F232C5F24C00032EEED /* OnScreenWidgetView.swift in Sources */,
|
84203F232C5F24C00032EEED /* OnScreenWidgetView.swift in Sources */,
|
||||||
|
A1900E012F9A100100BA853A /* InputAccessoryBar.swift in Sources */,
|
||||||
A1B2C3D42F90000100ABCDEF /* AbstractGamepadOverlayWidgetView.swift in Sources */,
|
A1B2C3D42F90000100ABCDEF /* AbstractGamepadOverlayWidgetView.swift in Sources */,
|
||||||
2AC1E3832F507D80004F76C0 /* GenericUtils.swift in Sources */,
|
2AC1E3832F507D80004F76C0 /* GenericUtils.swift in Sources */,
|
||||||
97E21BB52E2186A3000D5DCA /* imgui_impl_metal.mm in Sources */,
|
97E21BB52E2186A3000D5DCA /* imgui_impl_metal.mm in Sources */,
|
||||||
|
|||||||
@@ -3100,7 +3100,7 @@
|
|||||||
<segments>
|
<segments>
|
||||||
<segment title="slide-toggle"/>
|
<segment title="slide-toggle"/>
|
||||||
<segment title="slide-hold"/>
|
<segment title="slide-hold"/>
|
||||||
<segment title="regular"/>
|
<segment title="normal"/>
|
||||||
<segment title="tap to toggle"/>
|
<segment title="tap to toggle"/>
|
||||||
<segment title="movable"/>
|
<segment title="movable"/>
|
||||||
</segments>
|
</segments>
|
||||||
|
|||||||
@@ -794,7 +794,7 @@
|
|||||||
<segments>
|
<segments>
|
||||||
<segment title="slide-toggle"/>
|
<segment title="slide-toggle"/>
|
||||||
<segment title="slide-hold"/>
|
<segment title="slide-hold"/>
|
||||||
<segment title="regular"/>
|
<segment title="normal"/>
|
||||||
<segment title="tap to toggle"/>
|
<segment title="tap to toggle"/>
|
||||||
<segment title="movable"/>
|
<segment title="movable"/>
|
||||||
</segments>
|
</segments>
|
||||||
@@ -3556,19 +3556,19 @@
|
|||||||
<image name="trash.fill" catalog="system" width="117" height="128"/>
|
<image name="trash.fill" catalog="system" width="117" height="128"/>
|
||||||
<image name="tray.full.fill" catalog="system" width="128" height="88"/>
|
<image name="tray.full.fill" catalog="system" width="128" height="88"/>
|
||||||
<systemColor name="linkColor">
|
<systemColor name="linkColor">
|
||||||
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
<systemColor name="systemBrownColor">
|
<systemColor name="systemBrownColor">
|
||||||
<color red="0.67450980392156867" green="0.49803921568627452" blue="0.36862745098039218" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.63529411759999999" green="0.51764705879999995" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
<systemColor name="systemGray2Color">
|
<systemColor name="systemGray2Color">
|
||||||
<color red="0.68235294117647061" green="0.68235294117647061" blue="0.69803921568627447" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.68235294120000001" green="0.68235294120000001" blue="0.69803921570000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
<systemColor name="systemGray3Color">
|
<systemColor name="systemGray3Color">
|
||||||
<color red="0.7803921568627451" green="0.7803921568627451" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.78039215689999997" green="0.78039215689999997" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
<systemColor name="systemTealColor">
|
<systemColor name="systemTealColor">
|
||||||
<color red="0.0" green="0.76470588235294112" blue="0.81568627450980391" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.18823529410000001" green="0.69019607839999997" blue="0.78039215689999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -489,19 +489,6 @@ final class PressureCurveLUT {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UIView {
|
|
||||||
var parentViewController: UIViewController? {
|
|
||||||
var responder: UIResponder? = self
|
|
||||||
while let r = responder {
|
|
||||||
if let vc = r as? UIViewController {
|
|
||||||
return vc
|
|
||||||
}
|
|
||||||
responder = r.next
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - ViewController
|
// MARK: - ViewController
|
||||||
class PressureCurveViewController: UIViewController, UIGestureRecognizerDelegate {
|
class PressureCurveViewController: UIViewController, UIGestureRecognizerDelegate {
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
#if TARGET_OS_TV
|
#if TARGET_OS_TV
|
||||||
@interface StreamView : UIView <X1KitMouseDelegate, UITextFieldDelegate>
|
@interface StreamView : UIView <X1KitMouseDelegate, UITextFieldDelegate>
|
||||||
#else
|
#else
|
||||||
@interface StreamView : UIView <X1KitMouseDelegate, UITextFieldDelegate, UIPointerInteractionDelegate>
|
@interface StreamView : UIView <X1KitMouseDelegate, UITextFieldDelegate, UIPointerInteractionDelegate, InputAccessoryBarDelegate>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@property (weak, nonatomic) UIView* streamFrameTopLayerView;
|
@property (weak, nonatomic) UIView* streamFrameTopLayerView;
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
- (void)readyToBringUpSoftKeyboardByToolbox;
|
- (void)readyToBringUpSoftKeyboardByToolbox;
|
||||||
- (void)keyboardWillShow:(NSNotification *)notification;
|
- (void)keyboardWillShow:(NSNotification *)notification;
|
||||||
- (void)keyboardWillHide;
|
- (void)keyboardWillHide;
|
||||||
- (void)handleAbnormalKeyboards:(NSNotification *)notification;
|
- (void)handleNonStandardKeyboard:(NSNotification *)notification;
|
||||||
- (void)liftMetalVideoViewIfNeeded:(CGFloat)liftHeight;
|
- (void)liftMetalVideoViewIfNeeded:(CGFloat)liftHeight;
|
||||||
|
|
||||||
- (void)alterAbsTouchDragWith:(int32_t)mouseButton;
|
- (void)alterAbsTouchDragWith:(int32_t)mouseButton;
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
CGRect keyboardFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
CGRect keyboardFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
||||||
CGFloat screenHeight = GenericUtils.screenHeight;
|
CGFloat screenHeight = GenericUtils.screenHeight;
|
||||||
CGFloat totalKeyboardHeight = keyboardFrame.size.height;
|
CGFloat totalKeyboardHeight = keyboardFrame.size.height;
|
||||||
CGFloat toolbarHeight = settings.showKeyboardToolbar ? GenericUtils.legacyToolbarHeight : 0;
|
CGFloat toolbarHeight = settings.showKeyboardToolbar ? GenericUtils.inputAccessoryBarHeight : 0;
|
||||||
|
|
||||||
if(totalKeyboardHeight < screenHeight * 0.33333333333 + toolbarHeight
|
if(totalKeyboardHeight < screenHeight * 0.33333333333 + toolbarHeight
|
||||||
|| totalKeyboardHeight > screenHeight*0.8 + toolbarHeight){
|
|| totalKeyboardHeight > screenHeight*0.8 + toolbarHeight){
|
||||||
@@ -311,7 +311,18 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
NSLog(@"keyboard will show %f", CACurrentMediaTime());
|
NSLog(@"keyboard will show %f", CACurrentMediaTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)handleAbnormalKeyboards:(NSNotification *)notification{
|
- (void)handleNonStandardKeyboard:(NSNotification *)notification{
|
||||||
|
if(!self->dockedKeyboardActionDetected){
|
||||||
|
|
||||||
|
[self->keyboardToggleTip removeFromSuperview];
|
||||||
|
[self refreshKeyboardToggleRecognizer:settings.keyboardToggleFingers.intValue];
|
||||||
|
|
||||||
|
self->dockedKeyboardActionDetected = false;
|
||||||
|
self->isInputingText = !self->isInputingText;
|
||||||
|
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
/*
|
||||||
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC));
|
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC));
|
||||||
dispatch_after(delayTime, dispatch_get_main_queue(), ^{// Code to execute after the delay
|
dispatch_after(delayTime, dispatch_get_main_queue(), ^{// Code to execute after the delay
|
||||||
if(!self->dockedKeyboardActionDetected){
|
if(!self->dockedKeyboardActionDetected){
|
||||||
@@ -332,6 +343,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIViewController *)parentViewController {
|
- (UIViewController *)parentViewController {
|
||||||
@@ -347,6 +359,11 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
|
|
||||||
// this method also deals with recovering streamview when local keyboard is turned off
|
// this method also deals with recovering streamview when local keyboard is turned off
|
||||||
- (void)keyboardWillHide{
|
- (void)keyboardWillHide{
|
||||||
|
if (@available(iOS 13.0, *)) {
|
||||||
|
InputAccessoryBar* bar = (InputAccessoryBar* ) keyInputField.inputAccessoryView;
|
||||||
|
if(bar) [bar releasePressedKeys];
|
||||||
|
}
|
||||||
|
|
||||||
dockedKeyboardActionDetected = true;
|
dockedKeyboardActionDetected = true;
|
||||||
// NSLog(@"keyboard will hide markmark %f", CACurrentMediaTime());
|
// NSLog(@"keyboard will hide markmark %f", CACurrentMediaTime());
|
||||||
|
|
||||||
@@ -459,7 +476,16 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
[customToolbarView sendSubviewToBack:glassView];
|
[customToolbarView sendSubviewToBack:glassView];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
keyInputField.inputAccessoryView = customToolbarView;
|
if (@available(iOS 13.0, *)) {
|
||||||
|
InputAccessoryBar *barView = [[InputAccessoryBar alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, GenericUtils.inputAccessoryBarHeight)];
|
||||||
|
barView.delegate = self;
|
||||||
|
keyInputField.inputAccessoryView = barView;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
keyInputField.inputAccessoryView = customToolbarView;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
[keyInputField becomeFirstResponder];
|
[keyInputField becomeFirstResponder];
|
||||||
@@ -645,7 +671,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
MotionHandler* motionHandler = [MotionHandler sharedWithProfile:profile];
|
MotionHandler* motionHandler = [MotionHandler sharedWithProfile:profile];
|
||||||
|
|
||||||
// get streamFrameVC
|
// get streamFrameVC
|
||||||
if(!self->_streamFrameVC) self->_streamFrameVC = [self parentViewController];
|
if(!self->_streamFrameVC) self->_streamFrameVC = [GenericUtils parentViewControllerForView:self];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (@available(iOS 13.0, *)) {
|
if (@available(iOS 13.0, *)) {
|
||||||
@@ -1481,6 +1507,11 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
|
|||||||
[keysDown removeAllObjects];
|
[keysDown removeAllObjects];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)inputAccessoryBarDidTapClose:(InputAccessoryBar *)bar API_AVAILABLE(ios(13.0)){
|
||||||
|
[keyInputField resignFirstResponder];
|
||||||
|
isInputingText = false;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)onKeyboardPressed:(UITextField *)textField {
|
- (void)onKeyboardPressed:(UITextField *)textField {
|
||||||
|
|
||||||
if (textField.markedTextRange) return;
|
if (textField.markedTextRange) return;
|
||||||
|
|||||||
@@ -21,6 +21,11 @@
|
|||||||
<attribute name="uuid" optional="YES" attributeType="String" syncable="YES"/>
|
<attribute name="uuid" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
|
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
|
<entity name="InputAccessoryBarButton" representedClassName="InputAccessoryBarButton" syncable="YES" codeGenerationType="class">
|
||||||
|
<attribute name="buttonLabel" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="cmdString" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
|
<attribute name="index" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||||
|
</entity>
|
||||||
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
|
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
|
||||||
<attribute name="appTheme" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
<attribute name="appTheme" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||||
<attribute name="asyncFrameDequeue" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
<attribute name="asyncFrameDequeue" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||||
@@ -111,4 +116,4 @@
|
|||||||
<attribute name="useBuiltinMic" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
<attribute name="useBuiltinMic" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||||
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
|
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
</model>
|
</model>
|
||||||
@@ -2401,6 +2401,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Button mode" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "按键模式"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "按鍵模式"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "按鍵模式"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Calibrating..." : {
|
"Calibrating..." : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -6105,6 +6128,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"inputAccessoryBarTip" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "After long-pressing any button for 2 seconds, you can drag to reorder buttons or delete them."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "长按任意按键2秒后,可拖拽调整按键顺序、删除按键"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "長按任意按鍵2秒後,可拖曳調整按鍵順序、刪除按鍵"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "長按任意按鍵2秒後,可拖曳調整按鍵順序、刪除按鍵"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Interval of Multi-Touch Move Events: %d μs" : {
|
"Interval of Multi-Touch Move Events: %d μs" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -7580,6 +7632,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Normal" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "常规"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "常規"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "常規"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Nothing to Undo" : {
|
"Nothing to Undo" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -9565,6 +9640,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Regular" : {
|
||||||
|
"extractionState" : "manual"
|
||||||
|
},
|
||||||
"relativeTouchSlideThresholdStackLink" : {
|
"relativeTouchSlideThresholdStackLink" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -11005,6 +11083,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Soft Keyboard Toolbar" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "软键盘工具栏"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "軟鍵盤工具欄"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "軟鍵盤工具欄"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"SoftKeyboard" : {
|
"SoftKeyboard" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -11605,6 +11706,29 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Tap to toggle" : {
|
||||||
|
"extractionState" : "manual",
|
||||||
|
"localizations" : {
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "点按切换"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "點按切換"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-HK" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "點按切換"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Tap where you're going to input text, view will be lifted automatically " : {
|
"Tap where you're going to input text, view will be lifted automatically " : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
@objc public class GenericUtils: NSObject {
|
@objc public class GenericUtils: NSObject {
|
||||||
|
|
||||||
@@ -65,6 +66,18 @@ import Foundation
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc static func isFirstTappingInputAccessoryBar() -> Bool {
|
||||||
|
let key = "isFirstTappingInputAccessoryBar"
|
||||||
|
let defaults = UserDefaults.standard
|
||||||
|
let launchedBefore = defaults.bool(forKey: key)
|
||||||
|
|
||||||
|
if !launchedBefore {
|
||||||
|
defaults.set(true, forKey: key)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
@objc static func gamepadOverlayFeatureTipTitle() -> String {
|
@objc static func gamepadOverlayFeatureTipTitle() -> String {
|
||||||
SwiftLocalizationHelper.localizedString(forKey: "Gamepad Overlay")
|
SwiftLocalizationHelper.localizedString(forKey: "Gamepad Overlay")
|
||||||
@@ -102,6 +115,13 @@ import Foundation
|
|||||||
return 44
|
return 44
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc static var inputAccessoryBarHeight: CGFloat {
|
||||||
|
if #available(iOS 13.0, *){
|
||||||
|
return GenericUtils.isIPhone() ? 46 : 55
|
||||||
|
}
|
||||||
|
else {return 44}
|
||||||
|
}
|
||||||
|
|
||||||
@objc static var hostViewNavigationBarHeight: CGFloat {
|
@objc static var hostViewNavigationBarHeight: CGFloat {
|
||||||
switch UIDevice.current.userInterfaceIdiom {
|
switch UIDevice.current.userInterfaceIdiom {
|
||||||
case .phone:
|
case .phone:
|
||||||
@@ -248,4 +268,30 @@ import Foundation
|
|||||||
@objc static var screenHeight: CGFloat {
|
@objc static var screenHeight: CGFloat {
|
||||||
return UIScreen.main.bounds.height
|
return UIScreen.main.bounds.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@objc(parentViewControllerForView:)
|
||||||
|
static func parentViewController(for view: UIView?) -> UIViewController? {
|
||||||
|
var responder: UIResponder? = view
|
||||||
|
while let currentResponder = responder {
|
||||||
|
if let viewController = currentResponder as? UIViewController {
|
||||||
|
return viewController
|
||||||
|
}
|
||||||
|
responder = currentResponder.next
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UIView {
|
||||||
|
var parentViewController: UIViewController? {
|
||||||
|
var responder: UIResponder? = self
|
||||||
|
while let r = responder {
|
||||||
|
if let vc = r as? UIViewController {
|
||||||
|
return vc
|
||||||
|
}
|
||||||
|
responder = r.next
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -542,7 +542,7 @@
|
|||||||
object:nil];
|
object:nil];
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
selector:@selector(handleAbnormalKeyboards:)
|
selector:@selector(handleNonStandardKeyboard:)
|
||||||
name:UIKeyboardWillChangeFrameNotification
|
name:UIKeyboardWillChangeFrameNotification
|
||||||
object:nil];
|
object:nil];
|
||||||
|
|
||||||
@@ -768,7 +768,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)handleAbnormalKeyboards:(NSNotification *)notification{
|
- (void)handleAbnormalKeyboards:(NSNotification *)notification{
|
||||||
[_streamView handleAbnormalKeyboards:notification];
|
[_streamView handleNonStandardKeyboard:notification];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)handleWidgetLayoutGesture{
|
- (void)handleWidgetLayoutGesture{
|
||||||
|
|||||||
@@ -2276,30 +2276,30 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cKS-fy-Zpg.segmentTitles[2]" : {
|
"cKS-fy-Zpg.segmentTitles[2]" : {
|
||||||
"comment" : "Class = \"UISegmentedControl\"; cKS-fy-Zpg.segmentTitles[2] = \"regular\"; ObjectID = \"cKS-fy-Zpg\";",
|
"comment" : "Class = \"UISegmentedControl\"; cKS-fy-Zpg.segmentTitles[2] = \"normal\"; ObjectID = \"cKS-fy-Zpg\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "new",
|
"state" : "new",
|
||||||
"value" : "regular"
|
"value" : "normal"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"zh-Hans" : {
|
"zh-Hans" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "translated",
|
"state" : "needs_review",
|
||||||
"value" : "常规模式"
|
"value" : "常规模式"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"zh-Hant" : {
|
"zh-Hant" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "translated",
|
"state" : "needs_review",
|
||||||
"value" : "一般模式"
|
"value" : "一般模式"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"zh-HK" : {
|
"zh-HK" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "translated",
|
"state" : "needs_review",
|
||||||
"value" : "一般模式"
|
"value" : "一般模式"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4178,30 +4178,30 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ojp-aS-m8w.segmentTitles[2]" : {
|
"ojp-aS-m8w.segmentTitles[2]" : {
|
||||||
"comment" : "Class = \"UISegmentedControl\"; ojp-aS-m8w.segmentTitles[2] = \"regular\"; ObjectID = \"ojp-aS-m8w\";",
|
"comment" : "Class = \"UISegmentedControl\"; ojp-aS-m8w.segmentTitles[2] = \"normal\"; ObjectID = \"ojp-aS-m8w\";",
|
||||||
"extractionState" : "extracted_with_value",
|
"extractionState" : "extracted_with_value",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "new",
|
"state" : "new",
|
||||||
"value" : "regular"
|
"value" : "normal"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"zh-Hans" : {
|
"zh-Hans" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "translated",
|
"state" : "needs_review",
|
||||||
"value" : "常规模式"
|
"value" : "常规模式"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"zh-Hant" : {
|
"zh-Hant" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "translated",
|
"state" : "needs_review",
|
||||||
"value" : "一般模式"
|
"value" : "一般模式"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"zh-HK" : {
|
"zh-HK" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
"state" : "translated",
|
"state" : "needs_review",
|
||||||
"value" : "一般模式"
|
"value" : "一般模式"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,6 +171,23 @@ enum WidgetCreateComboMode: String, CaseIterable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
private enum ShortcutPickerButtonMode: String, CaseIterable, Identifiable {
|
||||||
|
case normal
|
||||||
|
case tapToToggle
|
||||||
|
|
||||||
|
var id: String { rawValue }
|
||||||
|
|
||||||
|
var title: String {
|
||||||
|
switch self {
|
||||||
|
case .normal:
|
||||||
|
return SwiftLocalizationHelper.localizedString(forKey: "Normal")
|
||||||
|
case .tapToToggle:
|
||||||
|
return SwiftLocalizationHelper.localizedString(forKey: "Tap to toggle")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@available(iOS 13.0, *)
|
@available(iOS 13.0, *)
|
||||||
private enum WidgetCreateShape: String, CaseIterable, Identifiable {
|
private enum WidgetCreateShape: String, CaseIterable, Identifiable {
|
||||||
case round = "r"
|
case round = "r"
|
||||||
@@ -303,6 +320,7 @@ struct WidgetPickerView: View {
|
|||||||
private let preferredInitialTab: WidgetPickerTab?
|
private let preferredInitialTab: WidgetPickerTab?
|
||||||
private let keyboardPickerMode: VirtualKeyboardMode
|
private let keyboardPickerMode: VirtualKeyboardMode
|
||||||
var shortcutPickerNeedAlias: Bool = false
|
var shortcutPickerNeedAlias: Bool = false
|
||||||
|
var shortcutPickerNeedButtonMode: Bool = false
|
||||||
private let shortcutPickerTipText: String?
|
private let shortcutPickerTipText: String?
|
||||||
private let shortcutIdentifier: String?
|
private let shortcutIdentifier: String?
|
||||||
@ObservedObject private var presentationState: WidgetPickerPresentationState
|
@ObservedObject private var presentationState: WidgetPickerPresentationState
|
||||||
@@ -324,6 +342,7 @@ struct WidgetPickerView: View {
|
|||||||
@SwiftUI.State private var selectedGyroCommand: String? = nil
|
@SwiftUI.State private var selectedGyroCommand: String? = nil
|
||||||
@SwiftUI.State private var showCreateWidgetSheet = false
|
@SwiftUI.State private var showCreateWidgetSheet = false
|
||||||
@SwiftUI.State private var widgetComboMode: WidgetCreateComboMode = .skill
|
@SwiftUI.State private var widgetComboMode: WidgetCreateComboMode = .skill
|
||||||
|
@SwiftUI.State private var shortcutPickerButtonMode: ShortcutPickerButtonMode = .normal
|
||||||
@SwiftUI.State private var widgetTriggerInterval: Double = 0
|
@SwiftUI.State private var widgetTriggerInterval: Double = 0
|
||||||
@SwiftUI.State private var widgetShape: WidgetCreateShape = .round
|
@SwiftUI.State private var widgetShape: WidgetCreateShape = .round
|
||||||
@SwiftUI.State private var widgetButtonLabel: String = ""
|
@SwiftUI.State private var widgetButtonLabel: String = ""
|
||||||
@@ -350,6 +369,7 @@ struct WidgetPickerView: View {
|
|||||||
preferredInitialTab: WidgetPickerTab? = nil,
|
preferredInitialTab: WidgetPickerTab? = nil,
|
||||||
keyboardPickerMode: VirtualKeyboardMode = .picker,
|
keyboardPickerMode: VirtualKeyboardMode = .picker,
|
||||||
shortcutPickerNeedAlias: Bool = false,
|
shortcutPickerNeedAlias: Bool = false,
|
||||||
|
shortcutPickerNeedButtonMode: Bool = false,
|
||||||
shortcutPickerTipText: String? = nil,
|
shortcutPickerTipText: String? = nil,
|
||||||
shortcutIdentififier: String? = nil,
|
shortcutIdentififier: String? = nil,
|
||||||
presentationState: WidgetPickerPresentationState = WidgetPickerPresentationState(),
|
presentationState: WidgetPickerPresentationState = WidgetPickerPresentationState(),
|
||||||
@@ -366,6 +386,7 @@ struct WidgetPickerView: View {
|
|||||||
self.preferredInitialTab = resolvedInitialTab
|
self.preferredInitialTab = resolvedInitialTab
|
||||||
self.keyboardPickerMode = keyboardPickerMode
|
self.keyboardPickerMode = keyboardPickerMode
|
||||||
self.shortcutPickerNeedAlias = shortcutPickerNeedAlias
|
self.shortcutPickerNeedAlias = shortcutPickerNeedAlias
|
||||||
|
self.shortcutPickerNeedButtonMode = shortcutPickerNeedButtonMode
|
||||||
self.shortcutPickerTipText = shortcutPickerTipText
|
self.shortcutPickerTipText = shortcutPickerTipText
|
||||||
self.shortcutIdentifier = shortcutIdentififier
|
self.shortcutIdentifier = shortcutIdentififier
|
||||||
self.presentationState = presentationState
|
self.presentationState = presentationState
|
||||||
@@ -1604,6 +1625,18 @@ struct WidgetPickerView: View {
|
|||||||
.opacity(isComboModeLocked ? 0.45 : 1.0)
|
.opacity(isComboModeLocked ? 0.45 : 1.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if showsShortcutButtonModeControl {
|
||||||
|
createWidgetSection(title: SwiftLocalizationHelper.localizedString(forKey: "Button mode")) {
|
||||||
|
Picker("", selection: $shortcutPickerButtonMode) {
|
||||||
|
ForEach(ShortcutPickerButtonMode.allCases) { mode in
|
||||||
|
Text(mode.title).tag(mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pickerStyle(SegmentedPickerStyle())
|
||||||
|
.environment(\.colorScheme, .light)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if showsIntervalSlider {
|
if showsIntervalSlider {
|
||||||
@@ -1792,6 +1825,10 @@ struct WidgetPickerView: View {
|
|||||||
return targetWidgetKind == .button
|
return targetWidgetKind == .button
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var showsShortcutButtonModeControl: Bool {
|
||||||
|
isShortcutPickerMode && shortcutPickerNeedButtonMode && targetWidgetKind == .button
|
||||||
|
}
|
||||||
|
|
||||||
private var showsIntervalSlider: Bool {
|
private var showsIntervalSlider: Bool {
|
||||||
if isShortcutPickerMode { return false }
|
if isShortcutPickerMode { return false }
|
||||||
guard buttonCommandCount >= 2 else { return false }
|
guard buttonCommandCount >= 2 else { return false }
|
||||||
@@ -1834,6 +1871,7 @@ struct WidgetPickerView: View {
|
|||||||
|
|
||||||
let hasVisibleConfigurationSection =
|
let hasVisibleConfigurationSection =
|
||||||
showsButtonLabelField ||
|
showsButtonLabelField ||
|
||||||
|
showsShortcutButtonModeControl ||
|
||||||
showsComboModeControl ||
|
showsComboModeControl ||
|
||||||
showsIntervalSlider ||
|
showsIntervalSlider ||
|
||||||
showsShapeControl
|
showsShapeControl
|
||||||
@@ -1861,6 +1899,7 @@ struct WidgetPickerView: View {
|
|||||||
} else {
|
} else {
|
||||||
widgetTriggerInterval = 0
|
widgetTriggerInterval = 0
|
||||||
}
|
}
|
||||||
|
shortcutPickerButtonMode = shortcutPickerNeedsTapToToggle(initialCmdString) ? .tapToToggle : .normal
|
||||||
if isEditMode,
|
if isEditMode,
|
||||||
submissionAction == .modify,
|
submissionAction == .modify,
|
||||||
let initialButtonLabel,
|
let initialButtonLabel,
|
||||||
@@ -1907,14 +1946,14 @@ struct WidgetPickerView: View {
|
|||||||
lastCreatedWidgetPayload = payload
|
lastCreatedWidgetPayload = payload
|
||||||
setTipMessage(SwiftLocalizationHelper.localizedString(forKey: "Widget config generated"))
|
setTipMessage(SwiftLocalizationHelper.localizedString(forKey: "Widget config generated"))
|
||||||
onWidgetCreated?(payload)
|
onWidgetCreated?(payload)
|
||||||
// print("widgetPicker payload =", payload as NSDictionary)
|
print("widgetPicker payload =", payload as NSDictionary)
|
||||||
setCreateWidgetSheetVisible(false)
|
setCreateWidgetSheetVisible(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeWidgetPayload() -> [String: String] {
|
private func makeWidgetPayload() -> [String: String] {
|
||||||
[
|
[
|
||||||
"cmdString": buildCmdString(),
|
"cmdString": buildCmdString(),
|
||||||
"buttonLabel": targetWidgetKind == .button ? widgetButtonLabel : "",
|
"buttonLabel": targetWidgetKind == .button ? resolvedWidgetButtonLabel() : "",
|
||||||
"shape": targetWidgetKind == .button ? widgetShape.rawValue : "",
|
"shape": targetWidgetKind == .button ? widgetShape.rawValue : "",
|
||||||
"pickerAction": pendingSubmissionAction.payloadValue,
|
"pickerAction": pendingSubmissionAction.payloadValue,
|
||||||
"shortcutIdentifier": shortcutIdentifier ?? ""
|
"shortcutIdentifier": shortcutIdentifier ?? ""
|
||||||
@@ -1923,10 +1962,14 @@ struct WidgetPickerView: View {
|
|||||||
|
|
||||||
private func buildCmdString() -> String {
|
private func buildCmdString() -> String {
|
||||||
if isShortcutPickerMode {
|
if isShortcutPickerMode {
|
||||||
return selectedCmds.joined(separator: "+")
|
var cmdString = selectedCmds.joined(separator: "+")
|
||||||
|
if shortcutPickerButtonMode == .tapToToggle, !cmdString.isEmpty {
|
||||||
|
cmdString += "+NULL"
|
||||||
|
}
|
||||||
|
return cmdString
|
||||||
}
|
}
|
||||||
|
|
||||||
let joiner = effectiveWidgetComboMode == .shortcut && showsComboModeControl ? "+" : "-"
|
let joiner = comboJoiner
|
||||||
var cmdString = selectedCmds.joined(separator: joiner)
|
var cmdString = selectedCmds.joined(separator: joiner)
|
||||||
|
|
||||||
let shouldAppendInterval = effectiveWidgetComboMode != .shortcut
|
let shouldAppendInterval = effectiveWidgetComboMode != .shortcut
|
||||||
@@ -1939,6 +1982,56 @@ struct WidgetPickerView: View {
|
|||||||
return cmdString
|
return cmdString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func resolvedWidgetButtonLabel() -> String {
|
||||||
|
let trimmedManualLabel = widgetButtonLabel.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
if !trimmedManualLabel.isEmpty {
|
||||||
|
return trimmedManualLabel
|
||||||
|
}
|
||||||
|
return defaultWidgetButtonLabel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func defaultWidgetButtonLabel() -> String {
|
||||||
|
if let selectedFunctionalButtonOption {
|
||||||
|
return selectedFunctionalButtonOption.cmd == "FOLDER"
|
||||||
|
? SwiftLocalizationHelper.localizedString(forKey: "=Folder")
|
||||||
|
: selectedFunctionalButtonOption.label
|
||||||
|
}
|
||||||
|
|
||||||
|
let labelParts = poolItems.compactMap(defaultLabelPart(for:))
|
||||||
|
guard !labelParts.isEmpty else { return "" }
|
||||||
|
return labelParts.joined(separator: comboJoiner)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func defaultLabelPart(for item: WidgetPoolItem) -> String? {
|
||||||
|
switch item.source {
|
||||||
|
case .gamepad:
|
||||||
|
return item.cmd.lowercased().capitalized
|
||||||
|
case .keyboard:
|
||||||
|
let label = (item.displayText ?? item.cmd).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
guard !label.isEmpty else { return nil }
|
||||||
|
if let firstCharacter = label.first, firstCharacter.isNumber {
|
||||||
|
return String(firstCharacter)
|
||||||
|
}
|
||||||
|
return label
|
||||||
|
case .functional:
|
||||||
|
if let option = functionalButtonOption(for: item.cmd) {
|
||||||
|
return option.cmd == "FOLDER"
|
||||||
|
? SwiftLocalizationHelper.localizedString(forKey: "=Folder")
|
||||||
|
: option.label
|
||||||
|
}
|
||||||
|
let label = item.displayCmd.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
return label.isEmpty ? nil : label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var comboJoiner: String {
|
||||||
|
usesExplicitSkillComboJoiner ? "-" : "+"
|
||||||
|
}
|
||||||
|
|
||||||
|
private var usesExplicitSkillComboJoiner: Bool {
|
||||||
|
!isShortcutPickerMode && showsComboModeControl && forcedComboMode == nil && widgetComboMode == .skill
|
||||||
|
}
|
||||||
|
|
||||||
private func setCreateWidgetSheetVisible(_ isVisible: Bool) {
|
private func setCreateWidgetSheetVisible(_ isVisible: Bool) {
|
||||||
var transaction = Transaction()
|
var transaction = Transaction()
|
||||||
transaction.animation = nil
|
transaction.animation = nil
|
||||||
@@ -2016,6 +2109,9 @@ struct WidgetPickerView: View {
|
|||||||
last.range(of: #"^\d+MS$"#, options: .regularExpression) != nil {
|
last.range(of: #"^\d+MS$"#, options: .regularExpression) != nil {
|
||||||
commands.removeLast()
|
commands.removeLast()
|
||||||
}
|
}
|
||||||
|
if isShortcutPickerMode, commands.last == "NULL" {
|
||||||
|
commands.removeLast()
|
||||||
|
}
|
||||||
|
|
||||||
var parsed: [(cmd: String, source: WidgetPoolSource, isTailLocked: Bool, displayText: String?)] = []
|
var parsed: [(cmd: String, source: WidgetPoolSource, isTailLocked: Bool, displayText: String?)] = []
|
||||||
for rawCommand in commands where !rawCommand.isEmpty {
|
for rawCommand in commands where !rawCommand.isEmpty {
|
||||||
@@ -2042,6 +2138,18 @@ struct WidgetPickerView: View {
|
|||||||
return parsed
|
return parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func shortcutPickerNeedsTapToToggle(_ cmdString: String?) -> Bool {
|
||||||
|
guard isShortcutPickerMode,
|
||||||
|
let normalized = cmdString?
|
||||||
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
.uppercased(),
|
||||||
|
!normalized.isEmpty else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized.split(separator: "+").last == "NULL"
|
||||||
|
}
|
||||||
|
|
||||||
private func source(for cmd: String) -> WidgetPoolSource? {
|
private func source(for cmd: String) -> WidgetPoolSource? {
|
||||||
if gamepadCommands.contains(cmd) {
|
if gamepadCommands.contains(cmd) {
|
||||||
return .gamepad
|
return .gamepad
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ import UIKit
|
|||||||
@available(iOS 13.0, *)
|
@available(iOS 13.0, *)
|
||||||
@objcMembers
|
@objcMembers
|
||||||
public final class WidgetPickerViewController: UIViewController {
|
public final class WidgetPickerViewController: UIViewController {
|
||||||
|
private static var overlayWindow: UIWindow?
|
||||||
|
private static weak var originalAppWindow: UIWindow?
|
||||||
|
private static weak var originalHostViewController: UIViewController?
|
||||||
|
|
||||||
public weak var delegate: WidgetPickerViewControllerDelegate?
|
public weak var delegate: WidgetPickerViewControllerDelegate?
|
||||||
public var isEditMode: Bool = false
|
public var isEditMode: Bool = false
|
||||||
public var initialCmdString: String?
|
public var initialCmdString: String?
|
||||||
@@ -27,8 +31,10 @@ public final class WidgetPickerViewController: UIViewController {
|
|||||||
public var initialTabIdentifier: String?
|
public var initialTabIdentifier: String?
|
||||||
public var keyboardPickerMode: VirtualKeyboardMode = .picker
|
public var keyboardPickerMode: VirtualKeyboardMode = .picker
|
||||||
var shortcutPickerNeedAlias: Bool = false
|
var shortcutPickerNeedAlias: Bool = false
|
||||||
|
var shortcutPickerNeedButtonMode: Bool = false
|
||||||
public var shortcutPickerTipText: String?
|
public var shortcutPickerTipText: String?
|
||||||
@objc public var shortcutIdentifier: String?
|
@objc public var shortcutIdentifier: String?
|
||||||
|
public var usesOverlayPresentation: Bool = false
|
||||||
|
|
||||||
private let presentationState = WidgetPickerPresentationState()
|
private let presentationState = WidgetPickerPresentationState()
|
||||||
private var hostingViewController: UIHostingController<WidgetPickerView>?
|
private var hostingViewController: UIHostingController<WidgetPickerView>?
|
||||||
@@ -36,7 +42,7 @@ public final class WidgetPickerViewController: UIViewController {
|
|||||||
public override func viewDidLoad() {
|
public override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
view.backgroundColor = .systemBackground
|
view.backgroundColor = usesOverlayPresentation ? .clear : .systemBackground
|
||||||
|
|
||||||
let rootView = WidgetPickerView(
|
let rootView = WidgetPickerView(
|
||||||
isEditMode: isEditMode,
|
isEditMode: isEditMode,
|
||||||
@@ -47,16 +53,17 @@ public final class WidgetPickerViewController: UIViewController {
|
|||||||
preferredInitialTab: resolvedInitialTab(),
|
preferredInitialTab: resolvedInitialTab(),
|
||||||
keyboardPickerMode: keyboardPickerMode,
|
keyboardPickerMode: keyboardPickerMode,
|
||||||
shortcutPickerNeedAlias: shortcutPickerNeedAlias,
|
shortcutPickerNeedAlias: shortcutPickerNeedAlias,
|
||||||
|
shortcutPickerNeedButtonMode: shortcutPickerNeedButtonMode,
|
||||||
shortcutPickerTipText: shortcutPickerTipText,
|
shortcutPickerTipText: shortcutPickerTipText,
|
||||||
shortcutIdentififier: shortcutIdentifier,
|
shortcutIdentififier: shortcutIdentifier,
|
||||||
presentationState: presentationState,
|
presentationState: presentationState,
|
||||||
onWidgetCreated: { [weak self] payload in
|
onWidgetCreated: { [weak self] payload in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
self.delegate?.widgetPickerViewController(self, didCreateWidget: payload as NSDictionary)
|
self.delegate?.widgetPickerViewController(self, didCreateWidget: payload as NSDictionary)
|
||||||
self.dismiss(animated: true)
|
self.closeSelf(animated: true)
|
||||||
},
|
},
|
||||||
onCloseRequested: { [weak self] in
|
onCloseRequested: { [weak self] in
|
||||||
self?.dismiss(animated: true)
|
self?.closeSelf(animated: true)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
let hostingViewController = UIHostingController(rootView: rootView)
|
let hostingViewController = UIHostingController(rootView: rootView)
|
||||||
@@ -96,4 +103,95 @@ public final class WidgetPickerViewController: UIViewController {
|
|||||||
guard let initialTabIdentifier else { return nil }
|
guard let initialTabIdentifier else { return nil }
|
||||||
return WidgetPickerTab(identifier: initialTabIdentifier)
|
return WidgetPickerTab(identifier: initialTabIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func presentAsOverlay(in parentViewController: UIViewController, animated: Bool = true) {
|
||||||
|
guard parent == nil else { return }
|
||||||
|
guard let originalAppWindow = parentViewController.view.window,
|
||||||
|
let windowScene = originalAppWindow.windowScene else { return }
|
||||||
|
|
||||||
|
usesOverlayPresentation = true
|
||||||
|
Self.originalAppWindow = originalAppWindow
|
||||||
|
Self.originalHostViewController = parentViewController
|
||||||
|
let overlayHostViewController = UIViewController()
|
||||||
|
overlayHostViewController.view.backgroundColor = .clear
|
||||||
|
|
||||||
|
let overlayWindow = UIWindow(windowScene: windowScene)
|
||||||
|
overlayWindow.frame = windowScene.screen.bounds
|
||||||
|
overlayWindow.windowLevel = .alert + 1
|
||||||
|
overlayWindow.backgroundColor = .clear
|
||||||
|
overlayWindow.rootViewController = overlayHostViewController
|
||||||
|
overlayWindow.isHidden = false
|
||||||
|
Self.overlayWindow = overlayWindow
|
||||||
|
|
||||||
|
loadViewIfNeeded()
|
||||||
|
view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
view.alpha = animated ? 0 : 1
|
||||||
|
|
||||||
|
overlayHostViewController.addChild(self)
|
||||||
|
overlayHostViewController.view.addSubview(view)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
view.topAnchor.constraint(equalTo: overlayHostViewController.view.topAnchor),
|
||||||
|
view.bottomAnchor.constraint(equalTo: overlayHostViewController.view.bottomAnchor),
|
||||||
|
view.leadingAnchor.constraint(equalTo: overlayHostViewController.view.leadingAnchor),
|
||||||
|
view.trailingAnchor.constraint(equalTo: overlayHostViewController.view.trailingAnchor),
|
||||||
|
])
|
||||||
|
didMove(toParent: overlayHostViewController)
|
||||||
|
|
||||||
|
guard animated else { return }
|
||||||
|
UIView.animate(withDuration: 0.18) {
|
||||||
|
self.view.alpha = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func closeSelf(animated: Bool) {
|
||||||
|
if presentingViewController != nil {
|
||||||
|
dismiss(animated: animated)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard parent != nil else {
|
||||||
|
Self.destroyOverlayWindowAndRestoreAppWindow()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let removeOverlay = {
|
||||||
|
self.willMove(toParent: nil)
|
||||||
|
self.view.removeFromSuperview()
|
||||||
|
self.removeFromParent()
|
||||||
|
Self.destroyOverlayWindowAndRestoreAppWindow()
|
||||||
|
}
|
||||||
|
|
||||||
|
guard animated else {
|
||||||
|
removeOverlay()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
UIView.animate(
|
||||||
|
withDuration: 0.18,
|
||||||
|
animations: {
|
||||||
|
self.view.alpha = 0
|
||||||
|
},
|
||||||
|
completion: { _ in
|
||||||
|
removeOverlay()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func destroyOverlayWindowAndRestoreAppWindow() {
|
||||||
|
overlayWindow?.isHidden = true
|
||||||
|
overlayWindow?.rootViewController = nil
|
||||||
|
overlayWindow = nil
|
||||||
|
originalAppWindow?.makeKeyAndVisible()
|
||||||
|
refreshSystemGestureDeferral()
|
||||||
|
originalAppWindow = nil
|
||||||
|
originalHostViewController = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func refreshSystemGestureDeferral() {
|
||||||
|
guard let hostViewController = originalHostViewController else { return }
|
||||||
|
if #available(iOS 11.0, *) {
|
||||||
|
hostViewController.setNeedsUpdateOfScreenEdgesDeferringSystemGestures()
|
||||||
|
hostViewController.setNeedsUpdateOfHomeIndicatorAutoHidden()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user