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