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