ba7e97b255349dbfedb30d8bdd6d48953c7789eb
[WebKit-https.git] / Source / WebCore / platform / chromium / ScrollbarThemeChromiumWin.cpp
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  * Copyright (C) 2008, 2009 Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "ScrollbarThemeChromiumWin.h"
29
30 #include <windows.h>
31 #include <vsstyle.h>
32
33 #include "ChromiumBridge.h"
34 #include "GraphicsContext.h"
35 #include "PlatformContextSkia.h"
36 #include "PlatformMouseEvent.h"
37 #include "Scrollbar.h"
38 #include "WindowsVersion.h"
39
40 namespace WebCore {
41
42 ScrollbarTheme* ScrollbarTheme::nativeTheme()
43 {
44     static ScrollbarThemeChromiumWin theme;
45     return &theme;
46 }
47
48 // The scrollbar size in DumpRenderTree on the Mac - so we can match their
49 // layout results.  Entries are for regular, small, and mini scrollbars.
50 // Metrics obtained using [NSScroller scrollerWidthForControlSize:]
51 static const int kMacScrollbarSize[3] = { 15, 11, 15 };
52
53 // Constants used to figure the drag rect outside which we should snap the
54 // scrollbar thumb back to its origin.  These calculations are based on
55 // observing the behavior of the MSVC8 main window scrollbar + some
56 // guessing/extrapolation.
57 static const int kOffEndMultiplier = 3;
58 static const int kOffSideMultiplier = 8;
59
60 int ScrollbarThemeChromiumWin::scrollbarThickness(ScrollbarControlSize controlSize)
61 {
62     static int thickness;
63     if (!thickness) {
64         if (ChromiumBridge::layoutTestMode())
65             return kMacScrollbarSize[controlSize];
66         thickness = GetSystemMetrics(SM_CXVSCROLL);
67     }
68     return thickness;
69 }
70
71 bool ScrollbarThemeChromiumWin::invalidateOnMouseEnterExit()
72 {
73     return isVistaOrNewer();
74 }
75
76 bool ScrollbarThemeChromiumWin::shouldSnapBackToDragOrigin(Scrollbar* scrollbar, const PlatformMouseEvent& evt)
77 {
78     // Find the rect within which we shouldn't snap, by expanding the track rect
79     // in both dimensions.
80     IntRect rect = trackRect(scrollbar);
81     const bool horz = scrollbar->orientation() == HorizontalScrollbar;
82     const int thickness = scrollbarThickness(scrollbar->controlSize());
83     rect.inflateX((horz ? kOffEndMultiplier : kOffSideMultiplier) * thickness);
84     rect.inflateY((horz ? kOffSideMultiplier : kOffEndMultiplier) * thickness);
85
86     // Convert the event to local coordinates.
87     IntPoint mousePosition = scrollbar->convertFromContainingWindow(evt.pos());
88     mousePosition.move(scrollbar->x(), scrollbar->y());
89
90     // We should snap iff the event is outside our calculated rect.
91     return !rect.contains(mousePosition);
92 }
93
94 void ScrollbarThemeChromiumWin::paintTrackPiece(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart partType)
95 {
96     bool horz = scrollbar->orientation() == HorizontalScrollbar;
97
98     int partId;
99     if (partType == BackTrackPart)
100         partId = horz ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT;
101     else
102         partId = horz ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
103
104     IntRect alignRect = trackRect(scrollbar, false);
105
106     // Draw the track area before/after the thumb on the scroll bar.
107     ChromiumBridge::paintScrollbarTrack(
108         gc,
109         partId,
110         getThemeState(scrollbar, partType),
111         getClassicThemeState(scrollbar, partType),
112         rect,
113         alignRect);
114 }
115
116 void ScrollbarThemeChromiumWin::paintButton(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part)
117 {
118     bool horz = scrollbar->orientation() == HorizontalScrollbar;
119
120     int partId;
121     if (part == BackButtonStartPart || part == ForwardButtonStartPart)
122         partId = horz ? DFCS_SCROLLLEFT : DFCS_SCROLLUP;
123     else
124         partId = horz ? DFCS_SCROLLRIGHT : DFCS_SCROLLDOWN;
125
126     // Draw the thumb (the box you drag in the scroll bar to scroll).
127     ChromiumBridge::paintScrollbarArrow(
128         gc,
129         getThemeArrowState(scrollbar, part),
130         partId | getClassicThemeState(scrollbar, part),
131         rect);
132 }
133
134 void ScrollbarThemeChromiumWin::paintThumb(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect)
135 {
136     bool horz = scrollbar->orientation() == HorizontalScrollbar;
137
138     // Draw the thumb (the box you drag in the scroll bar to scroll).
139     ChromiumBridge::paintScrollbarThumb(
140         gc,
141         horz ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT,
142         getThemeState(scrollbar, ThumbPart),
143         getClassicThemeState(scrollbar, ThumbPart),
144         rect);
145
146     // Draw the gripper (the three little lines on the thumb).
147     ChromiumBridge::paintScrollbarThumb(
148         gc,
149         horz ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT,
150         getThemeState(scrollbar, ThumbPart),
151         getClassicThemeState(scrollbar, ThumbPart),
152         rect);
153 }
154
155 int ScrollbarThemeChromiumWin::getThemeState(Scrollbar* scrollbar, ScrollbarPart part) const
156 {
157     // When dragging the thumb, draw thumb pressed and other segments normal
158     // regardless of where the cursor actually is.  See also four places in
159     // getThemeArrowState().
160     if (scrollbar->pressedPart() == ThumbPart) {
161         if (part == ThumbPart)
162             return SCRBS_PRESSED;
163         return isVistaOrNewer() ? SCRBS_HOVER : SCRBS_NORMAL;
164     }
165     if (!scrollbar->enabled())
166         return SCRBS_DISABLED;
167     if (scrollbar->hoveredPart() != part || part == BackTrackPart || part == ForwardTrackPart)
168         return (scrollbar->hoveredPart() == NoPart || !isVistaOrNewer()) ? SCRBS_NORMAL : SCRBS_HOVER;
169     if (scrollbar->pressedPart() == NoPart)
170         return SCRBS_HOT;
171     return (scrollbar->pressedPart() == part) ? SCRBS_PRESSED : SCRBS_NORMAL;
172 }
173
174 int ScrollbarThemeChromiumWin::getThemeArrowState(Scrollbar* scrollbar, ScrollbarPart part) const
175 {
176     // We could take advantage of knowing the values in the state enum to write
177     // some simpler code, but treating the state enum as a black box seems
178     // clearer and more future-proof.
179     if (part == BackButtonStartPart || part == ForwardButtonStartPart) {
180         if (scrollbar->orientation() == HorizontalScrollbar) {
181             if (scrollbar->pressedPart() == ThumbPart)
182                 return !isVistaOrNewer() ? ABS_LEFTNORMAL : ABS_LEFTHOVER;
183             if (!scrollbar->enabled())
184                 return ABS_LEFTDISABLED;
185             if (scrollbar->hoveredPart() != part)
186                 return ((scrollbar->hoveredPart() == NoPart) || !isVistaOrNewer()) ? ABS_LEFTNORMAL : ABS_LEFTHOVER;
187             if (scrollbar->pressedPart() == NoPart)
188                 return ABS_LEFTHOT;
189             return (scrollbar->pressedPart() == part) ?
190                 ABS_LEFTPRESSED : ABS_LEFTNORMAL;
191         }
192         if (scrollbar->pressedPart() == ThumbPart)
193             return !isVistaOrNewer() ? ABS_UPNORMAL : ABS_UPHOVER;
194         if (!scrollbar->enabled())
195             return ABS_UPDISABLED;
196         if (scrollbar->hoveredPart() != part)
197             return ((scrollbar->hoveredPart() == NoPart) || !isVistaOrNewer()) ? ABS_UPNORMAL : ABS_UPHOVER;
198         if (scrollbar->pressedPart() == NoPart)
199             return ABS_UPHOT;
200         return (scrollbar->pressedPart() == part) ? ABS_UPPRESSED : ABS_UPNORMAL;
201     }
202     if (scrollbar->orientation() == HorizontalScrollbar) {
203         if (scrollbar->pressedPart() == ThumbPart)
204             return !isVistaOrNewer() ? ABS_RIGHTNORMAL : ABS_RIGHTHOVER;
205         if (!scrollbar->enabled())
206             return ABS_RIGHTDISABLED;
207         if (scrollbar->hoveredPart() != part)
208             return ((scrollbar->hoveredPart() == NoPart) || !isVistaOrNewer()) ? ABS_RIGHTNORMAL : ABS_RIGHTHOVER;
209         if (scrollbar->pressedPart() == NoPart)
210             return ABS_RIGHTHOT;
211         return (scrollbar->pressedPart() == part) ? ABS_RIGHTPRESSED : ABS_RIGHTNORMAL;
212     }
213     if (scrollbar->pressedPart() == ThumbPart)
214         return !isVistaOrNewer() ? ABS_DOWNNORMAL : ABS_DOWNHOVER;
215     if (!scrollbar->enabled())
216         return ABS_DOWNDISABLED;
217     if (scrollbar->hoveredPart() != part)
218         return ((scrollbar->hoveredPart() == NoPart) || !isVistaOrNewer()) ? ABS_DOWNNORMAL : ABS_DOWNHOVER;
219     if (scrollbar->pressedPart() == NoPart)
220         return ABS_DOWNHOT;
221     return (scrollbar->pressedPart() == part) ? ABS_DOWNPRESSED : ABS_DOWNNORMAL;
222 }
223
224 int ScrollbarThemeChromiumWin::getClassicThemeState(Scrollbar* scrollbar, ScrollbarPart part) const
225 {
226     // When dragging the thumb, draw the buttons normal even when hovered.
227     if (scrollbar->pressedPart() == ThumbPart)
228         return 0;
229     if (!scrollbar->enabled())
230         return DFCS_INACTIVE;
231     if (scrollbar->hoveredPart() != part || part == BackTrackPart || part == ForwardTrackPart)
232         return 0;
233     if (scrollbar->pressedPart() == NoPart)
234         return DFCS_HOT;
235     return (scrollbar->pressedPart() == part) ? (DFCS_PUSHED | DFCS_FLAT) : 0;
236 }
237
238 bool ScrollbarThemeChromiumWin::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt)
239 {
240     return evt.shiftKey() && evt.button() == LeftButton;
241 }
242
243 IntSize ScrollbarThemeChromiumWin::buttonSize(Scrollbar* scrollbar)
244 {
245     // Our desired rect is essentially thickness by thickness.
246
247     // Our actual rect will shrink to half the available space when we have < 2
248     // times thickness pixels left.  This allows the scrollbar to scale down
249     // and function even at tiny sizes.
250
251     int thickness = scrollbarThickness(scrollbar->controlSize());
252
253     // In layout test mode, we force the button "girth" (i.e., the length of
254     // the button along the axis of the scrollbar) to be a fixed size.
255     // FIXME: This is retarded!  scrollbarThickness is already fixed in layout
256     // test mode so that should be enough to result in repeatable results, but
257     // preserving this hack avoids having to rebaseline pixel tests.
258     const int kLayoutTestModeGirth = 17;
259     int girth = ChromiumBridge::layoutTestMode() ? kLayoutTestModeGirth : thickness;
260
261     if (scrollbar->orientation() == HorizontalScrollbar) {
262         int width = scrollbar->width() < 2 * girth ? scrollbar->width() / 2 : girth;
263         return IntSize(width, thickness);
264     }
265
266     int height = scrollbar->height() < 2 * girth ? scrollbar->height() / 2 : girth;
267     return IntSize(thickness, height);
268 }
269
270
271 } // namespace WebCore