mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 13:07:02 +00:00
FIXED: CPView received multiple -viewDidHide/Unhide messages
FIXED: when a view is hidden by a hidden superview and added to a non hidden superview, the -viewDidUnhide message is sent. With test : CPViewTest.j
This commit is contained in:
+53
-35
@@ -172,6 +172,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
CPArray _registeredDraggedTypesArray;
|
||||
|
||||
BOOL _isHidden;
|
||||
BOOL _isHiddenOrHasHiddenAncestor;
|
||||
BOOL _hitTests;
|
||||
BOOL _clipsToBounds;
|
||||
|
||||
@@ -372,6 +373,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
_opacity = 1.0;
|
||||
_isHidden = NO;
|
||||
_isHiddenOrHasHiddenAncestor = NO;
|
||||
_hitTests = YES;
|
||||
|
||||
_hierarchyScaleSize = CGSizeMake(1.0 , 1.0);
|
||||
@@ -598,7 +600,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[aSubview _removeFromSuperview];
|
||||
|
||||
// Set ourselves as the superview.
|
||||
aSubview._superview = self;
|
||||
[aSubview _setSuperview:self];
|
||||
}
|
||||
|
||||
if (anIndex === CPNotFound || anIndex >= count)
|
||||
@@ -623,11 +625,6 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
[aSubview setNextResponder:self];
|
||||
[aSubview _scaleSizeUnitSquareToSize:[self _hierarchyScaleSize]];
|
||||
|
||||
// If the subview is not hidden and one of its ancestors is hidden,
|
||||
// notify the subview that it is now hidden.
|
||||
if (![aSubview isHidden] && [self isHiddenOrHasHiddenAncestor])
|
||||
[aSubview _notifyViewDidHide];
|
||||
|
||||
[aSubview viewDidMoveToSuperview];
|
||||
|
||||
// Set the subview's window to our own.
|
||||
@@ -697,13 +694,10 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
// If the view is not hidden and one of its ancestors is hidden,
|
||||
// notify the view that it is now unhidden.
|
||||
if (!_isHidden && [_superview isHiddenOrHasHiddenAncestor])
|
||||
[self _notifyViewDidUnhide];
|
||||
[self _setSuperview:nil];
|
||||
|
||||
[self _notifyWindowDidResignKey];
|
||||
[self _notifyViewDidResignFirstResponder];
|
||||
|
||||
_superview = nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1599,7 +1593,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
// FIXME: Should we return to visibility? This breaks in FireFox, Opera, and IE.
|
||||
// _DOMElement.style.visibility = (_isHidden = aFlag) ? "hidden" : "visible";
|
||||
_isHidden = aFlag;
|
||||
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
_DOMElement.style.display = _isHidden ? "none" : "block";
|
||||
@@ -1622,33 +1616,61 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
while (view = [view superview]);
|
||||
}
|
||||
|
||||
[self _notifyViewDidHide];
|
||||
[self _recursiveGainedHiddenAncestor];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setNeedsDisplay:YES];
|
||||
[self _notifyViewDidUnhide];
|
||||
|
||||
[self _recursiveLostHiddenAncestor];
|
||||
}
|
||||
|
||||
_isHidden = aFlag;
|
||||
}
|
||||
|
||||
- (void)_notifyViewDidHide
|
||||
- (void)_setSuperview:(CPView)aSuperview
|
||||
{
|
||||
[self viewDidHide];
|
||||
var hasOldSuperview = (_superview !== nil),
|
||||
hasNewSuperview = (aSuperview !== nil),
|
||||
oldSuperviewIsHidden = hasOldSuperview && [_superview isHiddenOrHasHiddenAncestor],
|
||||
newSuperviewIsHidden = hasNewSuperview && [aSuperview isHiddenOrHasHiddenAncestor];
|
||||
|
||||
var count = [_subviews count];
|
||||
if (!newSuperviewIsHidden && oldSuperviewIsHidden)
|
||||
[self _recursiveLostHiddenAncestor];
|
||||
|
||||
while (count--)
|
||||
[_subviews[count] _notifyViewDidHide];
|
||||
if (newSuperviewIsHidden && !oldSuperviewIsHidden)
|
||||
[self _recursiveGainedHiddenAncestor];
|
||||
|
||||
_superview = aSuperview;
|
||||
}
|
||||
|
||||
- (void)_notifyViewDidUnhide
|
||||
- (void)_recursiveLostHiddenAncestor
|
||||
{
|
||||
[self viewDidUnhide];
|
||||
if (_isHiddenOrHasHiddenAncestor)
|
||||
{
|
||||
_isHiddenOrHasHiddenAncestor = NO;
|
||||
[self viewDidUnhide];
|
||||
}
|
||||
|
||||
var count = [_subviews count];
|
||||
[_subviews enumerateObjectsUsingBlock:function(view, idx, stop)
|
||||
{
|
||||
[view _recursiveLostHiddenAncestor];
|
||||
}];
|
||||
}
|
||||
|
||||
while (count--)
|
||||
[_subviews[count] _notifyViewDidUnhide];
|
||||
- (void)_recursiveGainedHiddenAncestor
|
||||
{
|
||||
if (!_isHidden)
|
||||
{
|
||||
[self viewDidHide];
|
||||
}
|
||||
|
||||
_isHiddenOrHasHiddenAncestor = YES;
|
||||
|
||||
[_subviews enumerateObjectsUsingBlock:function(view, idx, stop)
|
||||
{
|
||||
[view _recursiveGainedHiddenAncestor];
|
||||
}];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1718,12 +1740,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
*/
|
||||
- (BOOL)isHiddenOrHasHiddenAncestor
|
||||
{
|
||||
var view = self;
|
||||
|
||||
while (view && ![view isHidden])
|
||||
view = [view superview];
|
||||
|
||||
return view !== nil;
|
||||
return _isHiddenOrHasHiddenAncestor;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -3494,14 +3511,14 @@ setBoundsOrigin:
|
||||
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 and add recomputed tracking areas;
|
||||
|
||||
|
||||
Cocoa calls this on every view, whereas they have tracking area(s) or not.
|
||||
Cappuccino behaves differently :
|
||||
- updateTrackingAreas is 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.
|
||||
@@ -3513,16 +3530,16 @@ setBoundsOrigin:
|
||||
|
||||
/*!
|
||||
This utility method is intended for CPView subclasses overriding updateTrackingAreas
|
||||
|
||||
|
||||
Typical use would be :
|
||||
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
[self removeAllTrackingAreas];
|
||||
|
||||
|
||||
... add your specific updated tracking areas ...
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
- (void)removeAllTrackingAreas
|
||||
{
|
||||
@@ -3707,6 +3724,7 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
#endif
|
||||
|
||||
[self setHidden:[aCoder decodeBoolForKey:CPViewIsHiddenKey]];
|
||||
_isHiddenOrHasHiddenAncestor = NO;
|
||||
|
||||
if ([aCoder containsValueForKey:CPViewOpacityKey])
|
||||
[self setAlphaValue:[aCoder decodeIntForKey:CPViewOpacityKey]];
|
||||
|
||||
+137
-28
@@ -895,70 +895,167 @@ var updateTrackingAreasCalls,
|
||||
[self assert:nil equals:[view effectiveAppearance]];
|
||||
}
|
||||
|
||||
- (void)testViewDidHideDidUnhide
|
||||
{
|
||||
var expectedResult = [@"viewDidHide_view1", @"viewDidUnhide_view1"];
|
||||
|
||||
[view1 setHidden:YES];
|
||||
[view1 setHidden:NO];
|
||||
|
||||
[self assert:expectedResult equals:methodCalled];
|
||||
}
|
||||
|
||||
- (void)testViewGainedHiddenAncestor
|
||||
{
|
||||
var expectedResult = [@"viewDidHide_view1",
|
||||
@"viewWillMoveToSuperview_view3",
|
||||
@"viewDidMoveToSuperview_view3",
|
||||
@"viewWillMoveToSuperview_view2",
|
||||
@"viewDidHide_view2",
|
||||
@"viewDidHide_view3",
|
||||
@"viewDidMoveToSuperview_view2"];
|
||||
|
||||
[view1 setHidden:YES];
|
||||
[view2 addSubview:view3];
|
||||
[view1 addSubview:view2];
|
||||
|
||||
[self assert:expectedResult equals:methodCalled];
|
||||
[self assertTrue: [view2 isHiddenOrHasHiddenAncestor] message:@"Expected isHiddenOrHasHiddenAncestor = YES"];
|
||||
[self assertTrue: [view3 isHiddenOrHasHiddenAncestor] message:@"Expected isHiddenOrHasHiddenAncestor = YES"];
|
||||
|
||||
[self assertFalse:[view2 isHidden]];
|
||||
[self assertFalse:[view3 isHidden]];
|
||||
}
|
||||
|
||||
- (void)testRemoveViewsHiddenByAncestor
|
||||
{
|
||||
var expectedResult = @[
|
||||
@"viewWillMoveToSuperview_view2",
|
||||
@"viewDidUnhide_view2",
|
||||
@"viewDidUnhide_view3",
|
||||
@"viewDidMoveToSuperview_view2",
|
||||
@"viewWillMoveToWindow_view2",
|
||||
@"viewWillMoveToWindow_view3",
|
||||
@"viewDidMoveToWindow_view3",
|
||||
@"viewDidMoveToWindow_view2"
|
||||
];
|
||||
|
||||
[view1 setHidden:YES];
|
||||
[view1 addSubview:view2];
|
||||
[view2 addSubview:view3];
|
||||
|
||||
[self assertTrue: [view2 isHiddenOrHasHiddenAncestor] message:@"Expected isHiddenOrHasHiddenAncestor = YES" ];
|
||||
[self assertFalse:[view2 isHidden]];
|
||||
|
||||
[self assertTrue: [view3 isHiddenOrHasHiddenAncestor] message:@"Expected isHiddenOrHasHiddenAncestor = YES" ];
|
||||
[self assertFalse:[view3 isHidden]];
|
||||
|
||||
methodCalled = [];
|
||||
|
||||
[view2 removeFromSuperview];
|
||||
|
||||
[self assertFalse: [view2 isHiddenOrHasHiddenAncestor] message:@"Expected isHiddenOrHasHiddenAncestor = NO" ];
|
||||
[self assertFalse: [view3 isHiddenOrHasHiddenAncestor] message:@"Expected isHiddenOrHasHiddenAncestor = NO" ];
|
||||
|
||||
[self assert:expectedResult equals:methodCalled];
|
||||
}
|
||||
|
||||
- (void)testRemoveHiddenView
|
||||
{
|
||||
var expectedResult = [@"viewWillMoveToSuperview_view2", @"viewDidMoveToSuperview_view2", @"viewWillMoveToWindow_view2", @"viewDidMoveToWindow_view2"];
|
||||
|
||||
[view1 addSubview:view2];
|
||||
[view2 setHidden:YES];
|
||||
|
||||
methodCalled = [];
|
||||
|
||||
[view2 removeFromSuperview];
|
||||
[self assertTrue: [view2 isHiddenOrHasHiddenAncestor] message:@"Expected isHiddenOrHasHiddenAncestor = YES" ];
|
||||
[self assert:expectedResult equals:methodCalled];
|
||||
}
|
||||
|
||||
- (void)testLostHiddenAncestorAfterMovingToNewSuperview
|
||||
{
|
||||
var expectedResult = @[
|
||||
@"viewWillMoveToSuperview_view2",
|
||||
@"viewDidMoveToSuperview_view2",
|
||||
@"viewDidHide_view1",
|
||||
@"viewDidHide_view2",
|
||||
@"viewWillMoveToSuperview_view2",
|
||||
@"viewDidUnhide_view2",
|
||||
@"viewDidMoveToSuperview_view2"
|
||||
];
|
||||
|
||||
[view1 addSubview:view2];
|
||||
[view1 setHidden:YES];
|
||||
[view3 addSubview:view2];
|
||||
|
||||
[self assert:expectedResult equals:methodCalled];
|
||||
}
|
||||
// TrackingAreaAdditions
|
||||
|
||||
- (void)testTrackingAreas
|
||||
{
|
||||
var trackingArea = [[CPTrackingArea alloc] initWithRect:CGRectMakeZero() options:CPTrackingMouseEnteredAndExited | CPTrackingActiveInKeyWindow | CPTrackingInVisibleRect owner:self userInfo:nil];
|
||||
|
||||
|
||||
[self assert:0 equals:[[view trackingAreas] count] message:@"Initially, a view has no tracking area"];
|
||||
|
||||
//
|
||||
|
||||
|
||||
[view addTrackingArea:trackingArea];
|
||||
[self assert:1 equals:[[view trackingAreas] count] message:@"After adding a tracking area"];
|
||||
[self assert:view equals:[trackingArea view] message:@"Tracking area should be linked to view"];
|
||||
|
||||
//
|
||||
|
||||
|
||||
[view removeTrackingArea:trackingArea];
|
||||
[self assert:0 equals:[[view trackingAreas] count] message:@"After removing the only tracking area"];
|
||||
[self assert:nil equals:[trackingArea view] message:@"Tracking area should be unlinked"];
|
||||
|
||||
|
||||
//
|
||||
|
||||
|
||||
[view addTrackingArea:trackingArea];
|
||||
[view addTrackingArea:trackingArea];
|
||||
[view addTrackingArea:trackingArea];
|
||||
[self assert:1 equals:[[view trackingAreas] count] message:@"Adding the same tracking area multiple times should add it once"];
|
||||
[self assert:view equals:[trackingArea view] message:@"Tracking area should be linked to view"];
|
||||
|
||||
|
||||
var trackingArea2 = [[CPTrackingArea alloc] initWithRect:CGRectMakeZero() options:CPTrackingMouseEnteredAndExited | CPTrackingActiveInKeyWindow owner:self userInfo:nil];
|
||||
|
||||
//
|
||||
|
||||
|
||||
[view addTrackingArea:trackingArea2];
|
||||
[self assert:2 equals:[[view trackingAreas] count] message:@"After adding a second tracking area"];
|
||||
[self assert:view equals:[trackingArea2 view] message:@"Tracking area should be linked to view"];
|
||||
|
||||
//
|
||||
|
||||
|
||||
[view removeAllTrackingAreas];
|
||||
[self assert:0 equals:[[view trackingAreas] count] message:@"After removing all tracking areas"];
|
||||
[self assert:nil equals:[trackingArea view] message:@"Tracking area should be unlinked"];
|
||||
[self assert:nil equals:[trackingArea2 view] message:@"Tracking area should be unlinked"];
|
||||
|
||||
//
|
||||
|
||||
|
||||
[view addTrackingArea:trackingArea];
|
||||
|
||||
|
||||
var contentView = [window contentView];
|
||||
|
||||
[contentView addSubview:view];
|
||||
[self assert:0 equals:updateTrackingAreasCalls message:@"Putting a view with a CPTrackingAreaInVisibleRect in a window should not call updateTrackingAreas"];
|
||||
|
||||
|
||||
[view removeFromSuperview];
|
||||
|
||||
//
|
||||
|
||||
|
||||
[view addTrackingArea:trackingArea2];
|
||||
[contentView addSubview:view];
|
||||
[self assert:1 equals:updateTrackingAreasCalls message:@"Putting a view with a non CPTrackingAreaInVisibleRect in a window should call updateTrackingAreas"];
|
||||
|
||||
|
||||
[view removeAllTrackingAreas];
|
||||
|
||||
|
||||
//
|
||||
|
||||
|
||||
var viewTA = [[CPTrackingAreaView alloc] initWithFrame:CGRectMakeZero()];
|
||||
updateTrackingAreasCalls = 0;
|
||||
|
||||
@@ -966,17 +1063,17 @@ var updateTrackingAreasCalls,
|
||||
[self assert:1 equals:updateTrackingAreasCalls message:@"Putting a view with no tracking areas in a window should call updateTrackingAreas"];
|
||||
|
||||
//
|
||||
|
||||
|
||||
updateTrackingAreasCalls = 0;
|
||||
|
||||
[viewTA addTrackingArea:trackingArea];
|
||||
[viewTA setFrame:CGRectMake(10, 10, 10, 10)];
|
||||
[self assert:0 equals:updateTrackingAreasCalls message:@"Changing geometry of a view with a CPTrackingAreaInVisibleRect should not call updateTrackingAreas"];
|
||||
|
||||
|
||||
//
|
||||
|
||||
|
||||
updateTrackingAreasCalls = 0;
|
||||
|
||||
|
||||
[viewTA addTrackingArea:trackingArea2];
|
||||
[viewTA setFrame:CGRectMake(20, 20, 20, 20)];
|
||||
[self assert:1 equals:updateTrackingAreasCalls message:@"Changing geometry of a view with a non CPTrackingAreaInVisibleRect should call updateTrackingAreas"];
|
||||
@@ -1048,7 +1145,7 @@ var updateTrackingAreasCalls,
|
||||
|
||||
[viewTA removeAllTrackingAreas];
|
||||
[viewTA addTrackingArea:trackingAreaAllWithDrag];
|
||||
|
||||
|
||||
// Mouse enters the tracking area while dragging (option set)
|
||||
|
||||
[self moveMouseAtPoint:CGPointMake(21, 21) dragging:YES];
|
||||
@@ -1103,7 +1200,7 @@ var updateTrackingAreasCalls,
|
||||
[self assert:0 equals:mouseExitedCalls message:@"Mouse entering inner tracking area should not call mouseExited"];
|
||||
[self assert:1 equals:mouseMovedCalls message:@"Mouse entering inner tracking area should call mouseMoved"];
|
||||
[self assert:1 equals:cursorUpdateCalls message:@"Mouse entering inner tracking area should call cursorUpdate"];
|
||||
|
||||
|
||||
// Mouse moves in inner view
|
||||
|
||||
[self moveMouseAtPoint:CGPointMake(27, 27) dragging:NO];
|
||||
@@ -1112,7 +1209,7 @@ var updateTrackingAreasCalls,
|
||||
[self assert:0 equals:mouseExitedCalls message:@"Mouse moving in inner tracking area should not call mouseExited"];
|
||||
[self assert:2 equals:mouseMovedCalls message:@"Mouse moving in inner tracking area should call mouseMoved for both views"];
|
||||
[self assert:0 equals:cursorUpdateCalls message:@"Mouse moving in inner tracking area should not call cursorUpdate"];
|
||||
|
||||
|
||||
// Mouse leaves inner view but remains in outer view
|
||||
|
||||
[self moveMouseAtPoint:CGPointMake(36, 36) dragging:NO];
|
||||
@@ -1121,7 +1218,7 @@ var updateTrackingAreasCalls,
|
||||
[self assert:1 equals:mouseExitedCalls message:@"Mouse moving from inner to outer tracking area should call mouseExited (for inner)"];
|
||||
[self assert:1 equals:mouseMovedCalls message:@"Mouse moving from inner to outer tracking area should call mouseMoved (for outer)"];
|
||||
[self assert:1 equals:cursorUpdateCalls message:@"Mouse moving from inner to outer tracking area should call cursorUpdate (for outer)"];
|
||||
|
||||
|
||||
[self assert:innerViewTA equals:involvedViewForMouseExited message:@"Inner view should receive mouseExited"];
|
||||
[self assert:viewTA equals:involvedViewForCursorUpdate message:@"Outer view should receive cursorUpdate"];
|
||||
|
||||
@@ -1278,7 +1375,7 @@ var updateTrackingAreasCalls,
|
||||
[self mouseUpAtPoint:CGPointMake(10, 10)];
|
||||
|
||||
[self assert:[CPCursor arrowCursor] equals:[CPCursor currentCursor] message:@"Step 1.7 : cursor should be an arrow"];
|
||||
|
||||
|
||||
//
|
||||
|
||||
[self moveMouseAtPoint:CGPointMake(1, 1) dragging:NO];
|
||||
@@ -1292,7 +1389,7 @@ var updateTrackingAreasCalls,
|
||||
[self moveMouseAtPoint:CGPointMake(5, 5) dragging:NO];
|
||||
|
||||
[self assert:[CPCursor arrowCursor] equals:[CPCursor currentCursor] message:@"Step 2.1 : cursor should be an arrow"];
|
||||
|
||||
|
||||
// Step 2.2 : inside the superview / outside the subview
|
||||
|
||||
[self moveMouseAtPoint:CGPointMake(15, 15) dragging:NO];
|
||||
@@ -1310,7 +1407,7 @@ var updateTrackingAreasCalls,
|
||||
[self moveMouseAtPoint:CGPointMake(15, 15) dragging:NO];
|
||||
|
||||
[self assert:[CPCursor crosshairCursor] equals:[CPCursor currentCursor] message:@"Step 2.4 : cursor should be a crosshair"];
|
||||
|
||||
|
||||
// Step 2.5 : outside the superview
|
||||
|
||||
[self moveMouseAtPoint:CGPointMake(5, 5) dragging:NO];
|
||||
@@ -1355,14 +1452,14 @@ var updateTrackingAreasCalls,
|
||||
[self moveMouseAtPoint:CGPointMake(5, 5) dragging:NO];
|
||||
|
||||
[self assert:[CPCursor arrowCursor] equals:[CPCursor currentCursor] message:@"Step 3.5 : cursor should be an arrow"];
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (void)testTrackingAreasLiveViewHierarchyModification
|
||||
{
|
||||
// 1. viewB inside viewA with mouseEntered removing itself
|
||||
|
||||
var viewA = [[CPTrackingAreaViewWithCursorUpdate alloc] initWithFrame:CGRectMake(20, 20, 40, 40)]
|
||||
var viewA = [[CPTrackingAreaViewWithCursorUpdate alloc] initWithFrame:CGRectMake(20, 20, 40, 40)],
|
||||
viewB = [[CPTrackingAreaViewLiveRemoval alloc] initWithFrame:CGRectMake(10, 10, 20, 20)];
|
||||
|
||||
[[window contentView] setSubviews:[CPArray arrayWithObject:viewA]];
|
||||
@@ -1691,6 +1788,18 @@ var updateTrackingAreasCalls,
|
||||
[methodCalled addObject:string];
|
||||
}
|
||||
|
||||
- (void)viewDidUnhide
|
||||
{
|
||||
var string = _cmd + @"_" + [self identifier];
|
||||
[methodCalled addObject:string];
|
||||
}
|
||||
|
||||
- (void)viewDidHide
|
||||
{
|
||||
var string = _cmd + @"_" + [self identifier];
|
||||
[methodCalled addObject:string];
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return YES;
|
||||
|
||||
Reference in New Issue
Block a user