blob: 0088012e240cd67361137082e522046a121c5ca3 [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 "third_party/blink/renderer/core/scroll/scrollbar_layer_delegate.h"
#include "third_party/blink/public/platform/web_point.h"
#include "third_party/blink/public/platform/web_rect.h"
#include "third_party/blink/renderer/core/scroll/scroll_types.h"
#include "third_party/blink/renderer/core/scroll/scrollable_area.h"
#include "third_party/blink/renderer/core/scroll/scrollbar.h"
#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
#include "ui/gfx/skia_util.h"
namespace blink {
namespace {
class ScopedScrollbarPainter {
public:
ScopedScrollbarPainter(cc::PaintCanvas& canvas, float device_scale_factor)
: canvas_(canvas) {
builder_.Context().SetDeviceScaleFactor(device_scale_factor);
}
~ScopedScrollbarPainter() { canvas_.drawPicture(builder_.EndRecording()); }
GraphicsContext& Context() { return builder_.Context(); }
private:
cc::PaintCanvas& canvas_;
PaintRecordBuilder builder_;
};
} // namespace
ScrollbarLayerDelegate::ScrollbarLayerDelegate(blink::Scrollbar& scrollbar,
float device_scale_factor)
: scrollbar_(&scrollbar),
theme_(scrollbar.GetTheme()),
device_scale_factor_(device_scale_factor) {}
ScrollbarLayerDelegate::~ScrollbarLayerDelegate() = default;
cc::ScrollbarOrientation ScrollbarLayerDelegate::Orientation() const {
if (scrollbar_->Orientation() == kHorizontalScrollbar)
return cc::HORIZONTAL;
return cc::VERTICAL;
}
bool ScrollbarLayerDelegate::IsLeftSideVerticalScrollbar() const {
return scrollbar_->IsLeftSideVerticalScrollbar();
}
bool ScrollbarLayerDelegate::HasThumb() const {
return theme_.HasThumb(*scrollbar_);
}
bool ScrollbarLayerDelegate::IsOverlay() const {
return scrollbar_->IsOverlayScrollbar();
}
gfx::Point ScrollbarLayerDelegate::Location() const {
return scrollbar_->Location();
}
int ScrollbarLayerDelegate::ThumbThickness() const {
IntRect thumb_rect = theme_.ThumbRect(*scrollbar_);
if (scrollbar_->Orientation() == kHorizontalScrollbar)
return thumb_rect.Height();
return thumb_rect.Width();
}
int ScrollbarLayerDelegate::ThumbLength() const {
IntRect thumb_rect = theme_.ThumbRect(*scrollbar_);
if (scrollbar_->Orientation() == kHorizontalScrollbar)
return thumb_rect.Width();
return thumb_rect.Height();
}
gfx::Rect ScrollbarLayerDelegate::TrackRect() const {
IntRect track_rect = theme_.TrackRect(*scrollbar_);
track_rect.MoveBy(-scrollbar_->Location());
return track_rect;
}
bool ScrollbarLayerDelegate::SupportsDragSnapBack() const {
return theme_.SupportsDragSnapBack();
}
gfx::Rect ScrollbarLayerDelegate::BackButtonRect() const {
IntRect back_button_rect =
theme_.BackButtonRect(*scrollbar_, blink::kBackButtonStartPart);
back_button_rect.MoveBy(-scrollbar_->Location());
return back_button_rect;
}
gfx::Rect ScrollbarLayerDelegate::ForwardButtonRect() const {
IntRect forward_button_rect =
theme_.ForwardButtonRect(*scrollbar_, blink::kForwardButtonEndPart);
forward_button_rect.MoveBy(-scrollbar_->Location());
return forward_button_rect;
}
float ScrollbarLayerDelegate::ThumbOpacity() const {
return theme_.ThumbOpacity(*scrollbar_);
}
bool ScrollbarLayerDelegate::NeedsPaintPart(cc::ScrollbarPart part) const {
if (part == cc::THUMB)
return scrollbar_->ThumbNeedsRepaint();
return scrollbar_->TrackNeedsRepaint();
}
bool ScrollbarLayerDelegate::UsesNinePatchThumbResource() const {
return theme_.UsesNinePatchThumbResource();
}
gfx::Size ScrollbarLayerDelegate::NinePatchThumbCanvasSize() const {
DCHECK(theme_.UsesNinePatchThumbResource());
return static_cast<gfx::Size>(theme_.NinePatchThumbCanvasSize(*scrollbar_));
}
gfx::Rect ScrollbarLayerDelegate::NinePatchThumbAperture() const {
DCHECK(theme_.UsesNinePatchThumbResource());
return theme_.NinePatchThumbAperture(*scrollbar_);
}
bool ScrollbarLayerDelegate::ShouldPaint() const {
// TODO(crbug.com/860499): Remove this condition, it should not occur.
// Layers may exist and be painted for a |scrollbar_| that has had its
// ScrollableArea detached. This seems weird because if the area is detached
// the layer should be destroyed but here we are. https://crbug.com/860499.
if (!scrollbar_->GetScrollableArea())
return false;
// When the frame is throttled, the scrollbar will not be painted because
// the frame has not had its lifecycle updated. Thus the actual value of
// HasTickmarks can't be known and may change once the frame is unthrottled.
if (scrollbar_->GetScrollableArea()->IsThrottled())
return false;
return true;
}
bool ScrollbarLayerDelegate::HasTickmarks() const {
return ShouldPaint() && scrollbar_->HasTickmarks();
}
void ScrollbarLayerDelegate::PaintPart(cc::PaintCanvas* canvas,
cc::ScrollbarPart part) {
if (!ShouldPaint())
return;
ScopedScrollbarPainter painter(*canvas, device_scale_factor_);
// The canvas coordinate space is relative to the part's origin.
switch (part) {
case cc::THUMB: {
IntRect rect(IntPoint(),
UsesNinePatchThumbResource()
? theme_.NinePatchThumbCanvasSize(*scrollbar_)
: theme_.ThumbRect(*scrollbar_).Size());
theme_.PaintThumb(painter.Context(), *scrollbar_, rect);
scrollbar_->ClearThumbNeedsRepaint();
break;
}
case cc::TRACK: {
theme_.PaintTrackAndButtonsForCompositor(painter.Context(), *scrollbar_);
theme_.PaintTickmarks(painter.Context(), *scrollbar_,
IntRect(TrackRect()));
scrollbar_->ClearTrackNeedsRepaint();
break;
}
case cc::TICKMARKS: {
IntRect rect(IntPoint(), theme_.TrackRect(*scrollbar_).Size());
theme_.PaintTickmarks(painter.Context(), *scrollbar_, rect);
break;
}
default:
NOTREACHED();
}
}
} // namespace blink