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