34a360b91ef93d3eb37cb231193396a340760b20
[WebKit-https.git] / Source / WebKit / UIProcess / mac / WebColorPickerMac.mm
1 /* 
2  * Copyright (c) 2013 The Chromium Authors. All rights reserved.
3  * Copyright (C) 2013 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *    * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *    * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *    * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #import "config.h"
33 #import "WebColorPickerMac.h"
34
35 #if ENABLE(INPUT_TYPE_COLOR)
36
37 #if USE(APPKIT)
38
39 #import <WebCore/Color.h>
40 #import <WebCore/ColorMac.h>
41 #import <pal/spi/mac/NSColorWellSPI.h>
42 #import <pal/spi/mac/NSPopoverColorWellSPI.h>
43 #import <pal/spi/mac/NSPopoverSPI.h>
44
45 static const size_t maxColorSuggestions = 12;
46 static const CGFloat colorPickerMatrixNumColumns = 12.0;
47 static const CGFloat colorPickerMatrixBorderWidth = 1.0;
48
49 // FIXME: <rdar://problem/41173525> We should not have to track changes in NSPopoverColorWell's implementation.
50 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
51 static const CGFloat colorPickerMatrixSwatchWidth = 13.0;
52 #else
53 static const CGFloat colorPickerMatrixSwatchWidth = 12.0;
54 #endif
55
56 @protocol WKPopoverColorWellDelegate <NSObject>
57 - (void)didClosePopover;
58 @end
59
60 @interface WKPopoverColorWell : NSPopoverColorWell {
61     RetainPtr<NSColorList> _suggestedColors;
62 }
63
64 @property (nonatomic, weak) id<WKPopoverColorWellDelegate> webDelegate;
65
66 - (void)setSuggestedColors:(NSColorList *)suggestedColors;
67 @end
68
69 @interface WKColorPopoverMac : NSObject<WKColorPickerUIMac, WKPopoverColorWellDelegate, NSWindowDelegate> {
70 @private
71     BOOL _lastChangedByUser;
72     WebKit::WebColorPickerMac *_picker;
73     RetainPtr<WKPopoverColorWell> _popoverWell;
74 }
75 - (id)initWithFrame:(const WebCore::IntRect &)rect inView:(NSView *)view;
76 @end
77
78 namespace WebKit {
79
80 Ref<WebColorPickerMac> WebColorPickerMac::create(WebColorPicker::Client* client, const WebCore::Color& initialColor, const WebCore::IntRect& rect, Vector<WebCore::Color>&& suggestions, NSView *view)
81 {
82     return adoptRef(*new WebColorPickerMac(client, initialColor, rect, WTFMove(suggestions), view));
83 }
84
85 WebColorPickerMac::~WebColorPickerMac()
86 {
87     if (m_colorPickerUI) {
88         [m_colorPickerUI invalidate];
89         m_colorPickerUI = nil;
90     }
91 }
92
93 WebColorPickerMac::WebColorPickerMac(WebColorPicker::Client* client, const WebCore::Color& initialColor, const WebCore::IntRect& rect, Vector<WebCore::Color>&& suggestions, NSView *view)
94     : WebColorPicker(client)
95     , m_suggestions(WTFMove(suggestions))
96 {
97     m_colorPickerUI = adoptNS([[WKColorPopoverMac alloc] initWithFrame:rect inView:view]);
98 }
99
100 void WebColorPickerMac::endPicker()
101 {
102     [m_colorPickerUI invalidate];
103     m_colorPickerUI = nil;
104     WebColorPicker::endPicker();
105 }
106
107 void WebColorPickerMac::setSelectedColor(const WebCore::Color& color)
108 {
109     if (!m_client || !m_colorPickerUI)
110         return;
111     
112     [m_colorPickerUI setColor:nsColor(color)];
113 }
114
115 void WebColorPickerMac::didChooseColor(const WebCore::Color& color)
116 {
117     if (!m_client)
118         return;
119     
120     m_client->didChooseColor(color);
121 }
122
123 void WebColorPickerMac::showColorPicker(const WebCore::Color& color)
124 {
125     if (!m_client)
126         return;
127
128     [m_colorPickerUI setAndShowPicker:this withColor:nsColor(color) suggestions:WTFMove(m_suggestions)];
129 }
130
131 } // namespace WebKit
132
133 @implementation WKPopoverColorWell
134
135 + (NSPopover *)_colorPopoverCreateIfNecessary:(BOOL)forceCreation
136 {
137     static NSPopover *colorPopover = nil;
138     if (forceCreation) {
139         NSPopover *popover = [[NSPopover alloc] init];
140         [popover _setRequiresCorrectContentAppearance:YES];
141         popover.behavior = NSPopoverBehaviorTransient;
142
143         NSColorPopoverController *controller = [[NSClassFromString(@"NSColorPopoverController") alloc] init];
144         popover.contentViewController = controller;
145         controller.popover = popover;
146         [controller release];
147
148         colorPopover = popover;
149     }
150
151     return colorPopover;
152 }
153
154 - (void)_showPopover
155 {
156     NSPopover *popover = [[self class] _colorPopoverCreateIfNecessary:YES];
157     popover.delegate = self;
158
159     [self deactivate];
160
161     // Deactivate previous NSPopoverColorWell
162     NSColorWell *owner = [NSColorWell _exclusiveColorPanelOwner];
163     if ([owner isKindOfClass:[NSPopoverColorWell class]])
164         [owner deactivate];
165
166     NSColorPopoverController *controller = (NSColorPopoverController *)[popover contentViewController];
167     controller.delegate = self;
168
169     if (_suggestedColors) {
170         NSUInteger numColors = [[_suggestedColors allKeys] count];
171         CGFloat swatchWidth = (colorPickerMatrixNumColumns * colorPickerMatrixSwatchWidth + (colorPickerMatrixNumColumns * colorPickerMatrixBorderWidth - numColors)) / numColors;
172         CGFloat swatchHeight = colorPickerMatrixSwatchWidth;
173
174         // topBarMatrixView cannot be accessed until view has been loaded
175         if (!controller.isViewLoaded)
176             [controller loadView];
177
178         NSColorPickerMatrixView *topMatrix = controller.topBarMatrixView;
179         [topMatrix setNumberOfColumns:numColors];
180         [topMatrix setSwatchSize:NSMakeSize(swatchWidth, swatchHeight)];
181         [topMatrix setColorList:_suggestedColors.get()];
182     }
183
184     [self activate:YES];
185     [popover showRelativeToRect:self.bounds ofView:self preferredEdge:NSMinYEdge];
186 }
187
188 - (void)popoverDidClose:(NSNotification *)notification {
189     [self.webDelegate didClosePopover];
190 }
191
192 - (NSView *)hitTest:(NSPoint)point
193 {
194     return nil;
195 }
196
197 - (void)setSuggestedColors:(NSColorList *)suggestedColors
198 {
199     _suggestedColors = suggestedColors;
200 }
201
202 @end
203
204 @implementation WKColorPopoverMac
205 - (id)initWithFrame:(const WebCore::IntRect &)rect inView:(NSView *)view
206 {
207     if(!(self = [super init]))
208         return self;
209
210     _popoverWell = adoptNS([[WKPopoverColorWell alloc] initWithFrame:[view convertRect:NSRectFromCGRect(rect) toView:nil]]);
211     if (!_popoverWell)
212         return self;
213
214     [_popoverWell setAlphaValue:0.0];
215     [[view window].contentView addSubview:_popoverWell.get()];
216
217     return self;
218 }
219
220 - (void)setAndShowPicker:(WebKit::WebColorPickerMac*)picker withColor:(NSColor *)color suggestions:(Vector<WebCore::Color>&&)suggestions
221 {
222     _picker = picker;
223
224     [_popoverWell setTarget:self];
225     [_popoverWell setWebDelegate:self];
226     [_popoverWell setAction:@selector(didChooseColor:)];
227     [_popoverWell setColor:color];
228
229     NSColorList *suggestedColors = nil;
230     if (suggestions.size()) {
231         suggestedColors = [[[NSColorList alloc] init] autorelease];
232         for (size_t i = 0; i < std::min(suggestions.size(), maxColorSuggestions); i++)
233             [suggestedColors insertColor:nsColor(suggestions.at(i)) key:@(i).stringValue atIndex:i];
234     }
235
236     [_popoverWell setSuggestedColors:suggestedColors];
237     [_popoverWell _showPopover];
238
239     [[NSColorPanel sharedColorPanel] setDelegate:self];
240     
241     _lastChangedByUser = YES;
242 }
243
244 - (void)invalidate
245 {
246     [_popoverWell removeFromSuperviewWithoutNeedingDisplay];
247     [_popoverWell setTarget:nil];
248     [_popoverWell setAction:nil];
249     [_popoverWell deactivate];
250     
251     _popoverWell = nil;
252     _picker = nil;
253
254     NSColorPanel *panel = [NSColorPanel sharedColorPanel];
255     if (panel.delegate == self) {
256         panel.delegate = nil;
257         [panel close];
258     }
259 }
260
261 - (void)windowWillClose:(NSNotification *)notification
262 {
263     if (!_picker)
264         return;
265
266     if (notification.object == [NSColorPanel sharedColorPanel]) {
267         _lastChangedByUser = YES;
268         _picker->endPicker();
269     }
270 }
271
272 - (void)didChooseColor:(id)sender
273 {
274     if (sender != _popoverWell)
275         return;
276
277     // Handle the case where the <input type='color'> value is programmatically set.
278     if (!_lastChangedByUser) {
279         _lastChangedByUser = YES;
280         return;
281     }
282
283     _picker->didChooseColor(WebCore::colorFromNSColor([_popoverWell color]));
284 }
285
286 - (void)setColor:(NSColor *)color
287 {
288     _lastChangedByUser = NO;
289     [_popoverWell setColor:color];
290 }
291
292 - (void)didClosePopover
293 {
294     if (!_picker)
295         return;
296
297     if (![NSColorPanel sharedColorPanel].isVisible)
298         _picker->endPicker();
299 }
300
301 @end
302
303 #endif // USE(APPKIT)
304
305 #endif // ENABLE(INPUT_TYPE_COLOR)