| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2011 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 "core/platform/ScrollbarTheme.h" | |
| 28 | |
| 29 #include "RuntimeEnabledFeatures.h" | |
| 30 #include "core/platform/mock/ScrollbarThemeMock.h" | |
| 31 #include "core/platform/mock/ScrollbarThemeOverlayMock.h" | |
| 32 #include "platform/scroll/ScrollbarThemeClient.h" | |
| 33 | |
| 34 namespace WebCore { | |
| 35 | |
| 36 ScrollbarTheme* ScrollbarTheme::theme() | |
| 37 { | |
| 38 if (ScrollbarTheme::mockScrollbarsEnabled()) { | |
| 39 if (RuntimeEnabledFeatures::overlayScrollbarsEnabled()) { | |
| 40 DEFINE_STATIC_LOCAL(ScrollbarThemeOverlayMock, overlayMockTheme, ())
; | |
| 41 return &overlayMockTheme; | |
| 42 } | |
| 43 | |
| 44 DEFINE_STATIC_LOCAL(ScrollbarThemeMock, mockTheme, ()); | |
| 45 return &mockTheme; | |
| 46 } | |
| 47 return nativeTheme(); | |
| 48 } | |
| 49 | |
| 50 bool ScrollbarTheme::gMockScrollbarsEnabled = false; | |
| 51 | |
| 52 void ScrollbarTheme::setMockScrollbarsEnabled(bool flag) | |
| 53 { | |
| 54 gMockScrollbarsEnabled = flag; | |
| 55 } | |
| 56 | |
| 57 bool ScrollbarTheme::mockScrollbarsEnabled() | |
| 58 { | |
| 59 return gMockScrollbarsEnabled; | |
| 60 } | |
| 61 | |
| 62 bool ScrollbarTheme::paint(ScrollbarThemeClient* scrollbar, GraphicsContext* gra
phicsContext, const IntRect& damageRect) | |
| 63 { | |
| 64 // Create the ScrollbarControlPartMask based on the damageRect | |
| 65 ScrollbarControlPartMask scrollMask = NoPart; | |
| 66 | |
| 67 IntRect backButtonStartPaintRect; | |
| 68 IntRect backButtonEndPaintRect; | |
| 69 IntRect forwardButtonStartPaintRect; | |
| 70 IntRect forwardButtonEndPaintRect; | |
| 71 if (hasButtons(scrollbar)) { | |
| 72 backButtonStartPaintRect = backButtonRect(scrollbar, BackButtonStartPart
, true); | |
| 73 if (damageRect.intersects(backButtonStartPaintRect)) | |
| 74 scrollMask |= BackButtonStartPart; | |
| 75 backButtonEndPaintRect = backButtonRect(scrollbar, BackButtonEndPart, tr
ue); | |
| 76 if (damageRect.intersects(backButtonEndPaintRect)) | |
| 77 scrollMask |= BackButtonEndPart; | |
| 78 forwardButtonStartPaintRect = forwardButtonRect(scrollbar, ForwardButton
StartPart, true); | |
| 79 if (damageRect.intersects(forwardButtonStartPaintRect)) | |
| 80 scrollMask |= ForwardButtonStartPart; | |
| 81 forwardButtonEndPaintRect = forwardButtonRect(scrollbar, ForwardButtonEn
dPart, true); | |
| 82 if (damageRect.intersects(forwardButtonEndPaintRect)) | |
| 83 scrollMask |= ForwardButtonEndPart; | |
| 84 } | |
| 85 | |
| 86 IntRect startTrackRect; | |
| 87 IntRect thumbRect; | |
| 88 IntRect endTrackRect; | |
| 89 IntRect trackPaintRect = trackRect(scrollbar, true); | |
| 90 if (damageRect.intersects(trackPaintRect)) | |
| 91 scrollMask |= TrackBGPart; | |
| 92 bool thumbPresent = hasThumb(scrollbar); | |
| 93 if (thumbPresent) { | |
| 94 IntRect track = trackRect(scrollbar); | |
| 95 splitTrack(scrollbar, track, startTrackRect, thumbRect, endTrackRect); | |
| 96 if (damageRect.intersects(thumbRect)) | |
| 97 scrollMask |= ThumbPart; | |
| 98 if (damageRect.intersects(startTrackRect)) | |
| 99 scrollMask |= BackTrackPart; | |
| 100 if (damageRect.intersects(endTrackRect)) | |
| 101 scrollMask |= ForwardTrackPart; | |
| 102 } | |
| 103 | |
| 104 // Paint the scrollbar background (only used by custom CSS scrollbars). | |
| 105 paintScrollbarBackground(graphicsContext, scrollbar); | |
| 106 | |
| 107 // Paint the back and forward buttons. | |
| 108 if (scrollMask & BackButtonStartPart) | |
| 109 paintButton(graphicsContext, scrollbar, backButtonStartPaintRect, BackBu
ttonStartPart); | |
| 110 if (scrollMask & BackButtonEndPart) | |
| 111 paintButton(graphicsContext, scrollbar, backButtonEndPaintRect, BackButt
onEndPart); | |
| 112 if (scrollMask & ForwardButtonStartPart) | |
| 113 paintButton(graphicsContext, scrollbar, forwardButtonStartPaintRect, For
wardButtonStartPart); | |
| 114 if (scrollMask & ForwardButtonEndPart) | |
| 115 paintButton(graphicsContext, scrollbar, forwardButtonEndPaintRect, Forwa
rdButtonEndPart); | |
| 116 | |
| 117 if (scrollMask & TrackBGPart) | |
| 118 paintTrackBackground(graphicsContext, scrollbar, trackPaintRect); | |
| 119 | |
| 120 if ((scrollMask & ForwardTrackPart) || (scrollMask & BackTrackPart)) { | |
| 121 // Paint the track pieces above and below the thumb. | |
| 122 if (scrollMask & BackTrackPart) | |
| 123 paintTrackPiece(graphicsContext, scrollbar, startTrackRect, BackTrac
kPart); | |
| 124 if (scrollMask & ForwardTrackPart) | |
| 125 paintTrackPiece(graphicsContext, scrollbar, endTrackRect, ForwardTra
ckPart); | |
| 126 | |
| 127 paintTickmarks(graphicsContext, scrollbar, trackPaintRect); | |
| 128 } | |
| 129 | |
| 130 // Paint the thumb. | |
| 131 if (scrollMask & ThumbPart) | |
| 132 paintThumb(graphicsContext, scrollbar, thumbRect); | |
| 133 | |
| 134 return true; | |
| 135 } | |
| 136 | |
| 137 ScrollbarPart ScrollbarTheme::hitTest(ScrollbarThemeClient* scrollbar, const Int
Point& position) | |
| 138 { | |
| 139 ScrollbarPart result = NoPart; | |
| 140 if (!scrollbar->enabled()) | |
| 141 return result; | |
| 142 | |
| 143 IntPoint testPosition = scrollbar->convertFromContainingWindow(position); | |
| 144 testPosition.move(scrollbar->x(), scrollbar->y()); | |
| 145 | |
| 146 if (!scrollbar->frameRect().contains(testPosition)) | |
| 147 return NoPart; | |
| 148 | |
| 149 result = ScrollbarBGPart; | |
| 150 | |
| 151 IntRect track = trackRect(scrollbar); | |
| 152 if (track.contains(testPosition)) { | |
| 153 IntRect beforeThumbRect; | |
| 154 IntRect thumbRect; | |
| 155 IntRect afterThumbRect; | |
| 156 splitTrack(scrollbar, track, beforeThumbRect, thumbRect, afterThumbRect)
; | |
| 157 if (thumbRect.contains(testPosition)) | |
| 158 result = ThumbPart; | |
| 159 else if (beforeThumbRect.contains(testPosition)) | |
| 160 result = BackTrackPart; | |
| 161 else if (afterThumbRect.contains(testPosition)) | |
| 162 result = ForwardTrackPart; | |
| 163 else | |
| 164 result = TrackBGPart; | |
| 165 } else if (backButtonRect(scrollbar, BackButtonStartPart).contains(testPosit
ion)) { | |
| 166 result = BackButtonStartPart; | |
| 167 } else if (backButtonRect(scrollbar, BackButtonEndPart).contains(testPositio
n)) { | |
| 168 result = BackButtonEndPart; | |
| 169 } else if (forwardButtonRect(scrollbar, ForwardButtonStartPart).contains(tes
tPosition)) { | |
| 170 result = ForwardButtonStartPart; | |
| 171 } else if (forwardButtonRect(scrollbar, ForwardButtonEndPart).contains(testP
osition)) { | |
| 172 result = ForwardButtonEndPart; | |
| 173 } | |
| 174 return result; | |
| 175 } | |
| 176 | |
| 177 void ScrollbarTheme::invalidatePart(ScrollbarThemeClient* scrollbar, ScrollbarPa
rt part) | |
| 178 { | |
| 179 if (part == NoPart) | |
| 180 return; | |
| 181 | |
| 182 IntRect result; | |
| 183 switch (part) { | |
| 184 case BackButtonStartPart: | |
| 185 result = backButtonRect(scrollbar, BackButtonStartPart, true); | |
| 186 break; | |
| 187 case BackButtonEndPart: | |
| 188 result = backButtonRect(scrollbar, BackButtonEndPart, true); | |
| 189 break; | |
| 190 case ForwardButtonStartPart: | |
| 191 result = forwardButtonRect(scrollbar, ForwardButtonStartPart, true); | |
| 192 break; | |
| 193 case ForwardButtonEndPart: | |
| 194 result = forwardButtonRect(scrollbar, ForwardButtonEndPart, true); | |
| 195 break; | |
| 196 case TrackBGPart: | |
| 197 result = trackRect(scrollbar, true); | |
| 198 break; | |
| 199 case ScrollbarBGPart: | |
| 200 result = scrollbar->frameRect(); | |
| 201 break; | |
| 202 default: { | |
| 203 IntRect beforeThumbRect, thumbRect, afterThumbRect; | |
| 204 splitTrack(scrollbar, trackRect(scrollbar), beforeThumbRect, thumbRect,
afterThumbRect); | |
| 205 if (part == BackTrackPart) | |
| 206 result = beforeThumbRect; | |
| 207 else if (part == ForwardTrackPart) | |
| 208 result = afterThumbRect; | |
| 209 else | |
| 210 result = thumbRect; | |
| 211 } | |
| 212 } | |
| 213 result.moveBy(-scrollbar->location()); | |
| 214 scrollbar->invalidateRect(result); | |
| 215 } | |
| 216 | |
| 217 void ScrollbarTheme::splitTrack(ScrollbarThemeClient* scrollbar, const IntRect&
unconstrainedTrackRect, IntRect& beforeThumbRect, IntRect& thumbRect, IntRect& a
fterThumbRect) | |
| 218 { | |
| 219 // This function won't even get called unless we're big enough to have some
combination of these three rects where at least | |
| 220 // one of them is non-empty. | |
| 221 IntRect trackRect = constrainTrackRectToTrackPieces(scrollbar, unconstrained
TrackRect); | |
| 222 int thumbPos = thumbPosition(scrollbar); | |
| 223 if (scrollbar->orientation() == HorizontalScrollbar) { | |
| 224 thumbRect = IntRect(trackRect.x() + thumbPos, trackRect.y(), thumbLength
(scrollbar), scrollbar->height()); | |
| 225 beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), thumbPos + thumb
Rect.width() / 2, trackRect.height()); | |
| 226 afterThumbRect = IntRect(trackRect.x() + beforeThumbRect.width(), trackR
ect.y(), trackRect.maxX() - beforeThumbRect.maxX(), trackRect.height()); | |
| 227 } else { | |
| 228 thumbRect = IntRect(trackRect.x(), trackRect.y() + thumbPos, scrollbar->
width(), thumbLength(scrollbar)); | |
| 229 beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), trackRect.width(
), thumbPos + thumbRect.height() / 2); | |
| 230 afterThumbRect = IntRect(trackRect.x(), trackRect.y() + beforeThumbRect.
height(), trackRect.width(), trackRect.maxY() - beforeThumbRect.maxY()); | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 // Returns the size represented by track taking into account scrolling past | |
| 235 // the end of the document. | |
| 236 static float usedTotalSize(ScrollbarThemeClient* scrollbar) | |
| 237 { | |
| 238 float overhangAtStart = -scrollbar->currentPos(); | |
| 239 float overhangAtEnd = scrollbar->currentPos() + scrollbar->visibleSize() - s
crollbar->totalSize(); | |
| 240 float overhang = std::max(0.0f, std::max(overhangAtStart, overhangAtEnd)); | |
| 241 return scrollbar->totalSize() + overhang; | |
| 242 } | |
| 243 | |
| 244 int ScrollbarTheme::thumbPosition(ScrollbarThemeClient* scrollbar) | |
| 245 { | |
| 246 if (scrollbar->enabled()) { | |
| 247 float size = usedTotalSize(scrollbar) - scrollbar->visibleSize(); | |
| 248 // Avoid doing a floating point divide by zero and return 1 when usedTot
alSize == visibleSize. | |
| 249 if (!size) | |
| 250 return 1; | |
| 251 float pos = std::max(0.0f, scrollbar->currentPos()) * (trackLength(scrol
lbar) - thumbLength(scrollbar)) / size; | |
| 252 return (pos < 1 && pos > 0) ? 1 : pos; | |
| 253 } | |
| 254 return 0; | |
| 255 } | |
| 256 | |
| 257 int ScrollbarTheme::thumbLength(ScrollbarThemeClient* scrollbar) | |
| 258 { | |
| 259 if (!scrollbar->enabled()) | |
| 260 return 0; | |
| 261 | |
| 262 float overhang = 0; | |
| 263 if (scrollbar->currentPos() < 0) | |
| 264 overhang = -scrollbar->currentPos(); | |
| 265 else if (scrollbar->visibleSize() + scrollbar->currentPos() > scrollbar->tot
alSize()) | |
| 266 overhang = scrollbar->currentPos() + scrollbar->visibleSize() - scrollba
r->totalSize(); | |
| 267 float proportion = (scrollbar->visibleSize() - overhang) / usedTotalSize(scr
ollbar); | |
| 268 int trackLen = trackLength(scrollbar); | |
| 269 int length = round(proportion * trackLen); | |
| 270 length = std::max(length, minimumThumbLength(scrollbar)); | |
| 271 if (length > trackLen) | |
| 272 length = 0; // Once the thumb is below the track length, it just goes aw
ay (to make more room for the track). | |
| 273 return length; | |
| 274 } | |
| 275 | |
| 276 int ScrollbarTheme::minimumThumbLength(ScrollbarThemeClient* scrollbar) | |
| 277 { | |
| 278 return scrollbarThickness(scrollbar->controlSize()); | |
| 279 } | |
| 280 | |
| 281 int ScrollbarTheme::trackPosition(ScrollbarThemeClient* scrollbar) | |
| 282 { | |
| 283 IntRect constrainedTrackRect = constrainTrackRectToTrackPieces(scrollbar, tr
ackRect(scrollbar)); | |
| 284 return (scrollbar->orientation() == HorizontalScrollbar) ? constrainedTrackR
ect.x() - scrollbar->x() : constrainedTrackRect.y() - scrollbar->y(); | |
| 285 } | |
| 286 | |
| 287 int ScrollbarTheme::trackLength(ScrollbarThemeClient* scrollbar) | |
| 288 { | |
| 289 IntRect constrainedTrackRect = constrainTrackRectToTrackPieces(scrollbar, tr
ackRect(scrollbar)); | |
| 290 return (scrollbar->orientation() == HorizontalScrollbar) ? constrainedTrackR
ect.width() : constrainedTrackRect.height(); | |
| 291 } | |
| 292 | |
| 293 void ScrollbarTheme::paintScrollCorner(GraphicsContext* context, const IntRect&
cornerRect) | |
| 294 { | |
| 295 context->fillRect(cornerRect, Color::white); | |
| 296 } | |
| 297 | |
| 298 IntRect ScrollbarTheme::thumbRect(ScrollbarThemeClient* scrollbar) | |
| 299 { | |
| 300 if (!hasThumb(scrollbar)) | |
| 301 return IntRect(); | |
| 302 | |
| 303 IntRect track = trackRect(scrollbar); | |
| 304 IntRect startTrackRect; | |
| 305 IntRect thumbRect; | |
| 306 IntRect endTrackRect; | |
| 307 splitTrack(scrollbar, track, startTrackRect, thumbRect, endTrackRect); | |
| 308 | |
| 309 return thumbRect; | |
| 310 } | |
| 311 | |
| 312 int ScrollbarTheme::thumbThickness(ScrollbarThemeClient* scrollbar) | |
| 313 { | |
| 314 IntRect track = trackRect(scrollbar); | |
| 315 return scrollbar->orientation() == HorizontalScrollbar ? track.height() : tr
ack.width(); | |
| 316 } | |
| 317 | |
| 318 void ScrollbarTheme::paintOverhangBackground(GraphicsContext* context, const Int
Rect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect
& dirtyRect) | |
| 319 { | |
| 320 context->setFillColor(Color::white); | |
| 321 if (!horizontalOverhangRect.isEmpty()) | |
| 322 context->fillRect(intersection(horizontalOverhangRect, dirtyRect)); | |
| 323 if (!verticalOverhangRect.isEmpty()) | |
| 324 context->fillRect(intersection(verticalOverhangRect, dirtyRect)); | |
| 325 } | |
| 326 | |
| 327 } | |
| OLD | NEW |