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