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 "GraphicsContext.h"
34 #include "PlatformBridge.h"
35 #include "PlatformContextSkia.h"
36 #include "PlatformMouseEvent.h"
37 #include "Scrollbar.h"
38 #include "SystemInfo.h"
39
40 namespace WebCore {
41
nativeTheme()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
scrollbarThickness(ScrollbarControlSize controlSize)60 int ScrollbarThemeChromiumWin::scrollbarThickness(ScrollbarControlSize controlSize)
61 {
62 static int thickness;
63 if (!thickness) {
64 if (PlatformBridge::layoutTestMode())
65 return kMacScrollbarSize[controlSize];
66 thickness = GetSystemMetrics(SM_CXVSCROLL);
67 }
68 return thickness;
69 }
70
invalidateOnMouseEnterExit()71 bool ScrollbarThemeChromiumWin::invalidateOnMouseEnterExit()
72 {
73 return windowsVersion() >= WindowsVista;
74 }
75
shouldSnapBackToDragOrigin(Scrollbar * scrollbar,const PlatformMouseEvent & evt)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
paintTrackPiece(GraphicsContext * gc,Scrollbar * scrollbar,const IntRect & rect,ScrollbarPart partType)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 PlatformBridge::paintScrollbarTrack(
108 gc,
109 partId,
110 getThemeState(scrollbar, partType),
111 getClassicThemeState(scrollbar, partType),
112 rect,
113 alignRect);
114 }
115
paintButton(GraphicsContext * gc,Scrollbar * scrollbar,const IntRect & rect,ScrollbarPart part)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 PlatformBridge::paintScrollbarArrow(
128 gc,
129 getThemeArrowState(scrollbar, part),
130 partId | getClassicThemeState(scrollbar, part),
131 rect);
132 }
133
paintThumb(GraphicsContext * gc,Scrollbar * scrollbar,const IntRect & rect)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 PlatformBridge::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 PlatformBridge::paintScrollbarThumb(
148 gc,
149 horz ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT,
150 getThemeState(scrollbar, ThumbPart),
151 getClassicThemeState(scrollbar, ThumbPart),
152 rect);
153 }
154
getThemeState(Scrollbar * scrollbar,ScrollbarPart part) const155 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 (windowsVersion() < WindowsVista) ? SCRBS_NORMAL : SCRBS_HOVER;
164 }
165 if (!scrollbar->enabled())
166 return SCRBS_DISABLED;
167 if (scrollbar->hoveredPart() != part || part == BackTrackPart || part == ForwardTrackPart)
168 return (scrollbar->hoveredPart() == NoPart || (windowsVersion() < WindowsVista)) ? SCRBS_NORMAL : SCRBS_HOVER;
169 if (scrollbar->pressedPart() == NoPart)
170 return SCRBS_HOT;
171 return (scrollbar->pressedPart() == part) ? SCRBS_PRESSED : SCRBS_NORMAL;
172 }
173
getThemeArrowState(Scrollbar * scrollbar,ScrollbarPart part) const174 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 (windowsVersion() < WindowsVista) ? ABS_LEFTNORMAL : ABS_LEFTHOVER;
183 if (!scrollbar->enabled())
184 return ABS_LEFTDISABLED;
185 if (scrollbar->hoveredPart() != part)
186 return ((scrollbar->hoveredPart() == NoPart) || (windowsVersion() < WindowsVista)) ? 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 (windowsVersion() < WindowsVista) ? ABS_UPNORMAL : ABS_UPHOVER;
194 if (!scrollbar->enabled())
195 return ABS_UPDISABLED;
196 if (scrollbar->hoveredPart() != part)
197 return ((scrollbar->hoveredPart() == NoPart) || (windowsVersion() < WindowsVista)) ? 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 (windowsVersion() < WindowsVista) ? ABS_RIGHTNORMAL : ABS_RIGHTHOVER;
205 if (!scrollbar->enabled())
206 return ABS_RIGHTDISABLED;
207 if (scrollbar->hoveredPart() != part)
208 return ((scrollbar->hoveredPart() == NoPart) || (windowsVersion() < WindowsVista)) ? 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 (windowsVersion() < WindowsVista) ? ABS_DOWNNORMAL : ABS_DOWNHOVER;
215 if (!scrollbar->enabled())
216 return ABS_DOWNDISABLED;
217 if (scrollbar->hoveredPart() != part)
218 return ((scrollbar->hoveredPart() == NoPart) || (windowsVersion() < WindowsVista)) ? ABS_DOWNNORMAL : ABS_DOWNHOVER;
219 if (scrollbar->pressedPart() == NoPart)
220 return ABS_DOWNHOT;
221 return (scrollbar->pressedPart() == part) ? ABS_DOWNPRESSED : ABS_DOWNNORMAL;
222 }
223
getClassicThemeState(Scrollbar * scrollbar,ScrollbarPart part) const224 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
shouldCenterOnThumb(Scrollbar *,const PlatformMouseEvent & evt)238 bool ScrollbarThemeChromiumWin::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt)
239 {
240 return evt.shiftKey() && evt.button() == LeftButton;
241 }
242
buttonSize(Scrollbar * scrollbar)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 = PlatformBridge::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
272