blob: 590438dfacf3b930eaecc6b51e9b658cdbdd2ad7 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/paint/ScrollableAreaPainter.h"
#include "core/layout/LayoutView.h"
#include "core/page/Page.h"
#include "core/paint/LayoutObjectDrawingRecorder.h"
#include "core/paint/PaintInfo.h"
#include "core/paint/PaintLayer.h"
#include "core/paint/PaintLayerScrollableArea.h"
#include "core/paint/ScrollbarPainter.h"
#include "core/paint/TransformRecorder.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/GraphicsContextStateSaver.h"
namespace blink {
void ScrollableAreaPainter::paintResizer(GraphicsContext& context, const IntPoint& paintOffset, const CullRect& cullRect)
{
if (scrollableArea().box().style()->resize() == RESIZE_NONE)
return;
IntRect absRect = scrollableArea().resizerCornerRect(scrollableArea().box().pixelSnappedBorderBoxRect(), ResizerForPointer);
if (absRect.isEmpty())
return;
absRect.moveBy(paintOffset);
if (scrollableArea().resizer()) {
if (!cullRect.intersectsCullRect(absRect))
return;
ScrollbarPainter::paintIntoRect(*scrollableArea().resizer(), context, paintOffset, LayoutRect(absRect));
return;
}
if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, scrollableArea().box(), DisplayItem::Resizer, paintOffset))
return;
LayoutObjectDrawingRecorder recorder(context, scrollableArea().box(), DisplayItem::Resizer, absRect, paintOffset);
drawPlatformResizerImage(context, absRect);
// Draw a frame around the resizer (1px grey line) if there are any scrollbars present.
// Clipping will exclude the right and bottom edges of this frame.
if (!scrollableArea().hasOverlayScrollbars() && scrollableArea().hasScrollbar()) {
GraphicsContextStateSaver stateSaver(context);
context.clip(absRect);
IntRect largerCorner = absRect;
largerCorner.setSize(IntSize(largerCorner.width() + 1, largerCorner.height() + 1));
context.setStrokeColor(Color(217, 217, 217));
context.setStrokeThickness(1.0f);
context.setFillColor(Color::transparent);
context.drawRect(largerCorner);
}
}
void ScrollableAreaPainter::drawPlatformResizerImage(GraphicsContext& context, IntRect resizerCornerRect)
{
float deviceScaleFactor = blink::deviceScaleFactor(scrollableArea().box().frame());
RefPtr<Image> resizeCornerImage;
IntSize cornerResizerSize;
if (deviceScaleFactor >= 2) {
DEFINE_STATIC_REF(Image, resizeCornerImageHiRes, (Image::loadPlatformResource("textAreaResizeCorner@2x")));
resizeCornerImage = resizeCornerImageHiRes;
cornerResizerSize = resizeCornerImage->size();
cornerResizerSize.scale(0.5f);
} else {
DEFINE_STATIC_REF(Image, resizeCornerImageLoRes, (Image::loadPlatformResource("textAreaResizeCorner")));
resizeCornerImage = resizeCornerImageLoRes;
cornerResizerSize = resizeCornerImage->size();
}
if (scrollableArea().box().shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
context.save();
context.translate(resizerCornerRect.x() + cornerResizerSize.width(), resizerCornerRect.y() + resizerCornerRect.height() - cornerResizerSize.height());
context.scale(-1.0, 1.0);
context.drawImage(resizeCornerImage.get(), IntRect(IntPoint(), cornerResizerSize));
context.restore();
return;
}
IntRect imageRect(resizerCornerRect.maxXMaxYCorner() - cornerResizerSize, cornerResizerSize);
context.drawImage(resizeCornerImage.get(), imageRect);
}
void ScrollableAreaPainter::paintOverflowControls(GraphicsContext& context, const IntPoint& paintOffset, const CullRect& cullRect, bool paintingOverlayControls)
{
// Don't do anything if we have no overflow.
if (!scrollableArea().box().hasOverflowClip())
return;
IntPoint adjustedPaintOffset = paintOffset;
if (paintingOverlayControls)
adjustedPaintOffset = scrollableArea().cachedOverlayScrollbarOffset();
CullRect adjustedCullRect(cullRect, -adjustedPaintOffset);
// Overlay scrollbars paint in a second pass through the layer tree so that they will paint
// on top of everything else. If this is the normal painting pass, paintingOverlayControls
// will be false, and we should just tell the root layer that there are overlay scrollbars
// that need to be painted. That will cause the second pass through the layer tree to run,
// and we'll paint the scrollbars then. In the meantime, cache tx and ty so that the
// second pass doesn't need to re-enter the LayoutTree to get it right.
if (scrollableArea().hasOverlayScrollbars() && !paintingOverlayControls) {
scrollableArea().setCachedOverlayScrollbarOffset(paintOffset);
// It's not necessary to do the second pass if the scrollbars paint into layers.
if ((scrollableArea().horizontalScrollbar() && scrollableArea().layerForHorizontalScrollbar()) || (scrollableArea().verticalScrollbar() && scrollableArea().layerForVerticalScrollbar()))
return;
if (!overflowControlsIntersectRect(adjustedCullRect))
return;
LayoutView* layoutView = scrollableArea().box().view();
PaintLayer* paintingRoot = scrollableArea().layer()->enclosingLayerWithCompositedLayerMapping(IncludeSelf);
if (!paintingRoot)
paintingRoot = layoutView->layer();
paintingRoot->setContainsDirtyOverlayScrollbars(true);
return;
}
// This check is required to avoid painting custom CSS scrollbars twice.
if (paintingOverlayControls && !scrollableArea().hasOverlayScrollbars())
return;
{
if (scrollableArea().horizontalScrollbar() && !scrollableArea().layerForHorizontalScrollbar()) {
TransformRecorder translateRecorder(context, *scrollableArea().horizontalScrollbar(), AffineTransform::translation(adjustedPaintOffset.x(), adjustedPaintOffset.y()));
scrollableArea().horizontalScrollbar()->paint(context, adjustedCullRect);
}
if (scrollableArea().verticalScrollbar() && !scrollableArea().layerForVerticalScrollbar()) {
TransformRecorder translateRecorder(context, *scrollableArea().verticalScrollbar(), AffineTransform::translation(adjustedPaintOffset.x(), adjustedPaintOffset.y()));
scrollableArea().verticalScrollbar()->paint(context, adjustedCullRect);
}
}
if (scrollableArea().layerForScrollCorner())
return;
// We fill our scroll corner with white if we have a scrollbar that doesn't run all the way up to the
// edge of the box.
paintScrollCorner(context, adjustedPaintOffset, cullRect);
// Paint our resizer last, since it sits on top of the scroll corner.
paintResizer(context, adjustedPaintOffset, cullRect);
}
bool ScrollableAreaPainter::overflowControlsIntersectRect(const CullRect& cullRect) const
{
const IntRect borderBox = scrollableArea().box().pixelSnappedBorderBoxRect();
if (cullRect.intersectsCullRect(scrollableArea().rectForHorizontalScrollbar(borderBox)))
return true;
if (cullRect.intersectsCullRect(scrollableArea().rectForVerticalScrollbar(borderBox)))
return true;
if (cullRect.intersectsCullRect(scrollableArea().scrollCornerRect()))
return true;
if (cullRect.intersectsCullRect(scrollableArea().resizerCornerRect(borderBox, ResizerForPointer)))
return true;
return false;
}
void ScrollableAreaPainter::paintScrollCorner(GraphicsContext& context, const IntPoint& paintOffset, const CullRect& adjustedCullRect)
{
IntRect absRect = scrollableArea().scrollCornerRect();
if (absRect.isEmpty())
return;
absRect.moveBy(paintOffset);
if (scrollableArea().scrollCorner()) {
if (!adjustedCullRect.intersectsCullRect(absRect))
return;
ScrollbarPainter::paintIntoRect(*scrollableArea().scrollCorner(), context, paintOffset, LayoutRect(absRect));
return;
}
// We don't want to paint white if we have overlay scrollbars, since we need
// to see what is behind it.
if (scrollableArea().hasOverlayScrollbars())
return;
if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, scrollableArea().box(), DisplayItem::ScrollbarCorner, paintOffset))
return;
LayoutObjectDrawingRecorder recorder(context, scrollableArea().box(), DisplayItem::ScrollbarCorner, absRect, paintOffset);
context.fillRect(absRect, Color::white);
}
PaintLayerScrollableArea& ScrollableAreaPainter::scrollableArea() const
{
return *m_scrollableArea;
}
} // namespace blink