Home | History | Annotate | Download | only in layers
      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 
      5 #include "cc/layers/scrollbar_layer_impl_base.h"
      6 
      7 #include <algorithm>
      8 #include "cc/trees/layer_tree_impl.h"
      9 #include "ui/gfx/rect_conversions.h"
     10 
     11 namespace cc {
     12 
     13 ScrollbarLayerImplBase::ScrollbarLayerImplBase(
     14     LayerTreeImpl* tree_impl,
     15     int id,
     16     ScrollbarOrientation orientation,
     17     bool is_left_side_vertical_scrollbar,
     18     bool is_overlay)
     19     : LayerImpl(tree_impl, id),
     20       scroll_layer_(NULL),
     21       clip_layer_(NULL),
     22       is_overlay_scrollbar_(is_overlay),
     23       thumb_thickness_scale_factor_(1.f),
     24       current_pos_(0.f),
     25       maximum_(0),
     26       orientation_(orientation),
     27       is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar),
     28       vertical_adjust_(0.f),
     29       visible_to_total_length_ratio_(1.f) {}
     30 
     31 ScrollbarLayerImplBase::~ScrollbarLayerImplBase() {}
     32 
     33 void ScrollbarLayerImplBase::PushPropertiesTo(LayerImpl* layer) {
     34   float active_opacity = layer->opacity();
     35   LayerImpl::PushPropertiesTo(layer);
     36   layer->SetOpacity(active_opacity);
     37   DCHECK(layer->ToScrollbarLayer());
     38   layer->ToScrollbarLayer()->set_is_overlay_scrollbar(is_overlay_scrollbar_);
     39   PushScrollClipPropertiesTo(layer);
     40 }
     41 
     42 void ScrollbarLayerImplBase::PushScrollClipPropertiesTo(LayerImpl* layer) {
     43   DCHECK(layer->ToScrollbarLayer());
     44   layer->ToScrollbarLayer()->SetScrollLayerById(ScrollLayerId());
     45   layer->ToScrollbarLayer()->SetClipLayerById(ClipLayerId());
     46 }
     47 
     48 ScrollbarLayerImplBase* ScrollbarLayerImplBase::ToScrollbarLayer() {
     49   return this;
     50 }
     51 
     52 namespace {
     53 
     54 typedef void (LayerImpl::*ScrollbarRegistrationOperation)(
     55     ScrollbarLayerImplBase*);
     56 
     57 void RegisterScrollbarWithLayers(ScrollbarLayerImplBase* scrollbar,
     58                                  LayerImpl* container_layer,
     59                                  LayerImpl* scroll_layer,
     60                                  ScrollbarRegistrationOperation operation) {
     61   if (!container_layer || !scroll_layer)
     62     return;
     63 
     64   DCHECK(scrollbar);
     65 
     66   // Scrollbars must be notifed of changes to their scroll and container layers
     67   // and all scrollable layers in between.
     68   for (LayerImpl* current_layer = scroll_layer;
     69        current_layer && current_layer != container_layer->parent();
     70        current_layer = current_layer->parent()) {
     71     // TODO(wjmaclean) We shouldn't need to exempt the scroll_layer from the
     72     // scrollable() test below. https://crbug.com/367858.
     73     if (current_layer->scrollable() || current_layer == container_layer ||
     74         current_layer == scroll_layer)
     75       (current_layer->*operation)(scrollbar);
     76   }
     77 }
     78 }  // namespace
     79 
     80 void ScrollbarLayerImplBase::SetScrollLayerById(int id) {
     81   LayerImpl* scroll_layer = layer_tree_impl()->LayerById(id);
     82   if (scroll_layer_ == scroll_layer)
     83     return;
     84 
     85   RegisterScrollbarWithLayers(
     86       this, clip_layer_, scroll_layer_, &LayerImpl::RemoveScrollbar);
     87   scroll_layer_ = scroll_layer;
     88   RegisterScrollbarWithLayers(
     89       this, clip_layer_, scroll_layer_, &LayerImpl::AddScrollbar);
     90 }
     91 
     92 void ScrollbarLayerImplBase::SetClipLayerById(int id) {
     93   LayerImpl* clip_layer = layer_tree_impl()->LayerById(id);
     94   if (clip_layer_ == clip_layer)
     95     return;
     96 
     97   RegisterScrollbarWithLayers(
     98       this, clip_layer_, scroll_layer_, &LayerImpl::RemoveScrollbar);
     99   clip_layer_ = clip_layer;
    100   RegisterScrollbarWithLayers(
    101       this, clip_layer_, scroll_layer_, &LayerImpl::AddScrollbar);
    102 }
    103 
    104 gfx::Rect ScrollbarLayerImplBase::ScrollbarLayerRectToContentRect(
    105     const gfx::RectF& layer_rect) const {
    106   // Don't intersect with the bounds as in LayerRectToContentRect() because
    107   // layer_rect here might be in coordinates of the containing layer.
    108   gfx::RectF content_rect = gfx::ScaleRect(layer_rect,
    109       contents_scale_x(),
    110       contents_scale_y());
    111   return gfx::ToEnclosingRect(content_rect);
    112 }
    113 
    114 void ScrollbarLayerImplBase::SetCurrentPos(float current_pos) {
    115   if (current_pos_ == current_pos)
    116     return;
    117   current_pos_ = current_pos;
    118   NoteLayerPropertyChanged();
    119 }
    120 
    121 void ScrollbarLayerImplBase::SetMaximum(int maximum) {
    122   if (maximum_ == maximum)
    123     return;
    124   maximum_ = maximum;
    125   NoteLayerPropertyChanged();
    126 }
    127 
    128 void ScrollbarLayerImplBase::SetVerticalAdjust(float vertical_adjust) {
    129   if (vertical_adjust_ == vertical_adjust)
    130     return;
    131   vertical_adjust_ = vertical_adjust;
    132   NoteLayerPropertyChanged();
    133 }
    134 
    135 void ScrollbarLayerImplBase::SetVisibleToTotalLengthRatio(float ratio) {
    136   if (!IsThumbResizable())
    137     return;
    138 
    139   if (visible_to_total_length_ratio_ == ratio)
    140     return;
    141   visible_to_total_length_ratio_ = ratio;
    142   NoteLayerPropertyChanged();
    143 }
    144 
    145 void ScrollbarLayerImplBase::SetThumbThicknessScaleFactor(float factor) {
    146   if (thumb_thickness_scale_factor_ == factor)
    147     return;
    148   thumb_thickness_scale_factor_ = factor;
    149   NoteLayerPropertyChanged();
    150 }
    151 
    152 gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRect() const {
    153   // Thumb extent is the length of the thumb in the scrolling direction, thumb
    154   // thickness is in the perpendicular direction. Here's an example of a
    155   // horizontal scrollbar - inputs are above the scrollbar, computed values
    156   // below:
    157   //
    158   //    |<------------------- track_length_ ------------------->|
    159   //
    160   // |--| <-- start_offset
    161   //
    162   // +--+----------------------------+------------------+-------+--+
    163   // |<||                            |##################|       ||>|
    164   // +--+----------------------------+------------------+-------+--+
    165   //
    166   //                                 |<- thumb_length ->|
    167   //
    168   // |<------- thumb_offset -------->|
    169   //
    170   // For painted, scrollbars, the length is fixed. For solid color scrollbars we
    171   // have to compute it. The ratio of the thumb's length to the track's length
    172   // is the same as that of the visible viewport to the total viewport, unless
    173   // that would make the thumb's length less than its thickness.
    174   //
    175   // vertical_adjust_ is used when the layer geometry from the main thread is
    176   // not in sync with what the user sees. For instance on Android scrolling the
    177   // top bar controls out of view reveals more of the page content. We want the
    178   // root layer scrollbars to reflect what the user sees even if we haven't
    179   // received new layer geometry from the main thread.  If the user has scrolled
    180   // down by 50px and the initial viewport size was 950px the geometry would
    181   // look something like this:
    182   //
    183   // vertical_adjust_ = 50, scroll position 0, visible ratios 99%
    184   // Layer geometry:             Desired thumb positions:
    185   // +--------------------+-+   +----------------------+   <-- 0px
    186   // |                    |v|   |                     #|
    187   // |                    |e|   |                     #|
    188   // |                    |r|   |                     #|
    189   // |                    |t|   |                     #|
    190   // |                    |i|   |                     #|
    191   // |                    |c|   |                     #|
    192   // |                    |a|   |                     #|
    193   // |                    |l|   |                     #|
    194   // |                    | |   |                     #|
    195   // |                    |l|   |                     #|
    196   // |                    |a|   |                     #|
    197   // |                    |y|   |                     #|
    198   // |                    |e|   |                     #|
    199   // |                    |r|   |                     #|
    200   // +--------------------+-+   |                     #|
    201   // | horizontal  layer  | |   |                     #|
    202   // +--------------------+-+   |                     #|  <-- 950px
    203   // |                      |   |                     #|
    204   // |                      |   |##################### |
    205   // +----------------------+   +----------------------+  <-- 1000px
    206   //
    207   // The layer geometry is set up for a 950px tall viewport, but the user can
    208   // actually see down to 1000px. Thus we have to move the quad for the
    209   // horizontal scrollbar down by the vertical_adjust_ factor and lay the
    210   // vertical thumb out on a track lengthed by the vertical_adjust_ factor. This
    211   // means the quads may extend outside the layer's bounds.
    212 
    213   // With the length known, we can compute the thumb's position.
    214   float track_length = TrackLength();
    215   int thumb_length = ThumbLength();
    216   int thumb_thickness = ThumbThickness();
    217 
    218   // With the length known, we can compute the thumb's position.
    219   float clamped_current_pos =
    220       std::min(std::max(current_pos_, 0.f), static_cast<float>(maximum_));
    221   float ratio = clamped_current_pos / maximum_;
    222   float max_offset = track_length - thumb_length;
    223   int thumb_offset = static_cast<int>(ratio * max_offset) + TrackStart();
    224 
    225   float thumb_thickness_adjustment =
    226       thumb_thickness * (1.f - thumb_thickness_scale_factor_);
    227 
    228   gfx::RectF thumb_rect;
    229   if (orientation_ == HORIZONTAL) {
    230     thumb_rect = gfx::RectF(thumb_offset,
    231                             vertical_adjust_ + thumb_thickness_adjustment,
    232                             thumb_length,
    233                             thumb_thickness - thumb_thickness_adjustment);
    234   } else {
    235     thumb_rect = gfx::RectF(
    236         is_left_side_vertical_scrollbar_
    237             ? bounds().width() - thumb_thickness
    238             : thumb_thickness_adjustment,
    239         thumb_offset,
    240         thumb_thickness - thumb_thickness_adjustment,
    241         thumb_length);
    242   }
    243 
    244   return ScrollbarLayerRectToContentRect(thumb_rect);
    245 }
    246 
    247 void ScrollbarLayerImplBase::ScrollbarParametersDidChange() {
    248   if (!clip_layer_ || !scroll_layer_)
    249     return;
    250 
    251   scroll_layer_->SetScrollbarPosition(this, clip_layer_);
    252 }
    253 
    254 }  // namespace cc
    255