Home | History | Annotate | Download | only in rendering
      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