1 // Copyright 2012 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.h" 6 7 #include <algorithm> 8 9 #include "cc/animation/scrollbar_animation_controller.h" 10 #include "cc/layers/layer.h" 11 #include "cc/layers/quad_sink.h" 12 #include "cc/quads/solid_color_draw_quad.h" 13 #include "cc/quads/texture_draw_quad.h" 14 #include "cc/trees/layer_tree_impl.h" 15 #include "cc/trees/layer_tree_settings.h" 16 #include "ui/gfx/rect_conversions.h" 17 18 namespace cc { 19 20 scoped_ptr<ScrollbarLayerImpl> ScrollbarLayerImpl::Create( 21 LayerTreeImpl* tree_impl, 22 int id, 23 ScrollbarOrientation orientation) { 24 return make_scoped_ptr(new ScrollbarLayerImpl(tree_impl, 25 id, 26 orientation)); 27 } 28 29 ScrollbarLayerImpl::ScrollbarLayerImpl( 30 LayerTreeImpl* tree_impl, 31 int id, 32 ScrollbarOrientation orientation) 33 : LayerImpl(tree_impl, id), 34 track_resource_id_(0), 35 thumb_resource_id_(0), 36 current_pos_(0.f), 37 maximum_(0), 38 thumb_thickness_(0), 39 thumb_length_(0), 40 track_start_(0), 41 track_length_(0), 42 orientation_(orientation), 43 vertical_adjust_(0.f), 44 visible_to_total_length_ratio_(1.f), 45 scroll_layer_id_(Layer::INVALID_ID), 46 is_overlay_scrollbar_(false) {} 47 48 ScrollbarLayerImpl::~ScrollbarLayerImpl() {} 49 50 ScrollbarLayerImpl* ScrollbarLayerImpl::ToScrollbarLayer() { 51 return this; 52 } 53 54 scoped_ptr<LayerImpl> ScrollbarLayerImpl::CreateLayerImpl( 55 LayerTreeImpl* tree_impl) { 56 return ScrollbarLayerImpl::Create(tree_impl, 57 id(), 58 orientation_).PassAs<LayerImpl>(); 59 } 60 61 void ScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { 62 LayerImpl::PushPropertiesTo(layer); 63 64 ScrollbarLayerImpl* scrollbar_layer = static_cast<ScrollbarLayerImpl*>(layer); 65 66 scrollbar_layer->SetThumbThickness(thumb_thickness_); 67 scrollbar_layer->SetThumbLength(thumb_length_); 68 scrollbar_layer->SetTrackStart(track_start_); 69 scrollbar_layer->SetTrackLength(track_length_); 70 scrollbar_layer->set_is_overlay_scrollbar(is_overlay_scrollbar_); 71 72 scrollbar_layer->set_track_resource_id(track_resource_id_); 73 scrollbar_layer->set_thumb_resource_id(thumb_resource_id_); 74 } 75 76 bool ScrollbarLayerImpl::WillDraw(DrawMode draw_mode, 77 ResourceProvider* resource_provider) { 78 if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE && 79 !layer_tree_impl()->settings().solid_color_scrollbars) 80 return false; 81 return LayerImpl::WillDraw(draw_mode, resource_provider); 82 } 83 84 void ScrollbarLayerImpl::AppendQuads(QuadSink* quad_sink, 85 AppendQuadsData* append_quads_data) { 86 bool premultipled_alpha = true; 87 bool flipped = false; 88 gfx::PointF uv_top_left(0.f, 0.f); 89 gfx::PointF uv_bottom_right(1.f, 1.f); 90 gfx::Rect bounds_rect(bounds()); 91 gfx::Rect content_bounds_rect(content_bounds()); 92 93 SharedQuadState* shared_quad_state = 94 quad_sink->UseSharedQuadState(CreateSharedQuadState()); 95 AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data); 96 97 gfx::Rect thumb_quad_rect = ComputeThumbQuadRect(); 98 99 if (layer_tree_impl()->settings().solid_color_scrollbars) { 100 scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); 101 quad->SetNew(shared_quad_state, 102 thumb_quad_rect, 103 layer_tree_impl()->settings().solid_color_scrollbar_color, 104 false); 105 quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); 106 return; 107 } 108 109 if (thumb_resource_id_ && !thumb_quad_rect.IsEmpty()) { 110 gfx::Rect opaque_rect; 111 const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; 112 scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create(); 113 quad->SetNew(shared_quad_state, 114 thumb_quad_rect, 115 opaque_rect, 116 thumb_resource_id_, 117 premultipled_alpha, 118 uv_top_left, 119 uv_bottom_right, 120 SK_ColorTRANSPARENT, 121 opacity, 122 flipped); 123 quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); 124 } 125 126 if (!track_resource_id_) 127 return; 128 129 // Order matters here: since the back track texture is being drawn to the 130 // entire contents rect, we must append it after the thumb and fore track 131 // quads. The back track texture contains (and displays) the buttons. 132 if (!content_bounds_rect.IsEmpty()) { 133 gfx::Rect quad_rect(content_bounds_rect); 134 gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect()); 135 const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; 136 scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create(); 137 quad->SetNew(shared_quad_state, 138 quad_rect, 139 opaque_rect, 140 track_resource_id_, 141 premultipled_alpha, 142 uv_top_left, 143 uv_bottom_right, 144 SK_ColorTRANSPARENT, 145 opacity, 146 flipped); 147 quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data); 148 } 149 } 150 151 ScrollbarOrientation ScrollbarLayerImpl::Orientation() const { 152 return orientation_; 153 } 154 155 float ScrollbarLayerImpl::CurrentPos() const { 156 return current_pos_; 157 } 158 159 int ScrollbarLayerImpl::Maximum() const { 160 return maximum_; 161 } 162 163 gfx::Rect ScrollbarLayerImpl::ScrollbarLayerRectToContentRect( 164 gfx::RectF layer_rect) const { 165 // Don't intersect with the bounds as in layerRectToContentRect() because 166 // layer_rect here might be in coordinates of the containing layer. 167 gfx::RectF content_rect = gfx::ScaleRect(layer_rect, 168 contents_scale_x(), 169 contents_scale_y()); 170 return gfx::ToEnclosingRect(content_rect); 171 } 172 173 void ScrollbarLayerImpl::SetThumbThickness(int thumb_thickness) { 174 if (thumb_thickness_ == thumb_thickness) 175 return; 176 thumb_thickness_ = thumb_thickness; 177 NoteLayerPropertyChanged(); 178 } 179 180 void ScrollbarLayerImpl::SetThumbLength(int thumb_length) { 181 if (thumb_length_ == thumb_length) 182 return; 183 thumb_length_ = thumb_length; 184 NoteLayerPropertyChanged(); 185 } 186 void ScrollbarLayerImpl::SetTrackStart(int track_start) { 187 if (track_start_ == track_start) 188 return; 189 track_start_ = track_start; 190 NoteLayerPropertyChanged(); 191 } 192 193 void ScrollbarLayerImpl::SetTrackLength(int track_length) { 194 if (track_length_ == track_length) 195 return; 196 track_length_ = track_length; 197 NoteLayerPropertyChanged(); 198 } 199 200 void ScrollbarLayerImpl::SetVerticalAdjust(float vertical_adjust) { 201 if (vertical_adjust_ == vertical_adjust) 202 return; 203 vertical_adjust_ = vertical_adjust; 204 NoteLayerPropertyChanged(); 205 } 206 207 void ScrollbarLayerImpl::SetVisibleToTotalLengthRatio(float ratio) { 208 if (visible_to_total_length_ratio_ == ratio) 209 return; 210 visible_to_total_length_ratio_ = ratio; 211 NoteLayerPropertyChanged(); 212 } 213 214 void ScrollbarLayerImpl::SetCurrentPos(float current_pos) { 215 if (current_pos_ == current_pos) 216 return; 217 current_pos_ = current_pos; 218 NoteLayerPropertyChanged(); 219 } 220 221 void ScrollbarLayerImpl::SetMaximum(int maximum) { 222 if (maximum_ == maximum) 223 return; 224 maximum_ = maximum; 225 NoteLayerPropertyChanged(); 226 } 227 228 gfx::Rect ScrollbarLayerImpl::ComputeThumbQuadRect() const { 229 // Thumb extent is the length of the thumb in the scrolling direction, thumb 230 // thickness is in the perpendicular direction. Here's an example of a 231 // horizontal scrollbar - inputs are above the scrollbar, computed values 232 // below: 233 // 234 // |<------------------- track_length_ ------------------->| 235 // 236 // |--| <-- start_offset 237 // 238 // +--+----------------------------+------------------+-------+--+ 239 // |<|| |##################| ||>| 240 // +--+----------------------------+------------------+-------+--+ 241 // 242 // |<- thumb_length ->| 243 // 244 // |<------- thumb_offset -------->| 245 // 246 // For painted, scrollbars, the length is fixed. For solid color scrollbars we 247 // have to compute it. The ratio of the thumb's length to the track's length 248 // is the same as that of the visible viewport to the total viewport, unless 249 // that would make the thumb's length less than its thickness. 250 // 251 // vertical_adjust_ is used when the layer geometry from the main thread is 252 // not in sync with what the user sees. For instance on Android scrolling the 253 // top bar controls out of view reveals more of the page content. We want the 254 // root layer scrollbars to reflect what the user sees even if we haven't 255 // received new layer geometry from the main thread. If the user has scrolled 256 // down by 50px and the initial viewport size was 950px the geometry would 257 // look something like this: 258 // 259 // vertical_adjust_ = 50, scroll position 0, visible ratios 99% 260 // Layer geometry: Desired thumb positions: 261 // +--------------------+-+ +----------------------+ <-- 0px 262 // | |v| | #| 263 // | |e| | #| 264 // | |r| | #| 265 // | |t| | #| 266 // | |i| | #| 267 // | |c| | #| 268 // | |a| | #| 269 // | |l| | #| 270 // | | | | #| 271 // | |l| | #| 272 // | |a| | #| 273 // | |y| | #| 274 // | |e| | #| 275 // | |r| | #| 276 // +--------------------+-+ | #| 277 // | horizontal layer | | | #| 278 // +--------------------+-+ | #| <-- 950px 279 // | | | #| 280 // | | |##################### | 281 // +----------------------+ +----------------------+ <-- 1000px 282 // 283 // The layer geometry is set up for a 950px tall viewport, but the user can 284 // actually see down to 1000px. Thus we have to move the quad for the 285 // horizontal scrollbar down by the vertical_adjust_ factor and lay the 286 // vertical thumb out on a track lengthed by the vertical_adjust_ factor. This 287 // means the quads may extend outside the layer's bounds. 288 289 int thumb_length = thumb_length_; 290 float track_length = track_length_; 291 if (orientation_ == VERTICAL) 292 track_length += vertical_adjust_; 293 294 if (layer_tree_impl()->settings().solid_color_scrollbars) { 295 thumb_length = std::max( 296 static_cast<int>(visible_to_total_length_ratio_ * track_length), 297 thumb_thickness_); 298 } 299 300 // With the length known, we can compute the thumb's position. 301 float clamped_current_pos = 302 std::min(std::max(current_pos_, 0.f), static_cast<float>(maximum_)); 303 float ratio = clamped_current_pos / maximum_; 304 float max_offset = track_length - thumb_length; 305 int thumb_offset = static_cast<int>(ratio * max_offset) + track_start_; 306 307 gfx::RectF thumb_rect; 308 if (orientation_ == HORIZONTAL) { 309 thumb_rect = gfx::RectF(thumb_offset, vertical_adjust_, 310 thumb_length, thumb_thickness_); 311 } else { 312 thumb_rect = gfx::RectF(0.f, thumb_offset, 313 thumb_thickness_, thumb_length); 314 } 315 316 return ScrollbarLayerRectToContentRect(thumb_rect); 317 } 318 319 void ScrollbarLayerImpl::DidLoseOutputSurface() { 320 track_resource_id_ = 0; 321 thumb_resource_id_ = 0; 322 } 323 324 const char* ScrollbarLayerImpl::LayerTypeAsString() const { 325 return "cc::ScrollbarLayerImpl"; 326 } 327 328 } // namespace cc 329