scrollbar_layer_impl_base.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. // Copyright 2013 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. #include "cc/layers/scrollbar_layer_impl_base.h"
  5. #include <algorithm>
  6. #include "base/cxx17_backports.h"
  7. #include "cc/trees/effect_node.h"
  8. #include "cc/trees/layer_tree_impl.h"
  9. #include "cc/trees/scroll_node.h"
  10. #include "ui/gfx/geometry/rect_conversions.h"
  11. namespace cc {
  12. ScrollbarLayerImplBase::ScrollbarLayerImplBase(
  13. LayerTreeImpl* tree_impl,
  14. int id,
  15. ScrollbarOrientation orientation,
  16. bool is_left_side_vertical_scrollbar,
  17. bool is_overlay)
  18. : LayerImpl(tree_impl, id),
  19. is_overlay_scrollbar_(is_overlay),
  20. thumb_thickness_scale_factor_(1.f),
  21. current_pos_(0.f),
  22. clip_layer_length_(0.f),
  23. scroll_layer_length_(0.f),
  24. orientation_(orientation),
  25. is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar),
  26. vertical_adjust_(0.f) {}
  27. ScrollbarLayerImplBase::~ScrollbarLayerImplBase() {
  28. layer_tree_impl()->UnregisterScrollbar(this);
  29. }
  30. void ScrollbarLayerImplBase::PushPropertiesTo(LayerImpl* layer) {
  31. LayerImpl::PushPropertiesTo(layer);
  32. DCHECK(layer->IsScrollbarLayer());
  33. ScrollbarLayerImplBase* scrollbar_layer = ToScrollbarLayer(layer);
  34. scrollbar_layer->set_is_overlay_scrollbar(is_overlay_scrollbar_);
  35. scrollbar_layer->SetScrollElementId(scroll_element_id());
  36. }
  37. bool ScrollbarLayerImplBase::IsScrollbarLayer() const {
  38. return true;
  39. }
  40. void ScrollbarLayerImplBase::SetScrollElementId(ElementId scroll_element_id) {
  41. if (scroll_element_id_ == scroll_element_id)
  42. return;
  43. layer_tree_impl()->UnregisterScrollbar(this);
  44. scroll_element_id_ = scroll_element_id;
  45. layer_tree_impl()->RegisterScrollbar(this);
  46. }
  47. bool ScrollbarLayerImplBase::SetCurrentPos(float current_pos) {
  48. if (current_pos_ == current_pos)
  49. return false;
  50. current_pos_ = current_pos;
  51. NoteLayerPropertyChanged();
  52. return true;
  53. }
  54. float ScrollbarLayerImplBase::current_pos() const {
  55. DCHECK(!layer_tree_impl()->ScrollbarGeometriesNeedUpdate());
  56. return current_pos_;
  57. }
  58. float ScrollbarLayerImplBase::clip_layer_length() const {
  59. DCHECK(!layer_tree_impl()->ScrollbarGeometriesNeedUpdate());
  60. return clip_layer_length_;
  61. }
  62. float ScrollbarLayerImplBase::scroll_layer_length() const {
  63. DCHECK(!layer_tree_impl()->ScrollbarGeometriesNeedUpdate());
  64. return scroll_layer_length_;
  65. }
  66. float ScrollbarLayerImplBase::vertical_adjust() const {
  67. DCHECK(!layer_tree_impl()->ScrollbarGeometriesNeedUpdate());
  68. return vertical_adjust_;
  69. }
  70. bool ScrollbarLayerImplBase::CanScrollOrientation() const {
  71. PropertyTrees* property_trees = layer_tree_impl()->property_trees();
  72. const auto* scroll_node =
  73. property_trees->scroll_tree().FindNodeFromElementId(scroll_element_id_);
  74. DCHECK(scroll_node);
  75. // TODO(bokan): Looks like we sometimes get here without a ScrollNode. It
  76. // should be safe to just return false here (we don't use scroll_element_id_
  77. // anywhere else) so we can merge the fix. Once merged, will investigate the
  78. // underlying cause. https://crbug.com/924068.
  79. if (!scroll_node)
  80. return false;
  81. if (orientation() == ScrollbarOrientation::HORIZONTAL) {
  82. if (!scroll_node->user_scrollable_horizontal)
  83. return false;
  84. } else {
  85. if (!scroll_node->user_scrollable_vertical)
  86. return false;
  87. }
  88. // Ensure the clip_layer_length and scroll_layer_length values are up-to-date.
  89. // TODO(pdr): Instead of using the clip and scroll layer lengths which require
  90. // an update, refactor to use the scroll tree (ScrollTree::MaxScrollOffset
  91. // as in LayerTreeHostImpl::TryScroll).
  92. layer_tree_impl()->UpdateScrollbarGeometries();
  93. // Ensure clip_layer_length is smaller than scroll_layer_length, not including
  94. // small deltas due to floating point error.
  95. return !MathUtil::IsFloatNearlyTheSame(clip_layer_length(),
  96. scroll_layer_length()) &&
  97. clip_layer_length() < scroll_layer_length();
  98. }
  99. void ScrollbarLayerImplBase::SetVerticalAdjust(float vertical_adjust) {
  100. if (vertical_adjust_ == vertical_adjust)
  101. return;
  102. vertical_adjust_ = vertical_adjust;
  103. NoteLayerPropertyChanged();
  104. }
  105. void ScrollbarLayerImplBase::SetClipLayerLength(float clip_layer_length) {
  106. if (clip_layer_length_ == clip_layer_length)
  107. return;
  108. clip_layer_length_ = clip_layer_length;
  109. NoteLayerPropertyChanged();
  110. }
  111. void ScrollbarLayerImplBase::SetScrollLayerLength(float scroll_layer_length) {
  112. if (scroll_layer_length_ == scroll_layer_length)
  113. return;
  114. scroll_layer_length_ = scroll_layer_length;
  115. NoteLayerPropertyChanged();
  116. return;
  117. }
  118. void ScrollbarLayerImplBase::SetThumbThicknessScaleFactor(float factor) {
  119. if (thumb_thickness_scale_factor_ == factor)
  120. return;
  121. thumb_thickness_scale_factor_ = factor;
  122. NoteLayerPropertyChanged();
  123. }
  124. gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRectWithThumbThicknessScale(
  125. float thumb_thickness_scale_factor) const {
  126. // Thumb extent is the length of the thumb in the scrolling direction, thumb
  127. // thickness is in the perpendicular direction. Here's an example of a
  128. // horizontal scrollbar - inputs are above the scrollbar, computed values
  129. // below:
  130. //
  131. // |<------------------- track_length_ ------------------->|
  132. //
  133. // |--| <-- start_offset
  134. //
  135. // +--+----------------------------+------------------+-------+--+
  136. // |<|| |##################| ||>|
  137. // +--+----------------------------+------------------+-------+--+
  138. //
  139. // |<- thumb_length ->|
  140. //
  141. // |<------- thumb_offset -------->|
  142. //
  143. // For painted, scrollbars, the length is fixed. For solid color scrollbars we
  144. // have to compute it. The ratio of the thumb's length to the track's length
  145. // is the same as that of the visible viewport to the total viewport, unless
  146. // that would make the thumb's length less than its thickness.
  147. //
  148. // vertical_adjust_ is used when the layer geometry from the main thread is
  149. // not in sync with what the user sees. For instance on Android scrolling the
  150. // top bar controls out of view reveals more of the page content. We want the
  151. // root layer scrollbars to reflect what the user sees even if we haven't
  152. // received new layer geometry from the main thread. If the user has scrolled
  153. // down by 50px and the initial viewport size was 950px the geometry would
  154. // look something like this:
  155. //
  156. // vertical_adjust_ = 50, scroll position 0, visible ratios 99%
  157. // Layer geometry: Desired thumb positions:
  158. // +--------------------+-+ +----------------------+ <-- 0px
  159. // | |v| | #|
  160. // | |e| | #|
  161. // | |r| | #|
  162. // | |t| | #|
  163. // | |i| | #|
  164. // | |c| | #|
  165. // | |a| | #|
  166. // | |l| | #|
  167. // | | | | #|
  168. // | |l| | #|
  169. // | |a| | #|
  170. // | |y| | #|
  171. // | |e| | #|
  172. // | |r| | #|
  173. // +--------------------+-+ | #|
  174. // | horizontal layer | | | #|
  175. // +--------------------+-+ | #| <-- 950px
  176. // | | | #|
  177. // | | |##################### |
  178. // +----------------------+ +----------------------+ <-- 1000px
  179. //
  180. // The layer geometry is set up for a 950px tall viewport, but the user can
  181. // actually see down to 1000px. Thus we have to move the quad for the
  182. // horizontal scrollbar down by the vertical_adjust_ factor and lay the
  183. // vertical thumb out on a track lengthed by the vertical_adjust_ factor. This
  184. // means the quads may extend outside the layer's bounds.
  185. // With the length known, we can compute the thumb's position.
  186. float track_length = TrackLength();
  187. int thumb_length = ThumbLength();
  188. int thumb_thickness = ThumbThickness();
  189. // TODO(crbug.com/1239770): This is a speculative fix.
  190. float maximum = std::max(scroll_layer_length() - clip_layer_length(), 0.0f);
  191. // TODO(crbug.com/1239510): Re-enable the following DCHECK once the
  192. // underlying issue is resolved.
  193. // DCHECK(scroll_layer_length() >= clip_layer_length());
  194. // With the length known, we can compute the thumb's position.
  195. float clamped_current_pos = base::clamp(current_pos(), 0.0f, maximum);
  196. int thumb_offset = TrackStart();
  197. if (maximum > 0) {
  198. float ratio = clamped_current_pos / maximum;
  199. float max_offset = track_length - thumb_length;
  200. thumb_offset += static_cast<int>(ratio * max_offset);
  201. }
  202. float thumb_thickness_adjustment =
  203. thumb_thickness * (1.f - thumb_thickness_scale_factor);
  204. gfx::RectF thumb_rect;
  205. if (orientation_ == ScrollbarOrientation::HORIZONTAL) {
  206. thumb_rect = gfx::RectF(thumb_offset,
  207. vertical_adjust_ + thumb_thickness_adjustment,
  208. thumb_length,
  209. thumb_thickness - thumb_thickness_adjustment);
  210. } else {
  211. thumb_rect = gfx::RectF(
  212. is_left_side_vertical_scrollbar_
  213. ? bounds().width() - thumb_thickness
  214. : thumb_thickness_adjustment,
  215. thumb_offset,
  216. thumb_thickness - thumb_thickness_adjustment,
  217. thumb_length);
  218. }
  219. return gfx::ToEnclosingRect(thumb_rect);
  220. }
  221. gfx::Rect ScrollbarLayerImplBase::ComputeExpandedThumbQuadRect() const {
  222. DCHECK(is_overlay_scrollbar());
  223. return ComputeThumbQuadRectWithThumbThicknessScale(1.f);
  224. }
  225. gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRect() const {
  226. return ComputeThumbQuadRectWithThumbThicknessScale(
  227. thumb_thickness_scale_factor_);
  228. }
  229. void ScrollbarLayerImplBase::SetOverlayScrollbarLayerOpacityAnimated(
  230. float opacity) {
  231. DCHECK(is_overlay_scrollbar());
  232. if (!layer_tree_impl())
  233. return;
  234. PropertyTrees* property_trees = layer_tree_impl()->property_trees();
  235. EffectNode* node =
  236. property_trees->effect_tree_mutable().Node(effect_tree_index());
  237. if (node->opacity == opacity)
  238. return;
  239. node->opacity = opacity;
  240. node->effect_changed = true;
  241. property_trees->set_changed(true);
  242. property_trees->effect_tree_mutable().set_needs_update(true);
  243. layer_tree_impl()->set_needs_update_draw_properties();
  244. }
  245. LayerTreeSettings::ScrollbarAnimator
  246. ScrollbarLayerImplBase::GetScrollbarAnimator() const {
  247. return layer_tree_impl()->settings().scrollbar_animator;
  248. }
  249. bool ScrollbarLayerImplBase::HasFindInPageTickmarks() const {
  250. return false;
  251. }
  252. float ScrollbarLayerImplBase::OverlayScrollbarOpacity() const {
  253. return Opacity();
  254. }
  255. bool ScrollbarLayerImplBase::SupportsDragSnapBack() const {
  256. return false;
  257. }
  258. bool ScrollbarLayerImplBase::JumpOnTrackClick() const {
  259. return false;
  260. }
  261. gfx::Rect ScrollbarLayerImplBase::BackButtonRect() const {
  262. return gfx::Rect(0, 0);
  263. }
  264. gfx::Rect ScrollbarLayerImplBase::ForwardButtonRect() const {
  265. return gfx::Rect(0, 0);
  266. }
  267. gfx::Rect ScrollbarLayerImplBase::BackTrackRect() const {
  268. return gfx::Rect(0, 0);
  269. }
  270. gfx::Rect ScrollbarLayerImplBase::ForwardTrackRect() const {
  271. return gfx::Rect(0, 0);
  272. }
  273. // This manages identifying which part of a composited scrollbar got hit based
  274. // on the position_in_widget.
  275. ScrollbarPart ScrollbarLayerImplBase::IdentifyScrollbarPart(
  276. const gfx::PointF position_in_widget) const {
  277. const gfx::Point pointer_location(position_in_widget.x(),
  278. position_in_widget.y());
  279. if (BackButtonRect().Contains(pointer_location))
  280. return ScrollbarPart::BACK_BUTTON;
  281. if (ForwardButtonRect().Contains(pointer_location))
  282. return ScrollbarPart::FORWARD_BUTTON;
  283. if (ComputeThumbQuadRect().Contains(pointer_location))
  284. return ScrollbarPart::THUMB;
  285. if (BackTrackRect().Contains(pointer_location))
  286. return ScrollbarPart::BACK_TRACK;
  287. if (ForwardTrackRect().Contains(pointer_location))
  288. return ScrollbarPart::FORWARD_TRACK;
  289. // TODO(arakeri): Once crbug.com/952314 is fixed, add a DCHECK to verify that
  290. // the point that is passed in is within the TrackRect. Also, please note that
  291. // hit testing other scrollbar parts is not yet implemented.
  292. return ScrollbarPart::NO_PART;
  293. }
  294. } // namespace cc