mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-19 00:43:29 +00:00
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:
+45
-69
@@ -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];
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user