Upstream version 9.38.198.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 "platform/graphics/GraphicsContext.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 #include <Carbon/Carbon.h>
42
43 namespace blink {
44
45 // FIXME: Get these numbers from CoreUI.
46 static int cRealButtonLength[] = { 28, 21 };
47 static int cButtonHitInset[] = { 3, 2 };
48 // cRealButtonLength - cButtonInset
49 static int cButtonLength[] = { 14, 10 };
50 static int cScrollbarThickness[] = { 15, 11 };
51 static int cButtonInset[] = { 14, 11 };
52 static int cThumbMinLength[] = { 26, 20 };
53
54 static int cOuterButtonLength[] = { 16, 14 }; // The outer button in a double button pair is a bit bigger.
55 static int cOuterButtonOverlap = 2;
56
57 static ScrollbarButtonsPlacement gButtonPlacement = ScrollbarButtonsDoubleEnd;
58
59 void ScrollbarThemeMacNonOverlayAPI::updateButtonPlacement()
60 {
61     NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
62     if ([buttonPlacement isEqualToString:@"Single"])
63         gButtonPlacement = ScrollbarButtonsSingle;
64     else if ([buttonPlacement isEqualToString:@"DoubleMin"])
65         gButtonPlacement = ScrollbarButtonsDoubleStart;
66     else if ([buttonPlacement isEqualToString:@"DoubleBoth"])
67         gButtonPlacement = ScrollbarButtonsDoubleBoth;
68     else {
69         gButtonPlacement = ScrollbarButtonsDoubleEnd;
70     }
71 }
72
73 static blink::WebThemeEngine::State scrollbarStateToThemeState(ScrollbarThemeClient* scrollbar)
74 {
75     if (!scrollbar->enabled())
76         return blink::WebThemeEngine::StateDisabled;
77     if (!scrollbar->isScrollableAreaActive())
78         return blink::WebThemeEngine::StateInactive;
79     if (scrollbar->pressedPart() == ThumbPart)
80         return blink::WebThemeEngine::StatePressed;
81
82     return blink::WebThemeEngine::StateActive;
83 }
84
85 // Override ScrollbarThemeMacCommon::paint() to add support for the following:
86 //     - drawing using WebThemeEngine functions
87 //     - drawing tickmarks
88 //     - Skia specific changes
89 bool ScrollbarThemeMacNonOverlayAPI::paint(ScrollbarThemeClient* scrollbar, GraphicsContext* context, const IntRect& damageRect)
90 {
91     // Get the tickmarks for the frameview.
92     Vector<IntRect> tickmarks;
93     scrollbar->getTickmarks(tickmarks);
94
95     HIThemeTrackDrawInfo trackInfo;
96     trackInfo.version = 0;
97     trackInfo.kind = scrollbar->controlSize() == RegularScrollbar ? kThemeMediumScrollBar : kThemeSmallScrollBar;
98     trackInfo.bounds = scrollbar->frameRect();
99     trackInfo.min = 0;
100     trackInfo.max = scrollbar->maximum();
101     trackInfo.value = scrollbar->currentPos();
102     trackInfo.trackInfo.scrollbar.viewsize = scrollbar->visibleSize();
103     trackInfo.attributes = 0;
104     if (scrollbar->orientation() == HorizontalScrollbar)
105         trackInfo.attributes |= kThemeTrackHorizontal;
106
107     if (!scrollbar->enabled())
108         trackInfo.enableState = kThemeTrackDisabled;
109     else
110         trackInfo.enableState = scrollbar->isScrollableAreaActive() ? kThemeTrackActive : kThemeTrackInactive;
111
112     if (!hasButtons(scrollbar))
113         trackInfo.enableState = kThemeTrackNothingToScroll;
114     trackInfo.trackInfo.scrollbar.pressState = scrollbarPartToHIPressedState(scrollbar->pressedPart());
115
116     SkCanvas* canvas = context->canvas();
117     CGAffineTransform currentCTM = gfx::SkMatrixToCGAffineTransform(canvas->getTotalMatrix());
118
119     // The Aqua scrollbar is buggy when rotated and scaled.  We will just draw into a bitmap if we detect a scale or rotation.
120     bool canDrawDirectly = currentCTM.a == 1.0f && currentCTM.b == 0.0f && currentCTM.c == 0.0f && (currentCTM.d == 1.0f || currentCTM.d == -1.0f);
121     GraphicsContext* drawingContext = context;
122     OwnPtr<ImageBuffer> imageBuffer;
123     if (!canDrawDirectly) {
124         trackInfo.bounds = IntRect(IntPoint(), scrollbar->frameRect().size());
125
126         IntRect bufferRect(scrollbar->frameRect());
127         bufferRect.intersect(damageRect);
128         bufferRect.move(-scrollbar->frameRect().x(), -scrollbar->frameRect().y());
129
130         imageBuffer = ImageBuffer::create(bufferRect.size());
131         if (!imageBuffer)
132             return true;
133
134         drawingContext = imageBuffer->context();
135     }
136
137     // Draw thumbless.
138     gfx::SkiaBitLocker bitLocker(drawingContext->canvas());
139     CGContextRef cgContext = bitLocker.cgContext();
140     HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal);
141
142     IntRect tickmarkTrackRect = trackRect(scrollbar, false);
143     if (!canDrawDirectly) {
144         tickmarkTrackRect.setX(0);
145         tickmarkTrackRect.setY(0);
146     }
147     // The ends are rounded and the thumb doesn't go there.
148     tickmarkTrackRect.inflateY(-tickmarkTrackRect.width());
149     // Inset a bit.
150     tickmarkTrackRect.setX(tickmarkTrackRect.x() + 2);
151     tickmarkTrackRect.setWidth(tickmarkTrackRect.width() - 5);
152     paintGivenTickmarks(drawingContext, scrollbar, tickmarkTrackRect, tickmarks);
153
154     if (hasThumb(scrollbar)) {
155         blink::WebThemeEngine::ScrollbarInfo scrollbarInfo;
156         scrollbarInfo.orientation = scrollbar->orientation() == HorizontalScrollbar ? blink::WebThemeEngine::ScrollbarOrientationHorizontal : blink::WebThemeEngine::ScrollbarOrientationVertical;
157         scrollbarInfo.parent = scrollbar->isScrollViewScrollbar() ? blink::WebThemeEngine::ScrollbarParentScrollView : blink::WebThemeEngine::ScrollbarParentRenderLayer;
158         scrollbarInfo.maxValue = scrollbar->maximum();
159         scrollbarInfo.currentValue = scrollbar->currentPos();
160         scrollbarInfo.visibleSize = scrollbar->visibleSize();
161         scrollbarInfo.totalSize = scrollbar->totalSize();
162
163         blink::WebCanvas* webCanvas = drawingContext->canvas();
164         blink::Platform::current()->themeEngine()->paintScrollbarThumb(
165             webCanvas,
166             scrollbarStateToThemeState(scrollbar),
167             scrollbar->controlSize() == RegularScrollbar ? blink::WebThemeEngine::SizeRegular : blink::WebThemeEngine::SizeSmall,
168             blink::WebRect(scrollbar->frameRect()),
169             scrollbarInfo);
170     }
171
172     if (!canDrawDirectly) {
173         ASSERT(imageBuffer);
174         context->drawImageBuffer(imageBuffer.get(),
175             FloatRect(scrollbar->frameRect().location(), imageBuffer->size()));
176     }
177
178     return true;
179 }
180
181 int ScrollbarThemeMacNonOverlayAPI::scrollbarThickness(ScrollbarControlSize controlSize)
182 {
183     return cScrollbarThickness[controlSize];
184 }
185
186 ScrollbarButtonsPlacement ScrollbarThemeMacNonOverlayAPI::buttonsPlacement() const
187 {
188     return gButtonPlacement;
189 }
190
191 bool ScrollbarThemeMacNonOverlayAPI::hasButtons(ScrollbarThemeClient* scrollbar)
192 {
193     return scrollbar->enabled() && buttonsPlacement() != ScrollbarButtonsNone
194              && (scrollbar->orientation() == HorizontalScrollbar
195              ? scrollbar->width()
196              : scrollbar->height()) >= 2 * (cRealButtonLength[scrollbar->controlSize()] - cButtonHitInset[scrollbar->controlSize()]);
197 }
198
199 bool ScrollbarThemeMacNonOverlayAPI::hasThumb(ScrollbarThemeClient* scrollbar)
200 {
201     int minLengthForThumb = 2 * cButtonInset[scrollbar->controlSize()] + cThumbMinLength[scrollbar->controlSize()] + 1;
202     return scrollbar->enabled() && (scrollbar->orientation() == HorizontalScrollbar ?
203              scrollbar->width() :
204              scrollbar->height()) >= minLengthForThumb;
205 }
206
207 static IntRect buttonRepaintRect(const IntRect& buttonRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize, bool start)
208 {
209     ASSERT(gButtonPlacement != ScrollbarButtonsNone);
210
211     IntRect paintRect(buttonRect);
212     if (orientation == HorizontalScrollbar) {
213         paintRect.setWidth(cRealButtonLength[controlSize]);
214         if (!start)
215             paintRect.setX(buttonRect.x() - (cRealButtonLength[controlSize] - buttonRect.width()));
216     } else {
217         paintRect.setHeight(cRealButtonLength[controlSize]);
218         if (!start)
219             paintRect.setY(buttonRect.y() - (cRealButtonLength[controlSize] - buttonRect.height()));
220     }
221
222     return paintRect;
223 }
224
225 IntRect ScrollbarThemeMacNonOverlayAPI::backButtonRect(ScrollbarThemeClient* scrollbar, ScrollbarPart part, bool painting)
226 {
227     IntRect result;
228
229     if (part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd))
230         return result;
231
232     if (part == BackButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsSingle))
233         return result;
234
235     int thickness = scrollbarThickness(scrollbar->controlSize());
236     bool outerButton = part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
237     if (outerButton) {
238         if (scrollbar->orientation() == HorizontalScrollbar)
239             result = IntRect(scrollbar->x(), scrollbar->y(), cOuterButtonLength[scrollbar->controlSize()] + (painting ? cOuterButtonOverlap : 0), thickness);
240         else
241             result = IntRect(scrollbar->x(), scrollbar->y(), thickness, cOuterButtonLength[scrollbar->controlSize()] + (painting ? cOuterButtonOverlap : 0));
242         return result;
243     }
244
245     // Our repaint rect is slightly larger, since we are a button that is adjacent to the track.
246     if (scrollbar->orientation() == HorizontalScrollbar) {
247         int start = part == BackButtonStartPart ? scrollbar->x() : scrollbar->x() + scrollbar->width() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
248         result = IntRect(start, scrollbar->y(), cButtonLength[scrollbar->controlSize()], thickness);
249     } else {
250         int start = part == BackButtonStartPart ? scrollbar->y() : scrollbar->y() + scrollbar->height() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
251         result = IntRect(scrollbar->x(), start, thickness, cButtonLength[scrollbar->controlSize()]);
252     }
253
254     if (painting)
255         return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == BackButtonStartPart);
256     return result;
257 }
258
259 IntRect ScrollbarThemeMacNonOverlayAPI::forwardButtonRect(ScrollbarThemeClient* scrollbar, ScrollbarPart part, bool painting)
260 {
261     IntRect result;
262
263     if (part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart))
264         return result;
265
266     if (part == ForwardButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsSingle))
267         return result;
268
269     int thickness = scrollbarThickness(scrollbar->controlSize());
270     int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
271     int buttonLength = cButtonLength[scrollbar->controlSize()];
272
273     bool outerButton = part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
274     if (outerButton) {
275         if (scrollbar->orientation() == HorizontalScrollbar) {
276             result = IntRect(scrollbar->x() + scrollbar->width() - outerButtonLength, scrollbar->y(), outerButtonLength, thickness);
277             if (painting)
278                 result.inflateX(cOuterButtonOverlap);
279         } else {
280             result = IntRect(scrollbar->x(), scrollbar->y() + scrollbar->height() - outerButtonLength, thickness, outerButtonLength);
281             if (painting)
282                 result.inflateY(cOuterButtonOverlap);
283         }
284         return result;
285     }
286
287     if (scrollbar->orientation() == HorizontalScrollbar) {
288         int start = part == ForwardButtonEndPart ? scrollbar->x() + scrollbar->width() - buttonLength : scrollbar->x() + outerButtonLength;
289         result = IntRect(start, scrollbar->y(), buttonLength, thickness);
290     } else {
291         int start = part == ForwardButtonEndPart ? scrollbar->y() + scrollbar->height() - buttonLength : scrollbar->y() + outerButtonLength;
292         result = IntRect(scrollbar->x(), start, thickness, buttonLength);
293     }
294     if (painting)
295         return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == ForwardButtonStartPart);
296     return result;
297 }
298
299 IntRect ScrollbarThemeMacNonOverlayAPI::trackRect(ScrollbarThemeClient* scrollbar, bool painting)
300 {
301     if (painting || !hasButtons(scrollbar))
302         return scrollbar->frameRect();
303
304     IntRect result;
305     int thickness = scrollbarThickness(scrollbar->controlSize());
306     int startWidth = 0;
307     int endWidth = 0;
308     int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
309     int buttonLength = cButtonLength[scrollbar->controlSize()];
310     int doubleButtonLength = outerButtonLength + buttonLength;
311     switch (buttonsPlacement()) {
312         case ScrollbarButtonsSingle:
313             startWidth = buttonLength;
314             endWidth = buttonLength;
315             break;
316         case ScrollbarButtonsDoubleStart:
317             startWidth = doubleButtonLength;
318             break;
319         case ScrollbarButtonsDoubleEnd:
320             endWidth = doubleButtonLength;
321             break;
322         case ScrollbarButtonsDoubleBoth:
323             startWidth = doubleButtonLength;
324             endWidth = doubleButtonLength;
325             break;
326         default:
327             break;
328     }
329
330     int totalWidth = startWidth + endWidth;
331     if (scrollbar->orientation() == HorizontalScrollbar)
332         return IntRect(scrollbar->x() + startWidth, scrollbar->y(), scrollbar->width() - totalWidth, thickness);
333     return IntRect(scrollbar->x(), scrollbar->y() + startWidth, thickness, scrollbar->height() - totalWidth);
334 }
335
336 int ScrollbarThemeMacNonOverlayAPI::minimumThumbLength(ScrollbarThemeClient* scrollbar)
337 {
338     return cThumbMinLength[scrollbar->controlSize()];
339 }
340
341 }