From b12aee53d3837107f222f88a44697f68ddf9ad39 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 22 Jun 2025 19:47:19 +0200 Subject: [PATCH 01/10] fixed: subview's window setting mus happen BEFORE _setSuperview --- AppKit/CPView.j | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/AppKit/CPView.j b/AppKit/CPView.j index df06eb78b..c266b5bda 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -637,8 +637,20 @@ var CPViewHighDPIDrawingEnabled = YES; // Remove the view from its previous superview. [aSubview _removeFromSuperview]; + // _setSuperview: posts the _didAppear notification, + // but the window is set *after* that call. The fix is to set the + // window property *before* calling _setSuperview:. + [aSubview _postViewWillAppearNotification]; - // Set ourselves as the superview. + + // Set the subview's window to our own. This must happen BEFORE _setSuperview + // so that the window is available during the _didAppear notification. + if (_window) + [aSubview _setWindow:_window]; + else if (lastWindow) // if we're moving out of a windowed view to a non-windowed view. + [aSubview _setWindow:nil]; + + // Set ourselves as the superview. This will post the _didAppear notification. [aSubview _setSuperview:self]; } @@ -666,13 +678,6 @@ var CPViewHighDPIDrawingEnabled = YES; [aSubview viewDidMoveToSuperview]; - // Set the subview's window to our own. - if (_window) - [aSubview _setWindow:_window]; - - if (!_window && lastWindow) - [aSubview _setWindow:nil]; - // This method might be called before we are fully unarchived, in which case the theme state isn't set up yet // and none of the below matters anyhow. if (_themeState) From 1f24e89c1b189988e9c4f82eb5fc6c6fe5e62ba2 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 22 Jun 2025 20:35:11 +0200 Subject: [PATCH 02/10] fixed wrong logic --- AppKit/CPView.j | 87 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 24 deletions(-) diff --git a/AppKit/CPView.j b/AppKit/CPView.j index c266b5bda..299b7fc77 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -637,21 +637,10 @@ var CPViewHighDPIDrawingEnabled = YES; // Remove the view from its previous superview. [aSubview _removeFromSuperview]; - // _setSuperview: posts the _didAppear notification, - // but the window is set *after* that call. The fix is to set the - // window property *before* calling _setSuperview:. - + // Defer didAppear notification by changing the _setSuperview call. [aSubview _postViewWillAppearNotification]; - - // Set the subview's window to our own. This must happen BEFORE _setSuperview - // so that the window is available during the _didAppear notification. - if (_window) - [aSubview _setWindow:_window]; - else if (lastWindow) // if we're moving out of a windowed view to a non-windowed view. - [aSubview _setWindow:nil]; - - // Set ourselves as the superview. This will post the _didAppear notification. - [aSubview _setSuperview:self]; + // Set ourselves as the superview. + [aSubview _setSuperview:self sendDidAppear:NO]; } if (anIndex === CPNotFound || anIndex >= count) @@ -678,6 +667,12 @@ var CPViewHighDPIDrawingEnabled = YES; [aSubview viewDidMoveToSuperview]; + // Set the subview's window to our own. This now respects the call order. + if (_window) + [aSubview _setWindow:_window]; + else if (lastWindow) + [aSubview _setWindow:nil]; + // This method might be called before we are fully unarchived, in which case the theme state isn't set up yet // and none of the below matters anyhow. if (_themeState) @@ -694,6 +689,10 @@ var CPViewHighDPIDrawingEnabled = YES; } [self didAddSubview:aSubview]; + + // Post didAppear notification for the subtree now that it's fully configured. + if (aSubview._superview) + [aSubview _postDidAppearRecursively]; } /*! @@ -832,7 +831,17 @@ var CPViewHighDPIDrawingEnabled = YES; } /* @ignore */ -- (void)_setWindow:(CPWindow)aWindow +- (void)_willMoveToWindow:(CPWindow)aWindow +{ + [self viewWillMoveToWindow:aWindow]; + + var count = _subviews.length; + while(count--) + [_subviews[count] _willMoveToWindow:aWindow]; +} + +/* @ignore */ +- (void)_propagateWindowAndSetup:(CPWindow)aWindow { [[self window] _dirtyKeyViewLoop]; @@ -840,9 +849,6 @@ var CPViewHighDPIDrawingEnabled = YES; if ([_window firstResponder] === self && _window != aWindow) [_window makeFirstResponder:nil]; - // Notify the view and its subviews - [self viewWillMoveToWindow:aWindow]; - // Unregister the drag events from the current window and register // them in the new window. if (_registeredDraggedTypes) @@ -878,20 +884,40 @@ var CPViewHighDPIDrawingEnabled = YES; var count = [_subviews count]; while (count--) - [_subviews[count] _setWindow:aWindow]; + [_subviews[count] _propagateWindowAndSetup:aWindow]; if ([_window isKeyWindow]) [self setThemeState:CPThemeStateKeyWindow]; else [self unsetThemeState:CPThemeStateKeyWindow]; - [self viewDidMoveToWindow]; - [self _manageToolTipInstallation]; [[self window] _dirtyKeyViewLoop]; } +/* @ignore */ +- (void)_didMoveToWindow +{ + [self viewDidMoveToWindow]; + + var count = _subviews.length; + while(count--) + [_subviews[count] _didMoveToWindow]; +} + +/* @ignore */ +- (void)_setWindow:(CPWindow)aWindow +{ + if (_window === aWindow) + return; + + [self _willMoveToWindow:aWindow]; + [self _propagateWindowAndSetup:aWindow]; + [self _didMoveToWindow]; +} + + /*! Returns \c YES if the receiver is, or is a descendant of, \c aView. @param aView the view to test for ancestry @@ -1699,6 +1725,14 @@ var CPViewHighDPIDrawingEnabled = YES; [[CPNotificationCenter defaultCenter] postNotificationName:_CPViewDidAppearNotification object:self userInfo:nil]; } +- (void)_postDidAppearRecursively +{ + [self _postViewDidAppearNotification]; + var count = _subviews.length; + while(count--) + [_subviews[count] _postDidAppearRecursively]; +} + - (void)_postViewWillDisappearNotification { [[CPNotificationCenter defaultCenter] postNotificationName:_CPViewWillDisappearNotification object:self userInfo:nil]; @@ -1709,7 +1743,7 @@ var CPViewHighDPIDrawingEnabled = YES; [[CPNotificationCenter defaultCenter] postNotificationName:_CPViewDidDisappearNotification object:self userInfo:nil]; } -- (void)_setSuperview:(CPView)aSuperview +- (void)_setSuperview:(CPView)aSuperview sendDidAppear:(BOOL)sendDidAppear { var hasOldSuperview = (_superview != nil), hasNewSuperview = (aSuperview != nil), @@ -1727,10 +1761,15 @@ var CPViewHighDPIDrawingEnabled = YES; if (hasOldSuperview) [self _postViewDidDisappearNotification]; - if (hasNewSuperview) + if (hasNewSuperview && sendDidAppear) [self _postViewDidAppearNotification]; } +- (void)_setSuperview:(CPView)aSuperview +{ + [self _setSuperview:aSuperview sendDidAppear:YES]; +} + - (void)_recursiveLostHiddenAncestor { if (_isHiddenOrHasHiddenAncestor) @@ -3639,7 +3678,7 @@ var CPAppearanceVibrantDark = [CPAppearance appearanceNamed:CPAppearanceNameVibr return; if (![_trackingAreas containsObjectIdenticalTo:trackingArea]) - [CPException raise:CPInternalInconsistencyException reason:"Trying to remove unreferenced trackingArea"]; + [CPException raise:CPInternalInconsistencyException reason:"Trying to remove unreferenced tracking area"]; [self _removeTrackingArea:trackingArea]; } From af61bcb93f787a0b6b7dbd534ed3404b10688236 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 22 Jun 2025 20:45:55 +0200 Subject: [PATCH 03/10] fixed: wrong logic --- AppKit/CPView.j | 104 +++++++++++++++++------------------------------- 1 file changed, 36 insertions(+), 68 deletions(-) diff --git a/AppKit/CPView.j b/AppKit/CPView.j index 299b7fc77..0e5dc1214 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -592,72 +592,39 @@ var CPViewHighDPIDrawingEnabled = YES; { if (aSubview === self) [CPException raise:CPInvalidArgumentException reason:"can't add a view as a subview of itself"]; -#if DEBUG - if (!aSubview._superview && _subviews.indexOf(aSubview) !== CPNotFound) - [CPException raise:CPInvalidArgumentException reason:"can't insert a subview in duplicate (probably partially decoded)"]; -#endif - // Notify the subview that it will be moving. - [aSubview viewWillMoveToSuperview:self]; - - // We will have to adjust the z-index of all views starting at this index. - var count = _subviews.length, - lastWindow; - - // Dirty the key view loop, in case the window wants to auto recalculate it - [[self window] _dirtyKeyViewLoop]; - - // If this is already one of our subviews, remove it. + // It's a no-op move, so return. if (aSubview._superview === self) { - var index = [_subviews indexOfObjectIdenticalTo:aSubview]; - - // FIXME: should this be anIndex >= count? (last one) - if (index === anIndex || index === count - 1 && anIndex === count) + var currentIndex = [_subviews indexOfObjectIdenticalTo:aSubview]; + var count = _subviews.length; + if (currentIndex === anIndex || (currentIndex === count - 1 && (anIndex === CPNotFound || anIndex === count))) return; - - [_subviews removeObjectAtIndex:index]; - -#if PLATFORM(DOM) - CPDOMDisplayServerRemoveChild(_DOMElement, aSubview._DOMElement); -#endif - - if (anIndex > index) - --anIndex; - - //We've effectively made the subviews array shorter, so represent that. - --count; } - else - { - var superview = aSubview._superview; - lastWindow = [superview window]; + [aSubview viewWillMoveToSuperview:self]; - // Remove the view from its previous superview. + if (aSubview._superview) [aSubview _removeFromSuperview]; - // Defer didAppear notification by changing the _setSuperview call. - [aSubview _postViewWillAppearNotification]; - // Set ourselves as the superview. - [aSubview _setSuperview:self sendDidAppear:NO]; - } + [[self window] _dirtyKeyViewLoop]; + + [aSubview _postViewWillAppearNotification]; + [aSubview _setSuperview:self sendDidAppear:NO]; + + var count = _subviews.length; if (anIndex === CPNotFound || anIndex >= count) { _subviews.push(aSubview); - #if PLATFORM(DOM) - // Attach the actual node. CPDOMDisplayServerAppendChild(_DOMElement, aSubview._DOMElement); #endif } else { _subviews.splice(anIndex, 0, aSubview); - #if PLATFORM(DOM) - // Attach the actual node. CPDOMDisplayServerInsertBefore(_DOMElement, aSubview._DOMElement, _subviews[anIndex + 1]._DOMElement); #endif } @@ -667,14 +634,9 @@ var CPViewHighDPIDrawingEnabled = YES; [aSubview viewDidMoveToSuperview]; - // Set the subview's window to our own. This now respects the call order. if (_window) [aSubview _setWindow:_window]; - else if (lastWindow) - [aSubview _setWindow:nil]; - // This method might be called before we are fully unarchived, in which case the theme state isn't set up yet - // and none of the below matters anyhow. if (_themeState) { if ([self hasThemeState:CPThemeStateFirstResponder]) @@ -689,8 +651,7 @@ var CPViewHighDPIDrawingEnabled = YES; } [self didAddSubview:aSubview]; - - // Post didAppear notification for the subtree now that it's fully configured. + if (aSubview._superview) [aSubview _postDidAppearRecursively]; } @@ -709,14 +670,9 @@ var CPViewHighDPIDrawingEnabled = YES; */ - (void)removeFromSuperview { - var superview = _superview; - [self viewWillMoveToSuperview:nil]; [self _removeFromSuperview]; [self viewDidMoveToSuperview]; - - if (superview) - [self _setWindow:nil]; } - (void)_removeFromSuperview @@ -724,24 +680,30 @@ var CPViewHighDPIDrawingEnabled = YES; if (!_superview) return; + var oldWindow = [self window]; + // Dirty the key view loop, in case the window wants to auto recalculate it - [[self window] _dirtyKeyViewLoop]; + [oldWindow _dirtyKeyViewLoop]; [_superview willRemoveSubview:self]; [self _postViewWillDisappearNotification]; + // Detach from window BEFORE detaching from superview + if (oldWindow) + [self _setWindow:nil]; + [_superview._subviews removeObjectIdenticalTo:self]; #if PLATFORM(DOM) CPDOMDisplayServerRemoveChild(_superview._DOMElement, _DOMElement); #endif - // If the view is not hidden and one of its ancestors is hidden, - // notify the view that it is now unhidden. - [self _setSuperview:nil]; + [self _setSuperview:nil sendDidAppear:NO]; [self _notifyWindowDidResignKey]; [self _notifyViewDidResignFirstResponder]; + + [self _postDidDisappearRecursively]; } /*! @@ -899,11 +861,11 @@ var CPViewHighDPIDrawingEnabled = YES; /* @ignore */ - (void)_didMoveToWindow { - [self viewDidMoveToWindow]; - var count = _subviews.length; while(count--) [_subviews[count] _didMoveToWindow]; + + [self viewDidMoveToWindow]; } /* @ignore */ @@ -1743,6 +1705,15 @@ var CPViewHighDPIDrawingEnabled = YES; [[CPNotificationCenter defaultCenter] postNotificationName:_CPViewDidDisappearNotification object:self userInfo:nil]; } +- (void)_postDidDisappearRecursively +{ + [self _postViewDidDisappearNotification]; + var count = _subviews.length; + + while(count--) + [_subviews[count] _postDidDisappearRecursively]; +} + - (void)_setSuperview:(CPView)aSuperview sendDidAppear:(BOOL)sendDidAppear { var hasOldSuperview = (_superview != nil), @@ -1758,11 +1729,8 @@ var CPViewHighDPIDrawingEnabled = YES; _superview = aSuperview; - if (hasOldSuperview) - [self _postViewDidDisappearNotification]; - - if (hasNewSuperview && sendDidAppear) - [self _postViewDidAppearNotification]; + // Disappearance is handled by _removeFromSuperview now + // Appearance is handled by _insertSubview } - (void)_setSuperview:(CPView)aSuperview From 64cc36a1acc8c3cf96242191831765d88812ce8a Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 22 Jun 2025 21:06:47 +0200 Subject: [PATCH 04/10] new logic --- AppKit/CPView.j | 223 +++++++++--------------------------------------- 1 file changed, 41 insertions(+), 182 deletions(-) diff --git a/AppKit/CPView.j b/AppKit/CPView.j index 0e5dc1214..a3a3a34f5 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -593,27 +593,17 @@ var CPViewHighDPIDrawingEnabled = YES; if (aSubview === self) [CPException raise:CPInvalidArgumentException reason:"can't add a view as a subview of itself"]; - // It's a no-op move, so return. - if (aSubview._superview === self) - { - var currentIndex = [_subviews indexOfObjectIdenticalTo:aSubview]; - var count = _subviews.length; - if (currentIndex === anIndex || (currentIndex === count - 1 && (anIndex === CPNotFound || anIndex === count))) - return; - } - - [aSubview viewWillMoveToSuperview:self]; - - if (aSubview._superview) - [aSubview _removeFromSuperview]; + // If a view is being moved, it must be fully removed from its old hierarchy first. + // This ensures all disappearance and detachment notifications are sent correctly. + if ([aSubview superview]) + [aSubview removeFromSuperview]; [[self window] _dirtyKeyViewLoop]; + [aSubview viewWillMoveToSuperview:self]; [aSubview _postViewWillAppearNotification]; - [aSubview _setSuperview:self sendDidAppear:NO]; var count = _subviews.length; - if (anIndex === CPNotFound || anIndex >= count) { _subviews.push(aSubview); @@ -629,9 +619,8 @@ var CPViewHighDPIDrawingEnabled = YES; #endif } + [aSubview _setSuperview:self sendDidAppear:NO]; [aSubview setNextResponder:self]; - [aSubview _scaleSizeUnitSquareToSize:[self _hierarchyScaleSize]]; - [aSubview viewDidMoveToSuperview]; if (_window) @@ -650,9 +639,11 @@ var CPViewHighDPIDrawingEnabled = YES; [aSubview _notifyWindowDidResignKey]; } + [aSubview _scaleSizeUnitSquareToSize:[self _hierarchyScaleSize]]; + [self didAddSubview:aSubview]; - if (aSubview._superview) + if ([aSubview superview]) [aSubview _postDidAppearRecursively]; } @@ -670,9 +661,7 @@ var CPViewHighDPIDrawingEnabled = YES; */ - (void)removeFromSuperview { - [self viewWillMoveToSuperview:nil]; [self _removeFromSuperview]; - [self viewDidMoveToSuperview]; } - (void)_removeFromSuperview @@ -680,30 +669,33 @@ var CPViewHighDPIDrawingEnabled = YES; if (!_superview) return; + var oldSuperview = _superview; var oldWindow = [self window]; - // Dirty the key view loop, in case the window wants to auto recalculate it - [oldWindow _dirtyKeyViewLoop]; + [self viewWillMoveToSuperview:nil]; - [_superview willRemoveSubview:self]; - [self _postViewWillDisappearNotification]; - - // Detach from window BEFORE detaching from superview + // Detach from window hierarchy first if (oldWindow) [self _setWindow:nil]; - [_superview._subviews removeObjectIdenticalTo:self]; + [self _postViewWillDisappearNotification]; + // Detach from view hierarchy + [oldSuperview willRemoveSubview:self]; + [oldSuperview._subviews removeObjectIdenticalTo:self]; #if PLATFORM(DOM) - CPDOMDisplayServerRemoveChild(_superview._DOMElement, _DOMElement); + CPDOMDisplayServerRemoveChild(oldSuperview._DOMElement, _DOMElement); #endif [self _setSuperview:nil sendDidAppear:NO]; + [self setNextResponder:nil]; - [self _notifyWindowDidResignKey]; [self _notifyViewDidResignFirstResponder]; + [self _notifyWindowDidResignKey]; [self _postDidDisappearRecursively]; + + [self viewDidMoveToSuperview]; } /*! @@ -718,9 +710,9 @@ var CPViewHighDPIDrawingEnabled = YES; var index = [_subviews indexOfObjectIdenticalTo:aSubview]; - [self _insertSubview:aView atIndex:index]; - + // This now correctly handles the full remove/add cycle. [aSubview removeFromSuperview]; + [self _insertSubview:aView atIndex:index]; } - (void)setSubviews:(CPArray)newSubviews @@ -732,64 +724,30 @@ var CPViewHighDPIDrawingEnabled = YES; if ([_subviews isEqual:newSubviews]) return; - // Trivial Case 1: No current subviews, simply add all new subviews. - if ([_subviews count] === 0) - { - var index = 0, - count = [newSubviews count]; - - for (; index < count; ++index) - [self addSubview:newSubviews[index]]; - - return; - } - - // Trivial Case 2: No new subviews, simply remove all current subviews. - if ([newSubviews count] === 0) - { - var count = [_subviews count]; - - while (count--) - [_subviews[count] removeFromSuperview]; - - return; - } - // Find out the views that were removed. var removedSubviews = [CPMutableSet setWithArray:_subviews]; - [removedSubviews removeObjectsInArray:newSubviews]; [removedSubviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; - // Find out which views need to be added. - var addedSubviews = [CPMutableSet setWithArray:newSubviews]; - - [addedSubviews removeObjectsInArray:_subviews]; - - var addedSubview = nil, - addedSubviewEnumerator = [addedSubviews objectEnumerator]; - - while ((addedSubview = [addedSubviewEnumerator nextObject]) != nil) - [self addSubview:addedSubview]; - - // If the order is fine, no need to reorder. - if ([_subviews isEqual:newSubviews]) - return; - - _subviews = [newSubviews copy]; - -#if PLATFORM(DOM) - var index = 0, - count = [_subviews count]; - - for (; index < count; ++index) + // Find out which views need to be added or reordered. + for (var i = 0, count = [newSubviews count]; i < count; ++i) { - var subview = _subviews[index]; - - CPDOMDisplayServerRemoveChild(_DOMElement, subview._DOMElement); - CPDOMDisplayServerAppendChild(_DOMElement, subview._DOMElement); - } + var subview = newSubviews[i]; + if ([subview superview] !== self) + [self _insertSubview:subview atIndex:i]; + else { + // Reorder existing subview + var currentIndex = [_subviews indexOfObjectIdenticalTo:subview]; + if (currentIndex !== i) { + [_subviews removeObjectAtIndex:currentIndex]; + _subviews.splice(i, 0, subview); +#if PLATFORM(DOM) + CPDOMDisplayServerRemoveChild(_DOMElement, subview._DOMElement); + CPDOMDisplayServerInsertBefore(_DOMElement, subview._DOMElement, (i + 1 < [_subviews count]) ? _subviews[i + 1]._DOMElement : null); #endif + } + } + } } /* @ignore */ @@ -984,13 +942,6 @@ var CPViewHighDPIDrawingEnabled = YES; return view._menuItem; return nil; -/* var view = self, - enclosingMenuItem = _enclosingMenuItem; - - while (!enclosingMenuItem && (view = view._enclosingMenuItem)) - view = [view superview]; - - return enclosingMenuItem;*/ } - (void)setTag:(CPInteger)aTag @@ -1709,7 +1660,6 @@ var CPViewHighDPIDrawingEnabled = YES; { [self _postViewDidDisappearNotification]; var count = _subviews.length; - while(count--) [_subviews[count] _postDidDisappearRecursively]; } @@ -1728,9 +1678,6 @@ var CPViewHighDPIDrawingEnabled = YES; [self _recursiveGainedHiddenAncestor]; _superview = aSuperview; - - // Disappearance is handled by _removeFromSuperview now - // Appearance is handled by _insertSubview } - (void)_setSuperview:(CPView)aSuperview @@ -2137,9 +2084,6 @@ var CPViewHighDPIDrawingEnabled = YES; var image = slices[i]; - // // If image was nil, size should have been nil too. - // assert(image != nil); - CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, size.height); _DOMImageParts[partIndex].style.background = "url(\"" + [image filename] + "\")"; @@ -2227,7 +2171,6 @@ var CPViewHighDPIDrawingEnabled = YES; partIndex = 0; - // Make sure to repeat the top and bottom pieces horizontally if they're not the exact width needed. if (top) { CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", top + "px"); @@ -2238,8 +2181,6 @@ var CPViewHighDPIDrawingEnabled = YES; if (_DOMImageSizes[1]) { var height = frameSize.height - top - bottom; - - //_DOMImageParts[partIndex].style.backgroundSize = frameSize.width + "px " + height + "px"; CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", height + "px"); CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, top); CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, height); @@ -2259,7 +2200,6 @@ var CPViewHighDPIDrawingEnabled = YES; partIndex = 0; - // Make sure to repeat the left and right pieces vertically if they're not the exact height needed. if (left) { CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], left + "px", frameSize.height + "px"); @@ -3602,19 +3542,11 @@ var CPAppearanceVibrantDark = [CPAppearance appearanceNamed:CPAppearanceNameVibr _currentAppearance = effectiveAppearance; } -// var start = [CPDate new]; - for (var i = 0, size = [_subviews count]; i < size; i++) { [[_subviews objectAtIndex:i] _recomputeAppearanceWithSuperviewEffectiveAppearance:effectiveAppearance]; } -// [_subviews makeObjectsPerformSelector:@selector(_recomputeAppearance)]; - -/* var now = [CPDate new]; - var elapsedSeconds = [now timeIntervalSinceReferenceDate] - [start timeIntervalSinceReferenceDate]; - - CPLog.trace(@"_recomputeAppearance " + [_subviews count] + " subviews in " + elapsedSeconds + @" seconds"); -*/} +} @end @@ -3653,20 +3585,6 @@ var CPAppearanceVibrantDark = [CPAppearance appearanceNamed:CPAppearanceNameVibr /*! Invoked automatically when the view’s geometry changes such that its tracking areas need to be recalculated. - - You should override this method to remove out of date tracking areas, add recomputed tracking areas and then call super; - - Cocoa calls this on every view, whereas they have tracking area(s) or not. - Cappuccino behaves differently : - - updateTrackingAreas is called during initWithFrame - - updateTrackingAreas is also called when placing a view in the view hierarchy (that is in a window) - - if you have only CPTrackingInVisibleRect tracking areas attached to a view, it will not be called again (until you move the view in the hierarchy) - - if you have at least one non-CPTrackingInVisibleRect tracking area attached, it will be called every time the view geometry could be modified - You don't have to touch to CPTrackingInVisibleRect tracking areas, they will be automatically updated - - Please note that it is the owner of a tracking area who is called for updateTrackingAreas. - But, if a view without any tracking area is inserted in the view hierarchy (that is, in a window), the view is called for updateTrackingAreas. - This enables you to use updateTrackingArea to initially attach your tracking areas to the view. */ - (void)updateTrackingAreas { @@ -3691,8 +3609,6 @@ var CPAppearanceVibrantDark = [CPAppearance appearanceNamed:CPAppearanceNameVibr /*! The referencingSuperViewVisibleRect is used to speed up the execution of this as the visibleRect method is a heavy operation. - It has to go up the view hierarchy and get every superviews visibleRect to transform and intersect them together. - Here we keep the superviews visible rect when we are going down the view hierarchy to make the operation much faster. */ - (void)_updateTrackingAreasWithRecursion:(BOOL)shouldCallRecursively withReferencingSuperViewVisibleRect:(CGRect)referencingSuperViewVisibleRect { @@ -3700,8 +3616,6 @@ var CPAppearanceVibrantDark = [CPAppearance appearanceNamed:CPAppearanceNameVibr if (shouldCallRecursively) { - // Now, call _updateTrackingAreasWithRecursion on subviews - for (var i = 0; i < _subviews.length; i++) [_subviews[i] _updateTrackingAreasWithRecursion:YES withReferencingSuperViewVisibleRect:[self _visibleRectWithSuperviewVisibleRect:referencingSuperViewVisibleRect]]; } @@ -3714,10 +3628,6 @@ var CPAppearanceVibrantDark = [CPAppearance appearanceNamed:CPAppearanceNameVibr - (CPArray)_calcTrackingAreaOwnersWithReferencingSuperViewVisibleRect:(CGRect)referencingSuperViewVisibleRect { - // First search all owners that must be notified - // Remark: 99.99% of time, the only owner will be the view itself - // In the same time, update the rects of InVisibleRect tracking areas - var owners = []; for (var i = 0; i < _trackingAreas.length; i++) @@ -3783,21 +3693,14 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", /*! Initializes the view from an archive. - @param aCoder the coder from which to initialize - @return the initialized view */ - (id)initWithCoder:(CPCoder)aCoder { - // We create the DOMElement "early" because there is a chance that we - // will decode our superview before we are done decoding, at which point - // we have to have an element to place in the tree. Perhaps there is - // a more "elegant" way to do this...? #if PLATFORM(DOM) _DOMElement = DOMElementPrototype.cloneNode(false); AppKitTagDOMElement(self, _DOMElement); #endif - // Also decode these "early". _frame = [aCoder decodeRectForKey:CPViewFrameKey]; _bounds = [aCoder decodeRectForKey:CPViewBoundsKey]; _scaleSize = [aCoder containsValueForKey:CPViewScaleKey] ? [aCoder decodeSizeForKey:CPViewScaleKey] : CGSizeMake(1.0, 1.0); @@ -3805,7 +3708,6 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", _isScaled = [aCoder containsValueForKey:CPViewIsScaledKey] ? [aCoder decodeBoolForKey:CPViewIsScaledKey] : NO; _subviews = @[]; - // Trying to fix "not ready" views _trackingAreas = [aCoder decodeObjectForKey:CPViewTrackingAreasKey] || @[]; [self _decodeThemeObjectsWithCoder:aCoder]; @@ -3815,30 +3717,23 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", if (self) { - // We have to manually check because it may be 0, so we can't use || _tag = [aCoder containsValueForKey:CPViewTagKey] ? [aCoder decodeIntForKey:CPViewTagKey] : -1; _identifier = [aCoder decodeObjectForKey:CPReuseIdentifierKey]; _window = [aCoder decodeObjectForKey:CPViewWindowKey]; _superview = [aCoder decodeObjectForKey:CPViewSuperviewKey]; - // We have to manually add the subviews so that they will receive - // viewWillMoveToSuperview: and viewDidMoveToSuperview: var subviews = [aCoder decodeObjectForKey:CPViewSubviewsKey] || []; for (var i = 0, count = [subviews count]; i < count; ++i) { - // addSubview won't do anything if the superview is already self, so clear it subviews[i]._superview = nil; [self addSubview:subviews[i]]; } - // FIXME: Should we encode/decode this? _registeredDraggedTypes = [CPSet set]; _registeredDraggedTypesArray = []; - // Other views (CPBox) might set an autoresizes mask on their subviews before it is actually decoded. - // We make sure we don't override the value by checking if it was already set. if (_autoresizingMask == nil) _autoresizingMask = [aCoder decodeIntForKey:CPViewAutoresizingMaskKey] || CPViewNotSizable; @@ -3851,7 +3746,6 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", if (_toolTip) [self _installToolTipEventHandlers]; - // DOM SETUP #if PLATFORM(DOM) _cssStylePreviousState = @[]; @@ -3867,7 +3761,6 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", for (; index < count; ++index) { CPDOMDisplayServerAppendChild(_DOMElement, _subviews[index]._DOMElement); - //_subviews[index]._superview = self; } #endif @@ -3883,7 +3776,6 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", [self _setupViewFlags]; [self setAppearance:[aCoder decodeObjectForKey:CPViewAppearanceKey]]; - // Set the current appearance to something that can't be the correct one so it will recalculate it at the first layout. _currentAppearance = _frame; [self updateTrackingAreas]; @@ -3896,7 +3788,6 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", /*! Archives the view to a coder. - @param aCoder the object into which the view's data will be archived. */ - (void)encodeWithCoder:(CPCoder)aCoder { @@ -3908,7 +3799,6 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", [aCoder encodeRect:_frame forKey:CPViewFrameKey]; [aCoder encodeRect:_bounds forKey:CPViewBoundsKey]; - // This will come out nil on the other side with decodeObjectForKey: if (_window != nil) [aCoder encodeConditionalObject:_window forKey:CPViewWindowKey]; @@ -3927,7 +3817,6 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", if (encodedSubviews.length > 0) [aCoder encodeObject:encodedSubviews forKey:CPViewSubviewsKey]; - // This will come out nil on the other side with decodeObjectForKey: if (_superview != nil) [aCoder encodeConditionalObject:_superview forKey:CPViewSuperviewKey]; @@ -3994,9 +3883,6 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) { var view = fromView; - // FIXME: This doesn't handle the case when the outside views are equal. - // If we have a fromView, "climb up" the view tree until - // we hit the root node or we hit the toLayer. while (view && view != toView) { var frame = view._frame; @@ -4026,7 +3912,6 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) view = view._superview; } - // If we hit toView, then we're done. if (view === toView) { return transform; @@ -4041,7 +3926,6 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) } } - // FIXME: For now we can do things this way, but eventually we need to do them the "hard" way. var view = toView, transform2 = CGAffineTransformMakeIdentity(); @@ -4049,7 +3933,6 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) { var frame = CGRectMakeCopy(view._frame); - // FIXME : For now we don't care about rotate transform and so on if (view._isScaled) { transform2.a *= 1 / view._scaleSize.width; @@ -4076,34 +3959,10 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) if (view === fromView) { - // toView is inside of fromView return transform2; } CGAffineTransformConcatTo(transform, transform2, transform); return transform; - - - -/* var views = [], - view = toView; - - while (view) - { - views.push(view); - view = view._superview; - } - - var index = views.length; - - while (index--) - { - var frame = views[index]._frame; - - transform.tx -= CGRectGetMinX(frame); - transform.ty -= CGRectGetMinY(frame); - }*/ - - return transform; }; From 170d6ac2e703d56187c58bb94d1a10fb81065d15 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 22 Jun 2025 21:19:03 +0200 Subject: [PATCH 05/10] fixed wrong logic --- AppKit/CPView.j | 330 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 219 insertions(+), 111 deletions(-) diff --git a/AppKit/CPView.j b/AppKit/CPView.j index a3a3a34f5..f24fac625 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -592,40 +592,91 @@ var CPViewHighDPIDrawingEnabled = YES; { if (aSubview === self) [CPException raise:CPInvalidArgumentException reason:"can't add a view as a subview of itself"]; +#if DEBUG + if (!aSubview._superview && _subviews.indexOf(aSubview) !== CPNotFound) + [CPException raise:CPInvalidArgumentException reason:"can't insert a subview in duplicate (probably partially decoded)"]; +#endif - // If a view is being moved, it must be fully removed from its old hierarchy first. - // This ensures all disappearance and detachment notifications are sent correctly. - if ([aSubview superview]) - [aSubview removeFromSuperview]; + // Notify the subview that it will be moving. + [aSubview viewWillMoveToSuperview:self]; + // We will have to adjust the z-index of all views starting at this index. + var count = _subviews.length, + lastWindow; + + // Dirty the key view loop, in case the window wants to auto recalculate it [[self window] _dirtyKeyViewLoop]; - [aSubview viewWillMoveToSuperview:self]; - [aSubview _postViewWillAppearNotification]; + // If this is already one of our subviews, remove it. + if (aSubview._superview === self) + { + var index = [_subviews indexOfObjectIdenticalTo:aSubview]; + + // FIXME: should this be anIndex >= count? (last one) + if (index === anIndex || index === count - 1 && anIndex === count) + return; + + [_subviews removeObjectAtIndex:index]; + +#if PLATFORM(DOM) + CPDOMDisplayServerRemoveChild(_DOMElement, aSubview._DOMElement); +#endif + + if (anIndex > index) + --anIndex; + + //We've effectively made the subviews array shorter, so represent that. + --count; + } + else + { + var superview = aSubview._superview; + + lastWindow = [superview window]; + + // Remove the view from its previous superview. + [aSubview _removeFromSuperview]; + + // Set the subview's window to our own. + // This must happen before _setSuperview so that the window is available + // in notifications like viewDidAppear. + if (_window) + [aSubview _setWindow:_window]; + + if (!_window && lastWindow) + [aSubview _setWindow:nil]; + + [aSubview _postViewWillAppearNotification]; + // Set ourselves as the superview. + [aSubview _setSuperview:self]; + } - var count = _subviews.length; if (anIndex === CPNotFound || anIndex >= count) { _subviews.push(aSubview); + #if PLATFORM(DOM) + // Attach the actual node. CPDOMDisplayServerAppendChild(_DOMElement, aSubview._DOMElement); #endif } else { _subviews.splice(anIndex, 0, aSubview); + #if PLATFORM(DOM) + // Attach the actual node. CPDOMDisplayServerInsertBefore(_DOMElement, aSubview._DOMElement, _subviews[anIndex + 1]._DOMElement); #endif } - [aSubview _setSuperview:self sendDidAppear:NO]; [aSubview setNextResponder:self]; + [aSubview _scaleSizeUnitSquareToSize:[self _hierarchyScaleSize]]; + [aSubview viewDidMoveToSuperview]; - if (_window) - [aSubview _setWindow:_window]; - + // This method might be called before we are fully unarchived, in which case the theme state isn't set up yet + // and none of the below matters anyhow. if (_themeState) { if ([self hasThemeState:CPThemeStateFirstResponder]) @@ -639,12 +690,7 @@ var CPViewHighDPIDrawingEnabled = YES; [aSubview _notifyWindowDidResignKey]; } - [aSubview _scaleSizeUnitSquareToSize:[self _hierarchyScaleSize]]; - [self didAddSubview:aSubview]; - - if ([aSubview superview]) - [aSubview _postDidAppearRecursively]; } /*! @@ -661,7 +707,14 @@ var CPViewHighDPIDrawingEnabled = YES; */ - (void)removeFromSuperview { + var superview = _superview; + + [self viewWillMoveToSuperview:nil]; [self _removeFromSuperview]; + [self viewDidMoveToSuperview]; + + if (superview) + [self _setWindow:nil]; } - (void)_removeFromSuperview @@ -669,33 +722,24 @@ var CPViewHighDPIDrawingEnabled = YES; if (!_superview) return; - var oldSuperview = _superview; - var oldWindow = [self window]; - - [self viewWillMoveToSuperview:nil]; - - // Detach from window hierarchy first - if (oldWindow) - [self _setWindow:nil]; + // Dirty the key view loop, in case the window wants to auto recalculate it + [[self window] _dirtyKeyViewLoop]; + [_superview willRemoveSubview:self]; [self _postViewWillDisappearNotification]; - // Detach from view hierarchy - [oldSuperview willRemoveSubview:self]; - [oldSuperview._subviews removeObjectIdenticalTo:self]; + [_superview._subviews removeObjectIdenticalTo:self]; + #if PLATFORM(DOM) - CPDOMDisplayServerRemoveChild(oldSuperview._DOMElement, _DOMElement); + CPDOMDisplayServerRemoveChild(_superview._DOMElement, _DOMElement); #endif - [self _setSuperview:nil sendDidAppear:NO]; - [self setNextResponder:nil]; + // If the view is not hidden and one of its ancestors is hidden, + // notify the view that it is now unhidden. + [self _setSuperview:nil]; - [self _notifyViewDidResignFirstResponder]; [self _notifyWindowDidResignKey]; - - [self _postDidDisappearRecursively]; - - [self viewDidMoveToSuperview]; + [self _notifyViewDidResignFirstResponder]; } /*! @@ -710,9 +754,9 @@ var CPViewHighDPIDrawingEnabled = YES; var index = [_subviews indexOfObjectIdenticalTo:aSubview]; - // This now correctly handles the full remove/add cycle. - [aSubview removeFromSuperview]; [self _insertSubview:aView atIndex:index]; + + [aSubview removeFromSuperview]; } - (void)setSubviews:(CPArray)newSubviews @@ -724,44 +768,68 @@ var CPViewHighDPIDrawingEnabled = YES; if ([_subviews isEqual:newSubviews]) return; + // Trivial Case 1: No current subviews, simply add all new subviews. + if ([_subviews count] === 0) + { + var index = 0, + count = [newSubviews count]; + + for (; index < count; ++index) + [self addSubview:newSubviews[index]]; + + return; + } + + // Trivial Case 2: No new subviews, simply remove all current subviews. + if ([newSubviews count] === 0) + { + var count = [_subviews count]; + + while (count--) + [_subviews[count] removeFromSuperview]; + + return; + } + // Find out the views that were removed. var removedSubviews = [CPMutableSet setWithArray:_subviews]; + [removedSubviews removeObjectsInArray:newSubviews]; [removedSubviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; - // Find out which views need to be added or reordered. - for (var i = 0, count = [newSubviews count]; i < count; ++i) - { - var subview = newSubviews[i]; - if ([subview superview] !== self) - [self _insertSubview:subview atIndex:i]; - else { - // Reorder existing subview - var currentIndex = [_subviews indexOfObjectIdenticalTo:subview]; - if (currentIndex !== i) { - [_subviews removeObjectAtIndex:currentIndex]; - _subviews.splice(i, 0, subview); + // Find out which views need to be added. + var addedSubviews = [CPMutableSet setWithArray:newSubviews]; + + [addedSubviews removeObjectsInArray:_subviews]; + + var addedSubview = nil, + addedSubviewEnumerator = [addedSubviews objectEnumerator]; + + while ((addedSubview = [addedSubviewEnumerator nextObject]) != nil) + [self addSubview:addedSubview]; + + // If the order is fine, no need to reorder. + if ([_subviews isEqual:newSubviews]) + return; + + _subviews = [newSubviews copy]; + #if PLATFORM(DOM) - CPDOMDisplayServerRemoveChild(_DOMElement, subview._DOMElement); - CPDOMDisplayServerInsertBefore(_DOMElement, subview._DOMElement, (i + 1 < [_subviews count]) ? _subviews[i + 1]._DOMElement : null); -#endif - } - } + var index = 0, + count = [_subviews count]; + + for (; index < count; ++index) + { + var subview = _subviews[index]; + + CPDOMDisplayServerRemoveChild(_DOMElement, subview._DOMElement); + CPDOMDisplayServerAppendChild(_DOMElement, subview._DOMElement); } +#endif } /* @ignore */ -- (void)_willMoveToWindow:(CPWindow)aWindow -{ - [self viewWillMoveToWindow:aWindow]; - - var count = _subviews.length; - while(count--) - [_subviews[count] _willMoveToWindow:aWindow]; -} - -/* @ignore */ -- (void)_propagateWindowAndSetup:(CPWindow)aWindow +- (void)_setWindow:(CPWindow)aWindow { [[self window] _dirtyKeyViewLoop]; @@ -769,6 +837,9 @@ var CPViewHighDPIDrawingEnabled = YES; if ([_window firstResponder] === self && _window != aWindow) [_window makeFirstResponder:nil]; + // Notify the view and its subviews + [self viewWillMoveToWindow:aWindow]; + // Unregister the drag events from the current window and register // them in the new window. if (_registeredDraggedTypes) @@ -804,40 +875,20 @@ var CPViewHighDPIDrawingEnabled = YES; var count = [_subviews count]; while (count--) - [_subviews[count] _propagateWindowAndSetup:aWindow]; + [_subviews[count] _setWindow:aWindow]; if ([_window isKeyWindow]) [self setThemeState:CPThemeStateKeyWindow]; else [self unsetThemeState:CPThemeStateKeyWindow]; + [self viewDidMoveToWindow]; + [self _manageToolTipInstallation]; [[self window] _dirtyKeyViewLoop]; } -/* @ignore */ -- (void)_didMoveToWindow -{ - var count = _subviews.length; - while(count--) - [_subviews[count] _didMoveToWindow]; - - [self viewDidMoveToWindow]; -} - -/* @ignore */ -- (void)_setWindow:(CPWindow)aWindow -{ - if (_window === aWindow) - return; - - [self _willMoveToWindow:aWindow]; - [self _propagateWindowAndSetup:aWindow]; - [self _didMoveToWindow]; -} - - /*! Returns \c YES if the receiver is, or is a descendant of, \c aView. @param aView the view to test for ancestry @@ -942,6 +993,13 @@ var CPViewHighDPIDrawingEnabled = YES; return view._menuItem; return nil; +/* var view = self, + enclosingMenuItem = _enclosingMenuItem; + + while (!enclosingMenuItem && (view = view._enclosingMenuItem)) + view = [view superview]; + + return enclosingMenuItem;*/ } - (void)setTag:(CPInteger)aTag @@ -1638,14 +1696,6 @@ var CPViewHighDPIDrawingEnabled = YES; [[CPNotificationCenter defaultCenter] postNotificationName:_CPViewDidAppearNotification object:self userInfo:nil]; } -- (void)_postDidAppearRecursively -{ - [self _postViewDidAppearNotification]; - var count = _subviews.length; - while(count--) - [_subviews[count] _postDidAppearRecursively]; -} - - (void)_postViewWillDisappearNotification { [[CPNotificationCenter defaultCenter] postNotificationName:_CPViewWillDisappearNotification object:self userInfo:nil]; @@ -1656,15 +1706,7 @@ var CPViewHighDPIDrawingEnabled = YES; [[CPNotificationCenter defaultCenter] postNotificationName:_CPViewDidDisappearNotification object:self userInfo:nil]; } -- (void)_postDidDisappearRecursively -{ - [self _postViewDidDisappearNotification]; - var count = _subviews.length; - while(count--) - [_subviews[count] _postDidDisappearRecursively]; -} - -- (void)_setSuperview:(CPView)aSuperview sendDidAppear:(BOOL)sendDidAppear +- (void)_setSuperview:(CPView)aSuperview { var hasOldSuperview = (_superview != nil), hasNewSuperview = (aSuperview != nil), @@ -1678,11 +1720,12 @@ var CPViewHighDPIDrawingEnabled = YES; [self _recursiveGainedHiddenAncestor]; _superview = aSuperview; -} -- (void)_setSuperview:(CPView)aSuperview -{ - [self _setSuperview:aSuperview sendDidAppear:YES]; + if (hasOldSuperview) + [self _postViewDidDisappearNotification]; + + if (hasNewSuperview) + [self _postViewDidAppearNotification]; } - (void)_recursiveLostHiddenAncestor @@ -2084,6 +2127,9 @@ var CPViewHighDPIDrawingEnabled = YES; var image = slices[i]; + // // If image was nil, size should have been nil too. + // assert(image != nil); + CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], size.width, size.height); _DOMImageParts[partIndex].style.background = "url(\"" + [image filename] + "\")"; @@ -2171,6 +2217,7 @@ var CPViewHighDPIDrawingEnabled = YES; partIndex = 0; + // Make sure to repeat the top and bottom pieces horizontally if they're not the exact width needed. if (top) { CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", top + "px"); @@ -2181,6 +2228,8 @@ var CPViewHighDPIDrawingEnabled = YES; if (_DOMImageSizes[1]) { var height = frameSize.height - top - bottom; + + //_DOMImageParts[partIndex].style.backgroundSize = frameSize.width + "px " + height + "px"; CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], frameSize.width + "px", height + "px"); CPDOMDisplayServerSetStyleLeftTop(_DOMImageParts[partIndex], NULL, 0.0, top); CPDOMDisplayServerSetStyleSize(_DOMImageParts[partIndex], frameSize.width, height); @@ -2200,6 +2249,7 @@ var CPViewHighDPIDrawingEnabled = YES; partIndex = 0; + // Make sure to repeat the left and right pieces vertically if they're not the exact height needed. if (left) { CPDOMDisplayServerSetStyleBackgroundSize(_DOMImageParts[partIndex], left + "px", frameSize.height + "px"); @@ -3542,11 +3592,19 @@ var CPAppearanceVibrantDark = [CPAppearance appearanceNamed:CPAppearanceNameVibr _currentAppearance = effectiveAppearance; } +// var start = [CPDate new]; + for (var i = 0, size = [_subviews count]; i < size; i++) { [[_subviews objectAtIndex:i] _recomputeAppearanceWithSuperviewEffectiveAppearance:effectiveAppearance]; } -} +// [_subviews makeObjectsPerformSelector:@selector(_recomputeAppearance)]; + +/* var now = [CPDate new]; + var elapsedSeconds = [now timeIntervalSinceReferenceDate] - [start timeIntervalSinceReferenceDate]; + + CPLog.trace(@"_recomputeAppearance " + [_subviews count] + " subviews in " + elapsedSeconds + @" seconds"); +*/} @end @@ -3578,13 +3636,27 @@ var CPAppearanceVibrantDark = [CPAppearance appearanceNamed:CPAppearanceNameVibr return; if (![_trackingAreas containsObjectIdenticalTo:trackingArea]) - [CPException raise:CPInternalInconsistencyException reason:"Trying to remove unreferenced tracking area"]; + [CPException raise:CPInternalInconsistencyException reason:"Trying to remove unreferenced trackingArea"]; [self _removeTrackingArea:trackingArea]; } /*! Invoked automatically when the view’s geometry changes such that its tracking areas need to be recalculated. + + You should override this method to remove out of date tracking areas, add recomputed tracking areas and then call super; + + Cocoa calls this on every view, whereas they have tracking area(s) or not. + Cappuccino behaves differently : + - updateTrackingAreas is called during initWithFrame + - updateTrackingAreas is also called when placing a view in the view hierarchy (that is in a window) + - if you have only CPTrackingInVisibleRect tracking areas attached to a view, it will not be called again (until you move the view in the hierarchy) + - if you have at least one non-CPTrackingInVisibleRect tracking area attached, it will be called every time the view geometry could be modified + You don't have to touch to CPTrackingInVisibleRect tracking areas, they will be automatically updated + + Please note that it is the owner of a tracking area who is called for updateTrackingAreas. + But, if a view without any tracking area is inserted in the view hierarchy (that is, in a window), the view is called for updateTrackingAreas. + This enables you to use updateTrackingArea to initially attach your tracking areas to the view. */ - (void)updateTrackingAreas { @@ -3609,6 +3681,8 @@ var CPAppearanceVibrantDark = [CPAppearance appearanceNamed:CPAppearanceNameVibr /*! The referencingSuperViewVisibleRect is used to speed up the execution of this as the visibleRect method is a heavy operation. + It has to go up the view hierarchy and get every superviews visibleRect to transform and intersect them together. + Here we keep the superviews visible rect when we are going down the view hierarchy to make the operation much faster. */ - (void)_updateTrackingAreasWithRecursion:(BOOL)shouldCallRecursively withReferencingSuperViewVisibleRect:(CGRect)referencingSuperViewVisibleRect { @@ -3616,6 +3690,8 @@ var CPAppearanceVibrantDark = [CPAppearance appearanceNamed:CPAppearanceNameVibr if (shouldCallRecursively) { + // Now, call _updateTrackingAreasWithRecursion on subviews + for (var i = 0; i < _subviews.length; i++) [_subviews[i] _updateTrackingAreasWithRecursion:YES withReferencingSuperViewVisibleRect:[self _visibleRectWithSuperviewVisibleRect:referencingSuperViewVisibleRect]]; } @@ -3628,6 +3704,10 @@ var CPAppearanceVibrantDark = [CPAppearance appearanceNamed:CPAppearanceNameVibr - (CPArray)_calcTrackingAreaOwnersWithReferencingSuperViewVisibleRect:(CGRect)referencingSuperViewVisibleRect { + // First search all owners that must be notified + // Remark: 99.99% of time, the only owner will be the view itself + // In the same time, update the rects of InVisibleRect tracking areas + var owners = []; for (var i = 0; i < _trackingAreas.length; i++) @@ -3693,14 +3773,21 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", /*! Initializes the view from an archive. + @param aCoder the coder from which to initialize + @return the initialized view */ - (id)initWithCoder:(CPCoder)aCoder { + // We create the DOMElement "early" because there is a chance that we + // will decode our superview before we are done decoding, at which point + // we have to have an element to place in the tree. Perhaps there is + // a more "elegant" way to do this...? #if PLATFORM(DOM) _DOMElement = DOMElementPrototype.cloneNode(false); AppKitTagDOMElement(self, _DOMElement); #endif + // Also decode these "early". _frame = [aCoder decodeRectForKey:CPViewFrameKey]; _bounds = [aCoder decodeRectForKey:CPViewBoundsKey]; _scaleSize = [aCoder containsValueForKey:CPViewScaleKey] ? [aCoder decodeSizeForKey:CPViewScaleKey] : CGSizeMake(1.0, 1.0); @@ -3708,6 +3795,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", _isScaled = [aCoder containsValueForKey:CPViewIsScaledKey] ? [aCoder decodeBoolForKey:CPViewIsScaledKey] : NO; _subviews = @[]; + // Trying to fix "not ready" views _trackingAreas = [aCoder decodeObjectForKey:CPViewTrackingAreasKey] || @[]; [self _decodeThemeObjectsWithCoder:aCoder]; @@ -3717,23 +3805,30 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", if (self) { + // We have to manually check because it may be 0, so we can't use || _tag = [aCoder containsValueForKey:CPViewTagKey] ? [aCoder decodeIntForKey:CPViewTagKey] : -1; _identifier = [aCoder decodeObjectForKey:CPReuseIdentifierKey]; _window = [aCoder decodeObjectForKey:CPViewWindowKey]; _superview = [aCoder decodeObjectForKey:CPViewSuperviewKey]; + // We have to manually add the subviews so that they will receive + // viewWillMoveToSuperview: and viewDidMoveToSuperview: var subviews = [aCoder decodeObjectForKey:CPViewSubviewsKey] || []; for (var i = 0, count = [subviews count]; i < count; ++i) { + // addSubview won't do anything if the superview is already self, so clear it subviews[i]._superview = nil; [self addSubview:subviews[i]]; } + // FIXME: Should we encode/decode this? _registeredDraggedTypes = [CPSet set]; _registeredDraggedTypesArray = []; + // Other views (CPBox) might set an autoresizes mask on their subviews before it is actually decoded. + // We make sure we don't override the value by checking if it was already set. if (_autoresizingMask == nil) _autoresizingMask = [aCoder decodeIntForKey:CPViewAutoresizingMaskKey] || CPViewNotSizable; @@ -3746,6 +3841,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", if (_toolTip) [self _installToolTipEventHandlers]; + // DOM SETUP #if PLATFORM(DOM) _cssStylePreviousState = @[]; @@ -3761,6 +3857,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", for (; index < count; ++index) { CPDOMDisplayServerAppendChild(_DOMElement, _subviews[index]._DOMElement); + //_subviews[index]._superview = self; } #endif @@ -3776,6 +3873,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", [self _setupViewFlags]; [self setAppearance:[aCoder decodeObjectForKey:CPViewAppearanceKey]]; + // Set the current appearance to something that can't be the correct one so it will recalculate it at the first layout. _currentAppearance = _frame; [self updateTrackingAreas]; @@ -3788,6 +3886,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", /*! Archives the view to a coder. + @param aCoder the object into which the view's data will be archived. */ - (void)encodeWithCoder:(CPCoder)aCoder { @@ -3799,6 +3898,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", [aCoder encodeRect:_frame forKey:CPViewFrameKey]; [aCoder encodeRect:_bounds forKey:CPViewBoundsKey]; + // This will come out nil on the other side with decodeObjectForKey: if (_window != nil) [aCoder encodeConditionalObject:_window forKey:CPViewWindowKey]; @@ -3817,6 +3917,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask", if (encodedSubviews.length > 0) [aCoder encodeObject:encodedSubviews forKey:CPViewSubviewsKey]; + // This will come out nil on the other side with decodeObjectForKey: if (_superview != nil) [aCoder encodeConditionalObject:_superview forKey:CPViewSuperviewKey]; @@ -3883,6 +3984,9 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) { var view = fromView; + // FIXME: This doesn't handle the case when the outside views are equal. + // If we have a fromView, "climb up" the view tree until + // we hit the root node or we hit the toLayer. while (view && view != toView) { var frame = view._frame; @@ -3912,6 +4016,7 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) view = view._superview; } + // If we hit toView, then we're done. if (view === toView) { return transform; @@ -3926,6 +4031,7 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) } } + // FIXME: For now we can do things this way, but eventually we need to do them the "hard" way. var view = toView, transform2 = CGAffineTransformMakeIdentity(); @@ -3933,6 +4039,7 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) { var frame = CGRectMakeCopy(view._frame); + // FIXME : For now we don't care about rotate transform and so on if (view._isScaled) { transform2.a *= 1 / view._scaleSize.width; @@ -3959,6 +4066,7 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) if (view === fromView) { + // toView is inside of fromView return transform2; } From c28a05e968015f60b737d04fca2dda13e3dd7866 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 22 Jun 2025 21:28:37 +0200 Subject: [PATCH 06/10] fixed logic --- AppKit/CPView.j | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/AppKit/CPView.j b/AppKit/CPView.j index f24fac625..6622bfa9c 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -602,7 +602,8 @@ var CPViewHighDPIDrawingEnabled = YES; // We will have to adjust the z-index of all views starting at this index. var count = _subviews.length, - lastWindow; + lastWindow, + isNewAddOrMove = aSubview._superview !== self; // Dirty the key view loop, in case the window wants to auto recalculate it [[self window] _dirtyKeyViewLoop]; @@ -637,15 +638,6 @@ var CPViewHighDPIDrawingEnabled = YES; // Remove the view from its previous superview. [aSubview _removeFromSuperview]; - // Set the subview's window to our own. - // This must happen before _setSuperview so that the window is available - // in notifications like viewDidAppear. - if (_window) - [aSubview _setWindow:_window]; - - if (!_window && lastWindow) - [aSubview _setWindow:nil]; - [aSubview _postViewWillAppearNotification]; // Set ourselves as the superview. [aSubview _setSuperview:self]; @@ -675,6 +667,16 @@ var CPViewHighDPIDrawingEnabled = YES; [aSubview viewDidMoveToSuperview]; + // Set the subview's window to our own. + if (_window) + [aSubview _setWindow:_window]; + + if (!_window && lastWindow) + [aSubview _setWindow:nil]; + + if (isNewAddOrMove) + [aSubview _postViewDidAppearNotification]; + // This method might be called before we are fully unarchived, in which case the theme state isn't set up yet // and none of the below matters anyhow. if (_themeState) @@ -737,6 +739,7 @@ var CPViewHighDPIDrawingEnabled = YES; // If the view is not hidden and one of its ancestors is hidden, // notify the view that it is now unhidden. [self _setSuperview:nil]; + [self _postViewDidDisappearNotification]; [self _notifyWindowDidResignKey]; [self _notifyViewDidResignFirstResponder]; @@ -1721,11 +1724,7 @@ var CPViewHighDPIDrawingEnabled = YES; _superview = aSuperview; - if (hasOldSuperview) - [self _postViewDidDisappearNotification]; - - if (hasNewSuperview) - [self _postViewDidAppearNotification]; + // Notifications are now posted manually from _insertSubview and _removeFromSuperview } - (void)_recursiveLostHiddenAncestor From 64f4b0fc13f7eca1d600b76435cb57a10582f992 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 22 Jun 2025 21:32:48 +0200 Subject: [PATCH 07/10] fixed: accidental removal of commented out code --- AppKit/CPView.j | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/AppKit/CPView.j b/AppKit/CPView.j index 6622bfa9c..22f4d0a22 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -4072,4 +4072,23 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) CGAffineTransformConcatTo(transform, transform2, transform); return transform; + + + +/* var views = [], + view = toView; + while (view) + { + views.push(view); + view = view._superview; + } + var index = views.length; + while (index--) + { + var frame = views[index]._frame; + transform.tx -= CGRectGetMinX(frame); + transform.ty -= CGRectGetMinY(frame); + }*/ + + return transform; }; From e0a8fa3fdef6dc59ea77e0a8866f74912a44a7db Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 22 Jun 2025 21:34:19 +0200 Subject: [PATCH 08/10] formatting --- AppKit/CPView.j | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AppKit/CPView.j b/AppKit/CPView.j index 22f4d0a22..1f5b949aa 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -4077,15 +4077,19 @@ var _CPViewGetTransform = function(/*CPView*/ fromView, /*CPView */ toView) /* var views = [], view = toView; + while (view) { views.push(view); view = view._superview; } + var index = views.length; + while (index--) { var frame = views[index]._frame; + transform.tx -= CGRectGetMinX(frame); transform.ty -= CGRectGetMinY(frame); }*/ From c0705448cd9664390d32beaf41302fff8b5f4415 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 22 Jun 2025 21:47:26 +0200 Subject: [PATCH 09/10] new: testcase --- Tests/AppKit/CPViewTest.j | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Tests/AppKit/CPViewTest.j b/Tests/AppKit/CPViewTest.j index 7c99fbe65..854a40443 100644 --- a/Tests/AppKit/CPViewTest.j +++ b/Tests/AppKit/CPViewTest.j @@ -9,7 +9,20 @@ var updateTrackingAreasCalls, cursorUpdateCalls, involvedViewForMouseEntered, involvedViewForMouseExited, - involvedViewForCursorUpdate; + involvedViewForCursorUpdate, + windowPropertyWasNilInViewDidAppear; + +@implementation CPTestableViewController : CPViewController +{ +} + +- (void)viewDidAppear +{ + [super viewDidAppear]; + windowPropertyWasNilInViewDidAppear = ([self view] && [[self view] window] === nil); +} + +@end @implementation CPViewTest : OJTestCase { @@ -39,6 +52,7 @@ var updateTrackingAreasCalls, methodCalled = []; updateTrackingAreasCalls = 0; + windowPropertyWasNilInViewDidAppear = NO; // Add this line [super setUp]; } @@ -1661,6 +1675,23 @@ var updateTrackingAreasCalls, [[CPApplication sharedApplication] sendEvent:anEvent]; } +- (void)testWindowIsSetBeforeViewDidAppear +{ + // 1. Create a view controller. This will also create its view. + var viewController = [[CPTestableViewController alloc] init]; + var controllerView = [viewController view]; + + // 2. Add the controller's view to the window's view hierarchy. + // This will trigger the chain of notifications, including _CPViewDidAppearNotification, + // which in turn calls -[CPViewController viewDidAppear]. + [[window contentView] addSubview:controllerView]; + + // 3. Check the flag set by our custom view controller. + // If the fix is correct, the window should have been set before viewDidAppear was called, + // so the flag should be NO. + [self assertFalse:windowPropertyWasNilInViewDidAppear message:@"The view's window property should be set before -[CPViewController viewDidAppear] is called."]; +} + @end @implementation CPTrackingAreaView : CPView From bae78b50552c2eb1dd22b21ff52808e4e5d7b5b0 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 22 Jun 2025 21:52:57 +0200 Subject: [PATCH 10/10] formatting --- Tests/AppKit/CPViewTest.j | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/AppKit/CPViewTest.j b/Tests/AppKit/CPViewTest.j index 854a40443..e3e1003d9 100644 --- a/Tests/AppKit/CPViewTest.j +++ b/Tests/AppKit/CPViewTest.j @@ -52,7 +52,7 @@ var updateTrackingAreasCalls, methodCalled = []; updateTrackingAreasCalls = 0; - windowPropertyWasNilInViewDidAppear = NO; // Add this line + windowPropertyWasNilInViewDidAppear = NO; [super setUp]; }