diff options
Diffstat (limited to 'Source/WebCore/rendering/RenderScrollbar.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderScrollbar.cpp | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/Source/WebCore/rendering/RenderScrollbar.cpp b/Source/WebCore/rendering/RenderScrollbar.cpp new file mode 100644 index 0000000..4091d51 --- /dev/null +++ b/Source/WebCore/rendering/RenderScrollbar.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderScrollbar.h" + +#include "Frame.h" +#include "FrameView.h" +#include "RenderPart.h" +#include "RenderScrollbarPart.h" +#include "RenderScrollbarTheme.h" + +namespace WebCore { + +PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, RenderBox* renderer, Frame* owningFrame) +{ + return adoptRef(new RenderScrollbar(scrollableArea, orientation, renderer, owningFrame)); +} + +RenderScrollbar::RenderScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, RenderBox* renderer, Frame* owningFrame) + : Scrollbar(scrollableArea, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme()) + , m_owner(renderer) + , m_owningFrame(owningFrame) +{ + // FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created. + + // Update the scrollbar size. + int width = 0; + int height = 0; + updateScrollbarPart(ScrollbarBGPart); + if (RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart)) { + part->layout(); + width = part->width(); + height = part->height(); + } else if (this->orientation() == HorizontalScrollbar) + width = this->width(); + else + height = this->height(); + + setFrameRect(IntRect(0, 0, width, height)); +} + +RenderScrollbar::~RenderScrollbar() +{ + ASSERT(m_parts.isEmpty()); +} + +RenderBox* RenderScrollbar::owningRenderer() const +{ + if (m_owningFrame) { + RenderBox* currentRenderer = m_owningFrame->ownerRenderer(); + return currentRenderer; + } + return m_owner; +} + +void RenderScrollbar::setParent(ScrollView* parent) +{ + Scrollbar::setParent(parent); + if (!parent) { + // Destroy all of the scrollbar's RenderBoxes. + updateScrollbarParts(true); + } +} + +void RenderScrollbar::setEnabled(bool e) +{ + bool wasEnabled = enabled(); + Scrollbar::setEnabled(e); + if (wasEnabled != e) + updateScrollbarParts(); +} + +void RenderScrollbar::styleChanged() +{ + updateScrollbarParts(); +} + +void RenderScrollbar::paint(GraphicsContext* context, const IntRect& damageRect) +{ + if (context->updatingControlTints()) { + updateScrollbarParts(); + return; + } + Scrollbar::paint(context, damageRect); +} + +void RenderScrollbar::setHoveredPart(ScrollbarPart part) +{ + if (part == m_hoveredPart) + return; + + ScrollbarPart oldPart = m_hoveredPart; + m_hoveredPart = part; + + updateScrollbarPart(oldPart); + updateScrollbarPart(m_hoveredPart); + + updateScrollbarPart(ScrollbarBGPart); + updateScrollbarPart(TrackBGPart); +} + +void RenderScrollbar::setPressedPart(ScrollbarPart part) +{ + ScrollbarPart oldPart = m_pressedPart; + Scrollbar::setPressedPart(part); + + updateScrollbarPart(oldPart); + updateScrollbarPart(part); + + updateScrollbarPart(ScrollbarBGPart); + updateScrollbarPart(TrackBGPart); +} + +static ScrollbarPart s_styleResolvePart; +static RenderScrollbar* s_styleResolveScrollbar; + +RenderScrollbar* RenderScrollbar::scrollbarForStyleResolve() +{ + return s_styleResolveScrollbar; +} + +ScrollbarPart RenderScrollbar::partForStyleResolve() +{ + return s_styleResolvePart; +} + +PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId) +{ + if (!m_owner) + return 0; + + s_styleResolvePart = partType; + s_styleResolveScrollbar = this; + RefPtr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(pseudoId, owningRenderer()->style()); + s_styleResolvePart = NoPart; + s_styleResolveScrollbar = 0; + + // Scrollbars for root frames should always have background color + // unless explicitly specified as transparent. So we force it. + // This is because WebKit assumes scrollbar to be always painted and missing background + // causes visual artifact like non-repainted dirty region. + if (result && m_owningFrame && m_owningFrame->view() && !m_owningFrame->view()->isTransparent() && !result->hasBackground()) + result->setBackgroundColor(Color::white); + + return result; +} + +void RenderScrollbar::updateScrollbarParts(bool destroy) +{ + updateScrollbarPart(ScrollbarBGPart, destroy); + updateScrollbarPart(BackButtonStartPart, destroy); + updateScrollbarPart(ForwardButtonStartPart, destroy); + updateScrollbarPart(BackTrackPart, destroy); + updateScrollbarPart(ThumbPart, destroy); + updateScrollbarPart(ForwardTrackPart, destroy); + updateScrollbarPart(BackButtonEndPart, destroy); + updateScrollbarPart(ForwardButtonEndPart, destroy); + updateScrollbarPart(TrackBGPart, destroy); + + if (destroy) + return; + + // See if the scrollbar's thickness changed. If so, we need to mark our owning object as needing a layout. + bool isHorizontal = orientation() == HorizontalScrollbar; + int oldThickness = isHorizontal ? height() : width(); + int newThickness = 0; + RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart); + if (part) { + part->layout(); + newThickness = isHorizontal ? part->height() : part->width(); + } + + if (newThickness != oldThickness) { + setFrameRect(IntRect(x(), y(), isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height())); + owningRenderer()->setChildNeedsLayout(true); + } +} + +static PseudoId pseudoForScrollbarPart(ScrollbarPart part) +{ + switch (part) { + case BackButtonStartPart: + case ForwardButtonStartPart: + case BackButtonEndPart: + case ForwardButtonEndPart: + return SCROLLBAR_BUTTON; + case BackTrackPart: + case ForwardTrackPart: + return SCROLLBAR_TRACK_PIECE; + case ThumbPart: + return SCROLLBAR_THUMB; + case TrackBGPart: + return SCROLLBAR_TRACK; + case ScrollbarBGPart: + return SCROLLBAR; + case NoPart: + case AllParts: + break; + } + ASSERT_NOT_REACHED(); + return SCROLLBAR; +} + +void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy) +{ + if (partType == NoPart) + return; + + RefPtr<RenderStyle> partStyle = !destroy ? getScrollbarPseudoStyle(partType, pseudoForScrollbarPart(partType)) : 0; + + bool needRenderer = !destroy && partStyle && partStyle->display() != NONE && partStyle->visibility() == VISIBLE; + + if (needRenderer && partStyle->display() != BLOCK) { + // See if we are a button that should not be visible according to OS settings. + ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement(); + switch (partType) { + case BackButtonStartPart: + needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart || + buttonsPlacement == ScrollbarButtonsDoubleBoth); + break; + case ForwardButtonStartPart: + needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth); + break; + case BackButtonEndPart: + needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth); + break; + case ForwardButtonEndPart: + needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd || + buttonsPlacement == ScrollbarButtonsDoubleBoth); + break; + default: + break; + } + } + + RenderScrollbarPart* partRenderer = m_parts.get(partType); + if (!partRenderer && needRenderer) { + partRenderer = new (owningRenderer()->renderArena()) RenderScrollbarPart(owningRenderer()->document(), this, partType); + m_parts.set(partType, partRenderer); + } else if (partRenderer && !needRenderer) { + m_parts.remove(partType); + partRenderer->destroy(); + partRenderer = 0; + } + + if (partRenderer) + partRenderer->setStyle(partStyle.release()); +} + +void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect) +{ + RenderScrollbarPart* partRenderer = m_parts.get(partType); + if (!partRenderer) + return; + partRenderer->paintIntoRect(graphicsContext, x(), y(), rect); +} + +IntRect RenderScrollbar::buttonRect(ScrollbarPart partType) +{ + RenderScrollbarPart* partRenderer = m_parts.get(partType); + if (!partRenderer) + return IntRect(); + + partRenderer->layout(); + + bool isHorizontal = orientation() == HorizontalScrollbar; + if (partType == BackButtonStartPart) + return IntRect(x(), y(), isHorizontal ? partRenderer->width() : width(), isHorizontal ? height() : partRenderer->height()); + if (partType == ForwardButtonEndPart) + return IntRect(isHorizontal ? x() + width() - partRenderer->width() : x(), + + isHorizontal ? y() : y() + height() - partRenderer->height(), + isHorizontal ? partRenderer->width() : width(), + isHorizontal ? height() : partRenderer->height()); + + if (partType == ForwardButtonStartPart) { + IntRect previousButton = buttonRect(BackButtonStartPart); + return IntRect(isHorizontal ? x() + previousButton.width() : x(), + isHorizontal ? y() : y() + previousButton.height(), + isHorizontal ? partRenderer->width() : width(), + isHorizontal ? height() : partRenderer->height()); + } + + IntRect followingButton = buttonRect(ForwardButtonEndPart); + return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->width() : x(), + isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->height(), + isHorizontal ? partRenderer->width() : width(), + isHorizontal ? height() : partRenderer->height()); +} + +IntRect RenderScrollbar::trackRect(int startLength, int endLength) +{ + RenderScrollbarPart* part = m_parts.get(TrackBGPart); + if (part) + part->layout(); + + if (orientation() == HorizontalScrollbar) { + int marginLeft = part ? part->marginLeft() : 0; + int marginRight = part ? part->marginRight() : 0; + startLength += marginLeft; + endLength += marginRight; + int totalLength = startLength + endLength; + return IntRect(x() + startLength, y(), width() - totalLength, height()); + } + + int marginTop = part ? part->marginTop() : 0; + int marginBottom = part ? part->marginBottom() : 0; + startLength += marginTop; + endLength += marginBottom; + int totalLength = startLength + endLength; + + return IntRect(x(), y() + startLength, width(), height() - totalLength); +} + +IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect) +{ + RenderScrollbarPart* partRenderer = m_parts.get(partType); + if (!partRenderer) + return oldRect; + + partRenderer->layout(); + + IntRect rect = oldRect; + if (orientation() == HorizontalScrollbar) { + rect.setX(rect.x() + partRenderer->marginLeft()); + rect.setWidth(rect.width() - (partRenderer->marginLeft() + partRenderer->marginRight())); + } else { + rect.setY(rect.y() + partRenderer->marginTop()); + rect.setHeight(rect.height() - (partRenderer->marginTop() + partRenderer->marginBottom())); + } + return rect; +} + +int RenderScrollbar::minimumThumbLength() +{ + RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart); + if (!partRenderer) + return 0; + partRenderer->layout(); + return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height(); +} + +} |