• 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 "PlatformMouseEvent.h"
31 #include "Scrollbar.h"
32 #include "SoftLinking.h"
33 #include "SystemInfo.h"
34 
35 // Generic state constants
36 #define TS_NORMAL    1
37 #define TS_HOVER     2
38 #define TS_ACTIVE    3
39 #define TS_DISABLED  4
40 
41 #define SP_BUTTON          1
42 #define SP_THUMBHOR        2
43 #define SP_THUMBVERT       3
44 #define SP_TRACKSTARTHOR   4
45 #define SP_TRACKENDHOR     5
46 #define SP_TRACKSTARTVERT  6
47 #define SP_TRACKENDVERT    7
48 #define SP_GRIPPERHOR      8
49 #define SP_GRIPPERVERT     9
50 
51 #define TS_UP_BUTTON       0
52 #define TS_DOWN_BUTTON     4
53 #define TS_LEFT_BUTTON     8
54 #define TS_RIGHT_BUTTON    12
55 #define TS_UP_BUTTON_HOVER   17
56 #define TS_DOWN_BUTTON_HOVER  18
57 #define TS_LEFT_BUTTON_HOVER  19
58 #define TS_RIGHT_BUTTON_HOVER   20
59 
60 using namespace std;
61 
62 namespace WebCore {
63 
64 static HANDLE scrollbarTheme;
65 static bool runningVista;
66 
67 // FIXME:  Refactor the soft-linking code so that it can be shared with RenderThemeWin
68 SOFT_LINK_LIBRARY(uxtheme)
69 SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList))
70 SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme))
71 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))
72 SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ())
73 SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId))
74 
75 // Constants used to figure the drag rect outside which we should snap the
76 // scrollbar thumb back to its origin.  These calculations are based on
77 // observing the behavior of the MSVC8 main window scrollbar + some
78 // guessing/extrapolation.
79 static const int kOffEndMultiplier = 3;
80 static const int kOffSideMultiplier = 8;
81 
checkAndInitScrollbarTheme()82 static void checkAndInitScrollbarTheme()
83 {
84     if (uxthemeLibrary() && !scrollbarTheme && IsThemeActive())
85         scrollbarTheme = OpenThemeData(0, L"Scrollbar");
86 }
87 
88 #if !USE(SAFARI_THEME)
nativeTheme()89 ScrollbarTheme* ScrollbarTheme::nativeTheme()
90 {
91     static ScrollbarThemeWin winTheme;
92     return &winTheme;
93 }
94 #endif
95 
ScrollbarThemeWin()96 ScrollbarThemeWin::ScrollbarThemeWin()
97 {
98     static bool initialized;
99     if (!initialized) {
100         initialized = true;
101         checkAndInitScrollbarTheme();
102         runningVista = isRunningOnVistaOrLater();
103     }
104 }
105 
~ScrollbarThemeWin()106 ScrollbarThemeWin::~ScrollbarThemeWin()
107 {
108 }
109 
scrollbarThickness(ScrollbarControlSize)110 int ScrollbarThemeWin::scrollbarThickness(ScrollbarControlSize)
111 {
112     static int thickness;
113     if (!thickness)
114         thickness = ::GetSystemMetrics(SM_CXVSCROLL);
115     return thickness;
116 }
117 
themeChanged()118 void ScrollbarThemeWin::themeChanged()
119 {
120     if (!scrollbarTheme)
121         return;
122 
123     CloseThemeData(scrollbarTheme);
124     scrollbarTheme = 0;
125 }
126 
invalidateOnMouseEnterExit()127 bool ScrollbarThemeWin::invalidateOnMouseEnterExit()
128 {
129     return runningVista;
130 }
131 
hasThumb(Scrollbar * scrollbar)132 bool ScrollbarThemeWin::hasThumb(Scrollbar* scrollbar)
133 {
134     return thumbLength(scrollbar) > 0;
135 }
136 
backButtonRect(Scrollbar * scrollbar,ScrollbarPart part,bool)137 IntRect ScrollbarThemeWin::backButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool)
138 {
139     // Windows just has single arrows.
140     if (part == BackButtonEndPart)
141         return IntRect();
142 
143     // Our desired rect is essentially 17x17.
144 
145     // Our actual rect will shrink to half the available space when
146     // we have < 34 pixels left.  This allows the scrollbar
147     // to scale down and function even at tiny sizes.
148     int thickness = scrollbarThickness();
149     if (scrollbar->orientation() == HorizontalScrollbar)
150         return IntRect(scrollbar->x(), scrollbar->y(),
151                        scrollbar->width() < 2 * thickness ? scrollbar->width() / 2 : thickness, thickness);
152     return IntRect(scrollbar->x(), scrollbar->y(),
153                    thickness, scrollbar->height() < 2 * thickness ? scrollbar->height() / 2 : thickness);
154 }
155 
forwardButtonRect(Scrollbar * scrollbar,ScrollbarPart part,bool)156 IntRect ScrollbarThemeWin::forwardButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool)
157 {
158     // Windows just has single arrows.
159     if (part == ForwardButtonStartPart)
160         return IntRect();
161 
162     // Our desired rect is essentially 17x17.
163 
164     // Our actual rect will shrink to half the available space when
165     // we have < 34 pixels left.  This allows the scrollbar
166     // to scale down and function even at tiny sizes.
167     int thickness = scrollbarThickness();
168     if (scrollbar->orientation() == HorizontalScrollbar) {
169         int w = scrollbar->width() < 2 * thickness ? scrollbar->width() / 2 : thickness;
170         return IntRect(scrollbar->x() + scrollbar->width() - w, scrollbar->y(), w, thickness);
171     }
172 
173     int h = scrollbar->height() < 2 * thickness ? scrollbar->height() / 2 : thickness;
174     return IntRect(scrollbar->x(), scrollbar->y() + scrollbar->height() - h, thickness, h);
175 }
176 
trackRect(Scrollbar * scrollbar,bool)177 IntRect ScrollbarThemeWin::trackRect(Scrollbar* scrollbar, bool)
178 {
179     int thickness = scrollbarThickness();
180     if (scrollbar->orientation() == HorizontalScrollbar) {
181         if (scrollbar->width() < 2 * thickness)
182             return IntRect();
183         return IntRect(scrollbar->x() + thickness, scrollbar->y(), scrollbar->width() - 2 * thickness, thickness);
184     }
185     if (scrollbar->height() < 2 * thickness)
186         return IntRect();
187     return IntRect(scrollbar->x(), scrollbar->y() + thickness, thickness, scrollbar->height() - 2 * thickness);
188 }
189 
shouldCenterOnThumb(Scrollbar *,const PlatformMouseEvent & evt)190 bool ScrollbarThemeWin::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt)
191 {
192     return evt.shiftKey() && evt.button() == LeftButton;
193 }
194 
shouldSnapBackToDragOrigin(Scrollbar * scrollbar,const PlatformMouseEvent & evt)195 bool ScrollbarThemeWin::shouldSnapBackToDragOrigin(Scrollbar* scrollbar, const PlatformMouseEvent& evt)
196 {
197     // Find the rect within which we shouldn't snap, by expanding the track rect
198     // in both dimensions.
199     IntRect rect = trackRect(scrollbar);
200     const bool horz = scrollbar->orientation() == HorizontalScrollbar;
201     const int thickness = scrollbarThickness(scrollbar->controlSize());
202     rect.inflateX((horz ? kOffEndMultiplier : kOffSideMultiplier) * thickness);
203     rect.inflateY((horz ? kOffSideMultiplier : kOffEndMultiplier) * thickness);
204 
205     // Convert the event to local coordinates.
206     IntPoint mousePosition = scrollbar->convertFromContainingWindow(evt.pos());
207     mousePosition.move(scrollbar->x(), scrollbar->y());
208 
209     // We should snap iff the event is outside our calculated rect.
210     return !rect.contains(mousePosition);
211 }
212 
paintTrackBackground(GraphicsContext * context,Scrollbar * scrollbar,const IntRect & rect)213 void ScrollbarThemeWin::paintTrackBackground(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect)
214 {
215     // Just assume a forward track part.  We only paint the track as a single piece when there is no thumb.
216     if (!hasThumb(scrollbar))
217         paintTrackPiece(context, scrollbar, rect, ForwardTrackPart);
218 }
219 
paintTrackPiece(GraphicsContext * context,Scrollbar * scrollbar,const IntRect & rect,ScrollbarPart partType)220 void ScrollbarThemeWin::paintTrackPiece(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart partType)
221 {
222     checkAndInitScrollbarTheme();
223 
224     bool start = partType == BackTrackPart;
225     int part;
226     if (scrollbar->orientation() == HorizontalScrollbar)
227         part = start ? SP_TRACKSTARTHOR : SP_TRACKENDHOR;
228     else
229         part = start ? SP_TRACKSTARTVERT : SP_TRACKENDVERT;
230 
231     int state;
232     if (!scrollbar->enabled())
233         state = TS_DISABLED;
234     else if ((scrollbar->hoveredPart() == BackTrackPart && start) ||
235              (scrollbar->hoveredPart() == ForwardTrackPart && !start))
236         state = (scrollbar->pressedPart() == scrollbar->hoveredPart() ? TS_ACTIVE : TS_HOVER);
237     else
238         state = TS_NORMAL;
239 
240     bool alphaBlend = false;
241     if (scrollbarTheme)
242         alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, part, state);
243     HDC hdc = context->getWindowsContext(rect, alphaBlend);
244     RECT themeRect(rect);
245     if (scrollbarTheme)
246         DrawThemeBackground(scrollbarTheme, hdc, part, state, &themeRect, 0);
247     else {
248         DWORD color3DFace = ::GetSysColor(COLOR_3DFACE);
249         DWORD colorScrollbar = ::GetSysColor(COLOR_SCROLLBAR);
250         DWORD colorWindow = ::GetSysColor(COLOR_WINDOW);
251         if ((color3DFace != colorScrollbar) && (colorWindow != colorScrollbar))
252             ::FillRect(hdc, &themeRect, HBRUSH(COLOR_SCROLLBAR+1));
253         else {
254             static WORD patternBits[8] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
255             HBITMAP patternBitmap = ::CreateBitmap(8, 8, 1, 1, patternBits);
256             HBRUSH brush = ::CreatePatternBrush(patternBitmap);
257             SaveDC(hdc);
258             ::SetTextColor(hdc, ::GetSysColor(COLOR_3DHILIGHT));
259             ::SetBkColor(hdc, ::GetSysColor(COLOR_3DFACE));
260             ::SetBrushOrgEx(hdc, rect.x(), rect.y(), NULL);
261             ::SelectObject(hdc, brush);
262             ::FillRect(hdc, &themeRect, brush);
263             ::RestoreDC(hdc, -1);
264             ::DeleteObject(brush);
265             ::DeleteObject(patternBitmap);
266         }
267     }
268     context->releaseWindowsContext(hdc, rect, alphaBlend);
269 }
270 
paintButton(GraphicsContext * context,Scrollbar * scrollbar,const IntRect & rect,ScrollbarPart part)271 void ScrollbarThemeWin::paintButton(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part)
272 {
273     checkAndInitScrollbarTheme();
274 
275     bool start = (part == BackButtonStartPart);
276     int xpState = 0;
277     int classicState = 0;
278     if (scrollbar->orientation() == HorizontalScrollbar)
279         xpState = start ? TS_LEFT_BUTTON : TS_RIGHT_BUTTON;
280     else
281         xpState = start ? TS_UP_BUTTON : TS_DOWN_BUTTON;
282     classicState = xpState / 4;
283 
284     if (!scrollbar->enabled()) {
285         xpState += TS_DISABLED;
286         classicState |= DFCS_INACTIVE;
287     } else if ((scrollbar->hoveredPart() == BackButtonStartPart && start) ||
288                (scrollbar->hoveredPart() == ForwardButtonEndPart && !start)) {
289         if (scrollbar->pressedPart() == scrollbar->hoveredPart()) {
290             xpState += TS_ACTIVE;
291             classicState |= DFCS_PUSHED | DFCS_FLAT;
292         } else
293             xpState += TS_HOVER;
294     } else {
295         if (scrollbar->hoveredPart() == NoPart || !runningVista)
296             xpState += TS_NORMAL;
297         else {
298             if (scrollbar->orientation() == HorizontalScrollbar)
299                 xpState = start ? TS_LEFT_BUTTON_HOVER : TS_RIGHT_BUTTON_HOVER;
300             else
301                 xpState = start ? TS_UP_BUTTON_HOVER : TS_DOWN_BUTTON_HOVER;
302         }
303     }
304 
305     bool alphaBlend = false;
306     if (scrollbarTheme)
307         alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, SP_BUTTON, xpState);
308     HDC hdc = context->getWindowsContext(rect, alphaBlend);
309 
310     RECT themeRect(rect);
311     if (scrollbarTheme)
312         DrawThemeBackground(scrollbarTheme, hdc, SP_BUTTON, xpState, &themeRect, 0);
313     else
314         ::DrawFrameControl(hdc, &themeRect, DFC_SCROLL, classicState);
315     context->releaseWindowsContext(hdc, rect, alphaBlend);
316 }
317 
gripperRect(int thickness,const IntRect & thumbRect)318 static IntRect gripperRect(int thickness, const IntRect& thumbRect)
319 {
320     // Center in the thumb.
321     int gripperThickness = thickness / 2;
322     return IntRect(thumbRect.x() + (thumbRect.width() - gripperThickness) / 2,
323                    thumbRect.y() + (thumbRect.height() - gripperThickness) / 2,
324                    gripperThickness, gripperThickness);
325 }
326 
paintGripper(Scrollbar * scrollbar,HDC hdc,const IntRect & rect)327 static void paintGripper(Scrollbar* scrollbar, HDC hdc, const IntRect& rect)
328 {
329     if (!scrollbarTheme)
330         return;  // Classic look has no gripper.
331 
332     int state;
333     if (!scrollbar->enabled())
334         state = TS_DISABLED;
335     else if (scrollbar->pressedPart() == ThumbPart)
336         state = TS_ACTIVE; // Thumb always stays active once pressed.
337     else if (scrollbar->hoveredPart() == ThumbPart)
338         state = TS_HOVER;
339     else
340         state = TS_NORMAL;
341 
342     RECT themeRect(rect);
343     DrawThemeBackground(scrollbarTheme, hdc, scrollbar->orientation() == HorizontalScrollbar ? SP_GRIPPERHOR : SP_GRIPPERVERT, state, &themeRect, 0);
344 }
345 
paintThumb(GraphicsContext * context,Scrollbar * scrollbar,const IntRect & rect)346 void ScrollbarThemeWin::paintThumb(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect)
347 {
348     checkAndInitScrollbarTheme();
349 
350     int state;
351     if (!scrollbar->enabled())
352         state = TS_DISABLED;
353     else if (scrollbar->pressedPart() == ThumbPart)
354         state = TS_ACTIVE; // Thumb always stays active once pressed.
355     else if (scrollbar->hoveredPart() == ThumbPart)
356         state = TS_HOVER;
357     else
358         state = TS_NORMAL;
359 
360     bool alphaBlend = false;
361     if (scrollbarTheme)
362         alphaBlend = IsThemeBackgroundPartiallyTransparent(scrollbarTheme, scrollbar->orientation() == HorizontalScrollbar ? SP_THUMBHOR : SP_THUMBVERT, state);
363     HDC hdc = context->getWindowsContext(rect, alphaBlend);
364     RECT themeRect(rect);
365     if (scrollbarTheme) {
366         DrawThemeBackground(scrollbarTheme, hdc, scrollbar->orientation() == HorizontalScrollbar ? SP_THUMBHOR : SP_THUMBVERT, state, &themeRect, 0);
367         paintGripper(scrollbar, hdc, gripperRect(scrollbarThickness(), rect));
368     } else
369         ::DrawEdge(hdc, &themeRect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
370     context->releaseWindowsContext(hdc, rect, alphaBlend);
371 }
372 
373 }
374 
375