| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 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 "ui/views/controls/scroll_view.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "ui/events/event.h" | |
| 9 #include "ui/gfx/canvas.h" | |
| 10 #include "ui/native_theme/native_theme.h" | |
| 11 #include "ui/views/border.h" | |
| 12 #include "ui/views/controls/scrollbar/native_scroll_bar.h" | |
| 13 #include "ui/views/widget/root_view.h" | |
| 14 | |
| 15 namespace views { | |
| 16 | |
| 17 const char ScrollView::kViewClassName[] = "ScrollView"; | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 // Subclass of ScrollView that resets the border when the theme changes. | |
| 22 class ScrollViewWithBorder : public views::ScrollView { | |
| 23 public: | |
| 24 ScrollViewWithBorder() {} | |
| 25 | |
| 26 // View overrides; | |
| 27 virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) override { | |
| 28 SetBorder(Border::CreateSolidBorder( | |
| 29 1, | |
| 30 theme->GetSystemColor(ui::NativeTheme::kColorId_UnfocusedBorderColor))); | |
| 31 } | |
| 32 | |
| 33 private: | |
| 34 DISALLOW_COPY_AND_ASSIGN(ScrollViewWithBorder); | |
| 35 }; | |
| 36 | |
| 37 class ScrollCornerView : public views::View { | |
| 38 public: | |
| 39 ScrollCornerView() {} | |
| 40 | |
| 41 virtual void OnPaint(gfx::Canvas* canvas) override { | |
| 42 ui::NativeTheme::ExtraParams ignored; | |
| 43 GetNativeTheme()->Paint(canvas->sk_canvas(), | |
| 44 ui::NativeTheme::kScrollbarCorner, | |
| 45 ui::NativeTheme::kNormal, | |
| 46 GetLocalBounds(), | |
| 47 ignored); | |
| 48 } | |
| 49 | |
| 50 private: | |
| 51 DISALLOW_COPY_AND_ASSIGN(ScrollCornerView); | |
| 52 }; | |
| 53 | |
| 54 // Returns the position for the view so that it isn't scrolled off the visible | |
| 55 // region. | |
| 56 int CheckScrollBounds(int viewport_size, int content_size, int current_pos) { | |
| 57 int max = std::max(content_size - viewport_size, 0); | |
| 58 if (current_pos < 0) | |
| 59 return 0; | |
| 60 if (current_pos > max) | |
| 61 return max; | |
| 62 return current_pos; | |
| 63 } | |
| 64 | |
| 65 // Make sure the content is not scrolled out of bounds | |
| 66 void CheckScrollBounds(View* viewport, View* view) { | |
| 67 if (!view) | |
| 68 return; | |
| 69 | |
| 70 int x = CheckScrollBounds(viewport->width(), view->width(), -view->x()); | |
| 71 int y = CheckScrollBounds(viewport->height(), view->height(), -view->y()); | |
| 72 | |
| 73 // This is no op if bounds are the same | |
| 74 view->SetBounds(-x, -y, view->width(), view->height()); | |
| 75 } | |
| 76 | |
| 77 // Used by ScrollToPosition() to make sure the new position fits within the | |
| 78 // allowed scroll range. | |
| 79 int AdjustPosition(int current_position, | |
| 80 int new_position, | |
| 81 int content_size, | |
| 82 int viewport_size) { | |
| 83 if (-current_position == new_position) | |
| 84 return new_position; | |
| 85 if (new_position < 0) | |
| 86 return 0; | |
| 87 const int max_position = std::max(0, content_size - viewport_size); | |
| 88 return (new_position > max_position) ? max_position : new_position; | |
| 89 } | |
| 90 | |
| 91 } // namespace | |
| 92 | |
| 93 // Viewport contains the contents View of the ScrollView. | |
| 94 class ScrollView::Viewport : public View { | |
| 95 public: | |
| 96 Viewport() {} | |
| 97 virtual ~Viewport() {} | |
| 98 | |
| 99 virtual const char* GetClassName() const override { | |
| 100 return "ScrollView::Viewport"; | |
| 101 } | |
| 102 | |
| 103 virtual void ScrollRectToVisible(const gfx::Rect& rect) override { | |
| 104 if (!has_children() || !parent()) | |
| 105 return; | |
| 106 | |
| 107 View* contents = child_at(0); | |
| 108 gfx::Rect scroll_rect(rect); | |
| 109 scroll_rect.Offset(-contents->x(), -contents->y()); | |
| 110 static_cast<ScrollView*>(parent())->ScrollContentsRegionToBeVisible( | |
| 111 scroll_rect); | |
| 112 } | |
| 113 | |
| 114 virtual void ChildPreferredSizeChanged(View* child) override { | |
| 115 if (parent()) | |
| 116 parent()->Layout(); | |
| 117 } | |
| 118 | |
| 119 private: | |
| 120 DISALLOW_COPY_AND_ASSIGN(Viewport); | |
| 121 }; | |
| 122 | |
| 123 ScrollView::ScrollView() | |
| 124 : contents_(NULL), | |
| 125 contents_viewport_(new Viewport()), | |
| 126 header_(NULL), | |
| 127 header_viewport_(new Viewport()), | |
| 128 horiz_sb_(new NativeScrollBar(true)), | |
| 129 vert_sb_(new NativeScrollBar(false)), | |
| 130 corner_view_(new ScrollCornerView()), | |
| 131 min_height_(-1), | |
| 132 max_height_(-1), | |
| 133 hide_horizontal_scrollbar_(false) { | |
| 134 set_notify_enter_exit_on_child(true); | |
| 135 | |
| 136 AddChildView(contents_viewport_); | |
| 137 AddChildView(header_viewport_); | |
| 138 | |
| 139 // Don't add the scrollbars as children until we discover we need them | |
| 140 // (ShowOrHideScrollBar). | |
| 141 horiz_sb_->SetVisible(false); | |
| 142 horiz_sb_->set_controller(this); | |
| 143 vert_sb_->SetVisible(false); | |
| 144 vert_sb_->set_controller(this); | |
| 145 corner_view_->SetVisible(false); | |
| 146 } | |
| 147 | |
| 148 ScrollView::~ScrollView() { | |
| 149 // The scrollbars may not have been added, delete them to ensure they get | |
| 150 // deleted. | |
| 151 delete horiz_sb_; | |
| 152 delete vert_sb_; | |
| 153 delete corner_view_; | |
| 154 } | |
| 155 | |
| 156 // static | |
| 157 ScrollView* ScrollView::CreateScrollViewWithBorder() { | |
| 158 return new ScrollViewWithBorder(); | |
| 159 } | |
| 160 | |
| 161 void ScrollView::SetContents(View* a_view) { | |
| 162 SetHeaderOrContents(contents_viewport_, a_view, &contents_); | |
| 163 } | |
| 164 | |
| 165 void ScrollView::SetHeader(View* header) { | |
| 166 SetHeaderOrContents(header_viewport_, header, &header_); | |
| 167 } | |
| 168 | |
| 169 gfx::Rect ScrollView::GetVisibleRect() const { | |
| 170 if (!contents_) | |
| 171 return gfx::Rect(); | |
| 172 return gfx::Rect(-contents_->x(), -contents_->y(), | |
| 173 contents_viewport_->width(), contents_viewport_->height()); | |
| 174 } | |
| 175 | |
| 176 void ScrollView::ClipHeightTo(int min_height, int max_height) { | |
| 177 min_height_ = min_height; | |
| 178 max_height_ = max_height; | |
| 179 } | |
| 180 | |
| 181 int ScrollView::GetScrollBarWidth() const { | |
| 182 return vert_sb_ ? vert_sb_->GetLayoutSize() : 0; | |
| 183 } | |
| 184 | |
| 185 int ScrollView::GetScrollBarHeight() const { | |
| 186 return horiz_sb_ ? horiz_sb_->GetLayoutSize() : 0; | |
| 187 } | |
| 188 | |
| 189 void ScrollView::SetHorizontalScrollBar(ScrollBar* horiz_sb) { | |
| 190 DCHECK(horiz_sb); | |
| 191 horiz_sb->SetVisible(horiz_sb_->visible()); | |
| 192 delete horiz_sb_; | |
| 193 horiz_sb->set_controller(this); | |
| 194 horiz_sb_ = horiz_sb; | |
| 195 } | |
| 196 | |
| 197 void ScrollView::SetVerticalScrollBar(ScrollBar* vert_sb) { | |
| 198 DCHECK(vert_sb); | |
| 199 vert_sb->SetVisible(vert_sb_->visible()); | |
| 200 delete vert_sb_; | |
| 201 vert_sb->set_controller(this); | |
| 202 vert_sb_ = vert_sb; | |
| 203 } | |
| 204 | |
| 205 gfx::Size ScrollView::GetPreferredSize() const { | |
| 206 if (!is_bounded()) | |
| 207 return View::GetPreferredSize(); | |
| 208 | |
| 209 gfx::Size size = contents()->GetPreferredSize(); | |
| 210 size.SetToMax(gfx::Size(size.width(), min_height_)); | |
| 211 size.SetToMin(gfx::Size(size.width(), max_height_)); | |
| 212 gfx::Insets insets = GetInsets(); | |
| 213 size.Enlarge(insets.width(), insets.height()); | |
| 214 return size; | |
| 215 } | |
| 216 | |
| 217 int ScrollView::GetHeightForWidth(int width) const { | |
| 218 if (!is_bounded()) | |
| 219 return View::GetHeightForWidth(width); | |
| 220 | |
| 221 gfx::Insets insets = GetInsets(); | |
| 222 width = std::max(0, width - insets.width()); | |
| 223 int height = contents()->GetHeightForWidth(width) + insets.height(); | |
| 224 return std::min(std::max(height, min_height_), max_height_); | |
| 225 } | |
| 226 | |
| 227 void ScrollView::Layout() { | |
| 228 if (is_bounded()) { | |
| 229 int content_width = width(); | |
| 230 int content_height = contents()->GetHeightForWidth(content_width); | |
| 231 if (content_height > height()) { | |
| 232 content_width = std::max(content_width - GetScrollBarWidth(), 0); | |
| 233 content_height = contents()->GetHeightForWidth(content_width); | |
| 234 } | |
| 235 if (contents()->bounds().size() != gfx::Size(content_width, content_height)) | |
| 236 contents()->SetBounds(0, 0, content_width, content_height); | |
| 237 } | |
| 238 | |
| 239 // Most views will want to auto-fit the available space. Most of them want to | |
| 240 // use all available width (without overflowing) and only overflow in | |
| 241 // height. Examples are HistoryView, MostVisitedView, DownloadTabView, etc. | |
| 242 // Other views want to fit in both ways. An example is PrintView. To make both | |
| 243 // happy, assume a vertical scrollbar but no horizontal scrollbar. To override | |
| 244 // this default behavior, the inner view has to calculate the available space, | |
| 245 // used ComputeScrollBarsVisibility() to use the same calculation that is done | |
| 246 // here and sets its bound to fit within. | |
| 247 gfx::Rect viewport_bounds = GetContentsBounds(); | |
| 248 const int contents_x = viewport_bounds.x(); | |
| 249 const int contents_y = viewport_bounds.y(); | |
| 250 if (viewport_bounds.IsEmpty()) { | |
| 251 // There's nothing to layout. | |
| 252 return; | |
| 253 } | |
| 254 | |
| 255 const int header_height = | |
| 256 std::min(viewport_bounds.height(), | |
| 257 header_ ? header_->GetPreferredSize().height() : 0); | |
| 258 viewport_bounds.set_height( | |
| 259 std::max(0, viewport_bounds.height() - header_height)); | |
| 260 viewport_bounds.set_y(viewport_bounds.y() + header_height); | |
| 261 // viewport_size is the total client space available. | |
| 262 gfx::Size viewport_size = viewport_bounds.size(); | |
| 263 // Assumes a vertical scrollbar since most of the current views are designed | |
| 264 // for this. | |
| 265 int horiz_sb_height = GetScrollBarHeight(); | |
| 266 int vert_sb_width = GetScrollBarWidth(); | |
| 267 viewport_bounds.set_width(viewport_bounds.width() - vert_sb_width); | |
| 268 // Update the bounds right now so the inner views can fit in it. | |
| 269 contents_viewport_->SetBoundsRect(viewport_bounds); | |
| 270 | |
| 271 // Give |contents_| a chance to update its bounds if it depends on the | |
| 272 // viewport. | |
| 273 if (contents_) | |
| 274 contents_->Layout(); | |
| 275 | |
| 276 bool should_layout_contents = false; | |
| 277 bool horiz_sb_required = false; | |
| 278 bool vert_sb_required = false; | |
| 279 if (contents_) { | |
| 280 gfx::Size content_size = contents_->size(); | |
| 281 ComputeScrollBarsVisibility(viewport_size, | |
| 282 content_size, | |
| 283 &horiz_sb_required, | |
| 284 &vert_sb_required); | |
| 285 } | |
| 286 bool corner_view_required = horiz_sb_required && vert_sb_required; | |
| 287 // Take action. | |
| 288 SetControlVisibility(horiz_sb_, horiz_sb_required); | |
| 289 SetControlVisibility(vert_sb_, vert_sb_required); | |
| 290 SetControlVisibility(corner_view_, corner_view_required); | |
| 291 | |
| 292 // Non-default. | |
| 293 if (horiz_sb_required) { | |
| 294 viewport_bounds.set_height( | |
| 295 std::max(0, viewport_bounds.height() - horiz_sb_height)); | |
| 296 should_layout_contents = true; | |
| 297 } | |
| 298 // Default. | |
| 299 if (!vert_sb_required) { | |
| 300 viewport_bounds.set_width(viewport_bounds.width() + vert_sb_width); | |
| 301 should_layout_contents = true; | |
| 302 } | |
| 303 | |
| 304 if (horiz_sb_required) { | |
| 305 int height_offset = horiz_sb_->GetContentOverlapSize(); | |
| 306 horiz_sb_->SetBounds(0, | |
| 307 viewport_bounds.bottom() - height_offset, | |
| 308 viewport_bounds.right(), | |
| 309 horiz_sb_height + height_offset); | |
| 310 } | |
| 311 if (vert_sb_required) { | |
| 312 int width_offset = vert_sb_->GetContentOverlapSize(); | |
| 313 vert_sb_->SetBounds(viewport_bounds.right() - width_offset, | |
| 314 0, | |
| 315 vert_sb_width + width_offset, | |
| 316 viewport_bounds.bottom()); | |
| 317 } | |
| 318 if (corner_view_required) { | |
| 319 // Show the resize corner. | |
| 320 corner_view_->SetBounds(viewport_bounds.right(), | |
| 321 viewport_bounds.bottom(), | |
| 322 vert_sb_width, | |
| 323 horiz_sb_height); | |
| 324 } | |
| 325 | |
| 326 // Update to the real client size with the visible scrollbars. | |
| 327 contents_viewport_->SetBoundsRect(viewport_bounds); | |
| 328 if (should_layout_contents && contents_) | |
| 329 contents_->Layout(); | |
| 330 | |
| 331 header_viewport_->SetBounds(contents_x, contents_y, | |
| 332 viewport_bounds.width(), header_height); | |
| 333 if (header_) | |
| 334 header_->Layout(); | |
| 335 | |
| 336 CheckScrollBounds(header_viewport_, header_); | |
| 337 CheckScrollBounds(contents_viewport_, contents_); | |
| 338 SchedulePaint(); | |
| 339 UpdateScrollBarPositions(); | |
| 340 } | |
| 341 | |
| 342 bool ScrollView::OnKeyPressed(const ui::KeyEvent& event) { | |
| 343 bool processed = false; | |
| 344 | |
| 345 // Give vertical scrollbar priority | |
| 346 if (vert_sb_->visible()) | |
| 347 processed = vert_sb_->OnKeyPressed(event); | |
| 348 | |
| 349 if (!processed && horiz_sb_->visible()) | |
| 350 processed = horiz_sb_->OnKeyPressed(event); | |
| 351 | |
| 352 return processed; | |
| 353 } | |
| 354 | |
| 355 bool ScrollView::OnMouseWheel(const ui::MouseWheelEvent& e) { | |
| 356 bool processed = false; | |
| 357 | |
| 358 if (vert_sb_->visible()) | |
| 359 processed = vert_sb_->OnMouseWheel(e); | |
| 360 | |
| 361 if (horiz_sb_->visible()) | |
| 362 processed = horiz_sb_->OnMouseWheel(e) || processed; | |
| 363 | |
| 364 return processed; | |
| 365 } | |
| 366 | |
| 367 void ScrollView::OnMouseEntered(const ui::MouseEvent& event) { | |
| 368 if (horiz_sb_) | |
| 369 horiz_sb_->OnMouseEnteredScrollView(event); | |
| 370 if (vert_sb_) | |
| 371 vert_sb_->OnMouseEnteredScrollView(event); | |
| 372 } | |
| 373 | |
| 374 void ScrollView::OnMouseExited(const ui::MouseEvent& event) { | |
| 375 if (horiz_sb_) | |
| 376 horiz_sb_->OnMouseExitedScrollView(event); | |
| 377 if (vert_sb_) | |
| 378 vert_sb_->OnMouseExitedScrollView(event); | |
| 379 } | |
| 380 | |
| 381 void ScrollView::OnGestureEvent(ui::GestureEvent* event) { | |
| 382 // If the event happened on one of the scrollbars, then those events are | |
| 383 // sent directly to the scrollbars. Otherwise, only scroll events are sent to | |
| 384 // the scrollbars. | |
| 385 bool scroll_event = event->type() == ui::ET_GESTURE_SCROLL_UPDATE || | |
| 386 event->type() == ui::ET_GESTURE_SCROLL_BEGIN || | |
| 387 event->type() == ui::ET_GESTURE_SCROLL_END || | |
| 388 event->type() == ui::ET_SCROLL_FLING_START; | |
| 389 | |
| 390 if (vert_sb_->visible()) { | |
| 391 if (vert_sb_->bounds().Contains(event->location()) || scroll_event) | |
| 392 vert_sb_->OnGestureEvent(event); | |
| 393 } | |
| 394 if (!event->handled() && horiz_sb_->visible()) { | |
| 395 if (horiz_sb_->bounds().Contains(event->location()) || scroll_event) | |
| 396 horiz_sb_->OnGestureEvent(event); | |
| 397 } | |
| 398 } | |
| 399 | |
| 400 const char* ScrollView::GetClassName() const { | |
| 401 return kViewClassName; | |
| 402 } | |
| 403 | |
| 404 void ScrollView::ScrollToPosition(ScrollBar* source, int position) { | |
| 405 if (!contents_) | |
| 406 return; | |
| 407 | |
| 408 if (source == horiz_sb_ && horiz_sb_->visible()) { | |
| 409 position = AdjustPosition(contents_->x(), position, contents_->width(), | |
| 410 contents_viewport_->width()); | |
| 411 if (-contents_->x() == position) | |
| 412 return; | |
| 413 contents_->SetX(-position); | |
| 414 if (header_) { | |
| 415 header_->SetX(-position); | |
| 416 header_->SchedulePaintInRect(header_->GetVisibleBounds()); | |
| 417 } | |
| 418 } else if (source == vert_sb_ && vert_sb_->visible()) { | |
| 419 position = AdjustPosition(contents_->y(), position, contents_->height(), | |
| 420 contents_viewport_->height()); | |
| 421 if (-contents_->y() == position) | |
| 422 return; | |
| 423 contents_->SetY(-position); | |
| 424 } | |
| 425 contents_->SchedulePaintInRect(contents_->GetVisibleBounds()); | |
| 426 } | |
| 427 | |
| 428 int ScrollView::GetScrollIncrement(ScrollBar* source, bool is_page, | |
| 429 bool is_positive) { | |
| 430 bool is_horizontal = source->IsHorizontal(); | |
| 431 int amount = 0; | |
| 432 if (contents_) { | |
| 433 if (is_page) { | |
| 434 amount = contents_->GetPageScrollIncrement( | |
| 435 this, is_horizontal, is_positive); | |
| 436 } else { | |
| 437 amount = contents_->GetLineScrollIncrement( | |
| 438 this, is_horizontal, is_positive); | |
| 439 } | |
| 440 if (amount > 0) | |
| 441 return amount; | |
| 442 } | |
| 443 // No view, or the view didn't return a valid amount. | |
| 444 if (is_page) { | |
| 445 return is_horizontal ? contents_viewport_->width() : | |
| 446 contents_viewport_->height(); | |
| 447 } | |
| 448 return is_horizontal ? contents_viewport_->width() / 5 : | |
| 449 contents_viewport_->height() / 5; | |
| 450 } | |
| 451 | |
| 452 void ScrollView::SetHeaderOrContents(View* parent, | |
| 453 View* new_view, | |
| 454 View** member) { | |
| 455 if (*member == new_view) | |
| 456 return; | |
| 457 | |
| 458 delete *member; | |
| 459 *member = new_view; | |
| 460 if (*member) | |
| 461 parent->AddChildView(*member); | |
| 462 Layout(); | |
| 463 } | |
| 464 | |
| 465 void ScrollView::ScrollContentsRegionToBeVisible(const gfx::Rect& rect) { | |
| 466 if (!contents_ || (!horiz_sb_->visible() && !vert_sb_->visible())) | |
| 467 return; | |
| 468 | |
| 469 // Figure out the maximums for this scroll view. | |
| 470 const int contents_max_x = | |
| 471 std::max(contents_viewport_->width(), contents_->width()); | |
| 472 const int contents_max_y = | |
| 473 std::max(contents_viewport_->height(), contents_->height()); | |
| 474 | |
| 475 // Make sure x and y are within the bounds of [0,contents_max_*]. | |
| 476 int x = std::max(0, std::min(contents_max_x, rect.x())); | |
| 477 int y = std::max(0, std::min(contents_max_y, rect.y())); | |
| 478 | |
| 479 // Figure out how far and down the rectangle will go taking width | |
| 480 // and height into account. This will be "clipped" by the viewport. | |
| 481 const int max_x = std::min(contents_max_x, | |
| 482 x + std::min(rect.width(), contents_viewport_->width())); | |
| 483 const int max_y = std::min(contents_max_y, | |
| 484 y + std::min(rect.height(), contents_viewport_->height())); | |
| 485 | |
| 486 // See if the rect is already visible. Note the width is (max_x - x) | |
| 487 // and the height is (max_y - y) to take into account the clipping of | |
| 488 // either viewport or the content size. | |
| 489 const gfx::Rect vis_rect = GetVisibleRect(); | |
| 490 if (vis_rect.Contains(gfx::Rect(x, y, max_x - x, max_y - y))) | |
| 491 return; | |
| 492 | |
| 493 // Shift contents_'s X and Y so that the region is visible. If we | |
| 494 // need to shift up or left from where we currently are then we need | |
| 495 // to get it so that the content appears in the upper/left | |
| 496 // corner. This is done by setting the offset to -X or -Y. For down | |
| 497 // or right shifts we need to make sure it appears in the | |
| 498 // lower/right corner. This is calculated by taking max_x or max_y | |
| 499 // and scaling it back by the size of the viewport. | |
| 500 const int new_x = | |
| 501 (vis_rect.x() > x) ? x : std::max(0, max_x - contents_viewport_->width()); | |
| 502 const int new_y = | |
| 503 (vis_rect.y() > y) ? y : std::max(0, max_y - | |
| 504 contents_viewport_->height()); | |
| 505 | |
| 506 contents_->SetX(-new_x); | |
| 507 if (header_) | |
| 508 header_->SetX(-new_x); | |
| 509 contents_->SetY(-new_y); | |
| 510 UpdateScrollBarPositions(); | |
| 511 } | |
| 512 | |
| 513 void ScrollView::ComputeScrollBarsVisibility(const gfx::Size& vp_size, | |
| 514 const gfx::Size& content_size, | |
| 515 bool* horiz_is_shown, | |
| 516 bool* vert_is_shown) const { | |
| 517 // Try to fit both ways first, then try vertical bar only, then horizontal | |
| 518 // bar only, then defaults to both shown. | |
| 519 if (content_size.width() <= vp_size.width() && | |
| 520 content_size.height() <= vp_size.height()) { | |
| 521 *horiz_is_shown = false; | |
| 522 *vert_is_shown = false; | |
| 523 } else if (content_size.width() <= vp_size.width() - GetScrollBarWidth()) { | |
| 524 *horiz_is_shown = false; | |
| 525 *vert_is_shown = true; | |
| 526 } else if (content_size.height() <= vp_size.height() - GetScrollBarHeight()) { | |
| 527 *horiz_is_shown = true; | |
| 528 *vert_is_shown = false; | |
| 529 } else { | |
| 530 *horiz_is_shown = true; | |
| 531 *vert_is_shown = true; | |
| 532 } | |
| 533 | |
| 534 if (hide_horizontal_scrollbar_) | |
| 535 *horiz_is_shown = false; | |
| 536 } | |
| 537 | |
| 538 // Make sure that a single scrollbar is created and visible as needed | |
| 539 void ScrollView::SetControlVisibility(View* control, bool should_show) { | |
| 540 if (!control) | |
| 541 return; | |
| 542 if (should_show) { | |
| 543 if (!control->visible()) { | |
| 544 AddChildView(control); | |
| 545 control->SetVisible(true); | |
| 546 } | |
| 547 } else { | |
| 548 RemoveChildView(control); | |
| 549 control->SetVisible(false); | |
| 550 } | |
| 551 } | |
| 552 | |
| 553 void ScrollView::UpdateScrollBarPositions() { | |
| 554 if (!contents_) | |
| 555 return; | |
| 556 | |
| 557 if (horiz_sb_->visible()) { | |
| 558 int vw = contents_viewport_->width(); | |
| 559 int cw = contents_->width(); | |
| 560 int origin = contents_->x(); | |
| 561 horiz_sb_->Update(vw, cw, -origin); | |
| 562 } | |
| 563 if (vert_sb_->visible()) { | |
| 564 int vh = contents_viewport_->height(); | |
| 565 int ch = contents_->height(); | |
| 566 int origin = contents_->y(); | |
| 567 vert_sb_->Update(vh, ch, -origin); | |
| 568 } | |
| 569 } | |
| 570 | |
| 571 // VariableRowHeightScrollHelper ---------------------------------------------- | |
| 572 | |
| 573 VariableRowHeightScrollHelper::VariableRowHeightScrollHelper( | |
| 574 Controller* controller) : controller_(controller) { | |
| 575 } | |
| 576 | |
| 577 VariableRowHeightScrollHelper::~VariableRowHeightScrollHelper() { | |
| 578 } | |
| 579 | |
| 580 int VariableRowHeightScrollHelper::GetPageScrollIncrement( | |
| 581 ScrollView* scroll_view, bool is_horizontal, bool is_positive) { | |
| 582 if (is_horizontal) | |
| 583 return 0; | |
| 584 // y coordinate is most likely negative. | |
| 585 int y = abs(scroll_view->contents()->y()); | |
| 586 int vis_height = scroll_view->contents()->parent()->height(); | |
| 587 if (is_positive) { | |
| 588 // Align the bottom most row to the top of the view. | |
| 589 int bottom = std::min(scroll_view->contents()->height() - 1, | |
| 590 y + vis_height); | |
| 591 RowInfo bottom_row_info = GetRowInfo(bottom); | |
| 592 // If 0, ScrollView will provide a default value. | |
| 593 return std::max(0, bottom_row_info.origin - y); | |
| 594 } else { | |
| 595 // Align the row on the previous page to to the top of the view. | |
| 596 int last_page_y = y - vis_height; | |
| 597 RowInfo last_page_info = GetRowInfo(std::max(0, last_page_y)); | |
| 598 if (last_page_y != last_page_info.origin) | |
| 599 return std::max(0, y - last_page_info.origin - last_page_info.height); | |
| 600 return std::max(0, y - last_page_info.origin); | |
| 601 } | |
| 602 } | |
| 603 | |
| 604 int VariableRowHeightScrollHelper::GetLineScrollIncrement( | |
| 605 ScrollView* scroll_view, bool is_horizontal, bool is_positive) { | |
| 606 if (is_horizontal) | |
| 607 return 0; | |
| 608 // y coordinate is most likely negative. | |
| 609 int y = abs(scroll_view->contents()->y()); | |
| 610 RowInfo row = GetRowInfo(y); | |
| 611 if (is_positive) { | |
| 612 return row.height - (y - row.origin); | |
| 613 } else if (y == row.origin) { | |
| 614 row = GetRowInfo(std::max(0, row.origin - 1)); | |
| 615 return y - row.origin; | |
| 616 } else { | |
| 617 return y - row.origin; | |
| 618 } | |
| 619 } | |
| 620 | |
| 621 VariableRowHeightScrollHelper::RowInfo | |
| 622 VariableRowHeightScrollHelper::GetRowInfo(int y) { | |
| 623 return controller_->GetRowInfo(y); | |
| 624 } | |
| 625 | |
| 626 // FixedRowHeightScrollHelper ----------------------------------------------- | |
| 627 | |
| 628 FixedRowHeightScrollHelper::FixedRowHeightScrollHelper(int top_margin, | |
| 629 int row_height) | |
| 630 : VariableRowHeightScrollHelper(NULL), | |
| 631 top_margin_(top_margin), | |
| 632 row_height_(row_height) { | |
| 633 DCHECK_GT(row_height, 0); | |
| 634 } | |
| 635 | |
| 636 VariableRowHeightScrollHelper::RowInfo | |
| 637 FixedRowHeightScrollHelper::GetRowInfo(int y) { | |
| 638 if (y < top_margin_) | |
| 639 return RowInfo(0, top_margin_); | |
| 640 return RowInfo((y - top_margin_) / row_height_ * row_height_ + top_margin_, | |
| 641 row_height_); | |
| 642 } | |
| 643 | |
| 644 } // namespace views | |
| OLD | NEW |