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 34 PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, RenderBox* renderer) 35 { 36 return adoptRef(new RenderScrollbar(client, orientation, renderer)); 37 } 38 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 55 RenderScrollbar::~RenderScrollbar() 56 { 57 ASSERT(m_parts.isEmpty()); 58 } 59 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 69 void RenderScrollbar::setEnabled(bool e) 70 { 71 bool wasEnabled = enabled(); 72 Scrollbar::setEnabled(e); 73 if (wasEnabled != e) 74 updateScrollbarParts(); 75 } 76 77 void RenderScrollbar::styleChanged() 78 { 79 updateScrollbarParts(); 80 } 81 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 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 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 121 RenderScrollbar* RenderScrollbar::scrollbarForStyleResolve() 122 { 123 return s_styleResolveScrollbar; 124 } 125 126 ScrollbarPart RenderScrollbar::partForStyleResolve() 127 { 128 return s_styleResolvePart; 129 } 130 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 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 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 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 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 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 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 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 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