/* * 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 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 RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId) { if (!m_owner) return 0; s_styleResolvePart = partType; s_styleResolveScrollbar = this; RefPtr 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 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(); } }