Use TextIndicator instead of the built in Lookup highlight
[WebKit-https.git] / Source / WebKit2 / UIProcess / mac / TextIndicatorWindow.mm
1 /*
2  * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "TextIndicatorWindow.h"
28
29 #if PLATFORM(MAC)
30
31 #import "TextIndicator.h"
32 #import "WKView.h"
33 #import <WebCore/GraphicsContext.h>
34
35 static const double bounceAnimationDuration = 0.12;
36 static const double timeBeforeFadeStarts = bounceAnimationDuration + 0.2;
37 static const double fadeOutAnimationDuration = 0.3;
38
39 using namespace WebCore;
40
41 @interface WKTextIndicatorView : NSView {
42     RefPtr<WebKit::TextIndicator> _textIndicator;
43 }
44
45 - (id)_initWithTextIndicator:(PassRefPtr<WebKit::TextIndicator>)textIndicator;
46 @end
47
48 @implementation WKTextIndicatorView
49
50 - (id)_initWithTextIndicator:(PassRefPtr<WebKit::TextIndicator>)textIndicator
51 {
52     if ((self = [super initWithFrame:NSZeroRect]))
53         _textIndicator = textIndicator;
54
55     return self;
56 }
57
58 - (void)drawRect:(NSRect)rect
59 {
60     GraphicsContext graphicsContext(static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]));
61
62     _textIndicator->draw(graphicsContext, enclosingIntRect(rect));
63 }
64
65 - (BOOL)isFlipped
66 {
67     return YES;
68 }
69
70 @end
71
72 @interface WKTextIndicatorWindowAnimation : NSAnimation<NSAnimationDelegate> {
73     WebKit::TextIndicatorWindow* _textIndicatorWindow;
74     void (WebKit::TextIndicatorWindow::*_animationProgressCallback)(double progress);
75     void (WebKit::TextIndicatorWindow::*_animationDidEndCallback)();
76 }
77
78 - (id)_initWithTextIndicatorWindow:(WebKit::TextIndicatorWindow *)textIndicatorWindow animationDuration:(CFTimeInterval)duration animationProgressCallback:(void (WebKit::TextIndicatorWindow::*)(double progress))animationProgressCallback animationDidEndCallback:(void (WebKit::TextIndicatorWindow::*)())animationDidEndCallback;
79 @end
80
81 @implementation WKTextIndicatorWindowAnimation
82
83 - (id)_initWithTextIndicatorWindow:(WebKit::TextIndicatorWindow *)textIndicatorWindow animationDuration:(CFTimeInterval)animationDuration animationProgressCallback:(void (WebKit::TextIndicatorWindow::*)(double progress))animationProgressCallback animationDidEndCallback:(void (WebKit::TextIndicatorWindow::*)())animationDidEndCallback
84 {
85     if ((self = [super initWithDuration:animationDuration animationCurve:NSAnimationEaseInOut])) {
86         _textIndicatorWindow = textIndicatorWindow;
87         _animationProgressCallback = animationProgressCallback;
88         _animationDidEndCallback = animationDidEndCallback;
89         [self setDelegate:self];
90         [self setAnimationBlockingMode:NSAnimationNonblocking];
91     }
92     return self;
93 }
94
95 - (void)setCurrentProgress:(NSAnimationProgress)progress
96 {
97     (_textIndicatorWindow->*_animationProgressCallback)(progress);
98 }
99
100 - (void)animationDidEnd:(NSAnimation *)animation
101 {
102     ASSERT(animation == self);
103
104     (_textIndicatorWindow->*_animationDidEndCallback)();
105 }
106
107 @end
108
109 namespace WebKit {
110
111 TextIndicatorWindow::TextIndicatorWindow(WKView *wkView)
112     : m_wkView(wkView)
113     , m_bounceAnimationContext(0)
114     , m_startFadeOutTimer(RunLoop::main(), this, &TextIndicatorWindow::startFadeOutTimerFired)
115 {
116 }
117
118 TextIndicatorWindow::~TextIndicatorWindow()
119 {
120     closeWindow();
121 }
122
123 void TextIndicatorWindow::setTextIndicator(PassRefPtr<TextIndicator> textIndicator, bool fadeOut, bool animate, std::function<void ()> animationCompletionHandler)
124 {
125     if (m_textIndicator == textIndicator)
126         return;
127
128     m_textIndicator = textIndicator;
129
130     // Get rid of the old window.
131     closeWindow();
132
133     if (!m_textIndicator)
134         return;
135
136     NSRect contentRect = m_textIndicator->frameRect();
137     NSRect windowFrameRect = NSIntegralRect([m_wkView convertRect:contentRect toView:nil]);
138 #pragma clang diagnostic push
139 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
140     windowFrameRect.origin = [[m_wkView window] convertBaseToScreen:windowFrameRect.origin];
141 #pragma clang diagnostic pop
142     NSRect windowContentRect = [NSWindow contentRectForFrameRect:windowFrameRect styleMask:NSBorderlessWindowMask];
143     
144     m_textIndicatorWindow = adoptNS([[NSWindow alloc] initWithContentRect:windowContentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]);
145
146     [m_textIndicatorWindow setBackgroundColor:[NSColor clearColor]];
147     [m_textIndicatorWindow setOpaque:NO];
148     [m_textIndicatorWindow setIgnoresMouseEvents:YES];
149
150     RetainPtr<WKTextIndicatorView> textIndicatorView = adoptNS([[WKTextIndicatorView alloc] _initWithTextIndicator:m_textIndicator]);
151     [m_textIndicatorWindow setContentView:textIndicatorView.get()];
152
153     [[m_wkView window] addChildWindow:m_textIndicatorWindow.get() ordered:NSWindowAbove];
154     [m_textIndicatorWindow setReleasedWhenClosed:NO];
155
156     if (animate) {
157         m_bounceAnimationCompletionHandler = WTF::move(animationCompletionHandler);
158         // Start the bounce animation.
159         m_bounceAnimationContext = WKWindowBounceAnimationContextCreate(m_textIndicatorWindow.get());
160         m_bounceAnimation = adoptNS([[WKTextIndicatorWindowAnimation alloc] _initWithTextIndicatorWindow:this animationDuration:bounceAnimationDuration
161             animationProgressCallback:&TextIndicatorWindow::bounceAnimationCallback animationDidEndCallback:&TextIndicatorWindow::bounceAnimationDidEnd]);
162         [m_bounceAnimation startAnimation];
163     }
164
165     if (fadeOut)
166         m_startFadeOutTimer.startOneShot(timeBeforeFadeStarts);
167 }
168
169 void TextIndicatorWindow::closeWindow()
170 {
171     if (!m_textIndicatorWindow)
172         return;
173
174     m_startFadeOutTimer.stop();
175
176     if (m_fadeOutAnimation) {
177         [m_fadeOutAnimation stopAnimation];
178         m_fadeOutAnimation = nullptr;
179     }
180
181     if (m_bounceAnimation) {
182         [m_bounceAnimation stopAnimation];
183         m_bounceAnimation = nullptr;
184     }
185
186     if (m_bounceAnimationContext)
187         WKWindowBounceAnimationContextDestroy(m_bounceAnimationContext);
188     
189     [[m_textIndicatorWindow parentWindow] removeChildWindow:m_textIndicatorWindow.get()];
190     [m_textIndicatorWindow close];
191     m_textIndicatorWindow = nullptr;
192 }
193
194 void TextIndicatorWindow::startFadeOutTimerFired()
195 {
196     ASSERT(!m_fadeOutAnimation);
197     
198     m_fadeOutAnimation = adoptNS([[WKTextIndicatorWindowAnimation alloc] _initWithTextIndicatorWindow:this animationDuration:fadeOutAnimationDuration
199         animationProgressCallback:&TextIndicatorWindow::fadeOutAnimationCallback animationDidEndCallback:&TextIndicatorWindow::fadeOutAnimationDidEnd]);
200     [m_fadeOutAnimation startAnimation];
201 }
202
203 void TextIndicatorWindow::fadeOutAnimationCallback(double progress)
204 {
205     ASSERT(m_fadeOutAnimation);
206
207     [m_textIndicatorWindow setAlphaValue:1.0 - progress];
208 }
209
210 void TextIndicatorWindow::fadeOutAnimationDidEnd()
211 {
212     ASSERT(m_fadeOutAnimation);
213     ASSERT(m_textIndicatorWindow);
214
215     closeWindow();
216 }
217
218 void TextIndicatorWindow::bounceAnimationCallback(double progress)
219 {
220     ASSERT(m_bounceAnimation);
221     ASSERT(m_bounceAnimationContext);
222
223     WKWindowBounceAnimationSetAnimationProgress(m_bounceAnimationContext, progress);
224 }
225
226 void TextIndicatorWindow::bounceAnimationDidEnd()
227 {
228     ASSERT(m_bounceAnimation);
229     ASSERT(m_bounceAnimationContext);
230     ASSERT(m_textIndicatorWindow);
231
232     WKWindowBounceAnimationContextDestroy(m_bounceAnimationContext);
233     m_bounceAnimationContext = 0;
234     m_bounceAnimationCompletionHandler();
235 }
236
237 } // namespace WebKit
238
239 #endif // PLATFORM(MAC)