- tool box menu improvements

- autoDock improvements
- live gamepad overlay close button
This commit is contained in:
True Zhuanjia committed 2026-04-16 14:31:21 +08:00
1 parent d769c98e22
commit a1a70fcca2
8 files changed
+212 -27

No files matched your search

@@ -9,6 +9,12 @@
import UIKit import UIKit
import SwiftUI import SwiftUI
@objc
protocol AbstractGamepadOverlayCloseButtonDelegate: AnyObject {
@objc(toggleGamepadOverlayWithOverlayEnabled:)
func toggleGamepadOverlay(withOverlayEnabled overlayEnabled: Bool)
}
private enum AbstractGamepadOverlayPersistence { private enum AbstractGamepadOverlayPersistence {
private static let defaults = UserDefaults.standard private static let defaults = UserDefaults.standard
@@ -126,6 +132,14 @@ private struct AbstractGamepadOverlayBridgeView: View {
@available(iOS 13.0, *) @available(iOS 13.0, *)
@objc @objc
final class AbstractGamepadOverlayView: UIView { final class AbstractGamepadOverlayView: UIView {
private enum CloseButtonMetrics {
static let size: CGFloat = 40
static let topInset: CGFloat = -10
static let trailingInset: CGFloat = 0
static let symbolPointSize: CGFloat = 16
static let hideDelay: TimeInterval = 3
}
private enum OverlayMaskMetrics { private enum OverlayMaskMetrics {
static let widthRatio: CGFloat = 0.85 static let widthRatio: CGFloat = 0.85
static let heightRatio: CGFloat = 1.03 static let heightRatio: CGFloat = 1.03
@@ -149,9 +163,12 @@ final class AbstractGamepadOverlayView: UIView {
private var panStartCenter: CGPoint = .zero private var panStartCenter: CGPoint = .zero
private var activeGestureCount = 0 private var activeGestureCount = 0
private var didRestorePersistedState = false private var didRestorePersistedState = false
private var hideCloseButtonWorkItem: DispatchWorkItem?
private let minWidth: CGFloat = 70 private let minWidth: CGFloat = 70
private let maxWidth: CGFloat = 520 private let maxWidth: CGFloat = 520
private let doubleTapWidths: [CGFloat] = [GenericUtils.isIPhone() ? 90 : 130, GenericUtils.isIPhone() ? 165 : 200] private let doubleTapWidths: [CGFloat] = [GenericUtils.isIPhone() ? 90 : 130, GenericUtils.isIPhone() ? 165 : 200]
private lazy var closeButton: UIButton = makeCloseButton()
@objc weak var closeButtonDelegate: AbstractGamepadOverlayCloseButtonDelegate?
@objc init(frame: CGRect, usesPlayStationFaceButtons: Bool) { @objc init(frame: CGRect, usesPlayStationFaceButtons: Bool) {
self.usesPlayStationFaceButtons = usesPlayStationFaceButtons self.usesPlayStationFaceButtons = usesPlayStationFaceButtons
@@ -189,16 +206,17 @@ final class AbstractGamepadOverlayView: UIView {
backgroundColor = .clear backgroundColor = .clear
clipsToBounds = false clipsToBounds = false
isMultipleTouchEnabled = true isMultipleTouchEnabled = true
layer.mask = visualMaskLayer
applyScale(forTargetWidth: initialDisplayWidth) applyScale(forTargetWidth: initialDisplayWidth)
addSubview(hostingController.view) addSubview(hostingController.view)
addSubview(closeButton)
installGesturesIfNeeded() installGesturesIfNeeded()
} }
override func layoutSubviews() { override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
hostedView?.frame = bounds hostedView?.frame = bounds
layoutCloseButton()
updateVisualMask() updateVisualMask()
restorePersistedStateIfNeeded() restorePersistedStateIfNeeded()
} }
@@ -237,17 +255,21 @@ final class AbstractGamepadOverlayView: UIView {
@objc private func handleSingleTap(_ recognizer: UITapGestureRecognizer) { @objc private func handleSingleTap(_ recognizer: UITapGestureRecognizer) {
guard recognizer.state == .ended else { return } guard recognizer.state == .ended else { return }
registerUserInteraction()
presentFirstTouchTipIfNeeded() presentFirstTouchTipIfNeeded()
scheduleCloseButtonHideIfNeeded()
} }
@objc private func handlePan(_ recognizer: UIPanGestureRecognizer) { @objc private func handlePan(_ recognizer: UIPanGestureRecognizer) {
guard let superview else { return } guard let superview else { return }
switch recognizer.state { switch recognizer.state {
case .began: case .began:
registerUserInteraction()
presentFirstTouchTipIfNeeded() presentFirstTouchTipIfNeeded()
beginGestureSession() beginGestureSession()
panStartCenter = center panStartCenter = center
case .changed, .ended: case .changed, .ended:
registerUserInteraction()
let translation = recognizer.translation(in: superview) let translation = recognizer.translation(in: superview)
center = CGPoint(x: panStartCenter.x + translation.x, y: panStartCenter.y + translation.y) center = CGPoint(x: panStartCenter.x + translation.x, y: panStartCenter.y + translation.y)
if recognizer.state == .ended { if recognizer.state == .ended {
@@ -263,10 +285,12 @@ final class AbstractGamepadOverlayView: UIView {
@objc private func handlePinch(_ recognizer: UIPinchGestureRecognizer) { @objc private func handlePinch(_ recognizer: UIPinchGestureRecognizer) {
switch recognizer.state { switch recognizer.state {
case .began: case .began:
registerUserInteraction()
presentFirstTouchTipIfNeeded() presentFirstTouchTipIfNeeded()
beginGestureSession() beginGestureSession()
pinchBaseWidth = frame.width pinchBaseWidth = frame.width
case .changed, .ended: case .changed, .ended:
registerUserInteraction()
let nextWidth = max(minWidth, min(maxWidth, pinchBaseWidth * recognizer.scale)) let nextWidth = max(minWidth, min(maxWidth, pinchBaseWidth * recognizer.scale))
applyScale(forTargetWidth: nextWidth) applyScale(forTargetWidth: nextWidth)
if recognizer.state == .ended { if recognizer.state == .ended {
@@ -281,14 +305,17 @@ final class AbstractGamepadOverlayView: UIView {
@objc private func handleDoubleTap(_ recognizer: UITapGestureRecognizer) { @objc private func handleDoubleTap(_ recognizer: UITapGestureRecognizer) {
guard recognizer.state == .ended else { return } guard recognizer.state == .ended else { return }
registerUserInteraction()
presentFirstTouchTipIfNeeded() presentFirstTouchTipIfNeeded()
applyScale(forTargetWidth: nextDoubleTapWidth()) applyScale(forTargetWidth: nextDoubleTapWidth())
persistState() persistState()
scheduleCloseButtonHideIfNeeded()
} }
private func updateVisualMask() { private func updateVisualMask() {
guard bounds.width > 1, bounds.height > 1 else { guard bounds.width > 1, bounds.height > 1 else {
visualMaskLayer.path = nil visualMaskLayer.path = nil
hostedView?.layer.mask = nil
return return
} }
@@ -325,16 +352,32 @@ final class AbstractGamepadOverlayView: UIView {
) )
) )
visualMaskLayer.path = path.cgPath visualMaskLayer.path = path.cgPath
hostedView?.layer.mask = visualMaskLayer
} }
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
bounds.contains(point) bounds.contains(point)
} }
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {} override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {} super.touchesBegan(touches, with: event)
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {} registerUserInteraction()
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {} }
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
registerUserInteraction()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
scheduleCloseButtonHideIfNeeded()
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
scheduleCloseButtonHideIfNeeded()
}
private func restorePersistedStateIfNeeded() { private func restorePersistedStateIfNeeded() {
guard !didRestorePersistedState, guard !didRestorePersistedState,
@@ -354,6 +397,11 @@ final class AbstractGamepadOverlayView: UIView {
activeGestureCount += 1 activeGestureCount += 1
} }
@objc func registerUserInteraction() {
showCloseButton()
cancelPendingCloseButtonHide()
}
private func presentFirstTouchTipIfNeeded() { private func presentFirstTouchTipIfNeeded() {
guard GenericUtils.isFirstLaunchGamepadOverlayFeature() else { return } guard GenericUtils.isFirstLaunchGamepadOverlayFeature() else { return }
guard let presentingViewController = nearestViewController() else { return } guard let presentingViewController = nearestViewController() else { return }
@@ -383,6 +431,7 @@ final class AbstractGamepadOverlayView: UIView {
activeGestureCount = max(0, activeGestureCount - 1) activeGestureCount = max(0, activeGestureCount - 1)
guard activeGestureCount == 0 else { return } guard activeGestureCount == 0 else { return }
persistState() persistState()
scheduleCloseButtonHideIfNeeded()
} }
private func persistState() { private func persistState() {
@@ -407,6 +456,79 @@ final class AbstractGamepadOverlayView: UIView {
let targetScale = clampedWidth / baseRenderWidth let targetScale = clampedWidth / baseRenderWidth
transform = CGAffineTransform(scaleX: targetScale, y: targetScale) transform = CGAffineTransform(scaleX: targetScale, y: targetScale)
} }
private func makeCloseButton() -> UIButton {
let button = UIButton(type: .system)
button.tintColor = UIColor.black.withAlphaComponent(0.66)
button.backgroundColor = UIColor.white.withAlphaComponent(0.78)
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.white.withAlphaComponent(0.9).cgColor
button.layer.shadowColor = UIColor.black.withAlphaComponent(0.05).cgColor
button.layer.shadowOpacity = 1
button.layer.shadowRadius = 4
button.layer.shadowOffset = CGSize(width: 0, height: 2)
button.alpha = 0
button.isHidden = true
button.adjustsImageWhenHighlighted = false
button.addTarget(self, action: #selector(handleCloseButtonTap), for: .touchUpInside)
let configuration = UIImage.SymbolConfiguration(pointSize: CloseButtonMetrics.symbolPointSize, weight: .bold)
button.setImage(UIImage(systemName: "xmark", withConfiguration: configuration), for: .normal)
return button
}
private func layoutCloseButton() {
let size = CGFloat(Int(CloseButtonMetrics.size/2))*2
closeButton.frame = CGRect(
x: bounds.width - CloseButtonMetrics.trailingInset - size,
y: CloseButtonMetrics.topInset,
width: size,
height: size
)
closeButton.layer.cornerRadius = size * 0.5
}
private func showCloseButton() {
closeButton.isHidden = false
guard closeButton.alpha < 1 else { return }
UIView.animate(withDuration: 0.18) {
self.closeButton.alpha = 1
}
}
private func hideCloseButton() {
guard !closeButton.isHidden else { return }
UIView.animate(
withDuration: 0.18,
animations: {
self.closeButton.alpha = 0
},
completion: { _ in
self.closeButton.isHidden = true
}
)
}
private func cancelPendingCloseButtonHide() {
hideCloseButtonWorkItem?.cancel()
hideCloseButtonWorkItem = nil
}
@objc func scheduleCloseButtonHideIfNeeded() {
guard activeGestureCount == 0 else { return }
cancelPendingCloseButtonHide()
let workItem = DispatchWorkItem { [weak self] in
self?.hideCloseButton()
self?.hideCloseButtonWorkItem = nil
}
hideCloseButtonWorkItem = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + CloseButtonMetrics.hideDelay, execute: workItem)
}
@objc private func handleCloseButtonTap() {
cancelPendingCloseButtonHide()
closeButtonDelegate?.toggleGamepadOverlay(withOverlayEnabled: false)
}
} }
@available(iOS 13.0, *) @available(iOS 13.0, *)
@@ -415,4 +537,9 @@ extension AbstractGamepadOverlayView: UIGestureRecognizerDelegate {
(gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPinchGestureRecognizer) (gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPinchGestureRecognizer)
|| (gestureRecognizer is UIPinchGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer) || (gestureRecognizer is UIPinchGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer)
} }
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
guard let touchedView = touch.view else { return true }
return touchedView !== closeButton && !touchedView.isDescendant(of: closeButton)
}
} }
+1 -1
View File
@@ -114,7 +114,7 @@
self.reverseGyroHoldButton = [decoder containsValueForKey:@"reverseGyroHoldButton"] ? [decoder decodeBoolForKey:@"reverseGyroHoldButton"] : false; self.reverseGyroHoldButton = [decoder containsValueForKey:@"reverseGyroHoldButton"] ? [decoder decodeBoolForKey:@"reverseGyroHoldButton"] : false;
self.controllerGyroSwitchHold = [decoder containsValueForKey:@"controllerGyroSwitchHold"] ? [decoder decodeIntForKey:@"controllerGyroSwitchHold"] : ControllerButtonNull; self.controllerGyroSwitchHold = [decoder containsValueForKey:@"controllerGyroSwitchHold"] ? [decoder decodeIntForKey:@"controllerGyroSwitchHold"] : ControllerButtonNull;
self.controllerGyroSwitchToggle = [decoder containsValueForKey:@"controllerGyroSwitchToggle"] ? [decoder decodeIntForKey:@"controllerGyroSwitchToggle"] : ControllerButtonNull; self.controllerGyroSwitchToggle = [decoder containsValueForKey:@"controllerGyroSwitchToggle"] ? [decoder decodeIntForKey:@"controllerGyroSwitchToggle"] : ControllerButtonNull;
self.gamepadOverlayEnabled = [decoder containsValueForKey:@"gamepadOverlayEnabled"] ? [decoder decodeBoolForKey:@"gamepadOverlayEnabled"] : true; self.gamepadOverlayEnabled = [decoder containsValueForKey:@"gamepadOverlayEnabled"] ? [decoder decodeBoolForKey:@"gamepadOverlayEnabled"] : false;
self.pressureCurvePoints = self.pressureCurvePoints =
[decoder containsValueForKey:@"pressureCurvePoints"] [decoder containsValueForKey:@"pressureCurvePoints"]
+17 -9
View File
@@ -143,6 +143,7 @@ import ObjectiveC.runtime
// for movable buttons during streaming // for movable buttons during streaming
@objc public var relocatedDuringStreaming: Bool = false @objc public var relocatedDuringStreaming: Bool = false
@objc static var relocatedDuringStreaming: Bool = false
// for all touchPad or buttons hybrid with touchPads // for all touchPad or buttons hybrid with touchPads
@objc public var hasMinStickOffset: Bool = false @objc public var hasMinStickOffset: Bool = false
@@ -847,7 +848,7 @@ import ObjectiveC.runtime
case "DISABLETOUCH": case "DISABLETOUCH":
self.nonEditableWidgetLabel = SwiftLocalizationHelper.localizedString(forKey: touchDisabledFLag ? "=EnableTouch" : "=DisableTouch" ) self.nonEditableWidgetLabel = SwiftLocalizationHelper.localizedString(forKey: touchDisabledFLag ? "=EnableTouch" : "=DisableTouch" )
case "GAMEPADOVERLAY": case "GAMEPADOVERLAY":
self.nonEditableWidgetLabel = SwiftLocalizationHelper.localizedString(forKey: gamepadOverlayFLag ? "=GamepadOverlayOn" : "=GamepadOverlayOff" ) self.nonEditableWidgetLabel = SwiftLocalizationHelper.localizedString(forKey: OnScreenWidgetView.gamepadOverlayFLag ? "=GamepadOverlayOn" : "=GamepadOverlayOff" )
default: default:
nonEditableWidgetLabel = "" nonEditableWidgetLabel = ""
} }
@@ -2715,13 +2716,13 @@ import ObjectiveC.runtime
self.functionalButtonDelegate?.toggleTouch(disabled: touchDisabledFLag) self.functionalButtonDelegate?.toggleTouch(disabled: touchDisabledFLag)
} }
@objc var gamepadOverlayFLag:Bool = false @objc static var gamepadOverlayFLag:Bool = false
private func gamepadOverlayButtonUp(){ private func gamepadOverlayButtonUp(){
self.relocatedDuringStreaming = true self.relocatedDuringStreaming = true
gamepadOverlayFLag = !gamepadOverlayFLag OnScreenWidgetView.gamepadOverlayFLag = !OnScreenWidgetView.gamepadOverlayFLag
self.setupAtrributedText() self.setupAtrributedText()
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
self.functionalButtonDelegate?.toggleGamepadOverlay(overlayEnabled: gamepadOverlayFLag) self.functionalButtonDelegate?.toggleGamepadOverlay(overlayEnabled: OnScreenWidgetView.gamepadOverlayFLag)
} }
} }
@@ -3093,6 +3094,7 @@ import ObjectiveC.runtime
private static let autoDockExposedThickness: CGFloat = 22 private static let autoDockExposedThickness: CGFloat = 22
private static let autoDockVerticalInset: CGFloat = 12 private static let autoDockVerticalInset: CGFloat = 12
@objc var autoDockIdleDuration: TimeInterval = 0 @objc var autoDockIdleDuration: TimeInterval = 0
@objc var storedAutoDockIdleDuration: TimeInterval = 0
private static let autoDockInitialAlpha: CGFloat = 0.8 private static let autoDockInitialAlpha: CGFloat = 0.8
@objc var autoDockSettledAlpha: CGFloat = 0.2 @objc var autoDockSettledAlpha: CGFloat = 0.2
private static let autoDockSettledAlphaDelay: TimeInterval = 2 private static let autoDockSettledAlphaDelay: TimeInterval = 2
@@ -3123,8 +3125,8 @@ import ObjectiveC.runtime
private var autoDockDockedCenter: CGPoint? private var autoDockDockedCenter: CGPoint?
private var autoDockDockedToBottomEdge: Bool = false private var autoDockDockedToBottomEdge: Bool = false
private var autoDockDistance: CGFloat = 3 private var autoDockDistance: CGFloat = 3
private var autoDockIsDocked: Bool = false @objc var autoDockIsDocked: Bool = false
private var autoDockEnabled: Bool = false @objc var autoDockEnabled: Bool = false
private var autoDockSettledAlphaTimer: Timer? private var autoDockSettledAlphaTimer: Timer?
private var autoDockOriginalBoundsSize: CGSize = .zero private var autoDockOriginalBoundsSize: CGSize = .zero
@@ -3174,7 +3176,7 @@ import ObjectiveC.runtime
autoDockRestartCountdownIfNeeded() autoDockRestartCountdownIfNeeded()
} }
@objc public func setAutoDockEnabled(_ enabled: Bool) { @objc public func setAutoDock(enabled: Bool) {
autoDockEnabled = enabled autoDockEnabled = enabled
if enabled { if enabled {
OnScreenWidgetView.installAutoDockIfNeeded() OnScreenWidgetView.installAutoDockIfNeeded()
@@ -3212,7 +3214,7 @@ import ObjectiveC.runtime
autoDockRestoreWidget(animated: animated) autoDockRestoreWidget(animated: animated)
} }
private func autoDockStopCountdown() { @objc func autoDockStopCountdown() {
autoDockTimer?.invalidate() autoDockTimer?.invalidate()
autoDockTimer = nil autoDockTimer = nil
} }
@@ -3285,6 +3287,8 @@ import ObjectiveC.runtime
private func autoDockWidgetToNearestEdge() { private func autoDockWidgetToNearestEdge() {
guard autoDockEnabled else {return} guard autoDockEnabled else {return}
autoDockIdleDuration = storedAutoDockIdleDuration
if !folded { if !folded {
restartAutoDockCountdown() restartAutoDockCountdown()
return return
@@ -3342,7 +3346,10 @@ import ObjectiveC.runtime
} }
private func autoDockRestoreWidget(animated: Bool) { private func autoDockRestoreWidget(animated: Bool) {
guard autoDockIsDocked else { return } guard autoDockIsDocked else {
restartAutoDockCountdown()
return
}
let restoreCenter = storedCenter let restoreCenter = storedCenter
OnScreenWidgetView.hasRestoredFromAutoDock = true OnScreenWidgetView.hasRestoredFromAutoDock = true
autoDockStopCountdown() autoDockStopCountdown()
@@ -3475,6 +3482,7 @@ import ObjectiveC.runtime
|| abs(widget.backgroundAlpha) < 0.1){ || abs(widget.backgroundAlpha) < 0.1){
widget.highlightBorder(highlighted: false) widget.highlightBorder(highlighted: false)
} }
if widget.hasNonEditableLabel {widget.setupAtrributedText()}
}) })
} }
} }
+5 -3
View File
@@ -603,7 +603,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
if(!hasFolderStateChanged) hasFolderStateChanged = widget.folded != widget.persistedFolded; if(!hasFolderStateChanged) hasFolderStateChanged = widget.folded != widget.persistedFolded;
} }
if(relocatedWidgetSequences.count == 0 && !hasFolderStateChanged) return; if(relocatedWidgetSequences.count == 0 && !hasFolderStateChanged && !OnScreenWidgetView.relocatedDuringStreaming) return;
NSLog(@"relocatedWidgetSequences.count %lu, hasFolderStateChanged %d %F", relocatedWidgetSequences.count, hasFolderStateChanged, CACurrentMediaTime()); NSLog(@"relocatedWidgetSequences.count %lu, hasFolderStateChanged %d %F", relocatedWidgetSequences.count, hasFolderStateChanged, CACurrentMediaTime());
oscProfileMan = [OSCProfilesManager sharedManager:self->_streamFrameTopLayerView.bounds]; oscProfileMan = [OSCProfilesManager sharedManager:self->_streamFrameTopLayerView.bounds];
@@ -651,6 +651,8 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
if(![self isOnScreenWidgetEnabled]) return; if(![self isOnScreenWidgetEnabled]) return;
OnScreenWidgetView.buttonVisualFeedbackEnabled = self->settings.buttonVisualFeedback; OnScreenWidgetView.buttonVisualFeedbackEnabled = self->settings.buttonVisualFeedback;
OnScreenWidgetView.gamepadOverlayFLag = oscProfile.gamepadOverlayEnabled;
OnScreenWidgetView.relocatedDuringStreaming = false;
bool hasLegacyWidget = false; bool hasLegacyWidget = false;
if(reload && !OnScreenWidgetView.editMode){ if(reload && !OnScreenWidgetView.editMode){
@@ -675,6 +677,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
widgetView.sequenceSet = buttonState.sequenceSet; widgetView.sequenceSet = buttonState.sequenceSet;
widgetView.parentSequence = buttonState.parentSequence; widgetView.parentSequence = buttonState.parentSequence;
widgetView.autoDockIdleDuration = buttonState.autoDockTimer; widgetView.autoDockIdleDuration = buttonState.autoDockTimer;
widgetView.storedAutoDockIdleDuration = widgetView.autoDockIdleDuration;
widgetView.autoDockSettledAlpha = buttonState.dockedAlpha; widgetView.autoDockSettledAlpha = buttonState.dockedAlpha;
widgetView.folded = buttonState.folded; widgetView.folded = buttonState.folded;
widgetView.persistedFolded = buttonState.folded; widgetView.persistedFolded = buttonState.folded;
@@ -723,10 +726,9 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
[widgetView setupAutoTapTimer]; [widgetView setupAutoTapTimer];
[widgetView setupInertialScrollerWithFps:self->settings.framerate.intValue]; [widgetView setupInertialScrollerWithFps:self->settings.framerate.intValue];
widgetView.gamepadOverlayFLag = oscProfile.gamepadOverlayEnabled;
[widgetView setupAtrributedText]; [widgetView setupAtrributedText];
if(widgetView.isFolder && widgetView.parentSequence<0 && widgetView.autoDockIdleDuration>0) [widgetView setAutoDockEnabled:true]; if(widgetView.isFolder && widgetView.parentSequence<0 && widgetView.autoDockIdleDuration>0) [widgetView setAutoDockWithEnabled:true];
if(sequenceGenerated){ if(sequenceGenerated){
// NSLog(@"widgetView.sequence %d %f", widgetView.sequence, CACurrentMediaTime()); // NSLog(@"widgetView.sequence %d %f", widgetView.sequence, CACurrentMediaTime());
+11 -7
View File
@@ -213,16 +213,20 @@ import Foundation
return CGFloat(Double(str) ?? 0) return CGFloat(Double(str) ?? 0)
} }
@available(iOS 13.0, *)
@objc static func isLandscape() -> Bool { @objc static func isLandscape() -> Bool {
guard let windowScene = UIApplication.shared.connectedScenes if #available(iOS 13.0, *) {
.compactMap({ $0 as? UIWindowScene }) guard let windowScene = UIApplication.shared.connectedScenes
.first(where: { $0.activationState == .foregroundActive }) .compactMap({ $0 as? UIWindowScene })
else { .first(where: { $0.activationState == .foregroundActive })
return false else {return false}
return windowScene.interfaceOrientation.isLandscape
} }
else {return GenericUtils.screenWidth > GenericUtils.screenHeight}
}
return windowScene.interfaceOrientation.isLandscape @objc static func viewIsLandscape(_ view: UIView?) -> Bool {
guard let view else {return false}
return view.bounds.width > view.bounds.height
} }
@objc static var screenWidth: CGFloat { @objc static var screenWidth: CGFloat {
@@ -28,7 +28,7 @@
@interface StreamFrameViewController : GCEventViewController <ConnectionCallbacks, ControllerSupportDelegate, UserInteractionDelegate, UIScrollViewDelegate, AVPictureInPictureControllerDelegate> @interface StreamFrameViewController : GCEventViewController <ConnectionCallbacks, ControllerSupportDelegate, UserInteractionDelegate, UIScrollViewDelegate, AVPictureInPictureControllerDelegate>
#else #else
@interface StreamFrameViewController : UIViewController <ConnectionCallbacks, ControllerSupportDelegate, UserInteractionDelegate, UIScrollViewDelegate, ToolboxSpecialEntryDelegate, AVPictureInPictureControllerDelegate, OnScreenFunctionalButtonDelegate> @interface StreamFrameViewController : UIViewController <ConnectionCallbacks, ControllerSupportDelegate, UserInteractionDelegate, UIScrollViewDelegate, ToolboxSpecialEntryDelegate, AVPictureInPictureControllerDelegate, OnScreenFunctionalButtonDelegate, AbstractGamepadOverlayCloseButtonDelegate>
#endif #endif
@property (nonatomic, strong) StreamManager* streamMan; @property (nonatomic, strong) StreamManager* streamMan;
@@ -48,6 +48,7 @@
- (void)setUserInteractionEnabledForStreamView:(bool)enabled; - (void)setUserInteractionEnabledForStreamView:(bool)enabled;
- (bool)shallDisableGyroHotSwitch; - (bool)shallDisableGyroHotSwitch;
- (void)loadGameProfileConfigs:(OSCProfile* )profile; - (void)loadGameProfileConfigs:(OSCProfile* )profile;
- (void)toggleGamepadOverlayWithOverlayEnabled:(BOOL)overlayEnabled API_AVAILABLE(ios(13.0));
- (void)loadAbstractGamepadOverlayIfNeeded API_AVAILABLE(ios(13.0)); - (void)loadAbstractGamepadOverlayIfNeeded API_AVAILABLE(ios(13.0));
@end @end
@@ -45,6 +45,7 @@
- (id)initWithRefreshRate:(float)arg1 videoDynamicRange:(int)arg2; - (id)initWithRefreshRate:(float)arg1 videoDynamicRange:(int)arg2;
@end @end
@implementation StreamFrameViewController { @implementation StreamFrameViewController {
ControllerSupport *_controllerSupport; ControllerSupport *_controllerSupport;
TemporarySettings *_settings; TemporarySettings *_settings;
@@ -1750,6 +1751,16 @@
for(OnScreenWidgetView* widget in OnScreenWidgetView.mapping.allValues){ for(OnScreenWidgetView* widget in OnScreenWidgetView.mapping.allValues){
CGPoint oldCenter = widget.center; CGPoint oldCenter = widget.center;
CGPoint oldStoredCenter = widget.storedCenter; CGPoint oldStoredCenter = widget.storedCenter;
if(widget.autoDockEnabled){
widget.autoDockIdleDuration = fmax(widget.autoDockIdleDuration, 2.0);
[widget autoDockStopCountdown];
}
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^{
if(widget.autoDockEnabled) [widget restoreFromAutoDockWithAnimated:true];
});
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
widget.center = CGPointMake(oldCenter.x * scaleX,oldCenter.y * scaleY); widget.center = CGPointMake(oldCenter.x * scaleX,oldCenter.y * scaleY);
widget.storedCenter = CGPointMake(oldStoredCenter.x * scaleX,oldStoredCenter.y * scaleY); widget.storedCenter = CGPointMake(oldStoredCenter.x * scaleX,oldStoredCenter.y * scaleY);
@@ -1793,6 +1804,9 @@
} }
- (void)toggleGamepadOverlayWithOverlayEnabled:(BOOL)overlayEnabled API_AVAILABLE(ios(13.0)){ - (void)toggleGamepadOverlayWithOverlayEnabled:(BOOL)overlayEnabled API_AVAILABLE(ios(13.0)){
OnScreenWidgetView.gamepadOverlayFLag = overlayEnabled;
OnScreenWidgetView.relocatedDuringStreaming = true;
if(overlayEnabled) [self loadAbstractGamepadOverlayIfNeeded]; if(overlayEnabled) [self loadAbstractGamepadOverlayIfNeeded];
else { else {
[_virtualGamepadOverlay removeFromSuperview]; [_virtualGamepadOverlay removeFromSuperview];
@@ -1819,9 +1833,12 @@
CGRect overlayFrame = CGRectMake(0, 0, standardWidth, standardhHeight); CGRect overlayFrame = CGRectMake(0, 0, standardWidth, standardhHeight);
AbstractGamepadOverlayView *overlayView = [[AbstractGamepadOverlayView alloc] initWithFrame:overlayFrame usesPlayStationFaceButtons:!usesXboxFaceButtons]; AbstractGamepadOverlayView *overlayView = [[AbstractGamepadOverlayView alloc] initWithFrame:overlayFrame usesPlayStationFaceButtons:!usesXboxFaceButtons];
overlayView.closeButtonDelegate = self;
overlayView.center = CGPointMake(self.view.bounds.size.width-standardWidth/2-20, self.view.bounds.size.height-standardhHeight/2-20); overlayView.center = CGPointMake(self.view.bounds.size.width-standardWidth/2-20, self.view.bounds.size.height-standardhHeight/2-20);
overlayView.userInteractionEnabled = YES; overlayView.userInteractionEnabled = YES;
[self.view addSubview:overlayView]; [self.view addSubview:overlayView];
[overlayView registerUserInteraction];
[overlayView scheduleCloseButtonHideIfNeeded];
self->_virtualGamepadOverlay = overlayView; self->_virtualGamepadOverlay = overlayView;
}); });
@@ -83,6 +83,19 @@ import UIKit
//setupConstraints() //setupConstraints()
} }
public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
DispatchQueue.main.asyncAfter(deadline: .now()+0.2) {
self.pinButton.isHidden = !GenericUtils.viewIsLandscape(self.view) && GenericUtils.isIPhone()
}
view.setNeedsUpdateConstraints()
coordinator.animate(alongsideTransition: { _ in
self.view.layoutIfNeeded()
})
}
private func setupViews() { private func setupViews() {
contentView = UIView(frame: self.view.frame) contentView = UIView(frame: self.view.frame)
@@ -129,6 +142,7 @@ import UIKit
contentView.addSubview(editButton) contentView.addSubview(editButton)
// contentView.addSubview(exitButton) // contentView.addSubview(exitButton)
contentView.addSubview(pinButton) contentView.addSubview(pinButton)
pinButton.isHidden = !GenericUtils.isLandscape()
self.view.addSubview(contentView) self.view.addSubview(contentView)
@@ -150,6 +164,18 @@ import UIKit
return UIDevice.current.userInterfaceIdiom == .phone return UIDevice.current.userInterfaceIdiom == .phone
} }
private func contentWidthMultiplier() -> CGFloat {
/*
if isIPhone() && GenericUtils.viewIsLandscape(self.view) {
return 0.85
}*/
return isIPhone()
? (GenericUtils.viewIsLandscape(self.view) ? 0.6 : 0.85)
: (GenericUtils.viewIsLandscape(self.view) ? 0.6 : 0.85)
}
@objc public func setupConstraints() { @objc public func setupConstraints() {
NSLayoutConstraint.deactivate(view.constraints) NSLayoutConstraint.deactivate(view.constraints)
@@ -167,7 +193,7 @@ import UIKit
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
// Set the width and height of the view // Set the width and height of the view
contentView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: isIPhone() ? 0.6 : 0.52), contentView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: contentWidthMultiplier()),
contentView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.93), contentView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.93),
// Set the width and height of the view // Set the width and height of the view
//view.leadingAnchor.constraint(equalTo: view.superview!.leadingAnchor, constant: 60), //view.leadingAnchor.constraint(equalTo: view.superview!.leadingAnchor, constant: 60),