• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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 "ScrollbarThemeComposite.h"
28 
29 #include "ChromeClient.h"
30 #include "Frame.h"
31 #include "FrameView.h"
32 #include "GraphicsContext.h"
33 #include "Page.h"
34 #include "PlatformMouseEvent.h"
35 #include "Scrollbar.h"
36 #include "ScrollbarClient.h"
37 #include "Settings.h"
38 
39 namespace WebCore {
40 
41 #if PLATFORM(WIN)
pageForScrollView(ScrollView * view)42 static Page* pageForScrollView(ScrollView* view)
43 {
44     if (!view)
45         return 0;
46     if (!view->isFrameView())
47         return 0;
48     FrameView* frameView = static_cast<FrameView*>(view);
49     if (!frameView->frame())
50         return 0;
51     return frameView->frame()->page();
52 }
53 #endif
54 
paint(Scrollbar * scrollbar,GraphicsContext * graphicsContext,const IntRect & damageRect)55 bool ScrollbarThemeComposite::paint(Scrollbar* scrollbar, GraphicsContext* graphicsContext, const IntRect& damageRect)
56 {
57     // Create the ScrollbarControlPartMask based on the damageRect
58     ScrollbarControlPartMask scrollMask = NoPart;
59 
60     IntRect backButtonStartPaintRect;
61     IntRect backButtonEndPaintRect;
62     IntRect forwardButtonStartPaintRect;
63     IntRect forwardButtonEndPaintRect;
64     if (hasButtons(scrollbar)) {
65         backButtonStartPaintRect = backButtonRect(scrollbar, BackButtonStartPart, true);
66         if (damageRect.intersects(backButtonStartPaintRect))
67             scrollMask |= BackButtonStartPart;
68         backButtonEndPaintRect = backButtonRect(scrollbar, BackButtonEndPart, true);
69         if (damageRect.intersects(backButtonEndPaintRect))
70             scrollMask |= BackButtonEndPart;
71         forwardButtonStartPaintRect = forwardButtonRect(scrollbar, ForwardButtonStartPart, true);
72         if (damageRect.intersects(forwardButtonStartPaintRect))
73             scrollMask |= ForwardButtonStartPart;
74         forwardButtonEndPaintRect = forwardButtonRect(scrollbar, ForwardButtonEndPart, true);
75         if (damageRect.intersects(forwardButtonEndPaintRect))
76             scrollMask |= ForwardButtonEndPart;
77     }
78 
79     IntRect startTrackRect;
80     IntRect thumbRect;
81     IntRect endTrackRect;
82     IntRect trackPaintRect = trackRect(scrollbar, true);
83     if (damageRect.intersects(trackPaintRect))
84         scrollMask |= TrackBGPart;
85     bool thumbPresent = hasThumb(scrollbar);
86     if (thumbPresent) {
87         IntRect track = trackRect(scrollbar);
88         splitTrack(scrollbar, track, startTrackRect, thumbRect, endTrackRect);
89         if (damageRect.intersects(thumbRect))
90             scrollMask |= ThumbPart;
91         if (damageRect.intersects(startTrackRect))
92             scrollMask |= BackTrackPart;
93         if (damageRect.intersects(endTrackRect))
94             scrollMask |= ForwardTrackPart;
95     }
96 
97 #if PLATFORM(WIN)
98     // FIXME: This API makes the assumption that the custom scrollbar's metrics will match
99     // the theme's metrics.  This is not a valid assumption.  The ability for a client to paint
100     // custom scrollbars should be removed once scrollbars can be styled via CSS.
101     if (Page* page = pageForScrollView(scrollbar->parent())) {
102         if (page->settings()->shouldPaintCustomScrollbars()) {
103             float proportion = static_cast<float>(scrollbar->visibleSize()) / scrollbar->totalSize();
104             float value = scrollbar->currentPos() / static_cast<float>(scrollbar->maximum());
105             ScrollbarControlState s = 0;
106             if (scrollbar->client()->isActive())
107                 s |= ActiveScrollbarState;
108             if (scrollbar->enabled())
109                 s |= EnabledScrollbarState;
110             if (scrollbar->pressedPart() != NoPart)
111                 s |= PressedScrollbarState;
112             if (page->chrome()->client()->paintCustomScrollbar(graphicsContext,
113                                                                scrollbar->frameRect(),
114                                                                scrollbar->controlSize(),
115                                                                s,
116                                                                scrollbar->pressedPart(),
117                                                                scrollbar->orientation() == VerticalScrollbar,
118                                                                value,
119                                                                proportion,
120                                                                scrollMask))
121                 return true;
122         }
123     }
124 #endif
125 
126     // Paint the scrollbar background (only used by custom CSS scrollbars).
127     paintScrollbarBackground(graphicsContext, scrollbar);
128 
129     // Paint the back and forward buttons.
130     if (scrollMask & BackButtonStartPart)
131         paintButton(graphicsContext, scrollbar, backButtonStartPaintRect, BackButtonStartPart);
132     if (scrollMask & BackButtonEndPart)
133         paintButton(graphicsContext, scrollbar, backButtonEndPaintRect, BackButtonEndPart);
134     if (scrollMask & ForwardButtonStartPart)
135         paintButton(graphicsContext, scrollbar, forwardButtonStartPaintRect, ForwardButtonStartPart);
136     if (scrollMask & ForwardButtonEndPart)
137         paintButton(graphicsContext, scrollbar, forwardButtonEndPaintRect, ForwardButtonEndPart);
138 
139     if (scrollMask & TrackBGPart)
140         paintTrackBackground(graphicsContext, scrollbar, trackPaintRect);
141 
142     if ((scrollMask & ForwardTrackPart) || (scrollMask & BackTrackPart)) {
143         // Paint the track pieces above and below the thumb.
144         if (scrollMask & BackTrackPart)
145             paintTrackPiece(graphicsContext, scrollbar, startTrackRect, BackTrackPart);
146         if (scrollMask & ForwardTrackPart)
147             paintTrackPiece(graphicsContext, scrollbar, endTrackRect, ForwardTrackPart);
148 
149         paintTickmarks(graphicsContext, scrollbar, trackPaintRect);
150     }
151 
152     // Paint the thumb.
153     if (scrollMask & ThumbPart)
154         paintThumb(graphicsContext, scrollbar, thumbRect);
155 
156     return true;
157 }
158 
hitTest(Scrollbar * scrollbar,const PlatformMouseEvent & evt)159 ScrollbarPart ScrollbarThemeComposite::hitTest(Scrollbar* scrollbar, const PlatformMouseEvent& evt)
160 {
161     ScrollbarPart result = NoPart;
162     if (!scrollbar->enabled())
163         return result;
164 
165     IntPoint mousePosition = scrollbar->convertFromContainingWindow(evt.pos());
166     mousePosition.move(scrollbar->x(), scrollbar->y());
167 
168     if (!scrollbar->frameRect().contains(mousePosition))
169         return NoPart;
170 
171     result = ScrollbarBGPart;
172 
173     IntRect track = trackRect(scrollbar);
174     if (track.contains(mousePosition)) {
175         IntRect beforeThumbRect;
176         IntRect thumbRect;
177         IntRect afterThumbRect;
178         splitTrack(scrollbar, track, beforeThumbRect, thumbRect, afterThumbRect);
179         if (thumbRect.contains(mousePosition))
180             result = ThumbPart;
181         else if (beforeThumbRect.contains(mousePosition))
182             result = BackTrackPart;
183         else if (afterThumbRect.contains(mousePosition))
184             result = ForwardTrackPart;
185         else
186             result = TrackBGPart;
187     } else if (backButtonRect(scrollbar, BackButtonStartPart).contains(mousePosition))
188         result = BackButtonStartPart;
189     else if (backButtonRect(scrollbar, BackButtonEndPart).contains(mousePosition))
190         result = BackButtonEndPart;
191     else if (forwardButtonRect(scrollbar, ForwardButtonStartPart).contains(mousePosition))
192         result = ForwardButtonStartPart;
193     else if (forwardButtonRect(scrollbar, ForwardButtonEndPart).contains(mousePosition))
194         result = ForwardButtonEndPart;
195     return result;
196 }
197 
invalidatePart(Scrollbar * scrollbar,ScrollbarPart part)198 void ScrollbarThemeComposite::invalidatePart(Scrollbar* scrollbar, ScrollbarPart part)
199 {
200     if (part == NoPart)
201         return;
202 
203     IntRect result;
204     switch (part) {
205         case BackButtonStartPart:
206             result = backButtonRect(scrollbar, BackButtonStartPart, true);
207             break;
208         case BackButtonEndPart:
209             result = backButtonRect(scrollbar, BackButtonEndPart, true);
210             break;
211         case ForwardButtonStartPart:
212             result = forwardButtonRect(scrollbar, ForwardButtonStartPart, true);
213             break;
214         case ForwardButtonEndPart:
215             result = forwardButtonRect(scrollbar, ForwardButtonEndPart, true);
216             break;
217         case TrackBGPart:
218             result = trackRect(scrollbar, true);
219             break;
220         case ScrollbarBGPart:
221             result = scrollbar->frameRect();
222             break;
223         default: {
224             IntRect beforeThumbRect, thumbRect, afterThumbRect;
225             splitTrack(scrollbar, trackRect(scrollbar), beforeThumbRect, thumbRect, afterThumbRect);
226             if (part == BackTrackPart)
227                 result = beforeThumbRect;
228             else if (part == ForwardTrackPart)
229                 result = afterThumbRect;
230             else
231                 result = thumbRect;
232         }
233     }
234     result.move(-scrollbar->x(), -scrollbar->y());
235     scrollbar->invalidateRect(result);
236 }
237 
splitTrack(Scrollbar * scrollbar,const IntRect & unconstrainedTrackRect,IntRect & beforeThumbRect,IntRect & thumbRect,IntRect & afterThumbRect)238 void ScrollbarThemeComposite::splitTrack(Scrollbar* scrollbar, const IntRect& unconstrainedTrackRect, IntRect& beforeThumbRect, IntRect& thumbRect, IntRect& afterThumbRect)
239 {
240     // This function won't even get called unless we're big enough to have some combination of these three rects where at least
241     // one of them is non-empty.
242     IntRect trackRect = constrainTrackRectToTrackPieces(scrollbar, unconstrainedTrackRect);
243     int thickness = scrollbar->orientation() == HorizontalScrollbar ? scrollbar->height() : scrollbar->width();
244     int thumbPos = thumbPosition(scrollbar);
245     if (scrollbar->orientation() == HorizontalScrollbar) {
246         thumbRect = IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - thickness) / 2, thumbLength(scrollbar), thickness);
247         beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), thumbPos + thumbRect.width() / 2, trackRect.height());
248         afterThumbRect = IntRect(trackRect.x() + beforeThumbRect.width(), trackRect.y(), trackRect.right() - beforeThumbRect.right(), trackRect.height());
249     } else {
250         thumbRect = IntRect(trackRect.x() + (trackRect.width() - thickness) / 2, trackRect.y() + thumbPos, thickness, thumbLength(scrollbar));
251         beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), trackRect.width(), thumbPos + thumbRect.height() / 2);
252         afterThumbRect = IntRect(trackRect.x(), trackRect.y() + beforeThumbRect.height(), trackRect.width(), trackRect.bottom() - beforeThumbRect.bottom());
253     }
254 }
255 
thumbPosition(Scrollbar * scrollbar)256 int ScrollbarThemeComposite::thumbPosition(Scrollbar* scrollbar)
257 {
258     if (scrollbar->enabled())
259         return scrollbar->currentPos() * (trackLength(scrollbar) - thumbLength(scrollbar)) / scrollbar->maximum();
260     return 0;
261 }
262 
thumbLength(Scrollbar * scrollbar)263 int ScrollbarThemeComposite::thumbLength(Scrollbar* scrollbar)
264 {
265     if (!scrollbar->enabled())
266         return 0;
267 
268     float proportion = (float)scrollbar->visibleSize() / scrollbar->totalSize();
269     int trackLen = trackLength(scrollbar);
270     int length = proportion * trackLen;
271     length = max(length, minimumThumbLength(scrollbar));
272     if (length > trackLen)
273         length = 0; // Once the thumb is below the track length, it just goes away (to make more room for the track).
274     return length;
275 }
276 
minimumThumbLength(Scrollbar * scrollbar)277 int ScrollbarThemeComposite::minimumThumbLength(Scrollbar* scrollbar)
278 {
279     return scrollbarThickness(scrollbar->controlSize());
280 }
281 
trackPosition(Scrollbar * scrollbar)282 int ScrollbarThemeComposite::trackPosition(Scrollbar* scrollbar)
283 {
284     IntRect constrainedTrackRect = constrainTrackRectToTrackPieces(scrollbar, trackRect(scrollbar));
285     return (scrollbar->orientation() == HorizontalScrollbar) ? constrainedTrackRect.x() - scrollbar->x() : constrainedTrackRect.y() - scrollbar->y();
286 }
287 
trackLength(Scrollbar * scrollbar)288 int ScrollbarThemeComposite::trackLength(Scrollbar* scrollbar)
289 {
290     IntRect constrainedTrackRect = constrainTrackRectToTrackPieces(scrollbar, trackRect(scrollbar));
291     return (scrollbar->orientation() == HorizontalScrollbar) ? constrainedTrackRect.width() : constrainedTrackRect.height();
292 }
293 
paintScrollCorner(ScrollView * view,GraphicsContext * context,const IntRect & cornerRect)294 void ScrollbarThemeComposite::paintScrollCorner(ScrollView* view, GraphicsContext* context, const IntRect& cornerRect)
295 {
296     FrameView* frameView = static_cast<FrameView*>(view);
297     Page* page = frameView->frame() ? frameView->frame()->page() : 0;
298     if (page && page->settings()->shouldPaintCustomScrollbars()) {
299         if (!page->chrome()->client()->paintCustomScrollCorner(context, cornerRect))
300             context->fillRect(cornerRect, Color::white);
301     }
302 }
303 
304 }
305