1 /*
2 * Copyright (C) 2008, 2009 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 "RenderScrollbar.h"
28
29 #include "RenderScrollbarPart.h"
30 #include "RenderScrollbarTheme.h"
31
32 namespace WebCore {
33
createCustomScrollbar(ScrollbarClient * client,ScrollbarOrientation orientation,RenderBox * renderer)34 PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, RenderBox* renderer)
35 {
36 return adoptRef(new RenderScrollbar(client, orientation, renderer));
37 }
38
RenderScrollbar(ScrollbarClient * client,ScrollbarOrientation orientation,RenderBox * renderer)39 RenderScrollbar::RenderScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, RenderBox* renderer)
40 : Scrollbar(client, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme())
41 , m_owner(renderer)
42 {
43 // FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created.
44
45 // Update the scrollbar size.
46 updateScrollbarPart(ScrollbarBGPart);
47 RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart);
48 if (!part)
49 return;
50
51 part->layout();
52 setFrameRect(IntRect(0, 0, part->width(), part->height()));
53 }
54
~RenderScrollbar()55 RenderScrollbar::~RenderScrollbar()
56 {
57 ASSERT(m_parts.isEmpty());
58 }
59
setParent(ScrollView * parent)60 void RenderScrollbar::setParent(ScrollView* parent)
61 {
62 Scrollbar::setParent(parent);
63 if (!parent) {
64 // Destroy all of the scrollbar's RenderBoxes.
65 updateScrollbarParts(true);
66 }
67 }
68
setEnabled(bool e)69 void RenderScrollbar::setEnabled(bool e)
70 {
71 bool wasEnabled = enabled();
72 Scrollbar::setEnabled(e);
73 if (wasEnabled != e)
74 updateScrollbarParts();
75 }
76
styleChanged()77 void RenderScrollbar::styleChanged()
78 {
79 updateScrollbarParts();
80 }
81
paint(GraphicsContext * context,const IntRect & damageRect)82 void RenderScrollbar::paint(GraphicsContext* context, const IntRect& damageRect)
83 {
84 if (context->updatingControlTints()) {
85 updateScrollbarParts();
86 return;
87 }
88 Scrollbar::paint(context, damageRect);
89 }
90
setHoveredPart(ScrollbarPart part)91 void RenderScrollbar::setHoveredPart(ScrollbarPart part)
92 {
93 if (part == m_hoveredPart)
94 return;
95
96 ScrollbarPart oldPart = m_hoveredPart;
97 m_hoveredPart = part;
98
99 updateScrollbarPart(oldPart);
100 updateScrollbarPart(m_hoveredPart);
101
102 updateScrollbarPart(ScrollbarBGPart);
103 updateScrollbarPart(TrackBGPart);
104 }
105
setPressedPart(ScrollbarPart part)106 void RenderScrollbar::setPressedPart(ScrollbarPart part)
107 {
108 ScrollbarPart oldPart = m_pressedPart;
109 Scrollbar::setPressedPart(part);
110
111 updateScrollbarPart(oldPart);
112 updateScrollbarPart(part);
113
114 updateScrollbarPart(ScrollbarBGPart);
115 updateScrollbarPart(TrackBGPart);
116 }
117
118 static ScrollbarPart s_styleResolvePart;
119 static RenderScrollbar* s_styleResolveScrollbar;
120
scrollbarForStyleResolve()121 RenderScrollbar* RenderScrollbar::scrollbarForStyleResolve()
122 {
123 return s_styleResolveScrollbar;
124 }
125
partForStyleResolve()126 ScrollbarPart RenderScrollbar::partForStyleResolve()
127 {
128 return s_styleResolvePart;
129 }
130
getScrollbarPseudoStyle(ScrollbarPart partType,PseudoId pseudoId)131 PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId)
132 {
133 s_styleResolvePart = partType;
134 s_styleResolveScrollbar = this;
135 RefPtr<RenderStyle> result = m_owner->getUncachedPseudoStyle(pseudoId, m_owner->style());
136 s_styleResolvePart = NoPart;
137 s_styleResolveScrollbar = 0;
138 return result;
139 }
140
updateScrollbarParts(bool destroy)141 void RenderScrollbar::updateScrollbarParts(bool destroy)
142 {
143 updateScrollbarPart(ScrollbarBGPart, destroy);
144 updateScrollbarPart(BackButtonStartPart, destroy);
145 updateScrollbarPart(ForwardButtonStartPart, destroy);
146 updateScrollbarPart(BackTrackPart, destroy);
147 updateScrollbarPart(ThumbPart, destroy);
148 updateScrollbarPart(ForwardTrackPart, destroy);
149 updateScrollbarPart(BackButtonEndPart, destroy);
150 updateScrollbarPart(ForwardButtonEndPart, destroy);
151 updateScrollbarPart(TrackBGPart, destroy);
152
153 if (destroy)
154 return;
155
156 // See if the scrollbar's thickness changed. If so, we need to mark our owning object as needing a layout.
157 bool isHorizontal = orientation() == HorizontalScrollbar;
158 int oldThickness = isHorizontal ? height() : width();
159 int newThickness = 0;
160 RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart);
161 if (part) {
162 part->layout();
163 newThickness = isHorizontal ? part->height() : part->width();
164 }
165
166 if (newThickness != oldThickness) {
167 setFrameRect(IntRect(x(), y(), isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height()));
168 m_owner->setChildNeedsLayout(true);
169 }
170 }
171
pseudoForScrollbarPart(ScrollbarPart part)172 static PseudoId pseudoForScrollbarPart(ScrollbarPart part)
173 {
174 switch (part) {
175 case BackButtonStartPart:
176 case ForwardButtonStartPart:
177 case BackButtonEndPart:
178 case ForwardButtonEndPart:
179 return SCROLLBAR_BUTTON;
180 case BackTrackPart:
181 case ForwardTrackPart:
182 return SCROLLBAR_TRACK_PIECE;
183 case ThumbPart:
184 return SCROLLBAR_THUMB;
185 case TrackBGPart:
186 return SCROLLBAR_TRACK;
187 case ScrollbarBGPart:
188 return SCROLLBAR;
189 case NoPart:
190 case AllParts:
191 break;
192 }
193 ASSERT_NOT_REACHED();
194 return SCROLLBAR;
195 }
196
updateScrollbarPart(ScrollbarPart partType,bool destroy)197 void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy)
198 {
199 if (partType == NoPart)
200 return;
201
202 RefPtr<RenderStyle> partStyle = !destroy ? getScrollbarPseudoStyle(partType, pseudoForScrollbarPart(partType)) : 0;
203
204 bool needRenderer = !destroy && partStyle && partStyle->display() != NONE && partStyle->visibility() == VISIBLE;
205
206 if (needRenderer && partStyle->display() != BLOCK) {
207 // See if we are a button that should not be visible according to OS settings.
208 ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement();
209 switch (partType) {
210 case BackButtonStartPart:
211 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart ||
212 buttonsPlacement == ScrollbarButtonsDoubleBoth);
213 break;
214 case ForwardButtonStartPart:
215 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth);
216 break;
217 case BackButtonEndPart:
218 needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth);
219 break;
220 case ForwardButtonEndPart:
221 needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd ||
222 buttonsPlacement == ScrollbarButtonsDoubleBoth);
223 break;
224 default:
225 break;
226 }
227 }
228
229 RenderScrollbarPart* partRenderer = m_parts.get(partType);
230 if (!partRenderer && needRenderer) {
231 partRenderer = new (m_owner->renderArena()) RenderScrollbarPart(m_owner->document(), this, partType);
232 m_parts.set(partType, partRenderer);
233 } else if (partRenderer && !needRenderer) {
234 m_parts.remove(partType);
235 partRenderer->destroy();
236 partRenderer = 0;
237 }
238
239 if (partRenderer)
240 partRenderer->setStyle(partStyle.release());
241 }
242
paintPart(GraphicsContext * graphicsContext,ScrollbarPart partType,const IntRect & rect)243 void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect)
244 {
245 RenderScrollbarPart* partRenderer = m_parts.get(partType);
246 if (!partRenderer)
247 return;
248 partRenderer->paintIntoRect(graphicsContext, x(), y(), rect);
249 }
250
buttonRect(ScrollbarPart partType)251 IntRect RenderScrollbar::buttonRect(ScrollbarPart partType)
252 {
253 RenderScrollbarPart* partRenderer = m_parts.get(partType);
254 if (!partRenderer)
255 return IntRect();
256
257 partRenderer->layout();
258
259 bool isHorizontal = orientation() == HorizontalScrollbar;
260 if (partType == BackButtonStartPart)
261 return IntRect(x(), y(), isHorizontal ? partRenderer->width() : width(), isHorizontal ? height() : partRenderer->height());
262 if (partType == ForwardButtonEndPart)
263 return IntRect(isHorizontal ? x() + width() - partRenderer->width() : x(),
264
265 isHorizontal ? y() : y() + height() - partRenderer->height(),
266 isHorizontal ? partRenderer->width() : width(),
267 isHorizontal ? height() : partRenderer->height());
268
269 if (partType == ForwardButtonStartPart) {
270 IntRect previousButton = buttonRect(BackButtonStartPart);
271 return IntRect(isHorizontal ? x() + previousButton.width() : x(),
272 isHorizontal ? y() : y() + previousButton.height(),
273 isHorizontal ? partRenderer->width() : width(),
274 isHorizontal ? height() : partRenderer->height());
275 }
276
277 IntRect followingButton = buttonRect(ForwardButtonEndPart);
278 return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->width() : x(),
279 isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->height(),
280 isHorizontal ? partRenderer->width() : width(),
281 isHorizontal ? height() : partRenderer->height());
282 }
283
trackRect(int startLength,int endLength)284 IntRect RenderScrollbar::trackRect(int startLength, int endLength)
285 {
286 RenderScrollbarPart* part = m_parts.get(TrackBGPart);
287 if (part)
288 part->layout();
289
290 if (orientation() == HorizontalScrollbar) {
291 int marginLeft = part ? part->marginLeft() : 0;
292 int marginRight = part ? part->marginRight() : 0;
293 startLength += marginLeft;
294 endLength += marginRight;
295 int totalLength = startLength + endLength;
296 return IntRect(x() + startLength, y(), width() - totalLength, height());
297 }
298
299 int marginTop = part ? part->marginTop() : 0;
300 int marginBottom = part ? part->marginBottom() : 0;
301 startLength += marginTop;
302 endLength += marginBottom;
303 int totalLength = startLength + endLength;
304
305 return IntRect(x(), y() + startLength, width(), height() - totalLength);
306 }
307
trackPieceRectWithMargins(ScrollbarPart partType,const IntRect & oldRect)308 IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect)
309 {
310 RenderScrollbarPart* partRenderer = m_parts.get(partType);
311 if (!partRenderer)
312 return oldRect;
313
314 partRenderer->layout();
315
316 IntRect rect = oldRect;
317 if (orientation() == HorizontalScrollbar) {
318 rect.setX(rect.x() + partRenderer->marginLeft());
319 rect.setWidth(rect.width() - (partRenderer->marginLeft() + partRenderer->marginRight()));
320 } else {
321 rect.setY(rect.y() + partRenderer->marginTop());
322 rect.setHeight(rect.height() - (partRenderer->marginTop() + partRenderer->marginBottom()));
323 }
324 return rect;
325 }
326
minimumThumbLength()327 int RenderScrollbar::minimumThumbLength()
328 {
329 RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart);
330 if (!partRenderer)
331 return 0;
332 partRenderer->layout();
333 return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height();
334 }
335
336 }
337