1 // Copyright (c) 2011 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/scrollbar/base_scroll_bar_thumb.h"
6 
7 #include "ui/gfx/canvas.h"
8 #include "ui/gfx/geometry/rect.h"
9 #include "ui/views/controls/scrollbar/scroll_bar.h"
10 #include "ui/views/metadata/metadata_impl_macros.h"
11 
12 namespace {
13 // The distance the mouse can be dragged outside the bounds of the thumb during
14 // dragging before the scrollbar will snap back to its regular position.
15 static constexpr int kScrollThumbDragOutSnap = 100;
16 }  // namespace
17 
18 namespace views {
19 
BaseScrollBarThumb(ScrollBar * scroll_bar)20 BaseScrollBarThumb::BaseScrollBarThumb(ScrollBar* scroll_bar)
21     : scroll_bar_(scroll_bar),
22       drag_start_position_(-1),
23       mouse_offset_(-1),
24       state_(Button::STATE_NORMAL) {}
25 
26 BaseScrollBarThumb::~BaseScrollBarThumb() = default;
27 
SetLength(int length)28 void BaseScrollBarThumb::SetLength(int length) {
29   // Make sure the thumb is never sized smaller than its minimum possible
30   // display size.
31   gfx::Size size = GetPreferredSize();
32   size.SetToMax(
33       gfx::Size(IsHorizontal() ? length : 0, IsHorizontal() ? 0 : length));
34   SetSize(size);
35 }
36 
GetSize() const37 int BaseScrollBarThumb::GetSize() const {
38   if (IsHorizontal())
39     return width();
40   return height();
41 }
42 
SetPosition(int position)43 void BaseScrollBarThumb::SetPosition(int position) {
44   gfx::Rect thumb_bounds = bounds();
45   gfx::Rect track_bounds = scroll_bar_->GetTrackBounds();
46   if (IsHorizontal()) {
47     thumb_bounds.set_x(track_bounds.x() + position);
48   } else {
49     thumb_bounds.set_y(track_bounds.y() + position);
50   }
51   SetBoundsRect(thumb_bounds);
52 }
53 
GetPosition() const54 int BaseScrollBarThumb::GetPosition() const {
55   gfx::Rect track_bounds = scroll_bar_->GetTrackBounds();
56   if (IsHorizontal())
57     return x() - track_bounds.x();
58   return y() - track_bounds.y();
59 }
60 
OnMouseEntered(const ui::MouseEvent & event)61 void BaseScrollBarThumb::OnMouseEntered(const ui::MouseEvent& event) {
62   SetState(Button::STATE_HOVERED);
63 }
64 
OnMouseExited(const ui::MouseEvent & event)65 void BaseScrollBarThumb::OnMouseExited(const ui::MouseEvent& event) {
66   SetState(Button::STATE_NORMAL);
67 }
68 
OnMousePressed(const ui::MouseEvent & event)69 bool BaseScrollBarThumb::OnMousePressed(const ui::MouseEvent& event) {
70   mouse_offset_ = IsHorizontal() ? event.x() : event.y();
71   drag_start_position_ = GetPosition();
72   SetState(Button::STATE_PRESSED);
73   return true;
74 }
75 
OnMouseDragged(const ui::MouseEvent & event)76 bool BaseScrollBarThumb::OnMouseDragged(const ui::MouseEvent& event) {
77   // If the user moves the mouse more than |kScrollThumbDragOutSnap| outside
78   // the bounds of the thumb, the scrollbar will snap the scroll back to the
79   // point it was at before the drag began.
80   if (IsHorizontal()) {
81     if ((event.y() < y() - kScrollThumbDragOutSnap) ||
82         (event.y() > (y() + height() + kScrollThumbDragOutSnap))) {
83       scroll_bar_->ScrollToThumbPosition(drag_start_position_, false);
84       return true;
85     }
86   } else {
87     if ((event.x() < x() - kScrollThumbDragOutSnap) ||
88         (event.x() > (x() + width() + kScrollThumbDragOutSnap))) {
89       scroll_bar_->ScrollToThumbPosition(drag_start_position_, false);
90       return true;
91     }
92   }
93   if (IsHorizontal()) {
94     int thumb_x = event.x() - mouse_offset_;
95     if (base::i18n::IsRTL())
96       thumb_x *= -1;
97     scroll_bar_->ScrollToThumbPosition(GetPosition() + thumb_x, false);
98   } else {
99     int thumb_y = event.y() - mouse_offset_;
100     scroll_bar_->ScrollToThumbPosition(GetPosition() + thumb_y, false);
101   }
102   return true;
103 }
104 
OnMouseReleased(const ui::MouseEvent & event)105 void BaseScrollBarThumb::OnMouseReleased(const ui::MouseEvent& event) {
106   SetState(HitTestPoint(event.location()) ? Button::STATE_HOVERED
107                                           : Button::STATE_NORMAL);
108 }
109 
OnMouseCaptureLost()110 void BaseScrollBarThumb::OnMouseCaptureLost() {
111   SetState(Button::STATE_HOVERED);
112 }
113 
GetState() const114 Button::ButtonState BaseScrollBarThumb::GetState() const {
115   return state_;
116 }
117 
SetState(Button::ButtonState state)118 void BaseScrollBarThumb::SetState(Button::ButtonState state) {
119   if (state_ == state)
120     return;
121 
122   state_ = state;
123   OnStateChanged();
124 }
125 
OnStateChanged()126 void BaseScrollBarThumb::OnStateChanged() {
127   SchedulePaint();
128 }
129 
IsHorizontal() const130 bool BaseScrollBarThumb::IsHorizontal() const {
131   return scroll_bar_->IsHorizontal();
132 }
133 
134 BEGIN_METADATA(BaseScrollBarThumb, View)
135 END_METADATA
136 
137 }  // namespace views
138