#include "cc/layers/scrollbar_layer_impl_base.h"
#include <algorithm>
#include "base/cancelable_callback.h"
#include "cc/base/features.h"
#include "cc/base/math_util.h"
#include "cc/input/scroll_utils.h"
#include "cc/input/scrollbar.h"
#include "cc/input/scrollbar_controller.h"
#include "cc/layers/viewport.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/scroll_node.h"
namespace cc {
ScrollbarController::~ScrollbarController() {
if (cancelable_autoscroll_task_) {
cancelable_autoscroll_task_->Cancel();
cancelable_autoscroll_task_.reset();
}
}
ScrollbarController::ScrollbarController(
LayerTreeHostImpl* layer_tree_host_impl)
: layer_tree_host_impl_(layer_tree_host_impl),
scrollbar_scroll_is_active_(false),
last_known_pointer_position_(gfx::PointF(0, 0)),
drag_processed_for_current_frame_(false),
cancelable_autoscroll_task_(nullptr) {}
void ScrollbarController::WillBeginImplFrame() {
drag_processed_for_current_frame_ = false;
RecomputeAutoscrollStateIfNeeded();
}
ScrollbarLayerImplBase* ScrollbarController::ScrollbarLayer() const {
if (!captured_scrollbar_metadata_.has_value())
return nullptr;
const ScrollbarSet scrollbars = layer_tree_host_impl_->ScrollbarsFor(
captured_scrollbar_metadata_->scroll_element_id);
for (ScrollbarLayerImplBase* scrollbar : scrollbars) {
if (captured_scrollbar_metadata_->orientation == scrollbar->orientation())
return scrollbar;
}
return nullptr;
}
const ScrollbarLayerImplBase* ScrollbarController::HitTest(
const gfx::PointF position_in_widget) const {
const LayerImpl* layer_impl = GetLayerHitByPoint(position_in_widget);
if (!(layer_impl && layer_impl->IsScrollbarLayer()))
return nullptr;
const ScrollbarLayerImplBase* scrollbar = ToScrollbarLayer(layer_impl);
return scrollbar->OverlayScrollbarOpacity() == 0.f ? nullptr : scrollbar;
}
InputHandlerPointerResult ScrollbarController::HandlePointerDown(
const gfx::PointF position_in_widget,
bool jump_key_modifier) {
const ScrollbarLayerImplBase* scrollbar = HitTest(position_in_widget);
if (!scrollbar) {
return InputHandlerPointerResult();
}
captured_scrollbar_metadata_ = CapturedScrollbarMetadata();
captured_scrollbar_metadata_->scroll_element_id =
scrollbar->scroll_element_id();
captured_scrollbar_metadata_->orientation = scrollbar->orientation();
InputHandlerPointerResult scroll_result;
scroll_result.target_scroller = scrollbar->scroll_element_id();
scroll_result.type = PointerResultType::kScrollbarScroll;
const ScrollbarPart scrollbar_part =
GetScrollbarPartFromPointerDown(position_in_widget);
const bool perform_jump_click_on_track =
scrollbar->JumpOnTrackClick() != jump_key_modifier;
last_known_pointer_position_ = position_in_widget;
scroll_result.scroll_delta = GetScrollDeltaForScrollbarPart(
scrollbar_part, perform_jump_click_on_track);
scrollbar_scroll_is_active_ = true;
scroll_result.scroll_units =
Granularity(scrollbar_part, perform_jump_click_on_track);
const bool trigger_jump_click_on_track_part =
perform_jump_click_on_track &&
(scrollbar_part == ScrollbarPart::kBackTrack ||
scrollbar_part == ScrollbarPart::kForwardTrack);
if (scrollbar_part == ScrollbarPart::kThumb ||
trigger_jump_click_on_track_part) {
drag_state_ = DragState();
bool clipped = false;
drag_state_->drag_origin =
trigger_jump_click_on_track_part
? DragOriginForJumpClick(scrollbar)
: GetScrollbarRelativePosition(position_in_widget, &clipped);
DCHECK(!clipped);
drag_state_->scroll_position_at_start_ = scrollbar->current_pos();
drag_state_->scroller_length_at_previous_move =
scrollbar->scroll_layer_length();
}
if (!scroll_result.scroll_delta.IsZero() && !perform_jump_click_on_track) {
DCHECK(scrollbar_part != ScrollbarPart::kThumb);
autoscroll_state_ = AutoScrollState();
autoscroll_state_->velocity =
InitialDeltaToAutoscrollVelocity(scroll_result.scroll_delta);
autoscroll_state_->pressed_scrollbar_part = scrollbar_part;
PostAutoscrollTask(kInitialAutoscrollTimerDelay);
}
return scroll_result;
}
void ScrollbarController::PostAutoscrollTask(const base::TimeDelta delay) {
cancelable_autoscroll_task_ =
std::make_unique<base::CancelableOnceClosure>(base::BindOnce(
&ScrollbarController::StartAutoScroll, base::Unretained(this)));
layer_tree_host_impl_->GetTaskRunner()->PostDelayedTask(
FROM_HERE, cancelable_autoscroll_task_->callback(), delay);
}
gfx::PointF ScrollbarController::DragOriginForJumpClick(
const ScrollbarLayerImplBase* scrollbar) const {
gfx::Rect thumb = scrollbar->ComputeThumbQuadRect();
return scrollbar->orientation() == ScrollbarOrientation::kHorizontal
? gfx::PointF(thumb.x() + thumb.width() / 2, 0)
: gfx::PointF(0, thumb.y() + thumb.height() / 2);
}
bool ScrollbarController::SnapToDragOrigin(
const gfx::PointF pointer_position_in_widget) const {
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
if (!(scrollbar && scrollbar->SupportsDragSnapBack()))
return false;
bool clipped = false;
const gfx::PointF pointer_position_in_layer =
GetScrollbarRelativePosition(pointer_position_in_widget, &clipped);
if (clipped)
return false;
const ScrollbarOrientation orientation = scrollbar->orientation();
const gfx::Rect forward_track_rect = scrollbar->ForwardTrackRect();
int track_thickness = orientation == ScrollbarOrientation::kVertical
? forward_track_rect.width()
: forward_track_rect.height();
if (!track_thickness) {
const int thumb_thickness = scrollbar->ThumbThickness();
if (!thumb_thickness)
return false;
track_thickness = thumb_thickness;
}
const float gutter_thickness = kOffSideMultiplier * track_thickness;
const float gutter_min_bound =
orientation == ScrollbarOrientation::kVertical
? (forward_track_rect.x() - gutter_thickness)
: (forward_track_rect.y() - gutter_thickness);
const float gutter_max_bound =
orientation == ScrollbarOrientation::kVertical
? (forward_track_rect.x() + track_thickness + gutter_thickness)
: (forward_track_rect.y() + track_thickness + gutter_thickness);
const float pointer_location = orientation == ScrollbarOrientation::kVertical
? pointer_position_in_layer.x()
: pointer_position_in_layer.y();
return pointer_location < gutter_min_bound ||
pointer_location > gutter_max_bound;
}
ui::ScrollGranularity ScrollbarController::Granularity(
const ScrollbarPart scrollbar_part,
const bool jump_key_modifier) const {
const bool shift_click_on_scrollbar_track =
jump_key_modifier && (scrollbar_part == ScrollbarPart::kForwardTrack ||
scrollbar_part == ScrollbarPart::kBackTrack);
if (shift_click_on_scrollbar_track ||
scrollbar_part == ScrollbarPart::kThumb) {
return ui::ScrollGranularity::kScrollByPrecisePixel;
}
return ui::ScrollGranularity::kScrollByPixel;
}
float ScrollbarController::GetScrollDistanceForAbsoluteJump() const {
bool clipped = false;
const gfx::PointF pointer_position_in_layer =
GetScrollbarRelativePosition(last_known_pointer_position_, &clipped);
if (clipped)
return 0;
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
const float pointer_location =
scrollbar->orientation() == ScrollbarOrientation::kVertical
? pointer_position_in_layer.y()
: pointer_position_in_layer.x();
const int thumb_length = scrollbar->ThumbLength();
const float desired_thumb_origin = pointer_location - thumb_length / 2.f;
const gfx::Rect thumb_rect(scrollbar->ComputeThumbQuadRect());
const float current_thumb_origin =
scrollbar->orientation() == ScrollbarOrientation::kVertical
? thumb_rect.y()
: thumb_rect.x();
const float distance =
round(std::abs(desired_thumb_origin - current_thumb_origin));
return distance * GetScrollerToScrollbarRatio() *
GetPageScaleFactorForScroll();
}
float ScrollbarController::GetScrollDistanceForDragPosition(
const gfx::PointF pointer_position_in_widget) const {
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
bool clipped = false;
const gfx::PointF scrollbar_relative_position(
GetScrollbarRelativePosition(pointer_position_in_widget, &clipped));
float pointer_delta =
scrollbar->orientation() == ScrollbarOrientation::kVertical
? scrollbar_relative_position.y() - drag_state_->drag_origin.y()
: scrollbar_relative_position.x() - drag_state_->drag_origin.x();
const float new_offset = pointer_delta * GetScrollerToScrollbarRatio();
float distance = drag_state_->scroll_position_at_start_ + new_offset -
scrollbar->current_pos();
distance *= GetPageScaleFactorForScroll();
return distance;
}
InputHandlerPointerResult ScrollbarController::HandlePointerMove(
const gfx::PointF position_in_widget) {
last_known_pointer_position_ = position_in_widget;
RecomputeAutoscrollStateIfNeeded();
InputHandlerPointerResult scroll_result;
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
if (!scrollbar || !drag_state_.has_value())
return scroll_result;
scroll_result.type = PointerResultType::kScrollbarScroll;
if (drag_processed_for_current_frame_)
return scroll_result;
if (SnapToDragOrigin(position_in_widget)) {
const float delta =
scrollbar->current_pos() - drag_state_->scroll_position_at_start_;
scroll_result.scroll_units = ui::ScrollGranularity::kScrollByPrecisePixel;
scroll_result.scroll_delta =
scrollbar->orientation() == ScrollbarOrientation::kVertical
? gfx::Vector2dF(0, -delta)
: gfx::Vector2dF(-delta, 0);
drag_processed_for_current_frame_ = true;
return scroll_result;
}
const ScrollNode* target_node =
layer_tree_host_impl_->active_tree()
->property_trees()
->scroll_tree()
.FindNodeFromElementId(scrollbar->scroll_element_id());
DCHECK(target_node);
float distance = GetScrollDistanceForDragPosition(position_in_widget);
if (drag_state_->scroller_length_at_previous_move !=
scrollbar->scroll_layer_length()) {
drag_state_->scroller_displacement = distance;
drag_state_->scroller_length_at_previous_move =
scrollbar->scroll_layer_length();
return scroll_result;
}
distance -= drag_state_->scroller_displacement;
const gfx::Vector2dF scroll_delta =
scrollbar->orientation() == ScrollbarOrientation::kVertical
? gfx::Vector2dF(0, distance)
: gfx::Vector2dF(distance, 0);
const gfx::Vector2dF clamped_scroll_delta =
ComputeClampedDelta(*target_node, scroll_delta);
if (clamped_scroll_delta.IsZero())
return scroll_result;
scroll_result.scroll_units = ui::ScrollGranularity::kScrollByPrecisePixel;
scroll_result.scroll_delta = clamped_scroll_delta;
drag_processed_for_current_frame_ = true;
return scroll_result;
}
gfx::Vector2dF ScrollbarController::ComputeClampedDelta(
const ScrollNode& target_node,
const gfx::Vector2dF& scroll_delta) const {
DCHECK(!target_node.scrolls_inner_viewport);
if (target_node.scrolls_outer_viewport)
return layer_tree_host_impl_->viewport().ComputeClampedDelta(scroll_delta);
gfx::Vector2dF clamped_delta =
layer_tree_host_impl_->GetInputHandler().ComputeScrollDelta(target_node,
scroll_delta);
const float scale_factor = GetPageScaleFactorForScroll();
clamped_delta.Scale(scale_factor);
return clamped_delta;
}
float ScrollbarController::GetScrollerToScrollbarRatio() const {
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
float scroll_layer_length = scrollbar->scroll_layer_length();
float scrollbar_track_length = scrollbar->TrackLength();
gfx::Rect thumb_rect(scrollbar->ComputeThumbQuadRect());
float scrollbar_thumb_length =
scrollbar->orientation() == ScrollbarOrientation::kVertical
? thumb_rect.height()
: thumb_rect.width();
float viewport_length = GetViewportLength();
if (scrollbar_track_length == scrollbar_thumb_length)
return 0;
return (scroll_layer_length - viewport_length) /
(scrollbar_track_length - scrollbar_thumb_length);
}
void ScrollbarController::ResetState() {
drag_processed_for_current_frame_ = false;
drag_state_ = std::nullopt;
autoscroll_state_ = std::nullopt;
captured_scrollbar_metadata_ = std::nullopt;
if (cancelable_autoscroll_task_) {
cancelable_autoscroll_task_->Cancel();
cancelable_autoscroll_task_.reset();
}
}
void ScrollbarController::DidRegisterScrollbar(
ElementId element_id,
ScrollbarOrientation orientation) {
if (autoscroll_state_.has_value() &&
captured_scrollbar_metadata_->scroll_element_id == element_id &&
captured_scrollbar_metadata_->orientation == orientation &&
autoscroll_state_->status == AutoScrollStatus::kAutoscrollReady) {
PostAutoscrollTask(base::TimeDelta::Min());
}
}
void ScrollbarController::DidUnregisterScrollbar(
ElementId element_id,
ScrollbarOrientation orientation) {
if (autoscroll_state_.has_value() &&
captured_scrollbar_metadata_->scroll_element_id == element_id &&
captured_scrollbar_metadata_->orientation == orientation &&
autoscroll_state_->status == AutoScrollStatus::kAutoscrollScrolling) {
layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(element_id);
autoscroll_state_->status = AutoScrollStatus::kAutoscrollReady;
}
}
void ScrollbarController::RecomputeAutoscrollStateIfNeeded() {
if (!autoscroll_state_.has_value() ||
!captured_scrollbar_metadata_.has_value() ||
autoscroll_state_->status != AutoScrollStatus::kAutoscrollScrolling) {
return;
}
bool clipped;
gfx::PointF scroller_relative_position(
GetScrollbarRelativePosition(last_known_pointer_position_, &clipped));
if (clipped)
return;
int thumb_start = 0;
int thumb_end = 0;
int pointer_position = 0;
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
const gfx::Rect thumb_quad = scrollbar->ComputeThumbQuadRect();
if (scrollbar->orientation() == ScrollbarOrientation::kVertical) {
thumb_start = thumb_quad.y();
thumb_end = thumb_quad.y() + thumb_quad.height();
pointer_position = scroller_relative_position.y();
} else {
thumb_start = thumb_quad.x();
thumb_end = thumb_quad.x() + thumb_quad.width();
pointer_position = scroller_relative_position.x();
}
if ((autoscroll_state_->direction ==
AutoScrollDirection::kAutoscrollForward &&
thumb_end > pointer_position) ||
(autoscroll_state_->direction ==
AutoScrollDirection::kAutoscrollBackward &&
thumb_start < pointer_position)) {
layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(
captured_scrollbar_metadata_->scroll_element_id);
}
if (autoscroll_state_->direction == AutoScrollDirection::kAutoscrollForward) {
const float scroll_layer_length = scrollbar->scroll_layer_length();
if (autoscroll_state_->scroll_layer_length != scroll_layer_length) {
layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(
scrollbar->scroll_element_id());
StartAutoScrollAnimation();
}
}
const gfx::RectF scrollbar_part_rect(
GetRectForScrollbarPart(autoscroll_state_->pressed_scrollbar_part));
if (!scrollbar_part_rect.Contains(scroller_relative_position)) {
layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(
scrollbar->scroll_element_id());
} else if (scrollbar_part_rect.Contains(scroller_relative_position) &&
!layer_tree_host_impl_->mutator_host()->IsElementAnimating(
scrollbar->scroll_element_id())) {
StartAutoScrollAnimation();
}
}
float ScrollbarController::InitialDeltaToAutoscrollVelocity(
gfx::Vector2dF scroll_delta) const {
DCHECK(captured_scrollbar_metadata_.has_value());
const float delta =
ScrollbarLayer()->orientation() == ScrollbarOrientation::kVertical
? scroll_delta.y()
: scroll_delta.x();
return delta * kAutoscrollMultiplier;
}
void ScrollbarController::StartAutoScroll() {
DCHECK(autoscroll_state_.has_value());
if (ScrollbarLayer()) {
autoscroll_state_->status = AutoScrollStatus::kAutoscrollScrolling;
StartAutoScrollAnimation();
} else {
autoscroll_state_->status = AutoScrollStatus::kAutoscrollReady;
}
}
void ScrollbarController::StartAutoScrollAnimation() {
DCHECK(!drag_state_.has_value());
DCHECK(captured_scrollbar_metadata_.has_value());
DCHECK(autoscroll_state_.has_value());
DCHECK(ScrollbarLayer());
DCHECK_EQ(autoscroll_state_->status, AutoScrollStatus::kAutoscrollScrolling);
DCHECK_NE(autoscroll_state_->velocity, 0);
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
const ScrollTree& scroll_tree =
layer_tree_host_impl_->active_tree()->property_trees()->scroll_tree();
const ScrollNode* scroll_node =
scroll_tree.FindNodeFromElementId(scrollbar->scroll_element_id());
if (!(scroll_node && scrollbar_scroll_is_active_))
return;
float scroll_layer_length = scrollbar->scroll_layer_length();
gfx::PointF current_offset =
scroll_tree.current_scroll_offset(scroll_node->element_id);
const float target_offset_in_orientation =
autoscroll_state_->velocity < 0 ? 0 : scroll_layer_length;
const gfx::PointF target_offset_2d =
scrollbar->orientation() == ScrollbarOrientation::kVertical
? gfx::PointF(current_offset.x(), target_offset_in_orientation)
: gfx::PointF(target_offset_in_orientation, current_offset.y());
autoscroll_state_->scroll_layer_length = scroll_layer_length;
autoscroll_state_->direction = autoscroll_state_->velocity < 0
? AutoScrollDirection::kAutoscrollBackward
: AutoScrollDirection::kAutoscrollForward;
layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(
scroll_node->element_id);
layer_tree_host_impl_->AutoScrollAnimationCreate(
*scroll_node, target_offset_2d, std::abs(autoscroll_state_->velocity));
}
InputHandlerPointerResult ScrollbarController::HandlePointerUp(
const gfx::PointF position_in_widget) {
InputHandlerPointerResult scroll_result;
if (scrollbar_scroll_is_active_) {
scrollbar_scroll_is_active_ = false;
scroll_result.type = PointerResultType::kScrollbarScroll;
}
if (autoscroll_state_.has_value() &&
autoscroll_state_->status == AutoScrollStatus::kAutoscrollScrolling) {
layer_tree_host_impl_->mutator_host()->ScrollAnimationAbort(
captured_scrollbar_metadata_->scroll_element_id);
}
ResetState();
return scroll_result;
}
LayerImpl* ScrollbarController::GetLayerHitByPoint(
const gfx::PointF position_in_widget) const {
LayerTreeImpl* active_tree = layer_tree_host_impl_->active_tree();
gfx::Point viewport_point(position_in_widget.x(), position_in_widget.y());
gfx::PointF device_viewport_point = gfx::ScalePoint(
gfx::PointF(viewport_point), active_tree->device_scale_factor());
LayerImpl* layer_impl =
active_tree->FindLayerThatIsHitByPoint(device_viewport_point);
return layer_impl;
}
float ScrollbarController::GetViewportLength() const {
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
const ScrollNode* scroll_node =
layer_tree_host_impl_->active_tree()
->property_trees()
->scroll_tree()
.FindNodeFromElementId(scrollbar->scroll_element_id());
DCHECK(scroll_node);
if (!scroll_node->scrolls_outer_viewport) {
float length = scrollbar->orientation() == ScrollbarOrientation::kVertical
? scroll_node->container_bounds.height()
: scroll_node->container_bounds.width();
return length;
}
gfx::SizeF viewport_size = layer_tree_host_impl_->viewport()
.GetInnerViewportSizeExcludingScrollbars();
float length = scrollbar->orientation() == ScrollbarOrientation::kVertical
? viewport_size.height()
: viewport_size.width();
return length / GetPageScaleFactorForScroll();
}
float ScrollbarController::GetPageScaleFactorForScroll() const {
return layer_tree_host_impl_->active_tree()->page_scale_factor_for_scroll();
}
float ScrollbarController::GetScrollDistanceForScrollbarPart(
const ScrollbarPart scrollbar_part,
const bool jump_key_modifier) const {
float scroll_delta = 0;
switch (scrollbar_part) {
case ScrollbarPart::kBackButton:
case ScrollbarPart::kForwardButton:
scroll_delta = kPixelsPerLineStep * ScreenSpaceScaleFactor();
break;
case ScrollbarPart::kBackTrack:
case ScrollbarPart::kForwardTrack: {
if (jump_key_modifier) {
scroll_delta = GetScrollDistanceForAbsoluteJump();
break;
}
int snapport_length = GetViewportLength();
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
const ScrollNode* target_node =
layer_tree_host_impl_->active_tree()
->property_trees()
->scroll_tree()
.FindNodeFromElementId(scrollbar->scroll_element_id());
if (target_node->snap_container_data) {
const gfx::RectF snapport =
target_node->snap_container_data.value().rect();
snapport_length =
scrollbar->orientation() == ScrollbarOrientation::kHorizontal
? snapport.x()
: snapport.y();
}
scroll_delta = ScrollUtils::CalculatePageStep(snapport_length);
break;
}
default:
scroll_delta = 0;
}
return scroll_delta;
}
float ScrollbarController::ScreenSpaceScaleFactor() const {
return layer_tree_host_impl_->active_tree()->painted_device_scale_factor();
}
gfx::PointF ScrollbarController::GetScrollbarRelativePosition(
const gfx::PointF position_in_widget,
bool* clipped) const {
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
if (!scrollbar) {
*clipped = true;
return gfx::PointF(0, 0);
}
gfx::Transform inverse_screen_space_transform;
gfx::Transform scaled_screen_space_transform(
scrollbar->ScreenSpaceTransform());
if (!scaled_screen_space_transform.GetInverse(
&inverse_screen_space_transform))
return gfx::PointF(0, 0);
return gfx::PointF(MathUtil::ProjectPoint(inverse_screen_space_transform,
position_in_widget, clipped));
}
ScrollbarPart ScrollbarController::GetScrollbarPartFromPointerDown(
const gfx::PointF position_in_widget) const {
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
bool clipped = false;
const gfx::PointF scroller_relative_position(
GetScrollbarRelativePosition(position_in_widget, &clipped));
if (clipped)
return ScrollbarPart::kNoPart;
return scrollbar->IdentifyScrollbarPart(scroller_relative_position);
}
gfx::Rect ScrollbarController::GetRectForScrollbarPart(
const ScrollbarPart scrollbar_part) const {
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
if (scrollbar_part == ScrollbarPart::kBackButton) {
return scrollbar->BackButtonRect();
}
if (scrollbar_part == ScrollbarPart::kForwardButton) {
return scrollbar->ForwardButtonRect();
}
if (scrollbar_part == ScrollbarPart::kBackTrack) {
return scrollbar->BackTrackRect();
}
if (scrollbar_part == ScrollbarPart::kForwardTrack) {
return scrollbar->ForwardTrackRect();
}
return gfx::Rect(0, 0);
}
gfx::Vector2dF ScrollbarController::GetScrollDeltaForScrollbarPart(
const ScrollbarPart scrollbar_part,
const bool jump_key_modifier) const {
const ScrollbarLayerImplBase* scrollbar = ScrollbarLayer();
float distance =
GetScrollDistanceForScrollbarPart(scrollbar_part, jump_key_modifier);
if (scrollbar_part == ScrollbarPart::kBackButton) {
return scrollbar->orientation() == ScrollbarOrientation::kVertical
? gfx::Vector2dF(0, -distance)
: gfx::Vector2dF(-distance, 0);
} else if (scrollbar_part == ScrollbarPart::kForwardButton) {
return scrollbar->orientation() == ScrollbarOrientation::kVertical
? gfx::Vector2dF(0, distance)
: gfx::Vector2dF(distance, 0);
} else if (scrollbar_part == ScrollbarPart::kBackTrack) {
return scrollbar->orientation() == ScrollbarOrientation::kVertical
? gfx::Vector2dF(0, -distance)
: gfx::Vector2dF(-distance, 0);
} else if (scrollbar_part == ScrollbarPart::kForwardTrack) {
return scrollbar->orientation() == ScrollbarOrientation::kVertical
? gfx::Vector2dF(0, distance)
: gfx::Vector2dF(distance, 0);
}
return gfx::Vector2dF(0, 0);
}
}