• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 RenderThemeWin
69 SOFT_LINK_LIBRARY(uxtheme)
70 SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (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 
checkAndInitScrollbarTheme()83 static void checkAndInitScrollbarTheme()
84 {
85     if (uxthemeLibrary() && !scrollbarTheme && IsThemeActive())
86         scrollbarTheme = OpenThemeData(0, L"Scrollbar");
87 }
88 
89 #if !USE(SAFARI_THEME)
nativeTheme()90 ScrollbarTheme* ScrollbarTheme::nativeTheme()
91 {
92     static ScrollbarThemeWin winTheme;
93     return &winTheme;
94 }
95 #endif
96 
ScrollbarThemeWin()97 ScrollbarThemeWin::ScrollbarThemeWin()
98 {
99     static bool initialized;
100     if (!initialized) {
101         initialized = true;
102         checkAndInitScrollbarTheme();
103         runningVista = (windowsVersion() >= WindowsVista);
104     }
105 }
106 
~ScrollbarThemeWin()107 ScrollbarThemeWin::~ScrollbarThemeWin()
108 {
109 }
110 
scrollbarThickness(ScrollbarControlSize)111 int ScrollbarThemeWin::scrollbarThickness(ScrollbarControlSize)
112 {
113     static int thickness;
114     if (!thickness)
115         thickness = ::GetSystemMetrics(SM_CXVSCROLL);
116     return thickness;
117 }
118 
themeChanged()119 void ScrollbarThemeWin::themeChanged()
120 {
121     if (!scrollbarTheme)
122         return;
123 
124     CloseThemeData(scrollbarTheme);
125     scrollbarTheme = 0;
126 }
127 
invalidateOnMouseEnterExit()128 bool ScrollbarThemeWin::invalidateOnMouseEnterExit()
129 {
130     return runningVista;
131 }
132 
hasThumb(Scrollbar * scrollbar)133 bool ScrollbarThemeWin::hasThumb(Scrollbar* scrollbar)
134 {
135     return thumbLength(scrollbar) > 0;
136 }
137 
backButtonRect(Scrollbar * scrollbar,ScrollbarPart part,bool)138 IntRect ScrollbarThemeWin::backButtonRect(Scrollbar* scrollbar, ScrollbarPart 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->height() / 2 : thickness);
155 }
156 
forwardButtonRect(Scrollbar * scrollbar,ScrollbarPart part,bool)157 IntRect ScrollbarThemeWin::forwardButtonRect(Scrollbar* scrollbar, ScrollbarPart 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 : thickness;
171         return IntRect(scrollbar->x() + scrollbar->width() - w, scrollbar->y(), w, thickness);
172     }
173 
174     int h = scrollbar->height() < 2 * thickness ? scrollbar->height() / 2 : thickness;
175     return IntRect(scrollbar->x(), scrollbar->y() + scrollbar->height() - h, thickness, h);
176 }
177 
trackRect(Scrollbar * scrollbar,bool)178 IntRect ScrollbarThemeWin::trackRect(Scrollbar* 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->width() - 2 * thickness, thickness);
185     }
186     if (scrollbar->height() < 2 * thickness)
187         return IntRect();
188     return IntRect(scrollbar->x(), scrollbar->y() + thickness, thickness, scrollbar->height() - 2 * thickness);
189 }
190 
shouldCenterOnThumb(Scrollbar *,const PlatformMouseEvent & evt)191 bool ScrollbarThemeWin::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt)
192 {
193     return evt.shiftKey() && evt.button() == LeftButton;
194 }
195 
shouldSnapBackToDragOrigin(Scrollbar * scrollbar,const PlatformMouseEvent & evt)196 bool ScrollbarThemeWin::shouldSnapBackToDragOrigin(Scrollbar* scrollbar, 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.pos());
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 
paintTrackBackground(GraphicsContext * context,Scrollbar * scrollbar,const IntRect & rect)214 void ScrollbarThemeWin::paintTrackBackground(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect)
215 {
216     // Just assume a forward track part.  We only paint the track as a single piece when there is no thumb.
217     if (!hasThumb(scrollbar))
218         paintTrackPiece(context, scrollbar, rect, ForwardTrackPart);
219 }
220 
paintTrackPiece(GraphicsContext * context,Scrollbar * scrollbar,const IntRect & rect,ScrollbarPart partType)221 void ScrollbarThemeWin::paintTrackPiece(GraphicsContext* context, Scrollbar* 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_ACTIVE : 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, 0xaa, 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 
paintButton(GraphicsContext * context,Scrollbar * scrollbar,const IntRect & rect,ScrollbarPart part)274 void ScrollbarThemeWin::paintButton(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part)
275 {
276     checkAndInitScrollbarTheme();
277 
278     bool start = (part == BackButtonStartPart);
279     int xpState = 0;
280     int classicState = 0;
281     if (scrollbar->orientation() == HorizontalScrollbar)
282         xpState = start ? TS_LEFT_BUTTON : TS_RIGHT_BUTTON;
283     else
284         xpState = start ? TS_UP_BUTTON : TS_DOWN_BUTTON;
285     classicState = xpState / 4;
286 
287     if (!scrollbar->enabled()) {
288         xpState += TS_DISABLED;
289         classicState |= DFCS_INACTIVE;
290     } else if ((scrollbar->hoveredPart() == BackButtonStartPart && start) ||
291                (scrollbar->hoveredPart() == ForwardButtonEndPart && !start)) {
292         if (scrollbar->pressedPart() == scrollbar->hoveredPart()) {
293             xpState += TS_ACTIVE;
294             classicState |= DFCS_PUSHED;
295 #if !OS(WINCE)
296             classicState |= DFCS_FLAT;
297 #endif
298         } else
299             xpState += TS_HOVER;
300     } else {
301         if (scrollbar->hoveredPart() == NoPart || !runningVista)
302             xpState += TS_NORMAL;
303         else {
304             if (scrollbar->orientation() == HorizontalScrollbar)
305                 xpState = start ? TS_LEFT_BUTTON_HOVER : TS_RIGHT_BUTTON_HOVER;
306             else
307                 xpState = start ? TS_UP_BUTTON_HOVER : TS_DOWN_BUTTON_HOVER;
308         }
309     }
310 
311     bool alphaBlend = false;
312     if (scrollbarTheme)
313         alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, SP_BUTTON, xpState);
314 
315     LocalWindowsContext windowsContext(context, rect, alphaBlend);
316     RECT themeRect(rect);
317     if (scrollbarTheme)
318         DrawThemeBackground(scrollbarTheme, windowsContext.hdc(), SP_BUTTON, xpState, &themeRect, 0);
319     else
320         ::DrawFrameControl(windowsContext.hdc(), &themeRect, DFC_SCROLL, classicState);
321 }
322 
gripperRect(int thickness,const IntRect & thumbRect)323 static IntRect gripperRect(int thickness, const IntRect& thumbRect)
324 {
325     // Center in the thumb.
326     int gripperThickness = thickness / 2;
327     return IntRect(thumbRect.x() + (thumbRect.width() - gripperThickness) / 2,
328                    thumbRect.y() + (thumbRect.height() - gripperThickness) / 2,
329                    gripperThickness, gripperThickness);
330 }
331 
paintGripper(Scrollbar * scrollbar,HDC hdc,const IntRect & rect)332 static void paintGripper(Scrollbar* scrollbar, HDC hdc, const IntRect& rect)
333 {
334     if (!scrollbarTheme)
335         return;  // Classic look has no gripper.
336 
337     int state;
338     if (!scrollbar->enabled())
339         state = TS_DISABLED;
340     else if (scrollbar->pressedPart() == ThumbPart)
341         state = TS_ACTIVE; // Thumb always stays active once pressed.
342     else if (scrollbar->hoveredPart() == ThumbPart)
343         state = TS_HOVER;
344     else
345         state = TS_NORMAL;
346 
347     RECT themeRect(rect);
348     DrawThemeBackground(scrollbarTheme, hdc, scrollbar->orientation() == HorizontalScrollbar ? SP_GRIPPERHOR : SP_GRIPPERVERT, state, &themeRect, 0);
349 }
350 
paintThumb(GraphicsContext * context,Scrollbar * scrollbar,const IntRect & rect)351 void ScrollbarThemeWin::paintThumb(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect)
352 {
353     checkAndInitScrollbarTheme();
354 
355     int state;
356     if (!scrollbar->enabled())
357         state = TS_DISABLED;
358     else if (scrollbar->pressedPart() == ThumbPart)
359         state = TS_ACTIVE; // Thumb always stays active once pressed.
360     else if (scrollbar->hoveredPart() == ThumbPart)
361         state = TS_HOVER;
362     else
363         state = TS_NORMAL;
364 
365     bool alphaBlend = false;
366     if (scrollbarTheme)
367         alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, scrollbar->orientation() == HorizontalScrollbar ? SP_THUMBHOR : SP_THUMBVERT, state);
368     HDC hdc = context->getWindowsContext(rect, alphaBlend);
369     RECT themeRect(rect);
370     if (scrollbarTheme) {
371         DrawThemeBackground(scrollbarTheme, hdc, scrollbar->orientation() == HorizontalScrollbar ? SP_THUMBHOR : SP_THUMBVERT, state, &themeRect, 0);
372         paintGripper(scrollbar, hdc, gripperRect(scrollbarThickness(), rect));
373     } else
374         ::DrawEdge(hdc, &themeRect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
375     context->releaseWindowsContext(hdc, rect, alphaBlend);
376 }
377 
378 }
379 
380