Home | History | Annotate | Download | only in win
      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 
     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(Scrollbar* scrollbar)
    134 {
    135     return thumbLength(scrollbar) > 0;
    136 }
    137 
    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 
    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 
    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 
    191 bool ScrollbarThemeWin::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt)
    192 {
    193     return evt.shiftKey() && evt.button() == LeftButton;
    194 }
    195 
    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 
    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 
    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 
    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 
    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 
    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 
    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