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 "core/platform/chromium/ScrollbarThemeChromiumWin.h" 29 30 #include <windows.h> 31 #include <vsstyle.h> 32 33 #include "core/platform/LayoutTestSupport.h" 34 #include "core/platform/PlatformMouseEvent.h" 35 #include "core/platform/Scrollbar.h" 36 #include "core/platform/graphics/GraphicsContext.h" 37 #include "core/platform/win/SystemInfo.h" 38 #include "public/platform/Platform.h" 39 #include "public/platform/WebRect.h" 40 #include "public/platform/win/WebThemeEngine.h" 41 42 namespace WebCore { 43 44 ScrollbarTheme* ScrollbarTheme::nativeTheme() 45 { 46 static ScrollbarThemeChromiumWin theme; 47 return &theme; 48 } 49 50 // The scrollbar size in DumpRenderTree on the Mac - so we can match their 51 // layout results. Entries are for regular, small, and mini scrollbars. 52 // Metrics obtained using [NSScroller scrollerWidthForControlSize:] 53 static const int kMacScrollbarSize[3] = { 15, 11, 15 }; 54 55 // Constants used to figure the drag rect outside which we should snap the 56 // scrollbar thumb back to its origin. These calculations are based on 57 // observing the behavior of the MSVC8 main window scrollbar + some 58 // guessing/extrapolation. 59 static const int kOffEndMultiplier = 3; 60 static const int kOffSideMultiplier = 8; 61 62 int ScrollbarThemeChromiumWin::scrollbarThickness(ScrollbarControlSize controlSize) 63 { 64 static int thickness; 65 if (!thickness) { 66 if (isRunningLayoutTest()) 67 return kMacScrollbarSize[controlSize]; 68 thickness = IntSize(WebKit::Platform::current()->themeEngine()->getSize(SBP_ARROWBTN)).width(); 69 } 70 return thickness; 71 } 72 73 bool ScrollbarThemeChromiumWin::invalidateOnMouseEnterExit() 74 { 75 return windowsVersion() >= WindowsVista; 76 } 77 78 bool ScrollbarThemeChromiumWin::shouldSnapBackToDragOrigin(ScrollbarThemeClient* scrollbar, const PlatformMouseEvent& evt) 79 { 80 // Find the rect within which we shouldn't snap, by expanding the track rect 81 // in both dimensions. 82 IntRect rect = trackRect(scrollbar); 83 const bool horz = scrollbar->orientation() == HorizontalScrollbar; 84 const int thickness = scrollbarThickness(scrollbar->controlSize()); 85 rect.inflateX((horz ? kOffEndMultiplier : kOffSideMultiplier) * thickness); 86 rect.inflateY((horz ? kOffSideMultiplier : kOffEndMultiplier) * thickness); 87 88 // Convert the event to local coordinates. 89 IntPoint mousePosition = scrollbar->convertFromContainingWindow(evt.position()); 90 mousePosition.move(scrollbar->x(), scrollbar->y()); 91 92 // We should snap iff the event is outside our calculated rect. 93 return !rect.contains(mousePosition); 94 } 95 96 void ScrollbarThemeChromiumWin::paintTrackPiece(GraphicsContext* gc, ScrollbarThemeClient* scrollbar, const IntRect& rect, ScrollbarPart partType) 97 { 98 bool horz = scrollbar->orientation() == HorizontalScrollbar; 99 100 int partId; 101 if (partType == BackTrackPart) 102 partId = horz ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT; 103 else 104 partId = horz ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; 105 106 IntRect alignRect = trackRect(scrollbar, false); 107 108 WebKit::WebCanvas* canvas = gc->canvas(); 109 // Draw the track area before/after the thumb on the scroll bar. 110 WebKit::Platform::current()->themeEngine()->paintScrollbarTrack(canvas, partId, getThemeState(scrollbar, partType), getClassicThemeState(scrollbar, partType), WebKit::WebRect(rect), WebKit::WebRect(alignRect)); 111 } 112 113 void ScrollbarThemeChromiumWin::paintButton(GraphicsContext* gc, ScrollbarThemeClient* scrollbar, const IntRect& rect, ScrollbarPart part) 114 { 115 bool horz = scrollbar->orientation() == HorizontalScrollbar; 116 117 int partId; 118 if (part == BackButtonStartPart || part == ForwardButtonStartPart) 119 partId = horz ? DFCS_SCROLLLEFT : DFCS_SCROLLUP; 120 else 121 partId = horz ? DFCS_SCROLLRIGHT : DFCS_SCROLLDOWN; 122 123 WebKit::WebCanvas* canvas = gc->canvas(); 124 // Draw the thumb (the box you drag in the scroll bar to scroll). 125 WebKit::Platform::current()->themeEngine()->paintScrollbarArrow(canvas, getThemeArrowState(scrollbar, part), partId | getClassicThemeState(scrollbar, part), WebKit::WebRect(rect)); 126 } 127 128 void ScrollbarThemeChromiumWin::paintThumb(GraphicsContext* gc, ScrollbarThemeClient* scrollbar, const IntRect& rect) 129 { 130 bool horz = scrollbar->orientation() == HorizontalScrollbar; 131 132 WebKit::WebCanvas* canvas = gc->canvas(); 133 // Draw the thumb (the box you drag in the scroll bar to scroll). 134 WebKit::Platform::current()->themeEngine()->paintScrollbarThumb(canvas, horz ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT, getThemeState(scrollbar, ThumbPart), getClassicThemeState(scrollbar, ThumbPart), WebKit::WebRect(rect)); 135 136 // Draw the gripper (the three little lines on the thumb). 137 WebKit::Platform::current()->themeEngine()->paintScrollbarThumb(canvas, horz ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT, getThemeState(scrollbar, ThumbPart), getClassicThemeState(scrollbar, ThumbPart), WebKit::WebRect(rect)); 138 } 139 140 int ScrollbarThemeChromiumWin::getThemeState(ScrollbarThemeClient* scrollbar, ScrollbarPart part) const 141 { 142 // When dragging the thumb, draw thumb pressed and other segments normal 143 // regardless of where the cursor actually is. See also four places in 144 // getThemeArrowState(). 145 if (scrollbar->pressedPart() == ThumbPart) { 146 if (part == ThumbPart) 147 return SCRBS_PRESSED; 148 return (windowsVersion() < WindowsVista) ? SCRBS_NORMAL : SCRBS_HOVER; 149 } 150 if (!scrollbar->enabled()) 151 return SCRBS_DISABLED; 152 if (scrollbar->hoveredPart() != part || part == BackTrackPart || part == ForwardTrackPart) 153 return (scrollbar->hoveredPart() == NoPart || (windowsVersion() < WindowsVista)) ? SCRBS_NORMAL : SCRBS_HOVER; 154 if (scrollbar->pressedPart() == NoPart) 155 return SCRBS_HOT; 156 return (scrollbar->pressedPart() == part) ? SCRBS_PRESSED : SCRBS_NORMAL; 157 } 158 159 int ScrollbarThemeChromiumWin::getThemeArrowState(ScrollbarThemeClient* scrollbar, ScrollbarPart part) const 160 { 161 // We could take advantage of knowing the values in the state enum to write 162 // some simpler code, but treating the state enum as a black box seems 163 // clearer and more future-proof. 164 if (part == BackButtonStartPart || part == ForwardButtonStartPart) { 165 if (scrollbar->orientation() == HorizontalScrollbar) { 166 if (scrollbar->pressedPart() == ThumbPart) 167 return (windowsVersion() < WindowsVista) ? ABS_LEFTNORMAL : ABS_LEFTHOVER; 168 if (!scrollbar->enabled()) 169 return ABS_LEFTDISABLED; 170 if (scrollbar->hoveredPart() != part) 171 return ((scrollbar->hoveredPart() == NoPart) || (windowsVersion() < WindowsVista)) ? ABS_LEFTNORMAL : ABS_LEFTHOVER; 172 if (scrollbar->pressedPart() == NoPart) 173 return ABS_LEFTHOT; 174 return (scrollbar->pressedPart() == part) ? 175 ABS_LEFTPRESSED : ABS_LEFTNORMAL; 176 } 177 if (scrollbar->pressedPart() == ThumbPart) 178 return (windowsVersion() < WindowsVista) ? ABS_UPNORMAL : ABS_UPHOVER; 179 if (!scrollbar->enabled()) 180 return ABS_UPDISABLED; 181 if (scrollbar->hoveredPart() != part) 182 return ((scrollbar->hoveredPart() == NoPart) || (windowsVersion() < WindowsVista)) ? ABS_UPNORMAL : ABS_UPHOVER; 183 if (scrollbar->pressedPart() == NoPart) 184 return ABS_UPHOT; 185 return (scrollbar->pressedPart() == part) ? ABS_UPPRESSED : ABS_UPNORMAL; 186 } 187 if (scrollbar->orientation() == HorizontalScrollbar) { 188 if (scrollbar->pressedPart() == ThumbPart) 189 return (windowsVersion() < WindowsVista) ? ABS_RIGHTNORMAL : ABS_RIGHTHOVER; 190 if (!scrollbar->enabled()) 191 return ABS_RIGHTDISABLED; 192 if (scrollbar->hoveredPart() != part) 193 return ((scrollbar->hoveredPart() == NoPart) || (windowsVersion() < WindowsVista)) ? ABS_RIGHTNORMAL : ABS_RIGHTHOVER; 194 if (scrollbar->pressedPart() == NoPart) 195 return ABS_RIGHTHOT; 196 return (scrollbar->pressedPart() == part) ? ABS_RIGHTPRESSED : ABS_RIGHTNORMAL; 197 } 198 if (scrollbar->pressedPart() == ThumbPart) 199 return (windowsVersion() < WindowsVista) ? ABS_DOWNNORMAL : ABS_DOWNHOVER; 200 if (!scrollbar->enabled()) 201 return ABS_DOWNDISABLED; 202 if (scrollbar->hoveredPart() != part) 203 return ((scrollbar->hoveredPart() == NoPart) || (windowsVersion() < WindowsVista)) ? ABS_DOWNNORMAL : ABS_DOWNHOVER; 204 if (scrollbar->pressedPart() == NoPart) 205 return ABS_DOWNHOT; 206 return (scrollbar->pressedPart() == part) ? ABS_DOWNPRESSED : ABS_DOWNNORMAL; 207 } 208 209 int ScrollbarThemeChromiumWin::getClassicThemeState(ScrollbarThemeClient* scrollbar, ScrollbarPart part) const 210 { 211 // When dragging the thumb, draw the buttons normal even when hovered. 212 if (scrollbar->pressedPart() == ThumbPart) 213 return 0; 214 if (!scrollbar->enabled()) 215 return DFCS_INACTIVE; 216 if (scrollbar->hoveredPart() != part || part == BackTrackPart || part == ForwardTrackPart) 217 return 0; 218 if (scrollbar->pressedPart() == NoPart) 219 return DFCS_HOT; 220 return (scrollbar->pressedPart() == part) ? (DFCS_PUSHED | DFCS_FLAT) : 0; 221 } 222 223 bool ScrollbarThemeChromiumWin::shouldCenterOnThumb(ScrollbarThemeClient*, const PlatformMouseEvent& evt) 224 { 225 return evt.shiftKey() && evt.button() == LeftButton; 226 } 227 228 IntSize ScrollbarThemeChromiumWin::buttonSize(ScrollbarThemeClient* scrollbar) 229 { 230 // Our desired rect is essentially thickness by thickness. 231 232 // Our actual rect will shrink to half the available space when we have < 2 233 // times thickness pixels left. This allows the scrollbar to scale down 234 // and function even at tiny sizes. 235 236 int thickness = scrollbarThickness(scrollbar->controlSize()); 237 238 // In layout test mode, we force the button "girth" (i.e., the length of 239 // the button along the axis of the scrollbar) to be a fixed size. 240 // FIXME: This is retarded! scrollbarThickness is already fixed in layout 241 // test mode so that should be enough to result in repeatable results, but 242 // preserving this hack avoids having to rebaseline pixel tests. 243 const int kLayoutTestModeGirth = 17; 244 int girth = isRunningLayoutTest() ? kLayoutTestModeGirth : thickness; 245 246 if (scrollbar->orientation() == HorizontalScrollbar) { 247 int width = scrollbar->width() < 2 * girth ? scrollbar->width() / 2 : girth; 248 return IntSize(width, thickness); 249 } 250 251 int height = scrollbar->height() < 2 * girth ? scrollbar->height() / 2 : girth; 252 return IntSize(thickness, height); 253 } 254 255 256 } // namespace WebCore 257