Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / scroll / ScrollbarThemeMacNonOverlayAPI.mm
1 /*
2  * Copyright (C) 2013 Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "platform/scroll/ScrollbarThemeMacNonOverlayAPI.h"
33
34 #include <Carbon/Carbon.h>
35 #include "platform/graphics/ImageBuffer.h"
36 #include "platform/scroll/ScrollbarThemeClient.h"
37 #include "public/platform/Platform.h"
38 #include "public/platform/WebRect.h"
39 #include "public/platform/WebThemeEngine.h"
40 #include "skia/ext/skia_utils_mac.h"
41
42 namespace WebCore {
43
44 // FIXME: Get these numbers from CoreUI.
45 static int cRealButtonLength[] = { 28, 21 };
46 static int cButtonHitInset[] = { 3, 2 };
47 // cRealButtonLength - cButtonInset
48 static int cButtonLength[] = { 14, 10 };
49 static int cScrollbarThickness[] = { 15, 11 };
50 static int cButtonInset[] = { 14, 11 };
51 static int cThumbMinLength[] = { 26, 20 };
52
53 static int cOuterButtonLength[] = { 16, 14 }; // The outer button in a double button pair is a bit bigger.
54 static int cOuterButtonOverlap = 2;
55
56 static ScrollbarButtonsPlacement gButtonPlacement = ScrollbarButtonsDoubleEnd;
57
58 void ScrollbarThemeMacNonOverlayAPI::updateButtonPlacement()
59 {
60     NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
61     if ([buttonPlacement isEqualToString:@"Single"])
62         gButtonPlacement = ScrollbarButtonsSingle;
63     else if ([buttonPlacement isEqualToString:@"DoubleMin"])
64         gButtonPlacement = ScrollbarButtonsDoubleStart;
65     else if ([buttonPlacement isEqualToString:@"DoubleBoth"])
66         gButtonPlacement = ScrollbarButtonsDoubleBoth;
67     else {
68         gButtonPlacement = ScrollbarButtonsDoubleEnd;
69     }
70 }
71
72 static blink::WebThemeEngine::State scrollbarStateToThemeState(ScrollbarThemeClient* scrollbar)
73 {
74     if (!scrollbar->enabled())
75         return blink::WebThemeEngine::StateDisabled;
76     if (!scrollbar->isScrollableAreaActive())
77         return blink::WebThemeEngine::StateInactive;
78     if (scrollbar->pressedPart() == ThumbPart)
79         return blink::WebThemeEngine::StatePressed;
80
81     return blink::WebThemeEngine::StateActive;
82 }
83
84 // Override ScrollbarThemeMacCommon::paint() to add support for the following:
85 //     - drawing using WebThemeEngine functions
86 //     - drawing tickmarks
87 //     - Skia specific changes
88 bool ScrollbarThemeMacNonOverlayAPI::paint(ScrollbarThemeClient* scrollbar, GraphicsContext* context, const IntRect& damageRect)
89 {
90     if (context->paintingDisabled())
91         return true;
92     // Get the tickmarks for the frameview.
93     Vector<IntRect> tickmarks;
94     scrollbar->getTickmarks(tickmarks);
95
96     HIThemeTrackDrawInfo trackInfo;
97     trackInfo.version = 0;
98     trackInfo.kind = scrollbar->controlSize() == RegularScrollbar ? kThemeMediumScrollBar : kThemeSmallScrollBar;
99     trackInfo.bounds = scrollbar->frameRect();
100     trackInfo.min = 0;
101     trackInfo.max = scrollbar->maximum();
102     trackInfo.value = scrollbar->currentPos();
103     trackInfo.trackInfo.scrollbar.viewsize = scrollbar->visibleSize();
104     trackInfo.attributes = 0;
105     if (scrollbar->orientation() == HorizontalScrollbar)
106         trackInfo.attributes |= kThemeTrackHorizontal;
107
108     if (!scrollbar->enabled())
109         trackInfo.enableState = kThemeTrackDisabled;
110     else
111         trackInfo.enableState = scrollbar->isScrollableAreaActive() ? kThemeTrackActive : kThemeTrackInactive;
112
113     if (!hasButtons(scrollbar))
114         trackInfo.enableState = kThemeTrackNothingToScroll;
115     trackInfo.trackInfo.scrollbar.pressState = scrollbarPartToHIPressedState(scrollbar->pressedPart());
116
117     SkCanvas* canvas = context->canvas();
118     CGAffineTransform currentCTM = gfx::SkMatrixToCGAffineTransform(canvas->getTotalMatrix());
119
120     // The Aqua scrollbar is buggy when rotated and scaled.  We will just draw into a bitmap if we detect a scale or rotation.
121     bool canDrawDirectly = currentCTM.a == 1.0f && currentCTM.b == 0.0f && currentCTM.c == 0.0f && (currentCTM.d == 1.0f || currentCTM.d == -1.0f);
122     GraphicsContext* drawingContext = context;
123     OwnPtr<ImageBuffer> imageBuffer;
124     if (!canDrawDirectly) {
125         trackInfo.bounds = IntRect(IntPoint(), scrollbar->frameRect().size());
126
127         IntRect bufferRect(scrollbar->frameRect());
128         bufferRect.intersect(damageRect);
129         bufferRect.move(-scrollbar->frameRect().x(), -scrollbar->frameRect().y());
130
131         imageBuffer = ImageBuffer::create(bufferRect.size());
132         if (!imageBuffer)
133             return true;
134
135         drawingContext = imageBuffer->context();
136     }
137
138     // Draw thumbless.
139     gfx::SkiaBitLocker bitLocker(drawingContext->canvas());
140     CGContextRef cgContext = bitLocker.cgContext();
141     HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal);
142
143     IntRect tickmarkTrackRect = trackRect(scrollbar, false);
144     if (!canDrawDirectly) {
145         tickmarkTrackRect.setX(0);
146         tickmarkTrackRect.setY(0);
147     }
148     // The ends are rounded and the thumb doesn't go there.
149     tickmarkTrackRect.inflateY(-tickmarkTrackRect.width());
150     // Inset a bit.
151     tickmarkTrackRect.setX(tickmarkTrackRect.x() + 2);
152     tickmarkTrackRect.setWidth(tickmarkTrackRect.width() - 5);
153     paintGivenTickmarks(drawingContext, scrollbar, tickmarkTrackRect, tickmarks);
154
155     if (hasThumb(scrollbar)) {
156         blink::WebThemeEngine::ScrollbarInfo scrollbarInfo;
157         scrollbarInfo.orientation = scrollbar->orientation() == HorizontalScrollbar ? blink::WebThemeEngine::ScrollbarOrientationHorizontal : blink::WebThemeEngine::ScrollbarOrientationVertical;
158         scrollbarInfo.parent = scrollbar->isScrollViewScrollbar() ? blink::WebThemeEngine::ScrollbarParentScrollView : blink::WebThemeEngine::ScrollbarParentRenderLayer;
159         scrollbarInfo.maxValue = scrollbar->maximum();
160         scrollbarInfo.currentValue = scrollbar->currentPos();
161         scrollbarInfo.visibleSize = scrollbar->visibleSize();
162         scrollbarInfo.totalSize = scrollbar->totalSize();
163
164         blink::WebCanvas* webCanvas = drawingContext->canvas();
165         blink::Platform::current()->themeEngine()->paintScrollbarThumb(
166             webCanvas,
167             scrollbarStateToThemeState(scrollbar),
168             scrollbar->controlSize() == RegularScrollbar ? blink::WebThemeEngine::SizeRegular : blink::WebThemeEngine::SizeSmall,
169             blink::WebRect(scrollbar->frameRect()),
170             scrollbarInfo);
171     }
172
173     if (!canDrawDirectly) {
174         ASSERT(imageBuffer);
175         context->drawImageBuffer(imageBuffer.get(),
176             FloatRect(scrollbar->frameRect().location(), imageBuffer->size()));
177     }
178
179     return true;
180 }
181
182 int ScrollbarThemeMacNonOverlayAPI::scrollbarThickness(ScrollbarControlSize controlSize)
183 {
184     return cScrollbarThickness[controlSize];
185 }
186
187 ScrollbarButtonsPlacement ScrollbarThemeMacNonOverlayAPI::buttonsPlacement() const
188 {
189     return gButtonPlacement;
190 }
191
192 bool ScrollbarThemeMacNonOverlayAPI::hasButtons(ScrollbarThemeClient* scrollbar)
193 {
194     return scrollbar->enabled() && buttonsPlacement() != ScrollbarButtonsNone
195              && (scrollbar->orientation() == HorizontalScrollbar
196              ? scrollbar->width()
197              : scrollbar->height()) >= 2 * (cRealButtonLength[scrollbar->controlSize()] - cButtonHitInset[scrollbar->controlSize()]);
198 }
199
200 bool ScrollbarThemeMacNonOverlayAPI::hasThumb(ScrollbarThemeClient* scrollbar)
201 {
202     int minLengthForThumb = 2 * cButtonInset[scrollbar->controlSize()] + cThumbMinLength[scrollbar->controlSize()] + 1;
203     return scrollbar->enabled() && (scrollbar->orientation() == HorizontalScrollbar ?
204              scrollbar->width() :
205              scrollbar->height()) >= minLengthForThumb;
206 }
207
208 static IntRect buttonRepaintRect(const IntRect& buttonRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize, bool start)
209 {
210     ASSERT(gButtonPlacement != ScrollbarButtonsNone);
211
212     IntRect paintRect(buttonRect);
213     if (orientation == HorizontalScrollbar) {
214         paintRect.setWidth(cRealButtonLength[controlSize]);
215         if (!start)
216             paintRect.setX(buttonRect.x() - (cRealButtonLength[controlSize] - buttonRect.width()));
217     } else {
218         paintRect.setHeight(cRealButtonLength[controlSize]);
219         if (!start)
220             paintRect.setY(buttonRect.y() - (cRealButtonLength[controlSize] - buttonRect.height()));
221     }
222
223     return paintRect;
224 }
225
226 IntRect ScrollbarThemeMacNonOverlayAPI::backButtonRect(ScrollbarThemeClient* scrollbar, ScrollbarPart part, bool painting)
227 {
228     IntRect result;
229
230     if (part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd))
231         return result;
232
233     if (part == BackButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsSingle))
234         return result;
235
236     int thickness = scrollbarThickness(scrollbar->controlSize());
237     bool outerButton = part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
238     if (outerButton) {
239         if (scrollbar->orientation() == HorizontalScrollbar)
240             result = IntRect(scrollbar->x(), scrollbar->y(), cOuterButtonLength[scrollbar->controlSize()] + (painting ? cOuterButtonOverlap : 0), thickness);
241         else
242             result = IntRect(scrollbar->x(), scrollbar->y(), thickness, cOuterButtonLength[scrollbar->controlSize()] + (painting ? cOuterButtonOverlap : 0));
243         return result;
244     }
245
246     // Our repaint rect is slightly larger, since we are a button that is adjacent to the track.
247     if (scrollbar->orientation() == HorizontalScrollbar) {
248         int start = part == BackButtonStartPart ? scrollbar->x() : scrollbar->x() + scrollbar->width() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
249         result = IntRect(start, scrollbar->y(), cButtonLength[scrollbar->controlSize()], thickness);
250     } else {
251         int start = part == BackButtonStartPart ? scrollbar->y() : scrollbar->y() + scrollbar->height() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
252         result = IntRect(scrollbar->x(), start, thickness, cButtonLength[scrollbar->controlSize()]);
253     }
254
255     if (painting)
256         return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == BackButtonStartPart);
257     return result;
258 }
259
260 IntRect ScrollbarThemeMacNonOverlayAPI::forwardButtonRect(ScrollbarThemeClient* scrollbar, ScrollbarPart part, bool painting)
261 {
262     IntRect result;
263
264     if (part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart))
265         return result;
266
267     if (part == ForwardButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsSingle))
268         return result;
269
270     int thickness = scrollbarThickness(scrollbar->controlSize());
271     int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
272     int buttonLength = cButtonLength[scrollbar->controlSize()];
273
274     bool outerButton = part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
275     if (outerButton) {
276         if (scrollbar->orientation() == HorizontalScrollbar) {
277             result = IntRect(scrollbar->x() + scrollbar->width() - outerButtonLength, scrollbar->y(), outerButtonLength, thickness);
278             if (painting)
279                 result.inflateX(cOuterButtonOverlap);
280         } else {
281             result = IntRect(scrollbar->x(), scrollbar->y() + scrollbar->height() - outerButtonLength, thickness, outerButtonLength);
282             if (painting)
283                 result.inflateY(cOuterButtonOverlap);
284         }
285         return result;
286     }
287
288     if (scrollbar->orientation() == HorizontalScrollbar) {
289         int start = part == ForwardButtonEndPart ? scrollbar->x() + scrollbar->width() - buttonLength : scrollbar->x() + outerButtonLength;
290         result = IntRect(start, scrollbar->y(), buttonLength, thickness);
291     } else {
292         int start = part == ForwardButtonEndPart ? scrollbar->y() + scrollbar->height() - buttonLength : scrollbar->y() + outerButtonLength;
293         result = IntRect(scrollbar->x(), start, thickness, buttonLength);
294     }
295     if (painting)
296         return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == ForwardButtonStartPart);
297     return result;
298 }
299
300 IntRect ScrollbarThemeMacNonOverlayAPI::trackRect(ScrollbarThemeClient* scrollbar, bool painting)
301 {
302     if (painting || !hasButtons(scrollbar))
303         return scrollbar->frameRect();
304
305     IntRect result;
306     int thickness = scrollbarThickness(scrollbar->controlSize());
307     int startWidth = 0;
308     int endWidth = 0;
309     int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
310     int buttonLength = cButtonLength[scrollbar->controlSize()];
311     int doubleButtonLength = outerButtonLength + buttonLength;
312     switch (buttonsPlacement()) {
313         case ScrollbarButtonsSingle:
314             startWidth = buttonLength;
315             endWidth = buttonLength;
316             break;
317         case ScrollbarButtonsDoubleStart:
318             startWidth = doubleButtonLength;
319             break;
320         case ScrollbarButtonsDoubleEnd:
321             endWidth = doubleButtonLength;
322             break;
323         case ScrollbarButtonsDoubleBoth:
324             startWidth = doubleButtonLength;
325             endWidth = doubleButtonLength;
326             break;
327         default:
328             break;
329     }
330
331     int totalWidth = startWidth + endWidth;
332     if (scrollbar->orientation() == HorizontalScrollbar)
333         return IntRect(scrollbar->x() + startWidth, scrollbar->y(), scrollbar->width() - totalWidth, thickness);
334     return IntRect(scrollbar->x(), scrollbar->y() + startWidth, thickness, scrollbar->height() - totalWidth);
335 }
336
337 int ScrollbarThemeMacNonOverlayAPI::minimumThumbLength(ScrollbarThemeClient* scrollbar)
338 {
339     return cThumbMinLength[scrollbar->controlSize()];
340 }
341
342 }