1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/vr/elements/scrollable_element.h"
6
7 #include "base/numerics/ranges.h"
8 #include "chrome/browser/vr/input_event.h"
9
10 namespace vr {
11
12 namespace {
13
14 constexpr float kScrollScaleFactor = 1.0f / 400.0f;
15 constexpr float kFloatTolerance = FLT_EPSILON * 2.0f;
16
SizeEquals(const gfx::SizeF & a,const gfx::SizeF & b)17 bool SizeEquals(const gfx::SizeF& a, const gfx::SizeF& b) {
18 return base::IsApproximatelyEqual(a.width(), b.width(), kFloatTolerance) &&
19 base::IsApproximatelyEqual(a.height(), b.height(), kFloatTolerance);
20 }
21
22 } // namespace
23
ScrollableElement(Orientation orientation)24 ScrollableElement::ScrollableElement(Orientation orientation)
25 : orientation_(orientation) {
26 set_clip_descendants(true);
27 set_bounds_contain_children(true);
28
29 auto inner_element = std::make_unique<UiElement>();
30 inner_element->set_bounds_contain_children(true);
31 inner_element_ = inner_element.get();
32 AddChild(std::move(inner_element));
33 }
34
35 ScrollableElement::~ScrollableElement() = default;
36
set_max_span(float span)37 void ScrollableElement::set_max_span(float span) {
38 DCHECK(span > 0.0f);
39 max_span_ = span;
40 }
41
OnSetSize(const gfx::SizeF & size)42 void ScrollableElement::OnSetSize(const gfx::SizeF& size) {
43 // If there is nothing to scroll, the element declares itself as
44 // non-scrollable for targeting purposes.
45 set_scrollable(size.width() < inner_element_->size().width() ||
46 size.height() < inner_element_->size().height());
47 }
48
SetScrollAnchoring(LayoutAlignment anchoring)49 void ScrollableElement::SetScrollAnchoring(LayoutAlignment anchoring) {
50 scrolling_anchoring_ = anchoring;
51 SetInitialScroll();
52 }
53
ComputeScrollSpan() const54 float ScrollableElement::ComputeScrollSpan() const {
55 float scroll_span;
56 if (orientation_ == kVertical) {
57 scroll_span = inner_element_->size().height() - size().height();
58 } else {
59 scroll_span = inner_element_->size().width() - size().width();
60 }
61 return std::max(scroll_span, 0.0f);
62 }
63
SetInitialScroll()64 void ScrollableElement::SetInitialScroll() {
65 float half_scroll_span = ComputeScrollSpan() / 2.0f;
66 if (scrolling_anchoring_ == BOTTOM || scrolling_anchoring_ == LEFT) {
67 scroll_offset_ = half_scroll_span;
68 } else if (scrolling_anchoring_ == TOP || scrolling_anchoring_ == RIGHT) {
69 scroll_offset_ = -half_scroll_span;
70 } else {
71 scroll_offset_ = 0.0f;
72 }
73 }
74
ComputeContributingChildrenBounds()75 gfx::RectF ScrollableElement::ComputeContributingChildrenBounds() {
76 auto size = inner_element_->size();
77 if (orientation_ == kHorizontal) {
78 size.set_width(std::min(size.width(), max_span_));
79 } else {
80 size.set_height(std::min(size.height(), max_span_));
81 }
82 return gfx::RectF(size);
83 }
84
LayOutNonContributingChildren()85 void ScrollableElement::LayOutNonContributingChildren() {
86 if (!SizeEquals(inner_size_, inner_element_->size())) {
87 inner_size_ = inner_element_->size();
88 SetInitialScroll();
89 }
90 if (orientation_ == kVertical) {
91 inner_element_->SetLayoutOffset(0.0f, scroll_offset_);
92 } else {
93 inner_element_->SetLayoutOffset(scroll_offset_, 0.0f);
94 }
95 }
96
AddScrollingChild(std::unique_ptr<UiElement> child)97 void ScrollableElement::AddScrollingChild(std::unique_ptr<UiElement> child) {
98 inner_element_->AddChild(std::move(child));
99 }
100
OnScrollBegin(std::unique_ptr<InputEvent> gesture,const gfx::PointF & position)101 void ScrollableElement::OnScrollBegin(std::unique_ptr<InputEvent> gesture,
102 const gfx::PointF& position) {
103 cached_transition_ = animation().transition();
104 animation().set_transition(Transition());
105 }
106
OnScrollUpdate(std::unique_ptr<InputEvent> gesture,const gfx::PointF & position)107 void ScrollableElement::OnScrollUpdate(std::unique_ptr<InputEvent> gesture,
108 const gfx::PointF& position) {
109 float half_scroll_span = ComputeScrollSpan() / 2.0f;
110 if (orientation_ == kHorizontal) {
111 scroll_offset_ -= gesture->scroll_data.delta_x * kScrollScaleFactor;
112 } else {
113 scroll_offset_ -= gesture->scroll_data.delta_y * kScrollScaleFactor;
114 }
115 scroll_offset_ =
116 base::ClampToRange(scroll_offset_, -half_scroll_span, half_scroll_span);
117 }
118
OnScrollEnd(std::unique_ptr<InputEvent> gesture,const gfx::PointF & position)119 void ScrollableElement::OnScrollEnd(std::unique_ptr<InputEvent> gesture,
120 const gfx::PointF& position) {
121 animation().set_transition(cached_transition_);
122 }
123
124 } // namespace vr
125