5f566e021863568369b3cdcaaa0b4b555a5eee64
[WebKit-https.git] / Source / WebCore / platform / mac / WebWindowAnimation.mm
1 /*
2  * Copyright (C) 2009-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27
28 #import "WebWindowAnimation.h"
29
30 #import "FloatConversion.h"
31 #import "WebCoreSystemInterface.h"
32 #import <pal/spi/cg/CoreGraphicsSPI.h>
33 #import <wtf/Assertions.h>
34 #import <wtf/MathExtras.h>
35
36 using namespace WebCore;
37
38 static const CGFloat slowMotionFactor = 10;
39
40 static NSTimeInterval WebWindowAnimationDurationFromDuration(NSTimeInterval duration)
41 {
42     return ([[NSApp currentEvent] modifierFlags] & NSEventModifierFlagShift) ? duration * slowMotionFactor : duration;
43 }
44
45 static NSRect scaledRect(NSRect _initialFrame, NSRect _finalFrame, CGFloat factor)
46 {
47     NSRect currentRect = _initialFrame;
48     currentRect.origin.x += (NSMinX(_finalFrame) - NSMinX(_initialFrame)) * factor;
49     currentRect.origin.y += (NSMinY(_finalFrame) - NSMinY(_initialFrame)) * factor;
50     currentRect.size.width += (NSWidth(_finalFrame) - NSWidth(_initialFrame)) * factor;
51     currentRect.size.height += (NSHeight(_finalFrame) - NSHeight(_initialFrame)) * factor;
52     return currentRect;
53 }
54
55 static CGFloat squaredDistance(NSPoint point1, NSPoint point2)
56 {
57     CGFloat deltaX = point1.x - point2.x;
58     CGFloat deltaY = point1.y - point2.y;
59     return deltaX * deltaX + deltaY * deltaY;
60 }
61
62 @implementation WebWindowScaleAnimation
63
64 - (id)init
65 {
66     self = [super init];
67     if (!self)
68         return nil;
69     [self setAnimationBlockingMode:NSAnimationNonblockingThreaded];
70     [self setFrameRate:60];
71     return self;
72 }
73
74 - (id)initWithHintedDuration:(NSTimeInterval)duration window:(NSWindow *)window initalFrame:(NSRect)initialFrame finalFrame:(NSRect)finalFrame
75 {
76     self = [self init];
77     if (!self)
78         return nil;
79     _hintedDuration = duration;
80     _window = window;
81     _initialFrame = initialFrame;
82     _finalFrame = finalFrame;
83     _realFrame = [window frame];
84     return self;
85 }
86
87 - (void) dealloc
88 {
89     [_subAnimation release];
90     [super dealloc];
91 }
92
93 - (void)setDuration:(NSTimeInterval)duration
94 {
95     [super setDuration:WebWindowAnimationDurationFromDuration(duration)];
96 }
97
98 - (void)setWindow:(NSWindow *)window
99 {
100     _window = window;
101 }
102
103 - (float)currentValue
104 {
105     return narrowPrecisionToFloat(0.5 - 0.5 * cos(piDouble * (1 - [self currentProgress])));
106 }
107
108 - (NSRect)currentFrame
109 {
110     return scaledRect(_finalFrame, _initialFrame, [self currentValue]);
111 }
112
113 static void flipRect(NSRect* rect)
114 {
115     rect->origin.y = NSMaxY([(NSScreen *)[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(*rect);
116 }
117
118 static CGSConnectionID mainWindowServerConnectionID()
119 {
120     static CGSConnectionID cgsId;
121     if (!cgsId)
122         cgsId = CGSMainConnectionID();
123     return cgsId;
124 }
125
126 static void setScaledFrameForWindow(NSWindow *window, NSRect scaleFrame, NSRect nonScaledFrame)
127 {
128     if (NSEqualRects(scaleFrame, nonScaledFrame)) {
129         CGSSetWindowWarp(mainWindowServerConnectionID(), window.windowNumber, 0, 0, nullptr);
130         return;
131     }
132     
133     float mesh[16];
134     
135     flipRect(&scaleFrame);
136     flipRect(&nonScaledFrame);
137     
138     // top-left point (to and from)
139     mesh[0] = 0;
140     mesh[1] = 0;
141     mesh[2] = NSMinX(scaleFrame);
142     mesh[3] = NSMinY(scaleFrame);
143     
144     // top-right point (to and from)
145     mesh[4] = NSWidth(nonScaledFrame);
146     mesh[5] = 0;
147     mesh[6] = NSMaxX(scaleFrame);
148     mesh[7] = NSMinY(scaleFrame);
149     
150     // bottom-left (to and from)
151     mesh[8] = 0;
152     mesh[9] = NSHeight(nonScaledFrame);
153     mesh[10] = NSMinX(scaleFrame);
154     mesh[11] = NSMaxY(scaleFrame);
155     
156     // bottom-right (to and from)
157     mesh[12] = NSWidth(nonScaledFrame);
158     mesh[13] = NSHeight(nonScaledFrame);
159     mesh[14] = NSMaxX(scaleFrame);
160     mesh[15] = NSMaxY(scaleFrame);
161     
162     // Apply the warp.
163     CGSSetWindowWarp(mainWindowServerConnectionID(), window.windowNumber, 2, 2, mesh);
164 }
165
166 - (void)setCurrentProgress:(NSAnimationProgress)progress
167 {
168     if (!_window)
169         return;
170
171     [super setCurrentProgress:progress];
172
173     NSRect currentRect = [self currentFrame];
174     setScaledFrameForWindow(_window, currentRect, _realFrame);
175     [_subAnimation setCurrentProgress:progress];
176 }
177
178 - (void)setSubAnimation:(NSAnimation *)animation
179 {
180     id oldAnimation = _subAnimation;
181     _subAnimation = [animation retain];
182     [oldAnimation release];
183 }
184
185 - (NSTimeInterval)additionalDurationNeededToReachFinalFrame
186 {
187     static const CGFloat maxAdditionalDuration = 1;
188     static const CGFloat speedFactor = 0.0001f;
189     
190     CGFloat maxDist = squaredDistance(_initialFrame.origin, _finalFrame.origin);
191     CGFloat dist;
192     
193     dist = squaredDistance(NSMakePoint(NSMaxX(_initialFrame), NSMinY(_initialFrame)), NSMakePoint(NSMaxX(_finalFrame), NSMinY(_finalFrame)));
194     if (dist > maxDist)
195         maxDist = dist;
196     
197     dist = squaredDistance(NSMakePoint(NSMaxX(_initialFrame), NSMaxY(_initialFrame)), NSMakePoint(NSMaxX(_finalFrame), NSMaxY(_finalFrame)));
198     if (dist > maxDist)
199         maxDist = dist;
200     
201     dist = squaredDistance(NSMakePoint(NSMinX(_initialFrame), NSMinY(_initialFrame)), NSMakePoint(NSMinX(_finalFrame), NSMinY(_finalFrame)));
202     if (dist > maxDist)
203         maxDist = dist;
204     
205     return MIN(sqrt(maxDist) * speedFactor, maxAdditionalDuration);    
206 }
207
208 - (void)startAnimation
209 {
210     // Compute extra time
211     if (_hintedDuration)
212         [self setDuration:_hintedDuration + [self additionalDurationNeededToReachFinalFrame]];
213     [super startAnimation];
214 }
215
216 - (void)stopAnimation
217 {
218     _window = nil;
219     [super stopAnimation];
220     [_subAnimation stopAnimation];
221 }
222
223 @end
224
225 @implementation WebWindowFadeAnimation
226
227 - (id)init
228 {
229     self = [super init];
230     if (!self)
231         return nil;
232     [self setAnimationBlockingMode:NSAnimationNonblockingThreaded];
233     [self setFrameRate:60];
234     [self setAnimationCurve:NSAnimationEaseInOut];
235     return self;
236 }
237
238 - (id)initWithDuration:(NSTimeInterval)duration window:(NSWindow *)window initialAlpha:(CGFloat)initialAlpha finalAlpha:(CGFloat)finalAlpha
239 {
240     self = [self init];
241     if (!self)
242         return nil;    
243     _window = window;
244     _initialAlpha = initialAlpha;
245     _finalAlpha = finalAlpha;
246     [self setDuration:duration];
247     return self;
248 }
249
250 - (void)setDuration:(NSTimeInterval)duration
251 {
252     [super setDuration:WebWindowAnimationDurationFromDuration(duration)];
253 }
254
255 - (CGFloat)currentAlpha
256 {
257     return MAX(0, MIN(1, _initialAlpha + [self currentValue] * (_finalAlpha - _initialAlpha)));
258 }
259
260 - (void)setCurrentProgress:(NSAnimationProgress)progress
261 {
262     if (_isStopped)
263         return;
264
265     ASSERT(_window);
266     [super setCurrentProgress:progress];
267
268     CGSSetWindowAlpha(mainWindowServerConnectionID(), _window.windowNumber, self.currentAlpha);
269 }
270
271 - (void)setWindow:(NSWindow*)window
272 {
273     _window = window;
274 }
275
276 - (void)stopAnimation
277 {
278     // This is relevant when we are a sub animation of a scale animation.
279     // In this case we are hosted in the animated thread of the parent
280     // and even after [super stopAnimation], the parent might call
281     // setCurrrentProgress.
282     _isStopped = YES;
283
284     [super stopAnimation];
285 }
286
287 @end
288