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