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()->SetScrollLayerAndClipLayerByIds(ScrollLayerId(),
     45                                                              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::SetScrollLayerAndClipLayerByIds(
     81     int scroll_layer_id,
     82     int clip_layer_id) {
     83   LayerImpl* scroll_layer = layer_tree_impl()->LayerById(scroll_layer_id);
     84   LayerImpl* clip_layer = layer_tree_impl()->LayerById(clip_layer_id);
     85   if (scroll_layer_ == scroll_layer && clip_layer_ == clip_layer)
     86     return;
     87 
     88   RegisterScrollbarWithLayers(
     89       this, clip_layer_, scroll_layer_, &LayerImpl::RemoveScrollbar);
     90   scroll_layer_ = scroll_layer;
     91   clip_layer_ = clip_layer;
     92   RegisterScrollbarWithLayers(
     93       this, clip_layer_, scroll_layer_, &LayerImpl::AddScrollbar);
     94 
     95   ScrollbarParametersDidChange();
     96 }
     97 
     98 gfx::Rect ScrollbarLayerImplBase::ScrollbarLayerRectToContentRect(
     99     const gfx::RectF& layer_rect) const {
    100   // Don't intersect with the bounds as in LayerRectToContentRect() because
    101   // layer_rect here might be in coordinates of the containing layer.
    102   gfx::RectF content_rect = gfx::ScaleRect(layer_rect,
    103       contents_scale_x(),
    104       contents_scale_y());
    105   return gfx::ToEnclosingRect(content_rect);
    106 }
    107 
    108 bool ScrollbarLayerImplBase::SetCurrentPos(float current_pos) {
    109   if (current_pos_ == current_pos)
    110     return false;
    111   current_pos_ = current_pos;
    112   NoteLayerPropertyChanged();
    113   return true;
    114 }
    115 
    116 bool ScrollbarLayerImplBase::SetMaximum(int maximum) {
    117   if (maximum_ == maximum)
    118     return false;
    119   maximum_ = maximum;
    120   NoteLayerPropertyChanged();
    121   return true;
    122 }
    123 
    124 bool ScrollbarLayerImplBase::SetVerticalAdjust(float vertical_adjust) {
    125   if (vertical_adjust_ == vertical_adjust)
    126     return false;
    127   vertical_adjust_ = vertical_adjust;
    128   NoteLayerPropertyChanged();
    129   return true;
    130 }
    131 
    132 bool ScrollbarLayerImplBase::SetVisibleToTotalLengthRatio(float ratio) {
    133   if (!IsThumbResizable())
    134     return false;
    135 
    136   if (visible_to_total_length_ratio_ == ratio)
    137     return false;
    138   visible_to_total_length_ratio_ = ratio;
    139   NoteLayerPropertyChanged();
    140   return true;
    141 }
    142 
    143 bool ScrollbarLayerImplBase::SetThumbThicknessScaleFactor(float factor) {
    144   if (thumb_thickness_scale_factor_ == factor)
    145     return false;
    146   thumb_thickness_scale_factor_ = factor;
    147   NoteLayerPropertyChanged();
    148   return true;
    149 }
    150 
    151 gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRect() const {
    152   // Thumb extent is the length of the thumb in the scrolling direction, thumb
    153   // thickness is in the perpendicular direction. Here's an example of a
    154   // horizontal scrollbar - inputs are above the scrollbar, computed values
    155   // below:
    156   //
    157   //    |<------------------- track_length_ ------------------->|
    158   //
    159   // |--| <-- start_offset
    160   //
    161   // +--+----------------------------+------------------+-------+--+
    162   // |<||                            |##################|       ||>|
    163   // +--+----------------------------+------------------+-------+--+
    164   //
    165   //                                 |<- thumb_length ->|
    166   //
    167   // |<------- thumb_offset -------->|
    168   //
    169   // For painted, scrollbars, the length is fixed. For solid color scrollbars we
    170   // have to compute it. The ratio of the thumb's length to the track's length
    171   // is the same as that of the visible viewport to the total viewport, unless
    172   // that would make the thumb's length less than its thickness.
    173   //
    174   // vertical_adjust_ is used when the layer geometry from the main thread is
    175   // not in sync with what the user sees. For instance on Android scrolling the
    176   // top bar controls out of view reveals more of the page content. We want the
    177   // root layer scrollbars to reflect what the user sees even if we haven't
    178   // received new layer geometry from the main thread.  If the user has scrolled
    179   // down by 50px and the initial viewport size was 950px the geometry would
    180   // look something like this:
    181   //
    182   // vertical_adjust_ = 50, scroll position 0, visible ratios 99%
    183   // Layer geometry:             Desired thumb positions:
    184   // +--------------------+-+   +----------------------+   <-- 0px
    185   // |                    |v|   |                     #|
    186   // |                    |e|   |                     #|
    187   // |                    |r|   |                     #|
    188   // |                    |t|   |                     #|
    189   // |                    |i|   |                     #|
    190   // |                    |c|   |                     #|
    191   // |                    |a|   |                     #|
    192   // |                    |l|   |                     #|
    193   // |                    | |   |                     #|
    194   // |                    |l|   |                     #|
    195   // |                    |a|   |                     #|
    196   // |                    |y|   |                     #|
    197   // |                    |e|   |                     #|
    198   // |                    |r|   |                     #|
    199   // +--------------------+-+   |                     #|
    200   // | horizontal  layer  | |   |                     #|
    201   // +--------------------+-+   |                     #|  <-- 950px
    202   // |                      |   |                     #|
    203   // |                      |   |##################### |
    204   // +----------------------+   +----------------------+  <-- 1000px
    205   //
    206   // The layer geometry is set up for a 950px tall viewport, but the user can
    207   // actually see down to 1000px. Thus we have to move the quad for the
    208   // horizontal scrollbar down by the vertical_adjust_ factor and lay the
    209   // vertical thumb out on a track lengthed by the vertical_adjust_ factor. This
    210   // means the quads may extend outside the layer's bounds.
    211 
    212   // With the length known, we can compute the thumb's position.
    213   float track_length = TrackLength();
    214   int thumb_length = ThumbLength();
    215   int thumb_thickness = ThumbThickness();
    216 
    217   // With the length known, we can compute the thumb's position.
    218   float clamped_current_pos =
    219       std::min(std::max(current_pos_, 0.f), static_cast<float>(maximum_));
    220 
    221   int thumb_offset = TrackStart();
    222   if (maximum_ > 0) {
    223     float ratio = clamped_current_pos / maximum_;
    224     float max_offset = track_length - thumb_length;
    225     thumb_offset += static_cast<int>(ratio * max_offset);
    226   }
    227 
    228   float thumb_thickness_adjustment =
    229       thumb_thickness * (1.f - thumb_thickness_scale_factor_);
    230 
    231   gfx::RectF thumb_rect;
    232   if (orientation_ == HORIZONTAL) {
    233     thumb_rect = gfx::RectF(thumb_offset,
    234                             vertical_adjust_ + thumb_thickness_adjustment,
    235                             thumb_length,
    236                             thumb_thickness - thumb_thickness_adjustment);
    237   } else {
    238     thumb_rect = gfx::RectF(
    239         is_left_side_vertical_scrollbar_
    240             ? bounds().width() - thumb_thickness
    241             : thumb_thickness_adjustment,
    242         thumb_offset,
    243         thumb_thickness - thumb_thickness_adjustment,
    244         thumb_length);
    245   }
    246 
    247   return ScrollbarLayerRectToContentRect(thumb_rect);
    248 }
    249 
    250 void ScrollbarLayerImplBase::ScrollbarParametersDidChange() {
    251   if (!clip_layer_ || !scroll_layer_)
    252     return;
    253 
    254   scroll_layer_->SetScrollbarPosition(this, clip_layer_);
    255 }
    256 
    257 }  // namespace cc
    258