2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "ScrollbarThemeComposite.h"
30 #include "ChromeClient.h"
32 #include "FrameView.h"
33 #include "GraphicsContext.h"
35 #include "PlatformMouseEvent.h"
36 #include "Scrollbar.h"
37 #include "ScrollableArea.h"
44 bool ScrollbarThemeComposite::paint(Scrollbar* scrollbar, GraphicsContext* graphicsContext, const IntRect& damageRect)
46 // Create the ScrollbarControlPartMask based on the damageRect
47 ScrollbarControlPartMask scrollMask = NoPart;
49 IntRect backButtonStartPaintRect;
50 IntRect backButtonEndPaintRect;
51 IntRect forwardButtonStartPaintRect;
52 IntRect forwardButtonEndPaintRect;
53 if (hasButtons(scrollbar)) {
54 backButtonStartPaintRect = backButtonRect(scrollbar, BackButtonStartPart, true);
55 if (damageRect.intersects(backButtonStartPaintRect))
56 scrollMask |= BackButtonStartPart;
57 backButtonEndPaintRect = backButtonRect(scrollbar, BackButtonEndPart, true);
58 if (damageRect.intersects(backButtonEndPaintRect))
59 scrollMask |= BackButtonEndPart;
60 forwardButtonStartPaintRect = forwardButtonRect(scrollbar, ForwardButtonStartPart, true);
61 if (damageRect.intersects(forwardButtonStartPaintRect))
62 scrollMask |= ForwardButtonStartPart;
63 forwardButtonEndPaintRect = forwardButtonRect(scrollbar, ForwardButtonEndPart, true);
64 if (damageRect.intersects(forwardButtonEndPaintRect))
65 scrollMask |= ForwardButtonEndPart;
68 IntRect startTrackRect;
71 IntRect trackPaintRect = trackRect(scrollbar, true);
72 if (damageRect.intersects(trackPaintRect))
73 scrollMask |= TrackBGPart;
74 bool thumbPresent = hasThumb(scrollbar);
76 IntRect track = trackRect(scrollbar);
77 splitTrack(scrollbar, track, startTrackRect, thumbRect, endTrackRect);
78 if (damageRect.intersects(thumbRect))
79 scrollMask |= ThumbPart;
80 if (damageRect.intersects(startTrackRect))
81 scrollMask |= BackTrackPart;
82 if (damageRect.intersects(endTrackRect))
83 scrollMask |= ForwardTrackPart;
86 // Paint the scrollbar background (only used by custom CSS scrollbars).
87 paintScrollbarBackground(graphicsContext, scrollbar);
89 // Paint the back and forward buttons.
90 if (scrollMask & BackButtonStartPart)
91 paintButton(graphicsContext, scrollbar, backButtonStartPaintRect, BackButtonStartPart);
92 if (scrollMask & BackButtonEndPart)
93 paintButton(graphicsContext, scrollbar, backButtonEndPaintRect, BackButtonEndPart);
94 if (scrollMask & ForwardButtonStartPart)
95 paintButton(graphicsContext, scrollbar, forwardButtonStartPaintRect, ForwardButtonStartPart);
96 if (scrollMask & ForwardButtonEndPart)
97 paintButton(graphicsContext, scrollbar, forwardButtonEndPaintRect, ForwardButtonEndPart);
99 if (scrollMask & TrackBGPart)
100 paintTrackBackground(graphicsContext, scrollbar, trackPaintRect);
102 if ((scrollMask & ForwardTrackPart) || (scrollMask & BackTrackPart)) {
103 // Paint the track pieces above and below the thumb.
104 if (scrollMask & BackTrackPart)
105 paintTrackPiece(graphicsContext, scrollbar, startTrackRect, BackTrackPart);
106 if (scrollMask & ForwardTrackPart)
107 paintTrackPiece(graphicsContext, scrollbar, endTrackRect, ForwardTrackPart);
109 paintTickmarks(graphicsContext, scrollbar, trackPaintRect);
113 if (scrollMask & ThumbPart)
114 paintThumb(graphicsContext, scrollbar, thumbRect);
119 ScrollbarPart ScrollbarThemeComposite::hitTest(Scrollbar* scrollbar, const PlatformMouseEvent& evt)
121 ScrollbarPart result = NoPart;
122 if (!scrollbar->enabled())
125 IntPoint mousePosition = scrollbar->convertFromContainingWindow(evt.pos());
126 mousePosition.move(scrollbar->x(), scrollbar->y());
128 if (!scrollbar->frameRect().contains(mousePosition))
131 result = ScrollbarBGPart;
133 IntRect track = trackRect(scrollbar);
134 if (track.contains(mousePosition)) {
135 IntRect beforeThumbRect;
137 IntRect afterThumbRect;
138 splitTrack(scrollbar, track, beforeThumbRect, thumbRect, afterThumbRect);
139 if (thumbRect.contains(mousePosition))
141 else if (beforeThumbRect.contains(mousePosition))
142 result = BackTrackPart;
143 else if (afterThumbRect.contains(mousePosition))
144 result = ForwardTrackPart;
146 result = TrackBGPart;
147 } else if (backButtonRect(scrollbar, BackButtonStartPart).contains(mousePosition))
148 result = BackButtonStartPart;
149 else if (backButtonRect(scrollbar, BackButtonEndPart).contains(mousePosition))
150 result = BackButtonEndPart;
151 else if (forwardButtonRect(scrollbar, ForwardButtonStartPart).contains(mousePosition))
152 result = ForwardButtonStartPart;
153 else if (forwardButtonRect(scrollbar, ForwardButtonEndPart).contains(mousePosition))
154 result = ForwardButtonEndPart;
158 void ScrollbarThemeComposite::invalidatePart(Scrollbar* scrollbar, ScrollbarPart part)
165 case BackButtonStartPart:
166 result = backButtonRect(scrollbar, BackButtonStartPart, true);
168 case BackButtonEndPart:
169 result = backButtonRect(scrollbar, BackButtonEndPart, true);
171 case ForwardButtonStartPart:
172 result = forwardButtonRect(scrollbar, ForwardButtonStartPart, true);
174 case ForwardButtonEndPart:
175 result = forwardButtonRect(scrollbar, ForwardButtonEndPart, true);
178 result = trackRect(scrollbar, true);
180 case ScrollbarBGPart:
181 result = scrollbar->frameRect();
184 IntRect beforeThumbRect, thumbRect, afterThumbRect;
185 splitTrack(scrollbar, trackRect(scrollbar), beforeThumbRect, thumbRect, afterThumbRect);
186 if (part == BackTrackPart)
187 result = beforeThumbRect;
188 else if (part == ForwardTrackPart)
189 result = afterThumbRect;
194 result.moveBy(-scrollbar->location());
195 scrollbar->invalidateRect(result);
198 void ScrollbarThemeComposite::splitTrack(Scrollbar* scrollbar, const IntRect& unconstrainedTrackRect, IntRect& beforeThumbRect, IntRect& thumbRect, IntRect& afterThumbRect)
200 // This function won't even get called unless we're big enough to have some combination of these three rects where at least
201 // one of them is non-empty.
202 IntRect trackRect = constrainTrackRectToTrackPieces(scrollbar, unconstrainedTrackRect);
203 int thickness = scrollbar->orientation() == HorizontalScrollbar ? scrollbar->height() : scrollbar->width();
204 int thumbPos = thumbPosition(scrollbar);
205 if (scrollbar->orientation() == HorizontalScrollbar) {
206 thumbRect = IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - thickness) / 2, thumbLength(scrollbar), thickness);
207 beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), thumbPos + thumbRect.width() / 2, trackRect.height());
208 afterThumbRect = IntRect(trackRect.x() + beforeThumbRect.width(), trackRect.y(), trackRect.maxX() - beforeThumbRect.maxX(), trackRect.height());
210 thumbRect = IntRect(trackRect.x() + (trackRect.width() - thickness) / 2, trackRect.y() + thumbPos, thickness, thumbLength(scrollbar));
211 beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), trackRect.width(), thumbPos + thumbRect.height() / 2);
212 afterThumbRect = IntRect(trackRect.x(), trackRect.y() + beforeThumbRect.height(), trackRect.width(), trackRect.maxY() - beforeThumbRect.maxY());
216 // Returns the size represented by track taking into account scrolling past
217 // the end of the document.
218 static float usedTotalSize(Scrollbar* scrollbar)
220 float overhangAtStart = -scrollbar->currentPos();
221 float overhangAtEnd = scrollbar->currentPos() + scrollbar->visibleSize() - scrollbar->totalSize();
222 float overhang = max(0.0f, max(overhangAtStart, overhangAtEnd));
223 return scrollbar->totalSize() + overhang;
226 int ScrollbarThemeComposite::thumbPosition(Scrollbar* scrollbar)
228 if (scrollbar->enabled()) {
229 float pos = max(0.0f, scrollbar->currentPos()) * (trackLength(scrollbar) - thumbLength(scrollbar)) / (usedTotalSize(scrollbar) - scrollbar->visibleSize());
230 return (pos < 1 && pos > 0) ? 1 : pos;
235 int ScrollbarThemeComposite::thumbLength(Scrollbar* scrollbar)
237 if (!scrollbar->enabled())
240 float proportion = scrollbar->visibleSize() / usedTotalSize(scrollbar);
241 int trackLen = trackLength(scrollbar);
242 int length = proportion * trackLen;
243 length = max(length, minimumThumbLength(scrollbar));
244 if (length > trackLen)
245 length = 0; // Once the thumb is below the track length, it just goes away (to make more room for the track).
249 int ScrollbarThemeComposite::minimumThumbLength(Scrollbar* scrollbar)
251 return scrollbarThickness(scrollbar->controlSize());
254 int ScrollbarThemeComposite::trackPosition(Scrollbar* scrollbar)
256 IntRect constrainedTrackRect = constrainTrackRectToTrackPieces(scrollbar, trackRect(scrollbar));
257 return (scrollbar->orientation() == HorizontalScrollbar) ? constrainedTrackRect.x() - scrollbar->x() : constrainedTrackRect.y() - scrollbar->y();
260 int ScrollbarThemeComposite::trackLength(Scrollbar* scrollbar)
262 IntRect constrainedTrackRect = constrainTrackRectToTrackPieces(scrollbar, trackRect(scrollbar));
263 return (scrollbar->orientation() == HorizontalScrollbar) ? constrainedTrackRect.width() : constrainedTrackRect.height();
266 void ScrollbarThemeComposite::paintScrollCorner(ScrollView*, GraphicsContext* context, const IntRect& cornerRect)
268 context->fillRect(cornerRect, Color::white, ColorSpaceDeviceRGB);
271 void ScrollbarThemeComposite::paintOverhangAreas(ScrollView*, GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
273 context->setFillColor(Color::white, ColorSpaceDeviceRGB);
274 if (!horizontalOverhangRect.isEmpty())
275 context->fillRect(intersection(horizontalOverhangRect, dirtyRect));
277 context->setFillColor(Color::white, ColorSpaceDeviceRGB);
278 if (!verticalOverhangRect.isEmpty())
279 context->fillRect(intersection(verticalOverhangRect, dirtyRect));