OLD | NEW |
---|---|
(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. | |
Ben Goodger (Google)
2011/08/23 16:05:15
Is this code identical to BitmapScrollBar with the
| |
4 | |
5 #include "views/controls/scrollbar/base_scroll_bar.h" | |
6 | |
7 #if defined(OS_LINUX) | |
8 #include "ui/gfx/screen.h" | |
9 #endif | |
10 | |
11 #include "base/callback.h" | |
12 #include "base/compiler_specific.h" | |
13 #include "base/message_loop.h" | |
14 #include "base/string16.h" | |
15 #include "base/utf_string_conversions.h" | |
16 #include "grit/ui_strings.h" | |
17 #include "ui/base/keycodes/keyboard_codes.h" | |
18 #include "ui/base/l10n/l10n_util.h" | |
19 #include "ui/gfx/canvas.h" | |
20 #include "views/controls/menu/menu.h" | |
21 #include "views/controls/scrollbar/base_scroll_bar_thumb.h" | |
22 #include "views/controls/scroll_view.h" | |
23 #include "views/widget/widget.h" | |
24 | |
25 #undef min | |
26 #undef max | |
27 | |
28 namespace views { | |
29 | |
30 /////////////////////////////////////////////////////////////////////////////// | |
31 // BaseScrollBar, public: | |
32 | |
33 BaseScrollBar::BaseScrollBar(bool horizontal, BaseScrollBarThumb* thumb) | |
34 : ScrollBar(horizontal), | |
35 thumb_(thumb), | |
36 contents_size_(0), | |
37 contents_scroll_offset_(0), | |
38 thumb_track_state_(CustomButton::BS_NORMAL), | |
39 last_scroll_amount_(SCROLL_NONE), | |
40 ALLOW_THIS_IN_INITIALIZER_LIST(repeater_( | |
41 NewCallback<BaseScrollBar>(this, | |
42 &BaseScrollBar::TrackClicked))), | |
43 context_menu_mouse_position_(0) { | |
44 | |
45 AddChildView(thumb_); | |
46 | |
47 set_context_menu_controller(this); | |
48 thumb_->set_context_menu_controller(this); | |
49 } | |
50 | |
51 void BaseScrollBar::ScrollByAmount(ScrollAmount amount) { | |
52 int offset = contents_scroll_offset_; | |
53 switch (amount) { | |
54 case SCROLL_START: | |
55 offset = GetMinPosition(); | |
56 break; | |
57 case SCROLL_END: | |
58 offset = GetMaxPosition(); | |
59 break; | |
60 case SCROLL_PREV_LINE: | |
61 offset -= GetScrollIncrement(false, false); | |
62 offset = std::max(GetMinPosition(), offset); | |
63 break; | |
64 case SCROLL_NEXT_LINE: | |
65 offset += GetScrollIncrement(false, true); | |
66 offset = std::min(GetMaxPosition(), offset); | |
67 break; | |
68 case SCROLL_PREV_PAGE: | |
69 offset -= GetScrollIncrement(true, false); | |
70 offset = std::max(GetMinPosition(), offset); | |
71 break; | |
72 case SCROLL_NEXT_PAGE: | |
73 offset += GetScrollIncrement(true, true); | |
74 offset = std::min(GetMaxPosition(), offset); | |
75 break; | |
76 default: | |
77 break; | |
78 } | |
79 contents_scroll_offset_ = offset; | |
80 ScrollContentsToOffset(); | |
81 } | |
82 | |
83 void BaseScrollBar::ScrollToThumbPosition(int thumb_position, | |
84 bool scroll_to_middle) { | |
85 contents_scroll_offset_ = | |
86 CalculateContentsOffset(thumb_position, scroll_to_middle); | |
87 if (contents_scroll_offset_ < GetMinPosition()) { | |
88 contents_scroll_offset_ = GetMinPosition(); | |
89 } else if (contents_scroll_offset_ > GetMaxPosition()) { | |
90 contents_scroll_offset_ = GetMaxPosition(); | |
91 } | |
92 ScrollContentsToOffset(); | |
93 SchedulePaint(); | |
94 } | |
95 | |
96 void BaseScrollBar::ScrollByContentsOffset(int contents_offset) { | |
97 contents_scroll_offset_ -= contents_offset; | |
98 if (contents_scroll_offset_ < GetMinPosition()) { | |
99 contents_scroll_offset_ = GetMinPosition(); | |
100 } else if (contents_scroll_offset_ > GetMaxPosition()) { | |
101 contents_scroll_offset_ = GetMaxPosition(); | |
102 } | |
103 ScrollContentsToOffset(); | |
104 } | |
105 | |
106 /////////////////////////////////////////////////////////////////////////////// | |
107 // BaseScrollBar, View implementation: | |
108 | |
109 bool BaseScrollBar::OnMousePressed(const MouseEvent& event) { | |
110 if (event.IsOnlyLeftMouseButton()) { | |
111 SetThumbTrackState(CustomButton::BS_PUSHED); | |
112 gfx::Rect thumb_bounds = thumb_->bounds(); | |
113 if (IsHorizontal()) { | |
114 if (event.x() < thumb_bounds.x()) { | |
115 last_scroll_amount_ = SCROLL_PREV_PAGE; | |
116 } else if (event.x() > thumb_bounds.right()) { | |
117 last_scroll_amount_ = SCROLL_NEXT_PAGE; | |
118 } | |
119 } else { | |
120 if (event.y() < thumb_bounds.y()) { | |
121 last_scroll_amount_ = SCROLL_PREV_PAGE; | |
122 } else if (event.y() > thumb_bounds.bottom()) { | |
123 last_scroll_amount_ = SCROLL_NEXT_PAGE; | |
124 } | |
125 } | |
126 TrackClicked(); | |
127 repeater_.Start(); | |
128 } | |
129 return true; | |
130 } | |
131 | |
132 void BaseScrollBar::OnMouseReleased(const MouseEvent& event) { | |
133 OnMouseCaptureLost(); | |
134 } | |
135 | |
136 void BaseScrollBar::OnMouseCaptureLost() { | |
137 SetThumbTrackState(CustomButton::BS_NORMAL); | |
138 repeater_.Stop(); | |
139 } | |
140 | |
141 bool BaseScrollBar::OnKeyPressed(const KeyEvent& event) { | |
142 ScrollAmount amount = SCROLL_NONE; | |
143 switch (event.key_code()) { | |
144 case ui::VKEY_UP: | |
145 if (!IsHorizontal()) | |
146 amount = SCROLL_PREV_LINE; | |
147 break; | |
148 case ui::VKEY_DOWN: | |
149 if (!IsHorizontal()) | |
150 amount = SCROLL_NEXT_LINE; | |
151 break; | |
152 case ui::VKEY_LEFT: | |
153 if (IsHorizontal()) | |
154 amount = SCROLL_PREV_LINE; | |
155 break; | |
156 case ui::VKEY_RIGHT: | |
157 if (IsHorizontal()) | |
158 amount = SCROLL_NEXT_LINE; | |
159 break; | |
160 case ui::VKEY_PRIOR: | |
161 amount = SCROLL_PREV_PAGE; | |
162 break; | |
163 case ui::VKEY_NEXT: | |
164 amount = SCROLL_NEXT_PAGE; | |
165 break; | |
166 case ui::VKEY_HOME: | |
167 amount = SCROLL_START; | |
168 break; | |
169 case ui::VKEY_END: | |
170 amount = SCROLL_END; | |
171 break; | |
172 default: | |
173 break; | |
174 } | |
175 if (amount != SCROLL_NONE) { | |
176 ScrollByAmount(amount); | |
177 return true; | |
178 } | |
179 return false; | |
180 } | |
181 | |
182 bool BaseScrollBar::OnMouseWheel(const MouseWheelEvent& event) { | |
183 ScrollByContentsOffset(event.offset()); | |
184 return true; | |
185 } | |
186 | |
187 /////////////////////////////////////////////////////////////////////////////// | |
188 // BaseScrollBar, ContextMenuController implementation: | |
189 | |
190 enum ScrollBarContextMenuCommands { | |
191 ScrollBarContextMenuCommand_ScrollHere = 1, | |
192 ScrollBarContextMenuCommand_ScrollStart, | |
193 ScrollBarContextMenuCommand_ScrollEnd, | |
194 ScrollBarContextMenuCommand_ScrollPageUp, | |
195 ScrollBarContextMenuCommand_ScrollPageDown, | |
196 ScrollBarContextMenuCommand_ScrollPrev, | |
197 ScrollBarContextMenuCommand_ScrollNext | |
198 }; | |
199 | |
200 void BaseScrollBar::ShowContextMenuForView(View* source, | |
201 const gfx::Point& p, | |
202 bool is_mouse_gesture) { | |
203 Widget* widget = GetWidget(); | |
204 gfx::Rect widget_bounds = widget->GetWindowScreenBounds(); | |
205 gfx::Point temp_pt(p.x() - widget_bounds.x(), p.y() - widget_bounds.y()); | |
206 View::ConvertPointFromWidget(this, &temp_pt); | |
207 context_menu_mouse_position_ = IsHorizontal() ? temp_pt.x() : temp_pt.y(); | |
208 | |
209 scoped_ptr<Menu> menu( | |
210 Menu::Create(this, Menu::TOPLEFT, GetWidget()->GetNativeView())); | |
211 menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollHere); | |
212 menu->AppendSeparator(); | |
213 menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollStart); | |
214 menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollEnd); | |
215 menu->AppendSeparator(); | |
216 menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPageUp); | |
217 menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPageDown); | |
218 menu->AppendSeparator(); | |
219 menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollPrev); | |
220 menu->AppendDelegateMenuItem(ScrollBarContextMenuCommand_ScrollNext); | |
221 menu->RunMenuAt(p.x(), p.y()); | |
222 } | |
223 | |
224 /////////////////////////////////////////////////////////////////////////////// | |
225 // BaseScrollBar, Menu::Delegate implementation: | |
226 | |
227 std::wstring BaseScrollBar::GetLabel(int id) const { | |
228 int ids_value = 0; | |
229 switch (id) { | |
230 case ScrollBarContextMenuCommand_ScrollHere: | |
231 ids_value = IDS_APP_SCROLLBAR_CXMENU_SCROLLHERE; | |
232 break; | |
233 case ScrollBarContextMenuCommand_ScrollStart: | |
234 ids_value = IsHorizontal() ? IDS_APP_SCROLLBAR_CXMENU_SCROLLLEFTEDGE | |
235 : IDS_APP_SCROLLBAR_CXMENU_SCROLLHOME; | |
236 break; | |
237 case ScrollBarContextMenuCommand_ScrollEnd: | |
238 ids_value = IsHorizontal() ? IDS_APP_SCROLLBAR_CXMENU_SCROLLRIGHTEDGE | |
239 : IDS_APP_SCROLLBAR_CXMENU_SCROLLEND; | |
240 break; | |
241 case ScrollBarContextMenuCommand_ScrollPageUp: | |
242 ids_value = IDS_APP_SCROLLBAR_CXMENU_SCROLLPAGEUP; | |
243 break; | |
244 case ScrollBarContextMenuCommand_ScrollPageDown: | |
245 ids_value = IDS_APP_SCROLLBAR_CXMENU_SCROLLPAGEDOWN; | |
246 break; | |
247 case ScrollBarContextMenuCommand_ScrollPrev: | |
248 ids_value = IsHorizontal() ? IDS_APP_SCROLLBAR_CXMENU_SCROLLLEFT | |
249 : IDS_APP_SCROLLBAR_CXMENU_SCROLLUP; | |
250 break; | |
251 case ScrollBarContextMenuCommand_ScrollNext: | |
252 ids_value = IsHorizontal() ? IDS_APP_SCROLLBAR_CXMENU_SCROLLRIGHT | |
253 : IDS_APP_SCROLLBAR_CXMENU_SCROLLDOWN; | |
254 break; | |
255 default: | |
256 NOTREACHED() << "Invalid BaseScrollBar Context Menu command!"; | |
257 } | |
258 | |
259 return ids_value ? UTF16ToWide(l10n_util::GetStringUTF16(ids_value)) : L""; | |
260 } | |
261 | |
262 bool BaseScrollBar::IsCommandEnabled(int id) const { | |
263 switch (id) { | |
264 case ScrollBarContextMenuCommand_ScrollPageUp: | |
265 case ScrollBarContextMenuCommand_ScrollPageDown: | |
266 return !IsHorizontal(); | |
267 } | |
268 return true; | |
269 } | |
270 | |
271 void BaseScrollBar::ExecuteCommand(int id) { | |
272 switch (id) { | |
273 case ScrollBarContextMenuCommand_ScrollHere: | |
274 ScrollToThumbPosition(context_menu_mouse_position_, true); | |
275 break; | |
276 case ScrollBarContextMenuCommand_ScrollStart: | |
277 ScrollByAmount(SCROLL_START); | |
278 break; | |
279 case ScrollBarContextMenuCommand_ScrollEnd: | |
280 ScrollByAmount(SCROLL_END); | |
281 break; | |
282 case ScrollBarContextMenuCommand_ScrollPageUp: | |
283 ScrollByAmount(SCROLL_PREV_PAGE); | |
284 break; | |
285 case ScrollBarContextMenuCommand_ScrollPageDown: | |
286 ScrollByAmount(SCROLL_NEXT_PAGE); | |
287 break; | |
288 case ScrollBarContextMenuCommand_ScrollPrev: | |
289 ScrollByAmount(SCROLL_PREV_LINE); | |
290 break; | |
291 case ScrollBarContextMenuCommand_ScrollNext: | |
292 ScrollByAmount(SCROLL_NEXT_LINE); | |
293 break; | |
294 } | |
295 } | |
296 | |
297 /////////////////////////////////////////////////////////////////////////////// | |
298 // BaseScrollBar, ScrollBar implementation: | |
299 | |
300 void BaseScrollBar::Update(int viewport_size, int content_size, | |
301 int contents_scroll_offset) { | |
302 ScrollBar::Update(viewport_size, content_size, contents_scroll_offset); | |
303 | |
304 // Make sure contents_size is always > 0 to avoid divide by zero errors in | |
305 // calculations throughout this code. | |
306 contents_size_ = std::max(1, content_size); | |
307 | |
308 if (content_size < 0) | |
309 content_size = 0; | |
310 if (contents_scroll_offset < 0) | |
311 contents_scroll_offset = 0; | |
312 if (contents_scroll_offset > content_size) | |
313 contents_scroll_offset = content_size; | |
314 | |
315 // Thumb Height and Thumb Pos. | |
316 // The height of the thumb is the ratio of the Viewport height to the | |
317 // content size multiplied by the height of the thumb track. | |
318 double ratio = static_cast<double>(viewport_size) / contents_size_; | |
319 int thumb_size = static_cast<int>(ratio * GetTrackSize()); | |
320 thumb_->SetSize(thumb_size); | |
321 | |
322 int thumb_position = CalculateThumbPosition(contents_scroll_offset); | |
323 thumb_->SetPosition(thumb_position); | |
324 } | |
325 | |
326 int BaseScrollBar::GetPosition() const { | |
327 return thumb_->GetPosition(); | |
328 } | |
329 | |
330 /////////////////////////////////////////////////////////////////////////////// | |
331 // BaseScrollBar, protected: | |
332 | |
333 BaseScrollBarThumb* BaseScrollBar::GetThumb() const { | |
334 return thumb_; | |
335 } | |
336 | |
337 void BaseScrollBar::ScrollToPosition(int position) { | |
338 GetController()->ScrollToPosition(this, position); | |
339 } | |
340 | |
341 int BaseScrollBar::GetScrollIncrement(bool is_page, bool is_positive) { | |
342 return GetController()->GetScrollIncrement(this, is_page, is_positive); | |
343 } | |
344 | |
345 | |
346 /////////////////////////////////////////////////////////////////////////////// | |
347 // BaseScrollBar, private: | |
348 | |
349 void BaseScrollBar::TrackClicked() { | |
350 if (last_scroll_amount_ != SCROLL_NONE) | |
351 ScrollByAmount(last_scroll_amount_); | |
352 } | |
353 | |
354 void BaseScrollBar::ScrollContentsToOffset() { | |
355 ScrollToPosition(contents_scroll_offset_); | |
356 thumb_->SetPosition(CalculateThumbPosition(contents_scroll_offset_)); | |
357 } | |
358 | |
359 int BaseScrollBar::GetTrackSize() const { | |
360 gfx::Rect track_bounds = GetTrackBounds(); | |
361 return IsHorizontal() ? track_bounds.width() : track_bounds.height(); | |
362 } | |
363 | |
364 int BaseScrollBar::CalculateThumbPosition(int contents_scroll_offset) const { | |
365 return (contents_scroll_offset * GetTrackSize()) / contents_size_; | |
366 } | |
367 | |
368 int BaseScrollBar::CalculateContentsOffset(int thumb_position, | |
369 bool scroll_to_middle) const { | |
370 if (scroll_to_middle) | |
371 thumb_position = thumb_position - (thumb_->GetSize() / 2); | |
372 return (thumb_position * contents_size_) / GetTrackSize(); | |
373 } | |
374 | |
375 void BaseScrollBar::SetThumbTrackState(CustomButton::ButtonState state) { | |
376 thumb_track_state_ = state; | |
377 SchedulePaint(); | |
378 } | |
379 | |
380 } // namespace views | |
OLD | NEW |