Home | History | Annotate | Download | only in chromium
      1 /*
      2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
      3  * Copyright (C) 2008, 2009 Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #include "ScrollbarThemeChromiumWin.h"
     29 
     30 #include <windows.h>
     31 #include <vsstyle.h>
     32 
     33 #include "GraphicsContext.h"
     34 #include "PlatformBridge.h"
     35 #include "PlatformContextSkia.h"
     36 #include "PlatformMouseEvent.h"
     37 #include "Scrollbar.h"
     38 #include "SystemInfo.h"
     39 
     40 namespace WebCore {
     41 
     42 ScrollbarTheme* ScrollbarTheme::nativeTheme()
     43 {
     44     static ScrollbarThemeChromiumWin theme;
     45     return &theme;
     46 }
     47 
     48 // The scrollbar size in DumpRenderTree on the Mac - so we can match their
     49 // layout results.  Entries are for regular, small, and mini scrollbars.
     50 // Metrics obtained using [NSScroller scrollerWidthForControlSize:]
     51 static const int kMacScrollbarSize[3] = { 15, 11, 15 };
     52 
     53 // Constants used to figure the drag rect outside which we should snap the
     54 // scrollbar thumb back to its origin.  These calculations are based on
     55 // observing the behavior of the MSVC8 main window scrollbar + some
     56 // guessing/extrapolation.
     57 static const int kOffEndMultiplier = 3;
     58 static const int kOffSideMultiplier = 8;
     59 
     60 int ScrollbarThemeChromiumWin::scrollbarThickness(ScrollbarControlSize controlSize)
     61 {
     62     static int thickness;
     63     if (!thickness) {
     64         if (PlatformBridge::layoutTestMode())
     65             return kMacScrollbarSize[controlSize];
     66         thickness = GetSystemMetrics(SM_CXVSCROLL);
     67     }
     68     return thickness;
     69 }
     70 
     71 bool ScrollbarThemeChromiumWin::invalidateOnMouseEnterExit()
     72 {
     73     return windowsVersion() >= WindowsVista;
     74 }
     75 
     76 bool ScrollbarThemeChromiumWin::shouldSnapBackToDragOrigin(Scrollbar* scrollbar, const PlatformMouseEvent& evt)
     77 {
     78     // Find the rect within which we shouldn't snap, by expanding the track rect
     79     // in both dimensions.
     80     IntRect rect = trackRect(scrollbar);
     81     const bool horz = scrollbar->orientation() == HorizontalScrollbar;
     82     const int thickness = scrollbarThickness(scrollbar->controlSize());
     83     rect.inflateX((horz ? kOffEndMultiplier : kOffSideMultiplier) * thickness);
     84     rect.inflateY((horz ? kOffSideMultiplier : kOffEndMultiplier) * thickness);
     85 
     86     // Convert the event to local coordinates.
     87     IntPoint mousePosition = scrollbar->convertFromContainingWindow(evt.pos());
     88     mousePosition.move(scrollbar->x(), scrollbar->y());
     89 
     90     // We should snap iff the event is outside our calculated rect.
     91     return !rect.contains(mousePosition);
     92 }
     93 
     94 void ScrollbarThemeChromiumWin::paintTrackPiece(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart partType)
     95 {
     96     bool horz = scrollbar->orientation() == HorizontalScrollbar;
     97 
     98     int partId;
     99     if (partType == BackTrackPart)
    100         partId = horz ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT;
    101     else
    102         partId = horz ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
    103 
    104     IntRect alignRect = trackRect(scrollbar, false);
    105 
    106     // Draw the track area before/after the thumb on the scroll bar.
    107     PlatformBridge::paintScrollbarTrack(
    108         gc,
    109         partId,
    110         getThemeState(scrollbar, partType),
    111         getClassicThemeState(scrollbar, partType),
    112         rect,
    113         alignRect);
    114 }
    115 
    116 void ScrollbarThemeChromiumWin::paintButton(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part)
    117 {
    118     bool horz = scrollbar->orientation() == HorizontalScrollbar;
    119 
    120     int partId;
    121     if (part == BackButtonStartPart || part == ForwardButtonStartPart)
    122         partId = horz ? DFCS_SCROLLLEFT : DFCS_SCROLLUP;
    123     else
    124         partId = horz ? DFCS_SCROLLRIGHT : DFCS_SCROLLDOWN;
    125 
    126     // Draw the thumb (the box you drag in the scroll bar to scroll).
    127     PlatformBridge::paintScrollbarArrow(
    128         gc,
    129         getThemeArrowState(scrollbar, part),
    130         partId | getClassicThemeState(scrollbar, part),
    131         rect);
    132 }
    133 
    134 void ScrollbarThemeChromiumWin::paintThumb(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect)
    135 {
    136     bool horz = scrollbar->orientation() == HorizontalScrollbar;
    137 
    138     // Draw the thumb (the box you drag in the scroll bar to scroll).
    139     PlatformBridge::paintScrollbarThumb(
    140         gc,
    141         horz ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT,
    142         getThemeState(scrollbar, ThumbPart),
    143         getClassicThemeState(scrollbar, ThumbPart),
    144         rect);
    145 
    146     // Draw the gripper (the three little lines on the thumb).
    147     PlatformBridge::paintScrollbarThumb(
    148         gc,
    149         horz ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT,
    150         getThemeState(scrollbar, ThumbPart),
    151         getClassicThemeState(scrollbar, ThumbPart),
    152         rect);
    153 }
    154 
    155 int ScrollbarThemeChromiumWin::getThemeState(Scrollbar* scrollbar, ScrollbarPart part) const
    156 {
    157     // When dragging the thumb, draw thumb pressed and other segments normal
    158     // regardless of where the cursor actually is.  See also four places in
    159     // getThemeArrowState().
    160     if (scrollbar->pressedPart() == ThumbPart) {
    161         if (part == ThumbPart)
    162             return SCRBS_PRESSED;
    163         return (windowsVersion() < WindowsVista) ? SCRBS_NORMAL : SCRBS_HOVER;
    164     }
    165     if (!scrollbar->enabled())
    166         return SCRBS_DISABLED;
    167     if (scrollbar->hoveredPart() != part || part == BackTrackPart || part == ForwardTrackPart)
    168         return (scrollbar->hoveredPart() == NoPart || (windowsVersion() < WindowsVista)) ? SCRBS_NORMAL : SCRBS_HOVER;
    169     if (scrollbar->pressedPart() == NoPart)
    170         return SCRBS_HOT;
    171     return (scrollbar->pressedPart() == part) ? SCRBS_PRESSED : SCRBS_NORMAL;
    172 }
    173 
    174 int ScrollbarThemeChromiumWin::getThemeArrowState(Scrollbar* scrollbar, ScrollbarPart part) const
    175 {
    176     // We could take advantage of knowing the values in the state enum to write
    177     // some simpler code, but treating the state enum as a black box seems
    178     // clearer and more future-proof.
    179     if (part == BackButtonStartPart || part == ForwardButtonStartPart) {
    180         if (scrollbar->orientation() == HorizontalScrollbar) {
    181             if (scrollbar->pressedPart() == ThumbPart)
    182                 return (windowsVersion() < WindowsVista) ? ABS_LEFTNORMAL : ABS_LEFTHOVER;
    183             if (!scrollbar->enabled())
    184                 return ABS_LEFTDISABLED;
    185             if (scrollbar->hoveredPart() != part)
    186                 return ((scrollbar->hoveredPart() == NoPart) || (windowsVersion() < WindowsVista)) ? ABS_LEFTNORMAL : ABS_LEFTHOVER;
    187             if (scrollbar->pressedPart() == NoPart)
    188                 return ABS_LEFTHOT;
    189             return (scrollbar->pressedPart() == part) ?
    190                 ABS_LEFTPRESSED : ABS_LEFTNORMAL;
    191         }
    192         if (scrollbar->pressedPart() == ThumbPart)
    193             return (windowsVersion() < WindowsVista) ? ABS_UPNORMAL : ABS_UPHOVER;
    194         if (!scrollbar->enabled())
    195             return ABS_UPDISABLED;
    196         if (scrollbar->hoveredPart() != part)
    197             return ((scrollbar->hoveredPart() == NoPart) || (windowsVersion() < WindowsVista)) ? ABS_UPNORMAL : ABS_UPHOVER;
    198         if (scrollbar->pressedPart() == NoPart)
    199             return ABS_UPHOT;
    200         return (scrollbar->pressedPart() == part) ? ABS_UPPRESSED : ABS_UPNORMAL;
    201     }
    202     if (scrollbar->orientation() == HorizontalScrollbar) {
    203         if (scrollbar->pressedPart() == ThumbPart)
    204             return (windowsVersion() < WindowsVista) ? ABS_RIGHTNORMAL : ABS_RIGHTHOVER;
    205         if (!scrollbar->enabled())
    206             return ABS_RIGHTDISABLED;
    207         if (scrollbar->hoveredPart() != part)
    208             return ((scrollbar->hoveredPart() == NoPart) || (windowsVersion() < WindowsVista)) ? ABS_RIGHTNORMAL : ABS_RIGHTHOVER;
    209         if (scrollbar->pressedPart() == NoPart)
    210             return ABS_RIGHTHOT;
    211         return (scrollbar->pressedPart() == part) ? ABS_RIGHTPRESSED : ABS_RIGHTNORMAL;
    212     }
    213     if (scrollbar->pressedPart() == ThumbPart)
    214         return (windowsVersion() < WindowsVista) ? ABS_DOWNNORMAL : ABS_DOWNHOVER;
    215     if (!scrollbar->enabled())
    216         return ABS_DOWNDISABLED;
    217     if (scrollbar->hoveredPart() != part)
    218         return ((scrollbar->hoveredPart() == NoPart) || (windowsVersion() < WindowsVista)) ? ABS_DOWNNORMAL : ABS_DOWNHOVER;
    219     if (scrollbar->pressedPart() == NoPart)
    220         return ABS_DOWNHOT;
    221     return (scrollbar->pressedPart() == part) ? ABS_DOWNPRESSED : ABS_DOWNNORMAL;
    222 }
    223 
    224 int ScrollbarThemeChromiumWin::getClassicThemeState(Scrollbar* scrollbar, ScrollbarPart part) const
    225 {
    226     // When dragging the thumb, draw the buttons normal even when hovered.
    227     if (scrollbar->pressedPart() == ThumbPart)
    228         return 0;
    229     if (!scrollbar->enabled())
    230         return DFCS_INACTIVE;
    231     if (scrollbar->hoveredPart() != part || part == BackTrackPart || part == ForwardTrackPart)
    232         return 0;
    233     if (scrollbar->pressedPart() == NoPart)
    234         return DFCS_HOT;
    235     return (scrollbar->pressedPart() == part) ? (DFCS_PUSHED | DFCS_FLAT) : 0;
    236 }
    237 
    238 bool ScrollbarThemeChromiumWin::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt)
    239 {
    240     return evt.shiftKey() && evt.button() == LeftButton;
    241 }
    242 
    243 IntSize ScrollbarThemeChromiumWin::buttonSize(Scrollbar* scrollbar)
    244 {
    245     // Our desired rect is essentially thickness by thickness.
    246 
    247     // Our actual rect will shrink to half the available space when we have < 2
    248     // times thickness pixels left.  This allows the scrollbar to scale down
    249     // and function even at tiny sizes.
    250 
    251     int thickness = scrollbarThickness(scrollbar->controlSize());
    252 
    253     // In layout test mode, we force the button "girth" (i.e., the length of
    254     // the button along the axis of the scrollbar) to be a fixed size.
    255     // FIXME: This is retarded!  scrollbarThickness is already fixed in layout
    256     // test mode so that should be enough to result in repeatable results, but
    257     // preserving this hack avoids having to rebaseline pixel tests.
    258     const int kLayoutTestModeGirth = 17;
    259     int girth = PlatformBridge::layoutTestMode() ? kLayoutTestModeGirth : thickness;
    260 
    261     if (scrollbar->orientation() == HorizontalScrollbar) {
    262         int width = scrollbar->width() < 2 * girth ? scrollbar->width() / 2 : girth;
    263         return IntSize(width, thickness);
    264     }
    265 
    266     int height = scrollbar->height() < 2 * girth ? scrollbar->height() / 2 : girth;
    267     return IntSize(thickness, height);
    268 }
    269 
    270 
    271 } // namespace WebCore
    272