restoring fold/unfold states

This commit is contained in:
True Zhuanjia
2026-02-01 16:02:57 +08:00
parent be69af925a
commit 600b882929
8 changed files with 83 additions and 18 deletions
+1
View File
@@ -31,6 +31,7 @@ typedef NS_ENUM(NSInteger, MapGyroTo) {
@property NSString *name;
@property NSMutableArray <NSData *> *buttonStatesEncoded;
@property (nonatomic, assign) int16_t unfoldedExclusiveFolderSequence;
@property NSSet<NSNumber *> *postExclusiveUnfoldedSequences;
@property BOOL isSelected;
@property (nonatomic, assign) MapGyroTo mapGyroTo;
@property (nonatomic, assign) bool yawPitchToRightStick;
+11
View File
@@ -32,6 +32,7 @@
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeObject:self.buttonStatesEncoded forKey:@"buttonStates"];
[encoder encodeInt32:self.unfoldedExclusiveFolderSequence forKey:@"unfoldedExclusiveFolderSequence"];
[encoder encodeObject:self.postExclusiveUnfoldedSequences forKey:@"postExclusiveUnfoldedSequences"];
[encoder encodeBool:self.isSelected forKey:@"isSelected"];
[encoder encodeInt64:self.mapGyroTo forKey:@"mapGyroTo"];
[encoder encodeBool:self.yawPitchToRightStick forKey:@"yawPitchToRightStick"];
@@ -75,6 +76,15 @@
nil]
forKey:@"buttonStates"];
self.unfoldedExclusiveFolderSequence = [decoder containsValueForKey:@"unfoldedExclusiveFolderSequence"] ? [decoder decodeInt32ForKey:@"unfoldedExclusiveFolderSequence"] : -1;
self.postExclusiveUnfoldedSequences = [decoder containsValueForKey:@"postExclusiveUnfoldedSequences"] ?
[decoder decodeObjectOfClasses:
[NSSet setWithObjects:
[NSMutableSet class],
[NSSet class],
[NSNumber class],
nil]
forKey:@"postExclusiveUnfoldedSequences"] :
[NSSet set];
self.isSelected = [decoder decodeBoolForKey:@"isSelected"];
self.mapGyroTo = [decoder containsValueForKey:@"mapGyroTo"] ? [decoder decodeInt64ForKey:@"mapGyroTo"] : mapGyroToMouse;
self.yawPitchToRightStick = [decoder containsValueForKey:@"yawPitchToRightStick"] ? [decoder decodeBoolForKey:@"yawPitchToRightStick"] : true;
@@ -134,6 +144,7 @@
copy.name = [self.name mutableCopy]; // NSString → NSMutableString
copy.buttonStatesEncoded = [[NSMutableArray alloc] initWithArray:self.buttonStatesEncoded copyItems:YES];
copy.unfoldedExclusiveFolderSequence = self.unfoldedExclusiveFolderSequence;
copy.postExclusiveUnfoldedSequences = [self.postExclusiveUnfoldedSequences copy];
copy.isSelected = self.isSelected;
copy.mapGyroTo = self.mapGyroTo;
copy.yawPitchToRightStick = self.yawPitchToRightStick;
@@ -209,7 +209,7 @@ static CGRect layoutViewBounds;
- (NSMutableArray *) getAllProfiles {
NSData *profilesArrayEncoded = [[NSUserDefaults standardUserDefaults] objectForKey: @"OSCProfiles"]; // Get the encoded array of encoded OSC profiles from persistent storage
NSSet *classes = [NSSet setWithObjects:[NSString class], [NSMutableData class], [NSMutableArray class], [OSCProfile class], [OnScreenButtonState class], nil];
NSMutableArray *profilesEncoded = [NSKeyedUnarchiver unarchivedObjectOfClasses:classes fromData:profilesArrayEncoded error:nil]; // Decode the encoded array itself, NOT the objects contained in the array
/* Decode each of the encoded profiles, place them into an array, then return the array */
@@ -292,6 +292,7 @@ static CGRect layoutViewBounds;
OSCProfile *selectedProfile = [self getSelectedProfile];
selectedProfile.buttonStatesEncoded = buttonStatesEncoded;
selectedProfile.unfoldedExclusiveFolderSequence = OnScreenWidgetView.unfoldedExclusiveFolderSequence;
selectedProfile.postExclusiveUnfoldedSequences = OnScreenWidgetView.postExclusiveUnfoldedSequences;
[self replaceSelectedProfileWith:selectedProfile overwriteDefault:NO];
return true;
}
@@ -481,7 +482,7 @@ static CGRect layoutViewBounds;
- (OnScreenButtonState *)unarchiveButtonStateEncoded:(NSData *)data {
OnScreenButtonState* buttonState;
buttonState = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithObjects:[NSString class], [OnScreenButtonState class], [NSSet class], nil]
buttonState = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithObjects:[NSString class], [OnScreenButtonState class], [NSSet class], [NSNumber class], nil]
fromData:data
error:nil];
return buttonState;
+34 -3
View File
@@ -11,9 +11,13 @@ import SVGKit
@objc class OnScreenWidgetView: UIView {
@objc public static var mapping: [Int16:OnScreenWidgetView] = [:]
@objc public static var isRestoring: Bool = false
@objc public static func set(widget:OnScreenWidgetView, for key:Int16) {
mapping[key] = widget
}
@objc public static func widgetFor(key:Int16) -> OnScreenWidgetView? {
return mapping[key]
}
@objc public static func removeWidgetFromMappings(key:Int16) {
mapping.removeValue(forKey: key)
}
@@ -21,6 +25,12 @@ import SVGKit
mapping.removeAll()
}
@objc public static var unfoldedExclusiveFolderSequence:Int16 = -1
@objc public static var postExclusiveUnfoldedSequences:Set<Int16> = Set()
@objc public static func setPostExclusiveUnfoldeds(_ sequences:NSSet){
let sequenceSet:Set<Int16> = sequences as? Set<Int16> ?? Set()
postExclusiveUnfoldedSequences = Set(sequenceSet)
print("postExclusiveUnfoldedSequences \(postExclusiveUnfoldedSequences) \(CACurrentMediaTime())")
}
@objc public weak var guidelineDelegate: OnScreenWidgetGuidelineUpdateDelegate?
@objc protocol OnScreenWidgetGuidelineUpdateDelegate: AnyObject {
@@ -274,6 +284,7 @@ import SVGKit
private var tickFlag: UInt8 = 0
@objc public var folded: Bool = false
@objc public var persistedFolded: Bool = false
@objc public var revealMode: RevealMode = .coexist
@objc public var sequenceSet: Set<Int16> = Set()
@objc public var parentSequence: Int16 = -1
@@ -2804,6 +2815,7 @@ import SVGKit
setCollection(hidden: folded, for: folder)
if !folded, folder.revealMode == .exclusive {
OnScreenWidgetView.unfoldedExclusiveFolderSequence = folder.sequence
if(!isRestoring) {OnScreenWidgetView.postExclusiveUnfoldedSequences.removeAll()}
let currentRootFolder = OnScreenWidgetView.getRootFolder(of: folder) ?? folder
var offshootRootFolders:Set<OnScreenWidgetView> = Set()
folder.forEachWidget{ widget in
@@ -2822,6 +2834,15 @@ import SVGKit
guard currentRootFolder != folder else {return}
setCollection(hidden: true, for:currentRootFolder, exception: folder, recursive: true)
}
if folded, folder.revealMode == .exclusive {
OnScreenWidgetView.unfoldedExclusiveFolderSequence = -1
}
if folder.revealMode == .coexist {
if folded {
OnScreenWidgetView.postExclusiveUnfoldedSequences.remove(folder.sequence)
}
else {OnScreenWidgetView.postExclusiveUnfoldedSequences.insert(folder.sequence)}
}
}
private static func getRootFolder(of widget:OnScreenWidgetView) -> OnScreenWidgetView?{
@@ -2867,17 +2888,27 @@ import SVGKit
}
@objc static func restoreFoldedStates(){
isRestoring = true
var unfoldedExclusiveFolder: OnScreenWidgetView?
if unfoldedExclusiveFolderSequence != -1 {
unfoldedExclusiveFolder = OnScreenWidgetView.mapping[unfoldedExclusiveFolderSequence]
if(unfoldedExclusiveFolder != nil){
OnScreenWidgetView.set(folded: false, for: unfoldedExclusiveFolder!)
}
}
for widget in OnScreenWidgetView.mapping.values {
OnScreenWidgetView.setCollection(hidden: widget.folded, for: widget, exception: unfoldedExclusiveFolder)
}
if(unfoldedExclusiveFolder != nil){
OnScreenWidgetView.set(folded: false, for: unfoldedExclusiveFolder!)
}
print("postExclusiveUnfoldedSequences \(postExclusiveUnfoldedSequences) stamp \(CACurrentMediaTime())")
for sequence in postExclusiveUnfoldedSequences {
guard let folder = OnScreenWidgetView.mapping[sequence] else { continue }
OnScreenWidgetView.set(folded: false, for: folder)
}
isRestoring = false
}
override func didMoveToSuperview() {
+1 -1
View File
@@ -60,7 +60,7 @@
andConfig:(StreamConfiguration*)streamConfig;
- (void) clearOnScreenWidgets;
- (void) reloadOnScreenWidgetViews:(bool)reload;
- (void) saveRelocatedWidgetViews;
- (void) saveStreamViewWidgetChanges;
- (bool) isOnScreenWidgetEnabled;
- (CGSize) getVideoAreaSize;
+26 -7
View File
@@ -494,19 +494,29 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
else return longSide;
}
- (void)saveRelocatedWidgetViews{
- (void)saveStreamViewWidgetChanges{
/*
NSMutableDictionary* relocatedWidgetDict = [NSMutableDictionary dictionary];
for(UIView* subview in self->_streamFrameTopLayerView.subviews){
if([subview isKindOfClass:[OnScreenWidgetView class]]){
OnScreenWidgetView* widget = (OnScreenWidgetView* )subview;
if(widget.relocatedDuringStreaming) [relocatedWidgetDict setObject:widget forKey:@(widget.sequence)];
OnScreenWidgetView* widget = [OnScreenWidgetView widgetForKey:newButtonState.sequence];
if(widget.isFolder)
}
}
*/
// NSLog(@"relocatedWidgetDict.allKeys.count %d, %f", relocatedWidgetDict.allKeys.count, CACurrentMediaTime());
if(relocatedWidgetDict.allKeys.count == 0) return;
NSMutableSet* relocatedWidgetSequences = [NSMutableSet set];
bool hasFolderStateChanged = false;
for(OnScreenWidgetView* widget in [OnScreenWidgetView.mapping allValues]){
if(widget.relocatedDuringStreaming) [relocatedWidgetSequences addObject:@(widget.sequence)];
if(!hasFolderStateChanged) hasFolderStateChanged = widget.folded != widget.persistedFolded;
}
if(relocatedWidgetSequences.count == 0 && !hasFolderStateChanged) return;
NSLog(@"relocatedWidgetSequences.count %lu, hasFolderStateChanged %d %F", relocatedWidgetSequences.count, hasFolderStateChanged, CACurrentMediaTime());
oscProfileMan = [OSCProfilesManager sharedManager:self->_streamFrameTopLayerView.bounds];
OSCProfile *newProfile = [oscProfileMan getSelectedProfile];
@@ -514,14 +524,20 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
for (NSInteger i = 0; i < newProfile.buttonStatesEncoded.count; i++) {
NSData* buttonStateEncoded = newProfile.buttonStatesEncoded[i];
OnScreenButtonState *newButtonState = [oscProfileMan unarchiveButtonStateEncoded:buttonStateEncoded];
if([relocatedWidgetDict.allKeys containsObject:@(newButtonState.sequence)]){
OnScreenWidgetView* widget = [relocatedWidgetDict objectForKey:@(newButtonState.sequence)];
OnScreenWidgetView* widget = [OnScreenWidgetView.mapping objectForKey:@(newButtonState.sequence)];
NSData *newButtonStateEncoded;
if([relocatedWidgetSequences containsObject:@(widget.sequence)]
|| widget.isFolder){
newButtonState.position = [oscProfileMan normalizeWidgetPosition:widget.storedCenter];
NSData *newButtonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:newButtonState requiringSecureCoding:YES error:nil];
newButtonState.folded = widget.folded;
newButtonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:newButtonState requiringSecureCoding:YES error:nil];
newProfile.buttonStatesEncoded[i] = newButtonStateEncoded;
}
}
newProfile.unfoldedExclusiveFolderSequence = OnScreenWidgetView.unfoldedExclusiveFolderSequence;
newProfile.postExclusiveUnfoldedSequences = OnScreenWidgetView.postExclusiveUnfoldedSequences;
[oscProfileMan replaceSelectedProfileWith:newProfile overwriteDefault:NO];
}
@@ -565,6 +581,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
widgetView.sequenceSet = buttonState.sequenceSet;
widgetView.parentSequence = buttonState.parentSequence;
widgetView.folded = buttonState.folded;
widgetView.persistedFolded = buttonState.folded;
widgetView.revealMode = buttonState.revealMode;
widgetView.translatesAutoresizingMaskIntoConstraints = NO; // weird but this is mandatory, or you will find no key views added to the right place
@@ -643,6 +660,8 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
}
}
OnScreenWidgetView.unfoldedExclusiveFolderSequence = oscProfile.unfoldedExclusiveFolderSequence;
[OnScreenWidgetView setPostExclusiveUnfoldeds:oscProfile.postExclusiveUnfoldedSequences];
[OnScreenWidgetView restoreFoldedStates];
NSLog(@"hasLegacyWidget %d %f", hasLegacyWidget, CACurrentMediaTime());
@@ -208,6 +208,8 @@ typedef NS_ENUM(NSUInteger, DecelerationRateSliderMode) {
// if(widget.isFolder) [OnScreenWidgetView setWithFolded:widget.folded for:widget];
}
OnScreenWidgetView.unfoldedExclusiveFolderSequence = oscProfile.unfoldedExclusiveFolderSequence;
[OnScreenWidgetView setPostExclusiveUnfoldeds:oscProfile.postExclusiveUnfoldedSequences];
[OnScreenWidgetView restoreFoldedStates];
NSLog(@"reloadOnScreenWidgets hasLegacyWidget %d %f", hasLegacyWidget, CACurrentMediaTime());
if(hasLegacyWidget) [self reloadLegacyOnScreenControls:oscProfile];
@@ -761,7 +761,7 @@
}
- (void)openWidgetLayoutTool{
[_streamView saveRelocatedWidgetViews];
[_streamView saveStreamViewWidgetChanges];
_streamView.widgetToolOpened = true;
[self->_streamView disableOnScreenControls];
[self->_streamView clearOnScreenWidgets]; // clear all onScreenKeyboardButtons before entering edit mode
@@ -772,7 +772,7 @@
}
- (void)openWidgetProfileTableWithPickProfile:(BOOL)pickProfile{
[_streamView saveRelocatedWidgetViews];
[_streamView saveStreamViewWidgetChanges];
_streamView.widgetToolOpened = true;
[self->_streamView disableOnScreenControls];
[self->_streamView clearOnScreenWidgets]; // clear all onScreenKeyboardButtons before entering edit mode
@@ -1066,7 +1066,7 @@
//sleep(1);
appDidEnterBackgroundWithoutPip = true;
[_streamView saveRelocatedWidgetViews];
[_streamView saveStreamViewWidgetChanges];
#if !TARGET_OS_TV
#endif
@@ -1150,7 +1150,7 @@
- (void)expandSettingsView{
self.mainFrameViewcontroller.settingsExpandedInStreamView = true; //notify mainFrameViewContorller that this is a setting expansion in stream view, some settings shall be disabled.
[_streamView saveRelocatedWidgetViews];
[_streamView saveStreamViewWidgetChanges];
[self.mainFrameViewcontroller expandSettingsView];
}
@@ -1675,7 +1675,7 @@
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[self->_streamView saveRelocatedWidgetViews];
[self->_streamView saveStreamViewWidgetChanges];
if (_isRestoringFromPiP) {
Log(LOG_I, @"View size changed during PiP restore, skipping redundant reconfiguration.");