OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/gtk/download_shelf_gtk.h" | 5 #include "chrome/browser/ui/gtk/download_shelf_gtk.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "chrome/browser/download/download_item.h" | 9 #include "chrome/browser/download/download_item.h" |
10 #include "chrome/browser/download/download_item_model.h" | 10 #include "chrome/browser/download/download_item_model.h" |
11 #include "chrome/browser/download/download_util.h" | 11 #include "chrome/browser/download/download_util.h" |
12 #include "chrome/browser/ui/browser.h" | 12 #include "chrome/browser/ui/browser.h" |
13 #include "chrome/browser/ui/gtk/browser_window_gtk.h" | 13 #include "chrome/browser/ui/gtk/browser_window_gtk.h" |
14 #include "chrome/browser/ui/gtk/custom_button.h" | 14 #include "chrome/browser/ui/gtk/custom_button.h" |
15 #include "chrome/browser/ui/gtk/download_item_gtk.h" | 15 #include "chrome/browser/ui/gtk/download_item_gtk.h" |
16 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h" | 16 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h" |
17 #include "chrome/browser/ui/gtk/gtk_chrome_shrinkable_hbox.h" | 17 #include "chrome/browser/ui/gtk/gtk_chrome_shrinkable_hbox.h" |
18 #include "chrome/browser/ui/gtk/gtk_theme_provider.h" | 18 #include "chrome/browser/ui/gtk/gtk_theme_provider.h" |
19 #include "chrome/browser/ui/gtk/gtk_util.h" | 19 #include "chrome/browser/ui/gtk/gtk_util.h" |
20 #include "chrome/common/notification_service.h" | 20 #include "chrome/common/notification_service.h" |
21 #include "gfx/gtk_util.h" | 21 #include "gfx/gtk_util.h" |
| 22 #include "gfx/insets.h" |
| 23 #include "gfx/point.h" |
| 24 #include "gfx/rect.h" |
22 #include "grit/generated_resources.h" | 25 #include "grit/generated_resources.h" |
23 #include "grit/theme_resources.h" | 26 #include "grit/theme_resources.h" |
24 #include "ui/base/l10n/l10n_util.h" | 27 #include "ui/base/l10n/l10n_util.h" |
25 #include "ui/base/resource/resource_bundle.h" | 28 #include "ui/base/resource/resource_bundle.h" |
26 | 29 |
27 namespace { | 30 namespace { |
28 | 31 |
29 // The height of the download items. | 32 // The height of the download items. |
30 const int kDownloadItemHeight = download_util::kSmallProgressIconSize; | 33 const int kDownloadItemHeight = download_util::kSmallProgressIconSize; |
31 | 34 |
32 // Padding between the download widgets. | 35 // Padding between the download widgets. |
33 const int kDownloadItemPadding = 10; | 36 const int kDownloadItemPadding = 10; |
34 | 37 |
35 // Padding between the top/bottom of the download widgets and the edge of the | 38 // Padding between the top/bottom of the download widgets and the edge of the |
36 // shelf. | 39 // shelf. |
37 const int kTopBottomPadding = 4; | 40 const int kTopBottomPadding = 4; |
38 | 41 |
39 // Padding between the left side of the shelf and the first download item. | 42 // Padding between the left side of the shelf and the first download item. |
40 const int kLeftPadding = 2; | 43 const int kLeftPadding = 2; |
41 | 44 |
42 // Padding between the right side of the shelf and the close button. | 45 // Padding between the right side of the shelf and the close button. |
43 const int kRightPadding = 10; | 46 const int kRightPadding = 10; |
44 | 47 |
45 // Speed of the shelf show/hide animation. | 48 // Speed of the shelf show/hide animation. |
46 const int kShelfAnimationDurationMs = 120; | 49 const int kShelfAnimationDurationMs = 120; |
47 | 50 |
| 51 // The time between when the user mouses out of the download shelf zone and |
| 52 // when the shelf closes (when auto-close is enabled). |
| 53 const int kAutoCloseDelayMs = 300; |
| 54 |
| 55 // The area to the top of the shelf that is considered part of its "zone". |
| 56 const int kShelfAuraSize = 40; |
| 57 |
48 } // namespace | 58 } // namespace |
49 | 59 |
50 DownloadShelfGtk::DownloadShelfGtk(Browser* browser, GtkWidget* parent) | 60 DownloadShelfGtk::DownloadShelfGtk(Browser* browser, GtkWidget* parent) |
51 : browser_(browser), | 61 : browser_(browser), |
52 is_showing_(false), | 62 is_showing_(false), |
53 theme_provider_(GtkThemeProvider::GetFrom(browser->profile())) { | 63 theme_provider_(GtkThemeProvider::GetFrom(browser->profile())), |
| 64 close_on_mouse_out_(false), |
| 65 mouse_in_shelf_(false), |
| 66 auto_close_factory_(this) { |
54 // Logically, the shelf is a vbox that contains two children: a one pixel | 67 // Logically, the shelf is a vbox that contains two children: a one pixel |
55 // tall event box, which serves as the top border, and an hbox, which holds | 68 // tall event box, which serves as the top border, and an hbox, which holds |
56 // the download items and other shelf widgets (close button, show-all- | 69 // the download items and other shelf widgets (close button, show-all- |
57 // downloads link). | 70 // downloads link). |
58 // To make things pretty, we have to add a few more widgets. To get padding | 71 // To make things pretty, we have to add a few more widgets. To get padding |
59 // right, we stick the hbox in an alignment. We put that alignment in an | 72 // right, we stick the hbox in an alignment. We put that alignment in an |
60 // event box so we can color the background. | 73 // event box so we can color the background. |
61 | 74 |
62 // Create the top border. | 75 // Create the top border. |
63 top_border_ = gtk_event_box_new(); | 76 top_border_ = gtk_event_box_new(); |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
142 } | 155 } |
143 | 156 |
144 DownloadShelfGtk::~DownloadShelfGtk() { | 157 DownloadShelfGtk::~DownloadShelfGtk() { |
145 for (std::vector<DownloadItemGtk*>::iterator iter = download_items_.begin(); | 158 for (std::vector<DownloadItemGtk*>::iterator iter = download_items_.begin(); |
146 iter != download_items_.end(); ++iter) { | 159 iter != download_items_.end(); ++iter) { |
147 delete *iter; | 160 delete *iter; |
148 } | 161 } |
149 | 162 |
150 shelf_.Destroy(); | 163 shelf_.Destroy(); |
151 items_hbox_.Destroy(); | 164 items_hbox_.Destroy(); |
| 165 |
| 166 // Make sure we're no longer an observer of the message loop. |
| 167 SetCloseOnMouseOut(false); |
152 } | 168 } |
153 | 169 |
154 void DownloadShelfGtk::AddDownload(BaseDownloadItemModel* download_model_) { | 170 void DownloadShelfGtk::AddDownload(BaseDownloadItemModel* download_model_) { |
155 download_items_.push_back(new DownloadItemGtk(this, download_model_)); | 171 download_items_.push_back(new DownloadItemGtk(this, download_model_)); |
156 Show(); | 172 Show(); |
157 } | 173 } |
158 | 174 |
159 bool DownloadShelfGtk::IsShowing() const { | 175 bool DownloadShelfGtk::IsShowing() const { |
160 return slide_widget_->IsShowing(); | 176 return slide_widget_->IsShowing(); |
161 } | 177 } |
162 | 178 |
163 bool DownloadShelfGtk::IsClosing() const { | 179 bool DownloadShelfGtk::IsClosing() const { |
164 return slide_widget_->IsClosing(); | 180 return slide_widget_->IsClosing(); |
165 } | 181 } |
166 | 182 |
167 void DownloadShelfGtk::Show() { | 183 void DownloadShelfGtk::Show() { |
168 slide_widget_->Open(); | 184 slide_widget_->Open(); |
169 browser_->UpdateDownloadShelfVisibility(true); | 185 browser_->UpdateDownloadShelfVisibility(true); |
| 186 CancelAutoClose(); |
170 } | 187 } |
171 | 188 |
172 void DownloadShelfGtk::Close() { | 189 void DownloadShelfGtk::Close() { |
173 // When we are closing, we can vertically overlap the render view. Make sure | 190 // When we are closing, we can vertically overlap the render view. Make sure |
174 // we are on top. | 191 // we are on top. |
175 gdk_window_raise(shelf_.get()->window); | 192 gdk_window_raise(shelf_.get()->window); |
176 slide_widget_->Close(); | 193 slide_widget_->Close(); |
177 browser_->UpdateDownloadShelfVisibility(false); | 194 browser_->UpdateDownloadShelfVisibility(false); |
| 195 SetCloseOnMouseOut(false); |
178 } | 196 } |
179 | 197 |
180 Browser* DownloadShelfGtk::browser() const { | 198 Browser* DownloadShelfGtk::browser() const { |
181 return browser_; | 199 return browser_; |
182 } | 200 } |
183 | 201 |
184 void DownloadShelfGtk::Closed() { | 202 void DownloadShelfGtk::Closed() { |
185 // When the close animation is complete, remove all completed downloads. | 203 // When the close animation is complete, remove all completed downloads. |
186 size_t i = 0; | 204 size_t i = 0; |
187 while (i < download_items_.size()) { | 205 while (i < download_items_.size()) { |
188 DownloadItem* download = download_items_[i]->get_download(); | 206 DownloadItem* download = download_items_[i]->get_download(); |
189 bool is_transfer_done = download->state() == DownloadItem::COMPLETE || | 207 bool is_transfer_done = download->state() == DownloadItem::COMPLETE || |
190 download->state() == DownloadItem::CANCELLED; | 208 download->state() == DownloadItem::CANCELLED; |
191 if (is_transfer_done && | 209 if (is_transfer_done && |
192 download->safety_state() != DownloadItem::DANGEROUS) { | 210 download->safety_state() != DownloadItem::DANGEROUS) { |
193 RemoveDownloadItem(download_items_[i]); | 211 RemoveDownloadItem(download_items_[i]); |
194 } else { | 212 } else { |
| 213 // We set all remaining items as "opened", so that the shelf will auto- |
| 214 // close in the future without the user clicking on them. |
| 215 download->set_opened(true); |
195 ++i; | 216 ++i; |
196 } | 217 } |
197 } | 218 } |
198 } | 219 } |
199 | 220 |
200 void DownloadShelfGtk::Observe(NotificationType type, | 221 void DownloadShelfGtk::Observe(NotificationType type, |
201 const NotificationSource& source, | 222 const NotificationSource& source, |
202 const NotificationDetails& details) { | 223 const NotificationDetails& details) { |
203 if (type == NotificationType::BROWSER_THEME_CHANGED) { | 224 if (type == NotificationType::BROWSER_THEME_CHANGED) { |
204 GdkColor color = theme_provider_->GetGdkColor( | 225 GdkColor color = theme_provider_->GetGdkColor( |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
239 void DownloadShelfGtk::RemoveDownloadItem(DownloadItemGtk* download_item) { | 260 void DownloadShelfGtk::RemoveDownloadItem(DownloadItemGtk* download_item) { |
240 DCHECK(download_item); | 261 DCHECK(download_item); |
241 std::vector<DownloadItemGtk*>::iterator i = | 262 std::vector<DownloadItemGtk*>::iterator i = |
242 find(download_items_.begin(), download_items_.end(), download_item); | 263 find(download_items_.begin(), download_items_.end(), download_item); |
243 DCHECK(i != download_items_.end()); | 264 DCHECK(i != download_items_.end()); |
244 download_items_.erase(i); | 265 download_items_.erase(i); |
245 delete download_item; | 266 delete download_item; |
246 if (download_items_.empty()) { | 267 if (download_items_.empty()) { |
247 slide_widget_->CloseWithoutAnimation(); | 268 slide_widget_->CloseWithoutAnimation(); |
248 browser_->UpdateDownloadShelfVisibility(false); | 269 browser_->UpdateDownloadShelfVisibility(false); |
| 270 } else { |
| 271 AutoCloseIfPossible(); |
249 } | 272 } |
250 } | 273 } |
251 | 274 |
252 GtkWidget* DownloadShelfGtk::GetHBox() const { | 275 GtkWidget* DownloadShelfGtk::GetHBox() const { |
253 return items_hbox_.get(); | 276 return items_hbox_.get(); |
254 } | 277 } |
255 | 278 |
256 void DownloadShelfGtk::MaybeShowMoreDownloadItems() { | 279 void DownloadShelfGtk::MaybeShowMoreDownloadItems() { |
257 // Show all existing download items. It'll trigger "size-allocate" signal, | 280 // Show all existing download items. It'll trigger "size-allocate" signal, |
258 // which will hide download items that don't have enough space to show. | 281 // which will hide download items that don't have enough space to show. |
259 gtk_widget_show_all(items_hbox_.get()); | 282 gtk_widget_show_all(items_hbox_.get()); |
260 } | 283 } |
261 | 284 |
262 void DownloadShelfGtk::OnButtonClick(GtkWidget* button) { | 285 void DownloadShelfGtk::OnButtonClick(GtkWidget* button) { |
263 if (button == close_button_->widget()) { | 286 if (button == close_button_->widget()) { |
264 Close(); | 287 Close(); |
265 } else { | 288 } else { |
266 // The link button was clicked. | 289 // The link button was clicked. |
267 browser_->ShowDownloadsTab(); | 290 browser_->ShowDownloadsTab(); |
268 } | 291 } |
269 } | 292 } |
| 293 |
| 294 void DownloadShelfGtk::AutoCloseIfPossible() { |
| 295 for (std::vector<DownloadItemGtk*>::iterator iter = download_items_.begin(); |
| 296 iter != download_items_.end(); ++iter) { |
| 297 if (!(*iter)->get_download()->opened()) |
| 298 return; |
| 299 } |
| 300 |
| 301 SetCloseOnMouseOut(true); |
| 302 } |
| 303 |
| 304 void DownloadShelfGtk::CancelAutoClose() { |
| 305 SetCloseOnMouseOut(false); |
| 306 auto_close_factory_.RevokeAll(); |
| 307 } |
| 308 |
| 309 void DownloadShelfGtk::ItemOpened() { |
| 310 AutoCloseIfPossible(); |
| 311 } |
| 312 |
| 313 void DownloadShelfGtk::SetCloseOnMouseOut(bool close) { |
| 314 if (close_on_mouse_out_ == close) |
| 315 return; |
| 316 |
| 317 close_on_mouse_out_ = close; |
| 318 mouse_in_shelf_ = close; |
| 319 if (close) |
| 320 MessageLoopForUI::current()->AddObserver(this); |
| 321 else |
| 322 MessageLoopForUI::current()->RemoveObserver(this); |
| 323 } |
| 324 |
| 325 void DownloadShelfGtk::WillProcessEvent(GdkEvent* event) { |
| 326 } |
| 327 |
| 328 void DownloadShelfGtk::DidProcessEvent(GdkEvent* event) { |
| 329 gfx::Point cursor_screen_coords; |
| 330 |
| 331 switch (event->type) { |
| 332 case GDK_MOTION_NOTIFY: |
| 333 cursor_screen_coords = |
| 334 gfx::Point(event->motion.x_root, event->motion.y_root); |
| 335 break; |
| 336 case GDK_LEAVE_NOTIFY: |
| 337 cursor_screen_coords = |
| 338 gfx::Point(event->crossing.x_root, event->crossing.y_root); |
| 339 break; |
| 340 default: |
| 341 return; |
| 342 } |
| 343 |
| 344 bool mouse_in_shelf = IsCursorInShelfZone(cursor_screen_coords); |
| 345 if (mouse_in_shelf == mouse_in_shelf_) |
| 346 return; |
| 347 mouse_in_shelf_ = mouse_in_shelf; |
| 348 |
| 349 if (mouse_in_shelf) |
| 350 MouseEnteredShelf(); |
| 351 else |
| 352 MouseLeftShelf(); |
| 353 } |
| 354 |
| 355 bool DownloadShelfGtk::IsCursorInShelfZone( |
| 356 const gfx::Point& cursor_screen_coords) { |
| 357 gfx::Rect bounds(gtk_util::GetWidgetScreenPosition(shelf_.get()), |
| 358 gfx::Size(shelf_.get()->allocation.width, |
| 359 shelf_.get()->allocation.height)); |
| 360 |
| 361 // Negative insets expand the rectangle. We only expand the top. |
| 362 bounds.Inset(gfx::Insets(-kShelfAuraSize, 0, 0, 0)); |
| 363 |
| 364 return bounds.Contains(cursor_screen_coords); |
| 365 } |
| 366 |
| 367 void DownloadShelfGtk::MouseLeftShelf() { |
| 368 DCHECK(close_on_mouse_out_); |
| 369 |
| 370 MessageLoop::current()->PostDelayedTask( |
| 371 FROM_HERE, |
| 372 auto_close_factory_.NewRunnableMethod(&DownloadShelfGtk::Close), |
| 373 kAutoCloseDelayMs); |
| 374 } |
| 375 |
| 376 void DownloadShelfGtk::MouseEnteredShelf() { |
| 377 auto_close_factory_.RevokeAll(); |
| 378 } |
OLD | NEW |