| 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 |