- add sources.
[platform/framework/web/crosswalk.git] / src / ui / views / controls / scrollbar / bitmap_scroll_bar.cc
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/bitmap_scroll_bar.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/compiler_specific.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "build/build_config.h"
15 #include "grit/ui_strings.h"
16 #include "ui/base/l10n/l10n_util.h"
17 #include "ui/events/keycodes/keyboard_codes.h"
18 #include "ui/gfx/canvas.h"
19 #include "ui/gfx/image/image_skia.h"
20 #include "ui/views/controls/menu/menu.h"
21 #include "ui/views/controls/scroll_view.h"
22 #include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h"
23 #include "ui/views/widget/widget.h"
24
25 #if defined(OS_LINUX)
26 #include "ui/views/screen.h"
27 #endif
28
29 #undef min
30 #undef max
31
32 namespace views {
33
34 namespace {
35
36 // The distance the mouse can be dragged outside the bounds of the thumb during
37 // dragging before the scrollbar will snap back to its regular position.
38 const int kScrollThumbDragOutSnap = 100;
39
40 ///////////////////////////////////////////////////////////////////////////////
41 //
42 // AutorepeatButton
43 //
44 //  A button that activates on mouse pressed rather than released, and that
45 //  continues to fire the clicked action as the mouse button remains pressed
46 //  down on the button.
47 //
48 ///////////////////////////////////////////////////////////////////////////////
49 class AutorepeatButton : public ImageButton {
50  public:
51   explicit AutorepeatButton(ButtonListener* listener)
52       : ImageButton(listener),
53         repeater_(base::Bind(&AutorepeatButton::NotifyClick,
54                              base::Unretained(this))) {
55   }
56   virtual ~AutorepeatButton() {}
57
58  protected:
59   virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
60     Button::NotifyClick(event);
61     repeater_.Start();
62     return true;
63   }
64
65   virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE {
66     OnMouseCaptureLost();
67   }
68
69   virtual void OnMouseCaptureLost() OVERRIDE {
70     repeater_.Stop();
71   }
72
73  private:
74   void NotifyClick() {
75 #if defined(OS_WIN)
76     gfx::Point cursor_point(GetMessagePos());
77 #elif defined(OS_LINUX)
78     gfx::Point cursor_point = gfx::Screen::GetCursorScreenPoint();
79 #endif
80     ui::MouseEvent event(ui::ET_MOUSE_RELEASED,
81                          cursor_point, cursor_point,
82                          ui::EF_LEFT_MOUSE_BUTTON);
83     Button::NotifyClick(event);
84   }
85
86   // The repeat controller that we use to repeatedly click the button when the
87   // mouse button is down.
88   RepeatController repeater_;
89
90   DISALLOW_COPY_AND_ASSIGN(AutorepeatButton);
91 };
92
93 ///////////////////////////////////////////////////////////////////////////////
94 //
95 // BitmapScrollBarThumb
96 //
97 //  A view that acts as the thumb in the scroll bar track that the user can
98 //  drag to scroll the associated contents view within the viewport.
99 //
100 ///////////////////////////////////////////////////////////////////////////////
101 class BitmapScrollBarThumb : public BaseScrollBarThumb {
102  public:
103   explicit BitmapScrollBarThumb(BitmapScrollBar* scroll_bar)
104       : BaseScrollBarThumb(scroll_bar),
105         scroll_bar_(scroll_bar) {
106   }
107   virtual ~BitmapScrollBarThumb() { }
108
109   // View overrides:
110   virtual gfx::Size GetPreferredSize() OVERRIDE {
111     return gfx::Size(background_image()->width(),
112                      start_cap_image()->height() +
113                          end_cap_image()->height() +
114                          grippy_image()->height());
115   }
116
117  protected:
118   // View overrides:
119   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
120     canvas->DrawImageInt(*start_cap_image(), 0, 0);
121     int top_cap_height = start_cap_image()->height();
122     int bottom_cap_height = end_cap_image()->height();
123     int thumb_body_height = height() - top_cap_height - bottom_cap_height;
124     canvas->TileImageInt(*background_image(), 0, top_cap_height,
125                          background_image()->width(), thumb_body_height);
126     canvas->DrawImageInt(*end_cap_image(), 0,
127                          height() - bottom_cap_height);
128
129     // Paint the grippy over the track.
130     int grippy_x = (width() - grippy_image()->width()) / 2;
131     int grippy_y = (thumb_body_height - grippy_image()->height()) / 2;
132     canvas->DrawImageInt(*grippy_image(), grippy_x, grippy_y);
133   }
134
135  private:
136   // Returns the image rendered at the start of the thumb.
137   gfx::ImageSkia* start_cap_image() const {
138     return scroll_bar_->images_[BitmapScrollBar::THUMB_START_CAP][GetState()];
139   }
140
141   // Returns the image rendered at the end of the thumb.
142   gfx::ImageSkia* end_cap_image() const {
143     return scroll_bar_->images_[BitmapScrollBar::THUMB_END_CAP][GetState()];
144   }
145
146   // Returns the image that is tiled in the background of the thumb between
147   // the start and the end caps.
148   gfx::ImageSkia* background_image() const {
149     return scroll_bar_->images_[BitmapScrollBar::THUMB_MIDDLE][GetState()];
150   }
151
152   // Returns the image that is rendered in the middle of the thumb
153   // transparently over the background image.
154   gfx::ImageSkia* grippy_image() const {
155     return scroll_bar_->images_[BitmapScrollBar::THUMB_GRIPPY]
156         [CustomButton::STATE_NORMAL];
157   }
158
159   // The BitmapScrollBar that owns us.
160   BitmapScrollBar* scroll_bar_;
161
162   DISALLOW_COPY_AND_ASSIGN(BitmapScrollBarThumb);
163 };
164
165 }  // namespace
166
167 ///////////////////////////////////////////////////////////////////////////////
168 // BitmapScrollBar, public:
169
170 BitmapScrollBar::BitmapScrollBar(bool horizontal, bool show_scroll_buttons)
171     : BaseScrollBar(horizontal, new BitmapScrollBarThumb(this)),
172       prev_button_(new AutorepeatButton(this)),
173       next_button_(new AutorepeatButton(this)),
174       show_scroll_buttons_(show_scroll_buttons) {
175   if (!show_scroll_buttons_) {
176     prev_button_->SetVisible(false);
177     next_button_->SetVisible(false);
178   }
179
180   AddChildView(prev_button_);
181   AddChildView(next_button_);
182
183   set_context_menu_controller(this);
184   prev_button_->set_context_menu_controller(this);
185   next_button_->set_context_menu_controller(this);
186 }
187
188 void BitmapScrollBar::SetImage(ScrollBarPart part,
189                                CustomButton::ButtonState state,
190                                gfx::ImageSkia* image_skia) {
191   DCHECK(part < PART_COUNT);
192   DCHECK(state < CustomButton::STATE_COUNT);
193   switch (part) {
194     case PREV_BUTTON:
195       prev_button_->SetImage(state, image_skia);
196       break;
197     case NEXT_BUTTON:
198       next_button_->SetImage(state, image_skia);
199       break;
200     case THUMB_START_CAP:
201     case THUMB_MIDDLE:
202     case THUMB_END_CAP:
203     case THUMB_GRIPPY:
204     case THUMB_TRACK:
205       images_[part][state] = image_skia;
206       break;
207   }
208 }
209
210 int BitmapScrollBar::GetLayoutSize() const {
211   gfx::Size prefsize = prev_button_->GetPreferredSize();
212   return IsHorizontal() ? prefsize.height() : prefsize.width();
213 }
214
215 gfx::Rect BitmapScrollBar::GetTrackBounds() const {
216   gfx::Size prefsize = prev_button_->GetPreferredSize();
217   if (IsHorizontal()) {
218     if (!show_scroll_buttons_)
219       prefsize.set_width(0);
220     int new_width =
221         std::max(0, width() - (prefsize.width() * 2));
222     gfx::Rect track_bounds(prefsize.width(), 0, new_width, prefsize.height());
223     return track_bounds;
224   }
225   if (!show_scroll_buttons_)
226     prefsize.set_height(0);
227   gfx::Rect track_bounds(0, prefsize.height(), prefsize.width(),
228                          std::max(0, height() - (prefsize.height() * 2)));
229   return track_bounds;
230 }
231
232 ///////////////////////////////////////////////////////////////////////////////
233 // BitmapScrollBar, View implementation:
234
235 gfx::Size BitmapScrollBar::GetPreferredSize() {
236   // In this case, we're returning the desired width of the scrollbar and its
237   // minimum allowable height.
238   gfx::Size button_prefsize = prev_button_->GetPreferredSize();
239   return gfx::Size(button_prefsize.width(), button_prefsize.height() * 2);
240 }
241
242 void BitmapScrollBar::Layout() {
243   // Size and place the two scroll buttons.
244   if (show_scroll_buttons_) {
245     gfx::Size prefsize = prev_button_->GetPreferredSize();
246     prev_button_->SetBounds(0, 0, prefsize.width(), prefsize.height());
247     prefsize = next_button_->GetPreferredSize();
248     if (IsHorizontal()) {
249       next_button_->SetBounds(width() - prefsize.width(), 0, prefsize.width(),
250                               prefsize.height());
251     } else {
252       next_button_->SetBounds(0, height() - prefsize.height(), prefsize.width(),
253                               prefsize.height());
254     }
255   } else {
256     prev_button_->SetBounds(0, 0, 0, 0);
257     next_button_->SetBounds(0, 0, 0, 0);
258   }
259
260   BaseScrollBarThumb* thumb = GetThumb();
261   // Size and place the thumb
262   gfx::Size thumb_prefsize = thumb->GetPreferredSize();
263   gfx::Rect track_bounds = GetTrackBounds();
264
265   // Preserve the height/width of the thumb (depending on orientation) as set
266   // by the last call to |Update|, but coerce the width/height to be the
267   // appropriate value for the images provided.
268   if (IsHorizontal()) {
269     thumb->SetBounds(thumb->x(), thumb->y(), thumb->width(),
270                       thumb_prefsize.height());
271   } else {
272     thumb->SetBounds(thumb->x(), thumb->y(), thumb_prefsize.width(),
273                      thumb->height());
274   }
275
276   // Hide the thumb if the track isn't tall enough to display even a tiny
277   // thumb. The user can only use the mousewheel, scroll buttons or keyboard
278   // in this scenario.
279   if ((IsHorizontal() && (track_bounds.width() < thumb_prefsize.width()) ||
280       (!IsHorizontal() && (track_bounds.height() < thumb_prefsize.height())))) {
281     thumb->SetVisible(false);
282   } else if (!thumb->visible()) {
283     thumb->SetVisible(true);
284   }
285 }
286
287 ///////////////////////////////////////////////////////////////////////////////
288 // BitmapScrollBar, View implementation:
289
290 void BitmapScrollBar::OnPaint(gfx::Canvas* canvas) {
291   // Paint the track.
292   gfx::Rect track_bounds = GetTrackBounds();
293   canvas->TileImageInt(*images_[THUMB_TRACK][GetThumbTrackState()],
294                        track_bounds.x(), track_bounds.y(),
295                        track_bounds.width(), track_bounds.height());
296 }
297
298 ///////////////////////////////////////////////////////////////////////////////
299 // BitmapScrollBar, ButtonListener implementation:
300
301 void BitmapScrollBar::ButtonPressed(Button* sender, const ui::Event& event) {
302   if (sender == prev_button_) {
303     ScrollByAmount(SCROLL_PREV_LINE);
304   } else if (sender == next_button_) {
305     ScrollByAmount(SCROLL_NEXT_LINE);
306   }
307 }
308
309 }  // namespace views