/* | |
* 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 "RenderScrollbarPart.h" | |
#include "RenderScrollbarTheme.h" | |
namespace WebCore { | |
PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, RenderBox* renderer) | |
{ | |
return adoptRef(new RenderScrollbar(client, orientation, renderer)); | |
} | |
RenderScrollbar::RenderScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, RenderBox* renderer) | |
: Scrollbar(client, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme()) | |
, m_owner(renderer) | |
{ | |
// FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created. | |
// Update the scrollbar size. | |
updateScrollbarPart(ScrollbarBGPart); | |
RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart); | |
if (!part) | |
return; | |
part->layout(); | |
setFrameRect(IntRect(0, 0, part->width(), part->height())); | |
} | |
RenderScrollbar::~RenderScrollbar() | |
{ | |
ASSERT(m_parts.isEmpty()); | |
} | |
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) | |
{ | |
s_styleResolvePart = partType; | |
s_styleResolveScrollbar = this; | |
RefPtr<RenderStyle> result = m_owner->getUncachedPseudoStyle(pseudoId, m_owner->style()); | |
s_styleResolvePart = NoPart; | |
s_styleResolveScrollbar = 0; | |
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())); | |
m_owner->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 (m_owner->renderArena()) RenderScrollbarPart(m_owner->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(); | |
} | |
} |