Fixed: sheet animation was resizing the sheet to zero, causing problems

Previously sheet animation was performed by sizing the sheet from zero to full height when animating in, and then back to zero height when animating out. This caused problems with the resize algorithms, which are not designed to deal with sizing from zero.

Analyzing Cocoa behavior revealed that sheets slide in and out with no resizing.

With this commit, a general purpose mechanism for clipping a window to a rect was added to CPDOMWindowLayer. Clipping was put in CPDOMWindowLayer because that is the class that deals with windows at the global level. CPWindow uses the clipping mechanism to slide the sheet in and out, thus avoiding all resizing problems.

Fixes #1840
This commit is contained in:
Aparajita Fishman
2013-03-12 10:20:13 -04:00
parent f208ff52fd
commit 17f9304adc
2 changed files with 108 additions and 69 deletions
+45 -69
View File
@@ -1169,7 +1169,7 @@ CPTexturedBackgroundWindowMask
if (_hasShadow && !_shadowView)
{
_shadowView = [[_CPShadowWindowView alloc] initWithFrame:CGRectMakeZero()];
_shadowView = [[_CPShadowWindowView alloc] initWithFrame:CGRectMakeZero()];
[_shadowView setWindowView:_windowView];
[_shadowView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable];
@@ -2473,7 +2473,6 @@ CPTexturedBackgroundWindowMask
return CPWindowResizeTime;
}
/* @ignore */
- (void)_setAttachedSheetFrameOrigin
{
// Position the sheet above the contentRect.
@@ -2487,7 +2486,7 @@ CPTexturedBackgroundWindowMask
[attachedSheet setFrame:sheetFrame display:YES animate:NO];
}
/* @ignore
/*
Starting point for sheet session, called from CPApplication beginSheet:
*/
- (void)_attachSheet:(CPWindow)aSheet modalDelegate:(id)aModalDelegate
@@ -2503,13 +2502,13 @@ CPTexturedBackgroundWindowMask
var sheetFrame = [aSheet frame];
_sheetContext = {"sheet": aSheet, "modalDelegate": aModalDelegate, "endSelector": aDidEndSelector,
"contextInfo": aContextInfo, "frame": _CGRectMakeCopy(sheetFrame), "returnCode": -1,
"contextInfo": aContextInfo, "returnCode": -1,
"opened": NO};
[self _attachSheetWindow];
}
/* @ignore
/*
Called to animate the sheet in. The timer seems to solve a bug where sheets would
be partially animated under certain conditions.
*/
@@ -2525,7 +2524,7 @@ CPTexturedBackgroundWindowMask
repeats:NO];
}
/* @ignore
/*
Called to end the sheet. Note that orderOut: is needed to animate the sheet out, as in Cocoa.
The sheet isn't completely gone until _cleanupSheetWindow gets called.
*/
@@ -2546,7 +2545,7 @@ CPTexturedBackgroundWindowMask
}
}
/* @ignore
/*
Called to animate the sheet out. If called while animating in, schedules an animate
out at completion
*/
@@ -2562,26 +2561,24 @@ CPTexturedBackgroundWindowMask
repeats:NO];
}
/* @ignore
/*
Called to cleanup sheet, when we are definitely done with it
*/
- (void)_cleanupSheetWindow
{
var sheet = _sheetContext["sheet"],
lastFrame = _sheetContext["frame"],
deferDidEnd = _sheetContext["deferDidEndSelector"];
[sheet setFrame:lastFrame];
[self _restoreMasksForView:[sheet contentView]];
// if the parent window is modal, the sheet started its own modal session
// If the parent window is modal, the sheet started its own modal session
if (sheet._isModal)
[CPApp stopModal];
// restore the state of window before it was sheetified
[self _removeClipForSheet:sheet];
// Restore the state of window before it was sheetified
[sheet._windowView _enableSheet:NO];
// close it
// Close it
sheet._isSheet = NO;
[sheet orderOut:self];
@@ -2594,7 +2591,7 @@ CPTexturedBackgroundWindowMask
returnCode = _sheetContext["returnCode"],
contextInfo = _sheetContext["contextInfo"];
// context must be destroyed, since didEnd might want to attach another sheet
// Context must be destroyed, since didEnd might want to attach another sheet
_sheetContext = nil;
sheet._parentView = nil;
@@ -2622,31 +2619,20 @@ CPTexturedBackgroundWindowMask
repeats:NO];
}
/* @ignore */
- (void)_sheetShouldAnimateIn:(CPTimer)timer
{
// can't open sheet while opening or closing animation is going on
// Can't open sheet while opening or closing animation is going on
if (_sheetContext["isOpening"] ||
_sheetContext["isClosing"])
return;
var sheet = _sheetContext["sheet"],
sheetFrame = [sheet frame],
frame = [self frame];
[self _setUpMasksForView:[sheet contentView]];
var sheet = _sheetContext["sheet"];
sheet._isSheet = YES;
sheet._parentView = self;
var originx = frame.origin.x + FLOOR((frame.size.width - sheetFrame.size.width) / 2),
originy = frame.origin.y + [_contentView frame].origin.y,
startFrame = _CGRectMake(originx, originy, sheetFrame.size.width, 0),
endFrame = _CGRectMake(originx, originy, sheetFrame.size.width, sheetFrame.size.height);
[[CPNotificationCenter defaultCenter] postNotificationName:CPWindowWillBeginSheetNotification object:self];
// if sheet is attached to a modal window, the sheet runs
// If sheet is attached to a modal window, the sheet runs
// as if itself and the parent window are modal
sheet._isModal = NO;
@@ -2656,7 +2642,17 @@ CPTexturedBackgroundWindowMask
sheet._isModal = YES;
}
// The sheet starts hidden just above the top of a clip rect
var sheetFrame = [sheet frame],
sheetShadowFrame = sheet._hasShadow ? [sheet._shadowView frame] : sheetFrame,
frame = [self frame],
originX = frame.origin.x + FLOOR((frame.size.width - sheetFrame.size.width) / 2),
startFrame = _CGRectMake(originX, -sheetShadowFrame.size.height, sheetFrame.size.width, sheetFrame.size.height),
endFrame = _CGRectMake(originX, 0, sheetFrame.size.width, sheetFrame.size.height);
[sheet orderFront:self];
[self _clipSheet:sheet];
[sheet setFrame:startFrame display:YES animate:NO];
_sheetContext["opened"] = YES;
@@ -2671,16 +2667,11 @@ CPTexturedBackgroundWindowMask
[sheet makeKeyWindow];
}
/* @ignore */
- (void)_sheetShouldAnimateOut:(CPTimer)timer
{
var sheet = _sheetContext["sheet"],
startFrame = [sheet frame],
endFrame = _CGRectMakeCopy(startFrame);
if (_sheetContext["isOpening"])
{
// allow sheet to be closed while opening, it will close when animate in completes
// Allow sheet to be closed while opening, it will close when animate in completes
_sheetContext["shouldClose"] = YES;
return;
}
@@ -2689,14 +2680,20 @@ CPTexturedBackgroundWindowMask
return;
_sheetContext["opened"] = NO;
_sheetContext["frame"] = startFrame;
_sheetContext["isClosing"] = YES;
// the parent window can be orderedOut to disable the sheet animate out, as in Cocoa
// The parent window can be orderedOut to disable the sheet animate out, as in Cocoa
if ([self isVisible])
{
endFrame.size.height = 0;
[self _setUpMasksForView:[sheet contentView]];
var sheet = _sheetContext["sheet"],
sheetFrame = [sheet frame],
fullHeight = sheet._hasShadow ? [sheet._shadowView frame].size.height : sheetFrame.size.height,
endFrame = _CGRectMakeCopy(sheetFrame);
[sheet setFrameOrigin:_CGPointMake(sheetFrame.origin.x, 0)];
[self _clipSheet:sheet];
endFrame.origin.y = -fullHeight;
[sheet _setFrame:endFrame delegate:self duration:[self animationResizeTime:endFrame] curve:CPAnimationEaseIn];
}
else
@@ -2705,7 +2702,6 @@ CPTexturedBackgroundWindowMask
}
}
/* @ignore */
- (void)_sheetAnimationDidEnd:(CPTimer)timer
{
var sheet = _sheetContext["sheet"];
@@ -2715,12 +2711,11 @@ CPTexturedBackgroundWindowMask
if (_sheetContext["opened"] === YES)
{
// sheet is open and completely visible
[self _restoreMasksForView:[sheet contentView]];
// we wanted to close the sheet while it animated in, do that now
if (_sheetContext["shouldClose"] === YES)
[self _detachSheetWindow];
else
[self _removeClipForSheet:sheet];
}
else
{
@@ -2729,36 +2724,17 @@ CPTexturedBackgroundWindowMask
}
}
- (void)_setUpMasksForView:(CPView)aView
- (void)_clipSheet:(CPWindow)aSheet
{
var views = [aView subviews];
var clipRect = [_platformWindow contentBounds];
clipRect.origin.y = [self frame].origin.y + [[self contentView] frame].origin.y;
[views addObject:aView];
for (var i = 0, count = [views count]; i < count; i++)
{
var view = [views objectAtIndex:i],
mask = [view autoresizingMask],
maskToAdd = (mask & CPViewMinYMargin) ? 128 : CPViewMinYMargin;
[view setAutoresizingMask:(mask | maskToAdd)];
}
[[_platformWindow layerAtLevel:_level create:NO] clipWindow:aSheet toRect:clipRect];
}
- (void)_restoreMasksForView:(CPView)aView
- (void)_removeClipForSheet:(CPWindow)aSheet
{
var views = [aView subviews];
[views addObject:aView];
for (var i = 0, count = [views count]; i < count; i++)
{
var view = [views objectAtIndex:i],
mask = [view autoresizingMask],
maskToRemove = (mask & 128) ? 128 : CPViewMinYMargin;
[view setAutoresizingMask:(mask & (~ maskToRemove))];
}
[[_platformWindow layerAtLevel:_level create:NO] removeClipForWindow:aSheet];
}
/*!
+63
View File
@@ -130,4 +130,67 @@
return _windows;
}
/*!
Places \c aWindow within an element that clips it to the global rect \c clipRect.
NOTE: This is only meant for temporary usage during animation and should be balanced
with a call to removeClipForWindow:. No attempt is made to make the clipping element follow
changes to the window.
*/
- (void)clipWindow:(CPWindow)aWindow toRect:(CGRect)clipRect
{
// First check to see if the window is already clipped.
// If so, just update its rect.
var windowElement = aWindow._DOMElement,
clip = windowElement.parentNode,
style = clip.style,
isClipped = style.className === "cpwindowclip";
if (!isClipped)
{
clip = document.createElement("div");
style = clip.style;
style.className = "cpwindowclip";
style.position = "absolute";
style.overflow = "hidden";
}
style.left = clipRect.origin.x + "px";
style.top = clipRect.origin.y + "px";
style.width = clipRect.size.width + "px";
style.height = clipRect.size.height + "px";
if (!isClipped)
{
// Replace the window with the clip element, then put it inside the clip
var parent = windowElement.parentNode;
CPDOMDisplayServerInsertBefore(parent, clip, windowElement);
CPDOMDisplayServerRemoveChild(parent, windowElement);
CPDOMDisplayServerAppendChild(clip, windowElement);
}
}
/*!
Unclips a window that was previously clipped with clipWindow:toWindow:.
If the window was not clipped, a warning is logged.
*/
- (void)removeClipForWindow:(CPWindow)aWindow
{
var windowElement = aWindow._DOMElement,
clip = windowElement.parentNode;
if (clip.style.className === "cpwindowclip")
{
var parent = clip.parentNode;
CPDOMDisplayServerRemoveChild(clip, windowElement);
[aWindow setFrameOrigin:_CGPointMake([aWindow frame].origin.x, clip.offsetTop)];
CPDOMDisplayServerInsertBefore(parent, windowElement, clip);
CPDOMDisplayServerRemoveChild(parent, clip);
}
else
CPLog.warn("%s %s was not previously clipped via clipWindow:toWindow:", _cmd, [aWindow description]);
}
@end