• 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 "ScrollbarThemeGtk.h"
28 
29 #include "PlatformMouseEvent.h"
30 #include "RenderThemeGtk.h"
31 #include "ScrollView.h"
32 #include "Scrollbar.h"
33 
34 namespace WebCore {
35 
36 static HashSet<Scrollbar*>* gScrollbars;
37 
nativeTheme()38 ScrollbarTheme* ScrollbarTheme::nativeTheme()
39 {
40     static ScrollbarThemeGtk theme;
41     return &theme;
42 }
43 
~ScrollbarThemeGtk()44 ScrollbarThemeGtk::~ScrollbarThemeGtk()
45 {
46 }
47 
registerScrollbar(Scrollbar * scrollbar)48 void ScrollbarThemeGtk::registerScrollbar(Scrollbar* scrollbar)
49 {
50     if (!gScrollbars)
51         gScrollbars = new HashSet<Scrollbar*>;
52     gScrollbars->add(scrollbar);
53 }
54 
unregisterScrollbar(Scrollbar * scrollbar)55 void ScrollbarThemeGtk::unregisterScrollbar(Scrollbar* scrollbar)
56 {
57     gScrollbars->remove(scrollbar);
58     if (gScrollbars->isEmpty()) {
59         delete gScrollbars;
60         gScrollbars = 0;
61     }
62 }
63 
updateScrollbarsFrameThickness()64 void ScrollbarThemeGtk::updateScrollbarsFrameThickness()
65 {
66     if (!gScrollbars)
67         return;
68 
69     // Update the thickness of every interior frame scrollbar widget. The
70     // platform-independent scrollbar them code isn't yet smart enough to get
71     // this information when it paints.
72     HashSet<Scrollbar*>::iterator end = gScrollbars->end();
73     for (HashSet<Scrollbar*>::iterator it = gScrollbars->begin(); it != end; ++it) {
74         Scrollbar* scrollbar = (*it);
75 
76         // Top-level scrollbar i.e. scrollbars who have a parent ScrollView
77         // with no parent are native, and thus do not need to be resized.
78         if (!scrollbar->parent() || !scrollbar->parent()->parent())
79             return;
80 
81         int thickness = scrollbarThickness(scrollbar->controlSize());
82         if (scrollbar->orientation() == HorizontalScrollbar)
83             scrollbar->setFrameRect(IntRect(0, scrollbar->parent()->height() - thickness, scrollbar->width(), thickness));
84         else
85             scrollbar->setFrameRect(IntRect(scrollbar->parent()->width() - thickness, 0, thickness, scrollbar->height()));
86     }
87 }
88 
hasThumb(Scrollbar * scrollbar)89 bool ScrollbarThemeGtk::hasThumb(Scrollbar* scrollbar)
90 {
91     // This method is just called as a paint-time optimization to see if
92     // painting the thumb can be skipped.  We don't have to be exact here.
93     return thumbLength(scrollbar) > 0;
94 }
95 
backButtonRect(Scrollbar * scrollbar,ScrollbarPart part,bool)96 IntRect ScrollbarThemeGtk::backButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool)
97 {
98     if (part == BackButtonEndPart && !m_hasBackButtonEndPart)
99         return IntRect();
100 
101     int x = scrollbar->x() + m_troughBorderWidth;
102     int y = scrollbar->y() + m_troughBorderWidth;
103     IntSize size = buttonSize(scrollbar);
104     if (part == BackButtonStartPart)
105         return IntRect(x, y, size.width(), size.height());
106 
107     // BackButtonEndPart (alternate button)
108     if (scrollbar->orientation() == HorizontalScrollbar)
109         return IntRect(scrollbar->x() + scrollbar->width() - m_troughBorderWidth - (2 * size.width()), y, size.width(), size.height());
110 
111     // VerticalScrollbar alternate button
112     return IntRect(x, scrollbar->y() + scrollbar->height() - m_troughBorderWidth - (2 * size.height()), size.width(), size.height());
113 }
114 
forwardButtonRect(Scrollbar * scrollbar,ScrollbarPart part,bool)115 IntRect ScrollbarThemeGtk::forwardButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool)
116 {
117     if (part == ForwardButtonStartPart && !m_hasForwardButtonStartPart)
118         return IntRect();
119 
120     IntSize size = buttonSize(scrollbar);
121     if (scrollbar->orientation() == HorizontalScrollbar) {
122         int y = scrollbar->y() + m_troughBorderWidth;
123         if (part == ForwardButtonEndPart)
124             return IntRect(scrollbar->x() + scrollbar->width() - size.width() - m_troughBorderWidth, y, size.width(), size.height());
125 
126         // ForwardButtonStartPart (alternate button)
127         return IntRect(scrollbar->x() + m_troughBorderWidth + size.width(), y, size.width(), size.height());
128     }
129 
130     // VerticalScrollbar
131     int x = scrollbar->x() + m_troughBorderWidth;
132     if (part == ForwardButtonEndPart)
133         return IntRect(x, scrollbar->y() + scrollbar->height() - size.height() - m_troughBorderWidth, size.width(), size.height());
134 
135     // ForwardButtonStartPart (alternate button)
136     return IntRect(x, scrollbar->y() + m_troughBorderWidth + size.height(), size.width(), size.height());
137 }
138 
trackRect(Scrollbar * scrollbar,bool)139 IntRect ScrollbarThemeGtk::trackRect(Scrollbar* scrollbar, bool)
140 {
141     // The padding along the thumb movement axis (from outside to in)
142     // is the size of trough border plus the size of the stepper (button)
143     // plus the size of stepper spacing (the space between the stepper and
144     // the place where the thumb stops). There is often no stepper spacing.
145     int movementAxisPadding = m_troughBorderWidth + m_stepperSize + m_stepperSpacing;
146 
147     // The fatness of the scrollbar on the non-movement axis.
148     int thickness = scrollbarThickness(scrollbar->controlSize());
149 
150     int alternateButtonOffset = 0;
151     int alternateButtonWidth = 0;
152     if (m_hasForwardButtonStartPart) {
153         alternateButtonOffset += m_stepperSize;
154         alternateButtonWidth += m_stepperSize;
155     }
156     if (m_hasBackButtonEndPart)
157         alternateButtonWidth += m_stepperSize;
158 
159     if (scrollbar->orientation() == HorizontalScrollbar) {
160         // Once the scrollbar becomes smaller than the natural size of the
161         // two buttons, the track disappears.
162         if (scrollbar->width() < 2 * thickness)
163             return IntRect();
164         return IntRect(scrollbar->x() + movementAxisPadding + alternateButtonOffset, scrollbar->y(),
165                        scrollbar->width() - (2 * movementAxisPadding) - alternateButtonWidth, thickness);
166     }
167 
168     if (scrollbar->height() < 2 * thickness)
169         return IntRect();
170     return IntRect(scrollbar->x(), scrollbar->y() + movementAxisPadding + alternateButtonOffset,
171                    thickness, scrollbar->height() - (2 * movementAxisPadding) - alternateButtonWidth);
172 }
173 
thumbRect(Scrollbar * scrollbar,const IntRect & unconstrainedTrackRect)174 IntRect ScrollbarThemeGtk::thumbRect(Scrollbar* scrollbar, const IntRect& unconstrainedTrackRect)
175 {
176     IntRect trackRect = constrainTrackRectToTrackPieces(scrollbar, unconstrainedTrackRect);
177     int thumbPos = thumbPosition(scrollbar);
178     if (scrollbar->orientation() == HorizontalScrollbar)
179         return IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - m_thumbFatness) / 2, thumbLength(scrollbar), m_thumbFatness);
180 
181     // VerticalScrollbar
182     return IntRect(trackRect.x() + (trackRect.width() - m_thumbFatness) / 2, trackRect.y() + thumbPos, m_thumbFatness, thumbLength(scrollbar));
183 }
184 
paint(Scrollbar * scrollbar,GraphicsContext * graphicsContext,const IntRect & damageRect)185 bool ScrollbarThemeGtk::paint(Scrollbar* scrollbar, GraphicsContext* graphicsContext, const IntRect& damageRect)
186 {
187     if (graphicsContext->paintingDisabled())
188         return false;
189 
190     // Create the ScrollbarControlPartMask based on the damageRect
191     ScrollbarControlPartMask scrollMask = NoPart;
192 
193     IntRect backButtonStartPaintRect;
194     IntRect backButtonEndPaintRect;
195     IntRect forwardButtonStartPaintRect;
196     IntRect forwardButtonEndPaintRect;
197     if (hasButtons(scrollbar)) {
198         backButtonStartPaintRect = backButtonRect(scrollbar, BackButtonStartPart, true);
199         if (damageRect.intersects(backButtonStartPaintRect))
200             scrollMask |= BackButtonStartPart;
201         backButtonEndPaintRect = backButtonRect(scrollbar, BackButtonEndPart, true);
202         if (damageRect.intersects(backButtonEndPaintRect))
203             scrollMask |= BackButtonEndPart;
204         forwardButtonStartPaintRect = forwardButtonRect(scrollbar, ForwardButtonStartPart, true);
205         if (damageRect.intersects(forwardButtonStartPaintRect))
206             scrollMask |= ForwardButtonStartPart;
207         forwardButtonEndPaintRect = forwardButtonRect(scrollbar, ForwardButtonEndPart, true);
208         if (damageRect.intersects(forwardButtonEndPaintRect))
209             scrollMask |= ForwardButtonEndPart;
210     }
211 
212     IntRect trackPaintRect = trackRect(scrollbar, true);
213     if (damageRect.intersects(trackPaintRect))
214         scrollMask |= TrackBGPart;
215 
216     if (m_troughUnderSteppers && (scrollMask & BackButtonStartPart
217             || scrollMask & BackButtonEndPart
218             || scrollMask & ForwardButtonStartPart
219             || scrollMask & ForwardButtonEndPart))
220         scrollMask |= TrackBGPart;
221 
222     bool thumbPresent = hasThumb(scrollbar);
223     IntRect currentThumbRect;
224     if (thumbPresent) {
225         IntRect track = trackRect(scrollbar, false);
226         currentThumbRect = thumbRect(scrollbar, track);
227         if (damageRect.intersects(currentThumbRect))
228             scrollMask |= ThumbPart;
229     }
230 
231     ScrollbarControlPartMask allButtons = BackButtonStartPart | BackButtonEndPart
232                                          | ForwardButtonStartPart | ForwardButtonEndPart;
233     if (scrollMask & TrackBGPart || scrollMask & ThumbPart || scrollMask & allButtons)
234         paintScrollbarBackground(graphicsContext, scrollbar);
235         paintTrackBackground(graphicsContext, scrollbar, trackPaintRect);
236 
237     // Paint the back and forward buttons.
238     if (scrollMask & BackButtonStartPart)
239         paintButton(graphicsContext, scrollbar, backButtonStartPaintRect, BackButtonStartPart);
240     if (scrollMask & BackButtonEndPart)
241         paintButton(graphicsContext, scrollbar, backButtonEndPaintRect, BackButtonEndPart);
242     if (scrollMask & ForwardButtonStartPart)
243         paintButton(graphicsContext, scrollbar, forwardButtonStartPaintRect, ForwardButtonStartPart);
244     if (scrollMask & ForwardButtonEndPart)
245         paintButton(graphicsContext, scrollbar, forwardButtonEndPaintRect, ForwardButtonEndPart);
246 
247     // Paint the thumb.
248     if (scrollMask & ThumbPart)
249         paintThumb(graphicsContext, scrollbar, currentThumbRect);
250 
251     return true;
252 }
253 
paintScrollCorner(ScrollView * view,GraphicsContext * context,const IntRect & cornerRect)254 void ScrollbarThemeGtk::paintScrollCorner(ScrollView* view, GraphicsContext* context, const IntRect& cornerRect)
255 {
256     // ScrollbarThemeComposite::paintScrollCorner incorrectly assumes that the
257     // ScrollView is a FrameView (see FramelessScrollView), so we cannot let
258     // that code run.  For FrameView's this is correct since we don't do custom
259     // scrollbar corner rendering, which ScrollbarThemeComposite supports.
260     ScrollbarTheme::paintScrollCorner(view, context, cornerRect);
261 }
262 
shouldCenterOnThumb(Scrollbar *,const PlatformMouseEvent & event)263 bool ScrollbarThemeGtk::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& event)
264 {
265     return (event.shiftKey() && event.button() == LeftButton) || (event.button() == MiddleButton);
266 }
267 
scrollbarThickness(ScrollbarControlSize)268 int ScrollbarThemeGtk::scrollbarThickness(ScrollbarControlSize)
269 {
270     return m_thumbFatness + (m_troughBorderWidth * 2);
271 }
272 
buttonSize(Scrollbar * scrollbar)273 IntSize ScrollbarThemeGtk::buttonSize(Scrollbar* scrollbar)
274 {
275     if (scrollbar->orientation() == VerticalScrollbar)
276         return IntSize(m_thumbFatness, m_stepperSize);
277 
278     // HorizontalScrollbar
279     return IntSize(m_stepperSize, m_thumbFatness);
280 }
281 
minimumThumbLength(Scrollbar * scrollbar)282 int ScrollbarThemeGtk::minimumThumbLength(Scrollbar* scrollbar)
283 {
284     return m_minThumbLength;
285 }
286 
287 }
288 
289