Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Side by Side Diff: views/controls/scrollbar/native_scroll_bar_views.cc

Issue 7669028: Adding a Views scrollbar implementation. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Fixed keyboard codes include Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "views/controls/scrollbar/native_scroll_bar_views.h"
6
7 #include "base/logging.h"
8 #include "ui/base/keycodes/keyboard_codes.h"
9 #include "ui/gfx/canvas.h"
10 #include "ui/gfx/canvas_skia.h"
11 #include "ui/gfx/path.h"
12 #include "views/controls/button/custom_button.h"
13 #include "views/controls/focusable_border.h"
14 #include "views/controls/scrollbar/native_scroll_bar.h"
15 #include "views/controls/scrollbar/scroll_bar.h"
16
17 namespace views {
18
19 namespace {
20
21 // Wrapper for the scroll buttons.
22 class ScrollBarButton : public CustomButton {
Ben Goodger (Google) 2011/08/19 17:44:33 Rather than reinvent the wheel here I'd rather you
23 public:
24 enum Type {
25 UP,
26 DOWN,
27 LEFT,
28 RIGHT,
29 };
30
31 ScrollBarButton(ButtonListener* listener, Type type);
32 virtual ~ScrollBarButton();
33
34 virtual gfx::Size GetPreferredSize() OVERRIDE;
35
36 protected:
37 virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE;
38 virtual void OnMouseReleased(const MouseEvent& event) OVERRIDE;
39 virtual void OnMouseEntered(const MouseEvent& event) OVERRIDE;
40 virtual void OnMouseExited(const MouseEvent& event) OVERRIDE;
41 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
42
43 private:
44 gfx::NativeTheme::ExtraParams params_;
45 gfx::NativeTheme::Part part_;
46 gfx::NativeTheme::State state_;
47 };
48
49 } // namespace
50
51 // Wrapper for the scroll thumb
52 class ScrollBarThumb : public View {
53 public:
54 enum Type {
55 VERTICAL,
56 HORIZONTAL,
57 };
58
59 ScrollBarThumb(NativeScrollBarWrapper* wrapper, Type type);
60 virtual ~ScrollBarThumb();
61
62 virtual gfx::Size GetPreferredSize() OVERRIDE;
63
64 void Update(int position);
65 void Update(int viewport_size, int content_size, int position);
66
67 int current_position() const { return current_position_; }
68
69 // This is called when the layout changes and the scrollbar changes
70 // size.
71 void set_track_bounds(const gfx::Rect& bounds) { track_bounds_ = bounds; }
72
73 protected:
74 virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE;
75 virtual bool OnMouseDragged(const MouseEvent& event) OVERRIDE;
76 virtual void OnMouseEntered(const MouseEvent& event) OVERRIDE;
77 virtual void OnMouseExited(const MouseEvent& event) OVERRIDE;
78 virtual void OnMouseMoved(const MouseEvent& event) OVERRIDE;
79 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
80
81 private:
82 // Converts the position of the thumb to the pixel size of the thumb
83 // relative to the size of the drawing area.
84 int GetThumbPosition(int drawing_size) const;
85
86 // Returns the size of the thumb relative to the drawing area size.
87 int GetThumbSize(int drawing_size) const;
88
89 NativeScrollBarWrapper* wrapper_;
90
91 Type type_;
92
93 // The current position of the thumb.
94 int current_position_;
95
96 // The size of the content we want to display.
97 int content_size_;
98 // The size of the viewport (displayable area).
99 int viewport_size_;
100
101 // The track bounds. We need this to compute the size of the thumb relative
102 // to the size of the track.
103 gfx::Rect track_bounds_;
104
105 // The distance from the tip of the thumb. Used when dragging to keep the
106 // position of the thumb at the same relative distance from the mouse click
107 // event.
108 int delta_;
109
110 // Parameters required for the native theme. We keep these here since
111 // we may change them based on mouse events (hover, click, ...).
112 gfx::NativeTheme::ExtraParams params_;
113 gfx::NativeTheme::Part part_;
114 gfx::NativeTheme::State state_;
115 };
116
117 namespace {
118
119 /////////////////////////////////////////////////////////////////////////////
120 // ScrollBarButton
121
122 ScrollBarButton::ScrollBarButton(ButtonListener* listener, Type type)
123 : CustomButton(listener),
124 params_(),
125 state_(gfx::NativeTheme::kNormal) {
126 switch (type) {
127 case UP:
128 part_ = gfx::NativeTheme::kScrollbarUpArrow;
129 break;
130 case DOWN:
131 part_ = gfx::NativeTheme::kScrollbarDownArrow;
132 break;
133 case LEFT:
134 part_ = gfx::NativeTheme::kScrollbarLeftArrow;
135 break;
136 case RIGHT:
137 part_ = gfx::NativeTheme::kScrollbarRightArrow;
138 break;
139 }
140
141 params_.scrollbar_arrow.is_hovering = false;
142 }
143
144 ScrollBarButton::~ScrollBarButton() {
145 }
146
147 gfx::Size ScrollBarButton::GetPreferredSize() {
148 const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance();
149 return native_theme->GetPartSize(part_, state_, params_);
150 }
151
152 bool ScrollBarButton::OnMousePressed(const MouseEvent& event) {
153 if (event.IsOnlyLeftMouseButton())
154 Button::NotifyClick(event);
155 return true;
156 }
157
158 void ScrollBarButton::OnMouseReleased(const MouseEvent& event) {
159 }
160
161 void ScrollBarButton::OnMouseEntered(const MouseEvent& event) {
162 state_ = gfx::NativeTheme::kHovered;
163 params_.scrollbar_arrow.is_hovering = true;
164 SchedulePaint();
165 }
166
167 void ScrollBarButton::OnMouseExited(const MouseEvent& event) {
168 state_ = gfx::NativeTheme::kNormal;
169 params_.scrollbar_arrow.is_hovering = false;
170 SchedulePaint();
171 }
172
173 void ScrollBarButton::OnPaint(gfx::Canvas* canvas) {
174 const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance();
175 gfx::Rect bounds;
176 bounds.set_size(GetPreferredSize());
177
178 native_theme->Paint(canvas->AsCanvasSkia(),
179 part_,
180 state_,
181 bounds,
182 params_);
183 }
184
185 } // namespace
186
187 /////////////////////////////////////////////////////////////////////////////
188 // ScrollBarThumb
189
190 ScrollBarThumb::ScrollBarThumb(NativeScrollBarWrapper* wrapper, Type type)
191 : wrapper_(wrapper),
192 type_(type),
193 state_(gfx::NativeTheme::kNormal) {
194 switch (type) {
195 case VERTICAL:
196 part_ = gfx::NativeTheme::kScrollbarVerticalThumb;
197 break;
198 case HORIZONTAL:
199 part_ = gfx::NativeTheme::kScrollbarHorizontalThumb;
200 break;
201 }
202 params_.scrollbar_thumb.is_hovering = false;
203 }
204
205 ScrollBarThumb::~ScrollBarThumb() {
206 }
207
208 gfx::Size ScrollBarThumb::GetPreferredSize() {
209 const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance();
210 return native_theme->GetPartSize(part_, state_, params_);
211 }
212
213 void ScrollBarThumb::Update(int position) {
214 Update(viewport_size_, content_size_, position);
215 }
216
217 void ScrollBarThumb::Update(int viewport_size,
218 int content_size,
219 int position) {
220 // First constrain the size and position to the actual values used by the
221 // scrollbar.
222 if (content_size < 0)
223 content_size = 0;
224
225 if (position < 0)
226 position = 0;
227
228 if (position > content_size - viewport_size)
229 position = content_size - viewport_size;
230
231 current_position_ = position;
232 content_size_ = content_size;
233 viewport_size_ = viewport_size;
234
235 gfx::Rect bounds = GetLocalBounds();
236
237 if (type_ == HORIZONTAL) {
238 bounds.set_x(track_bounds_.x() +
239 GetThumbPosition(track_bounds_.width()));
240 bounds.set_width(GetThumbSize(track_bounds_.width()));
241 } else {
242 bounds.set_y(track_bounds_.y() +
243 GetThumbPosition(track_bounds_.height()));
244 bounds.set_height(GetThumbSize(track_bounds_.height()));
245 }
246
247 // Update the position and size of the thumb.
248 SetBoundsRect(bounds);
249
250 SchedulePaint();
251 }
252
253 bool ScrollBarThumb::OnMousePressed(const MouseEvent& event) {
254 if (!event.IsOnlyLeftMouseButton())
255 return false;
256
257 // Keep the distance from the tip of the thumb so that scrolling movement
258 // looks nice. (We want events relative to the mouse click.)
259 if (type_ == HORIZONTAL) {
260 int track_size = track_bounds_.width();
261 delta_ = -event.x() * content_size_ / track_size;
262 } else {
263 int track_size = track_bounds_.height();
264 delta_ = -event.y() * content_size_ / track_size;
265 }
266
267 return true;
268 }
269
270
271 bool ScrollBarThumb::OnMouseDragged(const MouseEvent& event) {
272 // Let the scrollbar wrapper know that we're changing the current position.
273 // It should then all for UI updates.
274 if (type_ == HORIZONTAL) {
275 int track_size = track_bounds_.width();
276 wrapper_->Update(viewport_size_,
277 content_size_,
278 current_position_ + delta_ +
279 event.x() * content_size_ / track_size);
280 } else {
281 int track_size = track_bounds_.height();
282 wrapper_->Update(viewport_size_,
283 content_size_,
284 current_position_ + delta_ +
285 event.y() * content_size_ / track_size);
286 }
287
288 return true;
289 }
290
291 void ScrollBarThumb::OnMouseEntered(const MouseEvent& event) {
292 if (state_ != gfx::NativeTheme::kHovered) {
293 state_ = gfx::NativeTheme::kHovered;
294 params_.scrollbar_thumb.is_hovering = true;
295 SchedulePaint();
296 }
297 }
298
299 void ScrollBarThumb::OnMouseExited(const MouseEvent& event) {
300 if (state_ != gfx::NativeTheme::kNormal) {
301 state_ = gfx::NativeTheme::kNormal;
302 params_.scrollbar_thumb.is_hovering = false;
303 SchedulePaint();
304 }
305 }
306
307 void ScrollBarThumb::OnMouseMoved(const MouseEvent& event) {
308 OnMouseEntered(event);
309 }
310
311 void ScrollBarThumb::OnPaint(gfx::Canvas* canvas) {
312 const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance();
313
314 native_theme->Paint(canvas->AsCanvasSkia(),
315 part_,
316 state_,
317 GetLocalBounds(),
318 params_);
319 }
320
321 int ScrollBarThumb::GetThumbSize(int drawing_size) const {
322 return (viewport_size_ * drawing_size) /
323 (content_size_ ? content_size_ : 1);
324 }
325
326 int ScrollBarThumb::GetThumbPosition(int drawing_size) const {
327 return (current_position_ * drawing_size) /
328 (content_size_ ? content_size_ : 1);
329 }
330
331 ////////////////////////////////////////////////////////////////////////////////
332 // NativeScrollBarViews, public:
333
334 NativeScrollBarViews::NativeScrollBarViews(NativeScrollBar* scroll_bar)
335 : native_scroll_bar_(scroll_bar) {
336 if (native_scroll_bar_->IsHorizontal()) {
337 prev_button_ = new ScrollBarButton(this, ScrollBarButton::LEFT);
338 next_button_ = new ScrollBarButton(this, ScrollBarButton::RIGHT);
339 thumb_ = new ScrollBarThumb(this, ScrollBarThumb::HORIZONTAL);
340
341 part_ = gfx::NativeTheme::kScrollbarHorizontalTrack;
342 } else {
343 prev_button_ = new ScrollBarButton(this, ScrollBarButton::UP);
344 next_button_ = new ScrollBarButton(this, ScrollBarButton::DOWN);
345 thumb_ = new ScrollBarThumb(this, ScrollBarThumb::VERTICAL);
346
347 part_ = gfx::NativeTheme::kScrollbarVerticalTrack;
348 }
349
350 state_ = gfx::NativeTheme::kNormal;
351
352 AddChildView(prev_button_);
353 AddChildView(next_button_);
354 AddChildView(thumb_);
355 }
356
357 NativeScrollBarViews::~NativeScrollBarViews() {
358 }
359
360 ////////////////////////////////////////////////////////////////////////////////
361 // NativeScrollBarViews, View overrides:
362
363 void NativeScrollBarViews::Layout() {
364 SetBoundsRect(native_scroll_bar_->GetLocalBounds());
365
366 gfx::Size size = prev_button_->GetPreferredSize();
367 prev_button_->SetBounds(0, 0, size.width(), size.height());
368
369 if (native_scroll_bar_->IsHorizontal()) {
370 next_button_->SetBounds(width() - size.width(), 0,
371 size.width(), size.height());
372 } else {
373 next_button_->SetBounds(0, height() - size.height(),
374 size.width(), size.height());
375 }
376
377 // Update the size of the thumb and tell it that the track size also changed.
378 thumb_->set_track_bounds(GetTrackBounds());
379 thumb_->SetBoundsRect(GetTrackBounds());
380 }
381
382 gfx::Size NativeScrollBarViews::GetPreferredSize() {
383 if (native_scroll_bar_->IsHorizontal())
384 return gfx::Size(0, GetHorizontalScrollBarHeight());
385 return gfx::Size(GetVerticalScrollBarWidth(), 0);
386 }
387
388 // TODO(oshima|jcampan): key/mouse events are not delievered and
389 // the following code is not tested. It requires the focus manager to be fully
390 // implemented.
391 bool NativeScrollBarViews::OnKeyPressed(const KeyEvent& event) {
392 switch (event.key_code()) {
393 case ui::VKEY_UP:
394 if (!native_scroll_bar_->IsHorizontal())
395 MoveStep(false /* negative */);
396 break;
397 case ui::VKEY_DOWN:
398 if (!native_scroll_bar_->IsHorizontal())
399 MoveStep(true /* positive */);
400 break;
401 case ui::VKEY_LEFT:
402 if (native_scroll_bar_->IsHorizontal())
403 MoveStep(false /* negative */);
404 break;
405 case ui::VKEY_RIGHT:
406 if (native_scroll_bar_->IsHorizontal())
407 MoveStep(true /* positive */);
408 break;
409 case ui::VKEY_PRIOR:
410 MovePage(false /* negative */);
411 break;
412 case ui::VKEY_NEXT:
413 MovePage(true /* positive */);
414 break;
415 case ui::VKEY_HOME:
416 MoveTo(0);
417 break;
418 case ui::VKEY_END:
419 MoveToBottom();
420 break;
421 default:
422 return false;
423 }
424 return true;
425 }
426
427 bool NativeScrollBarViews::OnMousePressed(const MouseEvent& event) {
428 if (!event.IsOnlyLeftMouseButton())
429 return true;
430
431 if (native_scroll_bar_->IsHorizontal()) {
432 if (event.x() < thumb_->bounds().x())
433 MovePage(false);
434 else
435 MovePage(true);
436 } else {
437 if (event.y() < thumb_->bounds().y())
438 MovePage(false);
439 else
440 MovePage(true);
441 }
442
443 return true;
444 }
445
446 void NativeScrollBarViews::OnMouseEntered(const MouseEvent& event) {
447 state_ = gfx::NativeTheme::kHovered;
448 SchedulePaint();
449 }
450
451 void NativeScrollBarViews::OnMouseExited(const MouseEvent& event) {
452 state_ = gfx::NativeTheme::kNormal;
453 SchedulePaint();
454 }
455
456 bool NativeScrollBarViews::OnMouseWheel(const MouseWheelEvent& e) {
457 MoveBy(-e.offset()); // e.GetOffset() > 0 means scroll up.
458 return true;
459 }
460
461 void NativeScrollBarViews::OnPaint(gfx::Canvas* canvas) {
462 const gfx::NativeTheme* native_theme = gfx::NativeTheme::instance();
463 gfx::Rect bounds = GetTrackBounds();
464
465 params_.scrollbar_track.track_x = bounds.x();
466 params_.scrollbar_track.track_y = bounds.y();
467 params_.scrollbar_track.track_width = bounds.width();
468 params_.scrollbar_track.track_height = bounds.height();
469
470
471 native_theme->Paint(canvas->AsCanvasSkia(),
472 part_,
473 state_,
474 bounds,
475 params_);
476 }
477
478 void NativeScrollBarViews::ButtonPressed(Button* sender,
479 const views::Event& event) {
480 if (sender == prev_button_) {
481 MoveStep(false);
482 } else if (sender == next_button_) {
483 MoveStep(true);
484 }
485 }
486
487 ////////////////////////////////////////////////////////////////////////////////
488 // NativeScrollBarViews, NativeScrollBarWrapper overrides:
489
490 int NativeScrollBarViews::GetPosition() const {
491 return thumb_->current_position();
492 }
493
494 View* NativeScrollBarViews::GetView() {
495 return this;
496 }
497
498 void NativeScrollBarViews::Update(int viewport_size,
499 int content_size,
500 int current_pos) {
501 thumb_->Update(viewport_size, content_size, current_pos);
502
503 ValueChanged();
504 }
505
506 ////////////////////////////////////////////////////////////////////////////////
507 // NativeScrollBarViews, private:
508
509 void NativeScrollBarViews::ValueChanged() {
510 ScrollBarController* controller = native_scroll_bar_->GetController();
511 controller->ScrollToPosition(native_scroll_bar_, GetPosition());
512 }
513
514 void NativeScrollBarViews::MoveBy(int o) {
515 MoveTo(GetPosition() + o);
516 }
517
518 void NativeScrollBarViews::MovePage(bool positive) {
519 ScrollBarController* controller = native_scroll_bar_->GetController();
520 int scroll_increment = controller->GetScrollIncrement(native_scroll_bar_,
521 true,
522 positive);
523 if (!positive)
524 scroll_increment *= -1;
525
526 MoveBy(scroll_increment);
527 }
528
529 void NativeScrollBarViews::MoveStep(bool positive) {
530 ScrollBarController* controller = native_scroll_bar_->GetController();
531 int scroll_increment = controller->GetScrollIncrement(native_scroll_bar_,
532 false,
533 positive);
534 if (!positive)
535 scroll_increment *= -1;
536
537 MoveBy(scroll_increment);
538 }
539
540 void NativeScrollBarViews::MoveTo(int p) {
541 if (p < native_scroll_bar_->GetMinPosition())
542 p = native_scroll_bar_->GetMinPosition();
543 if (p > native_scroll_bar_->GetMaxPosition())
544 p = native_scroll_bar_->GetMaxPosition();
545
546 thumb_->Update(p);
547 ValueChanged();
548 }
549
550 void NativeScrollBarViews::MoveToBottom() {
551 MoveTo(native_scroll_bar_->GetMaxPosition());
552 }
553
554 gfx::Rect NativeScrollBarViews::GetTrackBounds() const {
555 gfx::Rect bounds = GetLocalBounds();
556 gfx::Size size = prev_button_->GetPreferredSize();
557
558 if (native_scroll_bar_->IsHorizontal()) {
559 bounds.set_x(bounds.x() + size.width());
560 bounds.set_width(bounds.width() - 2 * size.width());
561 bounds.set_height(thumb_->GetPreferredSize().height());
562 } else {
563 bounds.set_y(bounds.y() + size.height());
564 bounds.set_height(bounds.height() - 2 * size.height());
565 bounds.set_width(thumb_->GetPreferredSize().width());
566 }
567
568 return bounds;
569 }
570
571 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698