OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2008 Apple 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 | |
6 * are met: | |
7 * 1. Redistributions of source code must retain the above copyright | |
8 * notice, this list of conditions and the following disclaimer. | |
9 * 2. Redistributions in binary form must reproduce the above copyright | |
10 * notice, this list of conditions and the following disclaimer in the | |
11 * documentation and/or other materials provided with the distribution. | |
12 * | |
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 */ | |
25 | |
26 #include "config.h" | |
27 #include "ScrollbarThemeWin.h" | |
28 | |
29 #include "GraphicsContext.h" | |
30 #include "LocalWindowsContext.h" | |
31 #include "PlatformMouseEvent.h" | |
32 #include "Scrollbar.h" | |
33 #include "SoftLinking.h" | |
34 #include "SystemInfo.h" | |
35 | |
36 // Generic state constants | |
37 #define TS_NORMAL 1 | |
38 #define TS_HOVER 2 | |
39 #define TS_ACTIVE 3 | |
40 #define TS_DISABLED 4 | |
41 | |
42 #define SP_BUTTON 1 | |
43 #define SP_THUMBHOR 2 | |
44 #define SP_THUMBVERT 3 | |
45 #define SP_TRACKSTARTHOR 4 | |
46 #define SP_TRACKENDHOR 5 | |
47 #define SP_TRACKSTARTVERT 6 | |
48 #define SP_TRACKENDVERT 7 | |
49 #define SP_GRIPPERHOR 8 | |
50 #define SP_GRIPPERVERT 9 | |
51 | |
52 #define TS_UP_BUTTON 0 | |
53 #define TS_DOWN_BUTTON 4 | |
54 #define TS_LEFT_BUTTON 8 | |
55 #define TS_RIGHT_BUTTON 12 | |
56 #define TS_UP_BUTTON_HOVER 17 | |
57 #define TS_DOWN_BUTTON_HOVER 18 | |
58 #define TS_LEFT_BUTTON_HOVER 19 | |
59 #define TS_RIGHT_BUTTON_HOVER 20 | |
60 | |
61 using namespace std; | |
62 | |
63 namespace WebCore { | |
64 | |
65 static HANDLE scrollbarTheme; | |
66 static bool runningVista; | |
67 | |
68 // FIXME: Refactor the soft-linking code so that it can be shared with RenderTh
emeWin | |
69 SOFT_LINK_LIBRARY(uxtheme) | |
70 SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassLi
st), (hwnd, pszClassList)) | |
71 SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme)) | |
72 SOFT_LINK(uxtheme, DrawThemeBackground, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc
, int iPartId, int iStateId, const RECT* pRect, const RECT* pClipRect), (hTheme,
hdc, iPartId, iStateId, pRect, pClipRect)) | |
73 SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ()) | |
74 SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE
hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId)) | |
75 | |
76 // Constants used to figure the drag rect outside which we should snap the | |
77 // scrollbar thumb back to its origin. These calculations are based on | |
78 // observing the behavior of the MSVC8 main window scrollbar + some | |
79 // guessing/extrapolation. | |
80 static const int kOffEndMultiplier = 3; | |
81 static const int kOffSideMultiplier = 8; | |
82 | |
83 static void checkAndInitScrollbarTheme() | |
84 { | |
85 if (uxthemeLibrary() && !scrollbarTheme && IsThemeActive()) | |
86 scrollbarTheme = OpenThemeData(0, L"Scrollbar"); | |
87 } | |
88 | |
89 #if !USE(SAFARI_THEME) | |
90 ScrollbarTheme* ScrollbarTheme::nativeTheme() | |
91 { | |
92 static ScrollbarThemeWin winTheme; | |
93 return &winTheme; | |
94 } | |
95 #endif | |
96 | |
97 ScrollbarThemeWin::ScrollbarThemeWin() | |
98 { | |
99 static bool initialized; | |
100 if (!initialized) { | |
101 initialized = true; | |
102 checkAndInitScrollbarTheme(); | |
103 runningVista = (windowsVersion() >= WindowsVista); | |
104 } | |
105 } | |
106 | |
107 ScrollbarThemeWin::~ScrollbarThemeWin() | |
108 { | |
109 } | |
110 | |
111 int ScrollbarThemeWin::scrollbarThickness(ScrollbarControlSize) | |
112 { | |
113 static int thickness; | |
114 if (!thickness) | |
115 thickness = ::GetSystemMetrics(SM_CXVSCROLL); | |
116 return thickness; | |
117 } | |
118 | |
119 void ScrollbarThemeWin::themeChanged() | |
120 { | |
121 if (!scrollbarTheme) | |
122 return; | |
123 | |
124 CloseThemeData(scrollbarTheme); | |
125 scrollbarTheme = 0; | |
126 } | |
127 | |
128 bool ScrollbarThemeWin::invalidateOnMouseEnterExit() | |
129 { | |
130 return runningVista; | |
131 } | |
132 | |
133 bool ScrollbarThemeWin::hasThumb(ScrollbarThemeClient* scrollbar) | |
134 { | |
135 return thumbLength(scrollbar) > 0; | |
136 } | |
137 | |
138 IntRect ScrollbarThemeWin::backButtonRect(ScrollbarThemeClient* scrollbar, Scrol
lbarPart part, bool) | |
139 { | |
140 // Windows just has single arrows. | |
141 if (part == BackButtonEndPart) | |
142 return IntRect(); | |
143 | |
144 // Our desired rect is essentially 17x17. | |
145 | |
146 // Our actual rect will shrink to half the available space when | |
147 // we have < 34 pixels left. This allows the scrollbar | |
148 // to scale down and function even at tiny sizes. | |
149 int thickness = scrollbarThickness(); | |
150 if (scrollbar->orientation() == HorizontalScrollbar) | |
151 return IntRect(scrollbar->x(), scrollbar->y(), | |
152 scrollbar->width() < 2 * thickness ? scrollbar->width() /
2 : thickness, thickness); | |
153 return IntRect(scrollbar->x(), scrollbar->y(), | |
154 thickness, scrollbar->height() < 2 * thickness ? scrollbar->h
eight() / 2 : thickness); | |
155 } | |
156 | |
157 IntRect ScrollbarThemeWin::forwardButtonRect(ScrollbarThemeClient* scrollbar, Sc
rollbarPart part, bool) | |
158 { | |
159 // Windows just has single arrows. | |
160 if (part == ForwardButtonStartPart) | |
161 return IntRect(); | |
162 | |
163 // Our desired rect is essentially 17x17. | |
164 | |
165 // Our actual rect will shrink to half the available space when | |
166 // we have < 34 pixels left. This allows the scrollbar | |
167 // to scale down and function even at tiny sizes. | |
168 int thickness = scrollbarThickness(); | |
169 if (scrollbar->orientation() == HorizontalScrollbar) { | |
170 int w = scrollbar->width() < 2 * thickness ? scrollbar->width() / 2 : th
ickness; | |
171 return IntRect(scrollbar->x() + scrollbar->width() - w, scrollbar->y(),
w, thickness); | |
172 } | |
173 | |
174 int h = scrollbar->height() < 2 * thickness ? scrollbar->height() / 2 : thic
kness; | |
175 return IntRect(scrollbar->x(), scrollbar->y() + scrollbar->height() - h, thi
ckness, h); | |
176 } | |
177 | |
178 IntRect ScrollbarThemeWin::trackRect(ScrollbarThemeClient* scrollbar, bool) | |
179 { | |
180 int thickness = scrollbarThickness(); | |
181 if (scrollbar->orientation() == HorizontalScrollbar) { | |
182 if (scrollbar->width() < 2 * thickness) | |
183 return IntRect(); | |
184 return IntRect(scrollbar->x() + thickness, scrollbar->y(), scrollbar->wi
dth() - 2 * thickness, thickness); | |
185 } | |
186 if (scrollbar->height() < 2 * thickness) | |
187 return IntRect(); | |
188 return IntRect(scrollbar->x(), scrollbar->y() + thickness, thickness, scroll
bar->height() - 2 * thickness); | |
189 } | |
190 | |
191 bool ScrollbarThemeWin::shouldCenterOnThumb(ScrollbarThemeClient*, const Platfor
mMouseEvent& evt) | |
192 { | |
193 return evt.shiftKey() && evt.button() == LeftButton; | |
194 } | |
195 | |
196 bool ScrollbarThemeWin::shouldSnapBackToDragOrigin(ScrollbarThemeClient* scrollb
ar, const PlatformMouseEvent& evt) | |
197 { | |
198 // Find the rect within which we shouldn't snap, by expanding the track rect | |
199 // in both dimensions. | |
200 IntRect rect = trackRect(scrollbar); | |
201 const bool horz = scrollbar->orientation() == HorizontalScrollbar; | |
202 const int thickness = scrollbarThickness(scrollbar->controlSize()); | |
203 rect.inflateX((horz ? kOffEndMultiplier : kOffSideMultiplier) * thickness); | |
204 rect.inflateY((horz ? kOffSideMultiplier : kOffEndMultiplier) * thickness); | |
205 | |
206 // Convert the event to local coordinates. | |
207 IntPoint mousePosition = scrollbar->convertFromContainingWindow(evt.position
()); | |
208 mousePosition.move(scrollbar->x(), scrollbar->y()); | |
209 | |
210 // We should snap iff the event is outside our calculated rect. | |
211 return !rect.contains(mousePosition); | |
212 } | |
213 | |
214 void ScrollbarThemeWin::paintTrackBackground(GraphicsContext* context, Scrollbar
ThemeClient* scrollbar, const IntRect& rect) | |
215 { | |
216 // Just assume a forward track part. We only paint the track as a single pi
ece when there is no thumb. | |
217 if (!hasThumb(scrollbar)) | |
218 paintTrackPiece(context, scrollbar, rect, ForwardTrackPart); | |
219 } | |
220 | |
221 void ScrollbarThemeWin::paintTrackPiece(GraphicsContext* context, ScrollbarTheme
Client* scrollbar, const IntRect& rect, ScrollbarPart partType) | |
222 { | |
223 checkAndInitScrollbarTheme(); | |
224 | |
225 bool start = partType == BackTrackPart; | |
226 int part; | |
227 if (scrollbar->orientation() == HorizontalScrollbar) | |
228 part = start ? SP_TRACKSTARTHOR : SP_TRACKENDHOR; | |
229 else | |
230 part = start ? SP_TRACKSTARTVERT : SP_TRACKENDVERT; | |
231 | |
232 int state; | |
233 if (!scrollbar->enabled()) | |
234 state = TS_DISABLED; | |
235 else if ((scrollbar->hoveredPart() == BackTrackPart && start) || | |
236 (scrollbar->hoveredPart() == ForwardTrackPart && !start)) | |
237 state = (scrollbar->pressedPart() == scrollbar->hoveredPart() ? TS_ACTIV
E : TS_HOVER); | |
238 else | |
239 state = TS_NORMAL; | |
240 | |
241 bool alphaBlend = false; | |
242 if (scrollbarTheme) | |
243 alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, part,
state); | |
244 | |
245 LocalWindowsContext windowsContext(context, rect, alphaBlend); | |
246 RECT themeRect(rect); | |
247 | |
248 if (scrollbarTheme) | |
249 DrawThemeBackground(scrollbarTheme, windowsContext.hdc(), part, state, &
themeRect, 0); | |
250 else { | |
251 DWORD color3DFace = ::GetSysColor(COLOR_3DFACE); | |
252 DWORD colorScrollbar = ::GetSysColor(COLOR_SCROLLBAR); | |
253 DWORD colorWindow = ::GetSysColor(COLOR_WINDOW); | |
254 HDC hdc = windowsContext.hdc(); | |
255 if ((color3DFace != colorScrollbar) && (colorWindow != colorScrollbar)) | |
256 ::FillRect(hdc, &themeRect, HBRUSH(COLOR_SCROLLBAR+1)); | |
257 else { | |
258 static WORD patternBits[8] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0
xaa, 0x55 }; | |
259 HBITMAP patternBitmap = ::CreateBitmap(8, 8, 1, 1, patternBits); | |
260 HBRUSH brush = ::CreatePatternBrush(patternBitmap); | |
261 SaveDC(hdc); | |
262 ::SetTextColor(hdc, ::GetSysColor(COLOR_3DHILIGHT)); | |
263 ::SetBkColor(hdc, ::GetSysColor(COLOR_3DFACE)); | |
264 ::SetBrushOrgEx(hdc, rect.x(), rect.y(), NULL); | |
265 ::SelectObject(hdc, brush); | |
266 ::FillRect(hdc, &themeRect, brush); | |
267 ::RestoreDC(hdc, -1); | |
268 ::DeleteObject(brush); | |
269 ::DeleteObject(patternBitmap); | |
270 } | |
271 } | |
272 | |
273 #if !OS(WINCE) | |
274 if (!alphaBlend && !context->isInTransparencyLayer()) | |
275 DIBPixelData::setRGBABitmapAlpha(windowsContext.hdc(), rect, 255); | |
276 #endif | |
277 } | |
278 | |
279 void ScrollbarThemeWin::paintButton(GraphicsContext* context, ScrollbarThemeClie
nt* scrollbar, const IntRect& rect, ScrollbarPart part) | |
280 { | |
281 checkAndInitScrollbarTheme(); | |
282 | |
283 bool start = (part == BackButtonStartPart); | |
284 int xpState = 0; | |
285 int classicState = 0; | |
286 if (scrollbar->orientation() == HorizontalScrollbar) | |
287 xpState = start ? TS_LEFT_BUTTON : TS_RIGHT_BUTTON; | |
288 else | |
289 xpState = start ? TS_UP_BUTTON : TS_DOWN_BUTTON; | |
290 classicState = xpState / 4; | |
291 | |
292 if (!scrollbar->enabled()) { | |
293 xpState += TS_DISABLED; | |
294 classicState |= DFCS_INACTIVE; | |
295 } else if ((scrollbar->hoveredPart() == BackButtonStartPart && start) || | |
296 (scrollbar->hoveredPart() == ForwardButtonEndPart && !start)) { | |
297 if (scrollbar->pressedPart() == scrollbar->hoveredPart()) { | |
298 xpState += TS_ACTIVE; | |
299 classicState |= DFCS_PUSHED; | |
300 #if !OS(WINCE) | |
301 classicState |= DFCS_FLAT; | |
302 #endif | |
303 } else | |
304 xpState += TS_HOVER; | |
305 } else { | |
306 if (scrollbar->hoveredPart() == NoPart || !runningVista) | |
307 xpState += TS_NORMAL; | |
308 else { | |
309 if (scrollbar->orientation() == HorizontalScrollbar) | |
310 xpState = start ? TS_LEFT_BUTTON_HOVER : TS_RIGHT_BUTTON_HOVER; | |
311 else | |
312 xpState = start ? TS_UP_BUTTON_HOVER : TS_DOWN_BUTTON_HOVER; | |
313 } | |
314 } | |
315 | |
316 bool alphaBlend = false; | |
317 if (scrollbarTheme) | |
318 alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, SP_BU
TTON, xpState); | |
319 | |
320 LocalWindowsContext windowsContext(context, rect, alphaBlend); | |
321 RECT themeRect(rect); | |
322 if (scrollbarTheme) | |
323 DrawThemeBackground(scrollbarTheme, windowsContext.hdc(), SP_BUTTON, xpS
tate, &themeRect, 0); | |
324 else | |
325 ::DrawFrameControl(windowsContext.hdc(), &themeRect, DFC_SCROLL, classic
State); | |
326 | |
327 #if !OS(WINCE) | |
328 if (!alphaBlend && !context->isInTransparencyLayer()) | |
329 DIBPixelData::setRGBABitmapAlpha(windowsContext.hdc(), rect, 255); | |
330 #endif | |
331 } | |
332 | |
333 static IntRect gripperRect(int thickness, const IntRect& thumbRect) | |
334 { | |
335 // Center in the thumb. | |
336 int gripperThickness = thickness / 2; | |
337 return IntRect(thumbRect.x() + (thumbRect.width() - gripperThickness) / 2, | |
338 thumbRect.y() + (thumbRect.height() - gripperThickness) / 2, | |
339 gripperThickness, gripperThickness); | |
340 } | |
341 | |
342 static void paintGripper(ScrollbarThemeClient* scrollbar, HDC hdc, const IntRect
& rect) | |
343 { | |
344 if (!scrollbarTheme) | |
345 return; // Classic look has no gripper. | |
346 | |
347 int state; | |
348 if (!scrollbar->enabled()) | |
349 state = TS_DISABLED; | |
350 else if (scrollbar->pressedPart() == ThumbPart) | |
351 state = TS_ACTIVE; // Thumb always stays active once pressed. | |
352 else if (scrollbar->hoveredPart() == ThumbPart) | |
353 state = TS_HOVER; | |
354 else | |
355 state = TS_NORMAL; | |
356 | |
357 RECT themeRect(rect); | |
358 DrawThemeBackground(scrollbarTheme, hdc, scrollbar->orientation() == Horizon
talScrollbar ? SP_GRIPPERHOR : SP_GRIPPERVERT, state, &themeRect, 0); | |
359 } | |
360 | |
361 void ScrollbarThemeWin::paintThumb(GraphicsContext* context, ScrollbarThemeClien
t* scrollbar, const IntRect& rect) | |
362 { | |
363 checkAndInitScrollbarTheme(); | |
364 | |
365 int state; | |
366 if (!scrollbar->enabled()) | |
367 state = TS_DISABLED; | |
368 else if (scrollbar->pressedPart() == ThumbPart) | |
369 state = TS_ACTIVE; // Thumb always stays active once pressed. | |
370 else if (scrollbar->hoveredPart() == ThumbPart) | |
371 state = TS_HOVER; | |
372 else | |
373 state = TS_NORMAL; | |
374 | |
375 bool alphaBlend = false; | |
376 if (scrollbarTheme) | |
377 alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, scrol
lbar->orientation() == HorizontalScrollbar ? SP_THUMBHOR : SP_THUMBVERT, state); | |
378 LocalWindowsContext windowsContext(context, rect, alphaBlend); | |
379 RECT themeRect(rect); | |
380 if (scrollbarTheme) { | |
381 DrawThemeBackground(scrollbarTheme, windowsContext.hdc(), scrollbar->ori
entation() == HorizontalScrollbar ? SP_THUMBHOR : SP_THUMBVERT, state, &themeRec
t, 0); | |
382 paintGripper(scrollbar, windowsContext.hdc(), gripperRect(scrollbarThick
ness(), rect)); | |
383 } else | |
384 ::DrawEdge(windowsContext.hdc(), &themeRect, EDGE_RAISED, BF_RECT | BF_M
IDDLE); | |
385 | |
386 #if !OS(WINCE) | |
387 if (!alphaBlend && !context->isInTransparencyLayer()) | |
388 DIBPixelData::setRGBABitmapAlpha(windowsContext.hdc(), rect, 255); | |
389 #endif | |
390 } | |
391 | |
392 } | |
393 | |
OLD | NEW |