• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/scrollbar/native_scroll_bar_views.h"
6 
7 #include "base/logging.h"
8 #include "ui/events/keycodes/keyboard_codes.h"
9 #include "ui/gfx/canvas.h"
10 #include "ui/gfx/path.h"
11 #include "ui/views/controls/button/custom_button.h"
12 #include "ui/views/controls/focusable_border.h"
13 #include "ui/views/controls/scrollbar/base_scroll_bar_button.h"
14 #include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h"
15 #include "ui/views/controls/scrollbar/native_scroll_bar.h"
16 #include "ui/views/controls/scrollbar/scroll_bar.h"
17 
18 namespace views {
19 
20 namespace {
21 
22 // Wrapper for the scroll buttons.
23 class ScrollBarButton : public BaseScrollBarButton {
24  public:
25   enum Type {
26     UP,
27     DOWN,
28     LEFT,
29     RIGHT,
30   };
31 
32   ScrollBarButton(ButtonListener* listener, Type type);
33   virtual ~ScrollBarButton();
34 
35   virtual gfx::Size GetPreferredSize() OVERRIDE;
GetClassName() const36   virtual const char* GetClassName() const OVERRIDE {
37     return "ScrollBarButton";
38   }
39 
40  protected:
41   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
42 
43  private:
44   ui::NativeTheme::ExtraParams GetNativeThemeParams() const;
45   ui::NativeTheme::Part GetNativeThemePart() const;
46   ui::NativeTheme::State GetNativeThemeState() const;
47 
48   Type type_;
49 };
50 
51 // Wrapper for the scroll thumb
52 class ScrollBarThumb : public BaseScrollBarThumb {
53  public:
54   explicit ScrollBarThumb(BaseScrollBar* scroll_bar);
55   virtual ~ScrollBarThumb();
56 
57   virtual gfx::Size GetPreferredSize() OVERRIDE;
GetClassName() const58   virtual const char* GetClassName() const OVERRIDE {
59     return "ScrollBarThumb";
60   }
61 
62  protected:
63   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
64 
65  private:
66   ui::NativeTheme::ExtraParams GetNativeThemeParams() const;
67   ui::NativeTheme::Part GetNativeThemePart() const;
68   ui::NativeTheme::State GetNativeThemeState() const;
69 
70   ScrollBar* scroll_bar_;
71 };
72 
73 /////////////////////////////////////////////////////////////////////////////
74 // ScrollBarButton
75 
ScrollBarButton(ButtonListener * listener,Type type)76 ScrollBarButton::ScrollBarButton(ButtonListener* listener, Type type)
77     : BaseScrollBarButton(listener),
78       type_(type) {
79   SetFocusable(false);
80   SetAccessibilityFocusable(false);
81 }
82 
~ScrollBarButton()83 ScrollBarButton::~ScrollBarButton() {
84 }
85 
GetPreferredSize()86 gfx::Size ScrollBarButton::GetPreferredSize() {
87   return GetNativeTheme()->GetPartSize(GetNativeThemePart(),
88                                        GetNativeThemeState(),
89                                        GetNativeThemeParams());
90 }
91 
OnPaint(gfx::Canvas * canvas)92 void ScrollBarButton::OnPaint(gfx::Canvas* canvas) {
93   gfx::Rect bounds(GetPreferredSize());
94   GetNativeTheme()->Paint(canvas->sk_canvas(), GetNativeThemePart(),
95                           GetNativeThemeState(), bounds,
96                           GetNativeThemeParams());
97 }
98 
99 ui::NativeTheme::ExtraParams
GetNativeThemeParams() const100     ScrollBarButton::GetNativeThemeParams() const {
101   ui::NativeTheme::ExtraParams params;
102 
103   switch (state_) {
104     case CustomButton::STATE_HOVERED:
105       params.scrollbar_arrow.is_hovering = true;
106       break;
107     default:
108       params.scrollbar_arrow.is_hovering = false;
109       break;
110   }
111 
112   return params;
113 }
114 
115 ui::NativeTheme::Part
GetNativeThemePart() const116     ScrollBarButton::GetNativeThemePart() const {
117   switch (type_) {
118     case UP:
119       return ui::NativeTheme::kScrollbarUpArrow;
120     case DOWN:
121       return ui::NativeTheme::kScrollbarDownArrow;
122     case LEFT:
123       return ui::NativeTheme::kScrollbarLeftArrow;
124     case RIGHT:
125       return ui::NativeTheme::kScrollbarRightArrow;
126     default:
127       return ui::NativeTheme::kScrollbarUpArrow;
128   }
129 }
130 
131 ui::NativeTheme::State
GetNativeThemeState() const132     ScrollBarButton::GetNativeThemeState() const {
133   ui::NativeTheme::State state;
134 
135   switch (state_) {
136     case CustomButton::STATE_HOVERED:
137       state = ui::NativeTheme::kHovered;
138       break;
139     case CustomButton::STATE_PRESSED:
140       state = ui::NativeTheme::kPressed;
141       break;
142     case CustomButton::STATE_DISABLED:
143       state = ui::NativeTheme::kDisabled;
144       break;
145     case CustomButton::STATE_NORMAL:
146     default:
147       state = ui::NativeTheme::kNormal;
148       break;
149   }
150 
151   return state;
152 }
153 
154 /////////////////////////////////////////////////////////////////////////////
155 // ScrollBarThumb
156 
ScrollBarThumb(BaseScrollBar * scroll_bar)157 ScrollBarThumb::ScrollBarThumb(BaseScrollBar* scroll_bar)
158     : BaseScrollBarThumb(scroll_bar),
159       scroll_bar_(scroll_bar) {
160   SetFocusable(false);
161   SetAccessibilityFocusable(false);
162 }
163 
~ScrollBarThumb()164 ScrollBarThumb::~ScrollBarThumb() {
165 }
166 
GetPreferredSize()167 gfx::Size ScrollBarThumb::GetPreferredSize() {
168   return GetNativeTheme()->GetPartSize(GetNativeThemePart(),
169                                        GetNativeThemeState(),
170                                        GetNativeThemeParams());
171 }
172 
OnPaint(gfx::Canvas * canvas)173 void ScrollBarThumb::OnPaint(gfx::Canvas* canvas) {
174   const gfx::Rect local_bounds(GetLocalBounds());
175   const ui::NativeTheme::State theme_state = GetNativeThemeState();
176   const ui::NativeTheme::ExtraParams extra_params(GetNativeThemeParams());
177   GetNativeTheme()->Paint(canvas->sk_canvas(),
178                           GetNativeThemePart(),
179                           theme_state,
180                           local_bounds,
181                           extra_params);
182   const ui::NativeTheme::Part gripper_part = scroll_bar_->IsHorizontal() ?
183       ui::NativeTheme::kScrollbarHorizontalGripper :
184       ui::NativeTheme::kScrollbarVerticalGripper;
185   GetNativeTheme()->Paint(canvas->sk_canvas(), gripper_part, theme_state,
186                           local_bounds, extra_params);
187 }
188 
GetNativeThemeParams() const189 ui::NativeTheme::ExtraParams ScrollBarThumb::GetNativeThemeParams() const {
190   // This gives the behavior we want.
191   ui::NativeTheme::ExtraParams params;
192   params.scrollbar_thumb.is_hovering =
193       (GetState() != CustomButton::STATE_HOVERED);
194   return params;
195 }
196 
GetNativeThemePart() const197 ui::NativeTheme::Part ScrollBarThumb::GetNativeThemePart() const {
198   if (scroll_bar_->IsHorizontal())
199     return ui::NativeTheme::kScrollbarHorizontalThumb;
200   return ui::NativeTheme::kScrollbarVerticalThumb;
201 }
202 
GetNativeThemeState() const203 ui::NativeTheme::State ScrollBarThumb::GetNativeThemeState() const {
204   ui::NativeTheme::State state;
205 
206   switch (GetState()) {
207     case CustomButton::STATE_HOVERED:
208       state = ui::NativeTheme::kHovered;
209       break;
210     case CustomButton::STATE_PRESSED:
211       state = ui::NativeTheme::kPressed;
212       break;
213     case CustomButton::STATE_DISABLED:
214       state = ui::NativeTheme::kDisabled;
215       break;
216     case CustomButton::STATE_NORMAL:
217     default:
218       state = ui::NativeTheme::kNormal;
219       break;
220   }
221 
222   return state;
223 }
224 
225 }  // namespace
226 
227 ////////////////////////////////////////////////////////////////////////////////
228 // NativeScrollBarViews, public:
229 
230 const char NativeScrollBarViews::kViewClassName[] = "NativeScrollBarViews";
231 
NativeScrollBarViews(NativeScrollBar * scroll_bar)232 NativeScrollBarViews::NativeScrollBarViews(NativeScrollBar* scroll_bar)
233     : BaseScrollBar(scroll_bar->IsHorizontal(),
234                     new ScrollBarThumb(this)),
235       native_scroll_bar_(scroll_bar) {
236   set_controller(native_scroll_bar_->controller());
237 
238   if (native_scroll_bar_->IsHorizontal()) {
239     prev_button_ = new ScrollBarButton(this, ScrollBarButton::LEFT);
240     next_button_ = new ScrollBarButton(this, ScrollBarButton::RIGHT);
241 
242     part_ = ui::NativeTheme::kScrollbarHorizontalTrack;
243   } else {
244     prev_button_ = new ScrollBarButton(this, ScrollBarButton::UP);
245     next_button_ = new ScrollBarButton(this, ScrollBarButton::DOWN);
246 
247     part_ = ui::NativeTheme::kScrollbarVerticalTrack;
248   }
249 
250   state_ = ui::NativeTheme::kNormal;
251 
252   AddChildView(prev_button_);
253   AddChildView(next_button_);
254 
255   prev_button_->set_context_menu_controller(this);
256   next_button_->set_context_menu_controller(this);
257 }
258 
~NativeScrollBarViews()259 NativeScrollBarViews::~NativeScrollBarViews() {
260 }
261 
262 ////////////////////////////////////////////////////////////////////////////////
263 // NativeScrollBarViews, View overrides:
264 
Layout()265 void NativeScrollBarViews::Layout() {
266   gfx::Size size = prev_button_->GetPreferredSize();
267   prev_button_->SetBounds(0, 0, size.width(), size.height());
268 
269   if (native_scroll_bar_->IsHorizontal()) {
270     next_button_->SetBounds(width() - size.width(), 0,
271                             size.width(), size.height());
272   } else {
273     next_button_->SetBounds(0, height() - size.height(),
274                             size.width(), size.height());
275   }
276 
277   GetThumb()->SetBoundsRect(GetTrackBounds());
278 }
279 
OnPaint(gfx::Canvas * canvas)280 void NativeScrollBarViews::OnPaint(gfx::Canvas* canvas) {
281   gfx::Rect bounds = GetTrackBounds();
282 
283   if (bounds.IsEmpty())
284     return;
285 
286   params_.scrollbar_track.track_x = bounds.x();
287   params_.scrollbar_track.track_y = bounds.y();
288   params_.scrollbar_track.track_width = bounds.width();
289   params_.scrollbar_track.track_height = bounds.height();
290   params_.scrollbar_track.classic_state = 0;
291 
292   GetNativeTheme()->Paint(canvas->sk_canvas(), part_, state_, bounds, params_);
293 }
294 
GetPreferredSize()295 gfx::Size NativeScrollBarViews::GetPreferredSize() {
296   const ui::NativeTheme* theme = native_scroll_bar_->GetNativeTheme();
297   if (native_scroll_bar_->IsHorizontal())
298     return gfx::Size(0, GetHorizontalScrollBarHeight(theme));
299   return gfx::Size(GetVerticalScrollBarWidth(theme), 0);
300 }
301 
GetClassName() const302 const char* NativeScrollBarViews::GetClassName() const {
303   return kViewClassName;
304 }
305 
GetLayoutSize() const306 int NativeScrollBarViews::GetLayoutSize() const {
307   gfx::Size size = prev_button_->GetPreferredSize();
308   return IsHorizontal() ? size.height() : size.width();
309 }
310 
ScrollToPosition(int position)311 void NativeScrollBarViews::ScrollToPosition(int position) {
312   controller()->ScrollToPosition(native_scroll_bar_, position);
313 }
314 
GetScrollIncrement(bool is_page,bool is_positive)315 int NativeScrollBarViews::GetScrollIncrement(bool is_page, bool is_positive) {
316   return controller()->GetScrollIncrement(native_scroll_bar_,
317                                           is_page,
318                                           is_positive);
319 }
320 
321 //////////////////////////////////////////////////////////////////////////////
322 // BaseButton::ButtonListener overrides:
323 
ButtonPressed(Button * sender,const ui::Event & event)324 void NativeScrollBarViews::ButtonPressed(Button* sender,
325                                          const ui::Event& event) {
326   if (sender == prev_button_) {
327     ScrollByAmount(SCROLL_PREV_LINE);
328   } else if (sender == next_button_) {
329     ScrollByAmount(SCROLL_NEXT_LINE);
330   }
331 }
332 
333 ////////////////////////////////////////////////////////////////////////////////
334 // NativeScrollBarViews, NativeScrollBarWrapper overrides:
335 
GetPosition() const336 int NativeScrollBarViews::GetPosition() const {
337   return BaseScrollBar::GetPosition();
338 }
339 
GetView()340 View* NativeScrollBarViews::GetView() {
341   return this;
342 }
343 
Update(int viewport_size,int content_size,int current_pos)344 void NativeScrollBarViews::Update(int viewport_size,
345                                   int content_size,
346                                   int current_pos) {
347   BaseScrollBar::Update(viewport_size, content_size, current_pos);
348 }
349 
350 ////////////////////////////////////////////////////////////////////////////////
351 // NativeScrollBarViews, private:
352 
GetTrackBounds() const353 gfx::Rect NativeScrollBarViews::GetTrackBounds() const {
354   gfx::Rect bounds = GetLocalBounds();
355   gfx::Size size = prev_button_->GetPreferredSize();
356   BaseScrollBarThumb* thumb = GetThumb();
357 
358   if (native_scroll_bar_->IsHorizontal()) {
359     bounds.set_x(bounds.x() + size.width());
360     bounds.set_width(std::max(0, bounds.width() - 2 * size.width()));
361     bounds.set_height(thumb->GetPreferredSize().height());
362   } else {
363     bounds.set_y(bounds.y() + size.height());
364     bounds.set_height(std::max(0, bounds.height() - 2 * size.height()));
365     bounds.set_width(thumb->GetPreferredSize().width());
366   }
367 
368   return bounds;
369 }
370 
371 ////////////////////////////////////////////////////////////////////////////////
372 // NativewScrollBarWrapper, public:
373 
374 // static
CreateWrapper(NativeScrollBar * scroll_bar)375 NativeScrollBarWrapper* NativeScrollBarWrapper::CreateWrapper(
376     NativeScrollBar* scroll_bar) {
377   return new NativeScrollBarViews(scroll_bar);
378 }
379 
380 // static
GetHorizontalScrollBarHeight(const ui::NativeTheme * theme)381 int NativeScrollBarWrapper::GetHorizontalScrollBarHeight(
382     const ui::NativeTheme* theme) {
383   if (!theme)
384     theme = ui::NativeTheme::instance();
385   ui::NativeTheme::ExtraParams button_params;
386   button_params.scrollbar_arrow.is_hovering = false;
387   gfx::Size button_size = theme->GetPartSize(
388       ui::NativeTheme::kScrollbarLeftArrow,
389       ui::NativeTheme::kNormal,
390       button_params);
391 
392   ui::NativeTheme::ExtraParams thumb_params;
393   thumb_params.scrollbar_thumb.is_hovering = false;
394   gfx::Size track_size = theme->GetPartSize(
395       ui::NativeTheme::kScrollbarHorizontalThumb,
396       ui::NativeTheme::kNormal,
397       thumb_params);
398 
399   return std::max(track_size.height(), button_size.height());
400 }
401 
402 // static
GetVerticalScrollBarWidth(const ui::NativeTheme * theme)403 int NativeScrollBarWrapper::GetVerticalScrollBarWidth(
404     const ui::NativeTheme* theme) {
405   if (!theme)
406     theme = ui::NativeTheme::instance();
407   ui::NativeTheme::ExtraParams button_params;
408   button_params.scrollbar_arrow.is_hovering = false;
409   gfx::Size button_size = theme->GetPartSize(
410       ui::NativeTheme::kScrollbarUpArrow,
411       ui::NativeTheme::kNormal,
412       button_params);
413 
414   ui::NativeTheme::ExtraParams thumb_params;
415   thumb_params.scrollbar_thumb.is_hovering = false;
416   gfx::Size track_size = theme->GetPartSize(
417       ui::NativeTheme::kScrollbarVerticalThumb,
418       ui::NativeTheme::kNormal,
419       thumb_params);
420 
421   return std::max(track_size.width(), button_size.width());
422 }
423 
424 }  // namespace views
425