Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / download / download_shelf_view.cc
blob2be559bf5be777c72f4bb051e277ee422ab059bb
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.
5 #include "chrome/browser/ui/views/download/download_shelf_view.h"
7 #include <algorithm>
8 #include <vector>
10 #include "base/logging.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/download/download_item_model.h"
13 #include "chrome/browser/download/download_stats.h"
14 #include "chrome/browser/themes/theme_properties.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/chrome_pages.h"
17 #include "chrome/browser/ui/view_ids.h"
18 #include "chrome/browser/ui/views/download/download_item_view.h"
19 #include "chrome/browser/ui/views/frame/browser_view.h"
20 #include "content/public/browser/download_item.h"
21 #include "content/public/browser/download_manager.h"
22 #include "content/public/browser/page_navigator.h"
23 #include "grit/generated_resources.h"
24 #include "grit/theme_resources.h"
25 #include "grit/ui_resources.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/base/resource/resource_bundle.h"
28 #include "ui/base/theme_provider.h"
29 #include "ui/gfx/animation/slide_animation.h"
30 #include "ui/gfx/canvas.h"
31 #include "ui/views/background.h"
32 #include "ui/views/controls/button/image_button.h"
33 #include "ui/views/controls/image_view.h"
34 #include "ui/views/controls/link.h"
35 #include "ui/views/mouse_watcher_view_host.h"
37 // Max number of download views we'll contain. Any time a view is added and
38 // we already have this many download views, one is removed.
39 static const size_t kMaxDownloadViews = 15;
41 // Padding from left edge and first download view.
42 static const int kLeftPadding = 2;
44 // Padding from right edge and close button/show downloads link.
45 static const int kRightPadding = 10;
47 // Padding between the show all link and close button.
48 static const int kCloseAndLinkPadding = 14;
50 // Padding between the download views.
51 static const int kDownloadPadding = 10;
53 // Padding between the top/bottom and the content.
54 static const int kTopBottomPadding = 2;
56 // Padding between the icon and 'show all downloads' link
57 static const int kDownloadsTitlePadding = 4;
59 // Border color.
60 static const SkColor kBorderColor = SkColorSetRGB(214, 214, 214);
62 // New download item animation speed in milliseconds.
63 static const int kNewItemAnimationDurationMs = 800;
65 // Shelf show/hide speed.
66 static const int kShelfAnimationDurationMs = 120;
68 // Amount of time to delay if the mouse leaves the shelf by way of entering
69 // another window. This is much larger than the normal delay as openning a
70 // download is most likely going to trigger a new window to appear over the
71 // button. Delay the time so that the user has a chance to quickly close the
72 // other app and return to chrome with the download shelf still open.
73 static const int kNotifyOnExitTimeMS = 5000;
75 using content::DownloadItem;
77 namespace {
79 // Sets size->width() to view's preferred width + size->width().s
80 // Sets size->height() to the max of the view's preferred height and
81 // size->height();
82 void AdjustSize(views::View* view, gfx::Size* size) {
83 gfx::Size view_preferred = view->GetPreferredSize();
84 size->Enlarge(view_preferred.width(), 0);
85 size->set_height(std::max(view_preferred.height(), size->height()));
88 int CenterPosition(int size, int target_size) {
89 return std::max((target_size - size) / 2, kTopBottomPadding);
92 } // namespace
94 DownloadShelfView::DownloadShelfView(Browser* browser, BrowserView* parent)
95 : browser_(browser),
96 arrow_image_(NULL),
97 show_all_view_(NULL),
98 close_button_(NULL),
99 parent_(parent),
100 mouse_watcher_(new views::MouseWatcherViewHost(this, gfx::Insets()),
101 this) {
102 mouse_watcher_.set_notify_on_exit_time(
103 base::TimeDelta::FromMilliseconds(kNotifyOnExitTimeMS));
104 set_id(VIEW_ID_DOWNLOAD_SHELF);
105 parent->AddChildView(this);
108 DownloadShelfView::~DownloadShelfView() {
109 parent_->RemoveChildView(this);
112 void DownloadShelfView::AddDownloadView(DownloadItemView* view) {
113 mouse_watcher_.Stop();
115 DCHECK(view);
116 download_views_.push_back(view);
117 AddChildView(view);
118 if (download_views_.size() > kMaxDownloadViews)
119 RemoveDownloadView(*download_views_.begin());
121 new_item_animation_->Reset();
122 new_item_animation_->Show();
125 void DownloadShelfView::DoAddDownload(DownloadItem* download) {
126 DownloadItemView* view = new DownloadItemView(download, this);
127 AddDownloadView(view);
130 void DownloadShelfView::MouseMovedOutOfHost() {
131 Close(AUTOMATIC);
134 void DownloadShelfView::RemoveDownloadView(View* view) {
135 DCHECK(view);
136 std::vector<DownloadItemView*>::iterator i =
137 find(download_views_.begin(), download_views_.end(), view);
138 DCHECK(i != download_views_.end());
139 download_views_.erase(i);
140 RemoveChildView(view);
141 delete view;
142 if (download_views_.empty())
143 Close(AUTOMATIC);
144 else if (CanAutoClose())
145 mouse_watcher_.Start();
146 Layout();
147 SchedulePaint();
150 views::View* DownloadShelfView::GetDefaultFocusableChild() {
151 return download_views_.empty() ?
152 static_cast<View*>(show_all_view_) : download_views_[0];
155 void DownloadShelfView::OnPaintBorder(gfx::Canvas* canvas) {
156 canvas->FillRect(gfx::Rect(0, 0, width(), 1), kBorderColor);
159 void DownloadShelfView::OpenedDownload(DownloadItemView* view) {
160 if (CanAutoClose())
161 mouse_watcher_.Start();
164 content::PageNavigator* DownloadShelfView::GetNavigator() {
165 return browser_;
168 gfx::Size DownloadShelfView::GetPreferredSize() {
169 gfx::Size prefsize(kRightPadding + kLeftPadding + kCloseAndLinkPadding, 0);
170 AdjustSize(close_button_, &prefsize);
171 AdjustSize(show_all_view_, &prefsize);
172 // Add one download view to the preferred size.
173 if (!download_views_.empty()) {
174 AdjustSize(*download_views_.begin(), &prefsize);
175 prefsize.Enlarge(kDownloadPadding, 0);
177 prefsize.Enlarge(0, kTopBottomPadding + kTopBottomPadding);
178 if (shelf_animation_->is_animating()) {
179 prefsize.set_height(static_cast<int>(
180 static_cast<double>(prefsize.height()) *
181 shelf_animation_->GetCurrentValue()));
183 return prefsize;
186 void DownloadShelfView::AnimationProgressed(const gfx::Animation *animation) {
187 if (animation == new_item_animation_.get()) {
188 Layout();
189 SchedulePaint();
190 } else if (animation == shelf_animation_.get()) {
191 // Force a re-layout of the parent, which will call back into
192 // GetPreferredSize, where we will do our animation. In the case where the
193 // animation is hiding, we do a full resize - the fast resizing would
194 // otherwise leave blank white areas where the shelf was and where the
195 // user's eye is. Thankfully bottom-resizing is a lot faster than
196 // top-resizing.
197 parent_->ToolbarSizeChanged(shelf_animation_->IsShowing());
201 void DownloadShelfView::AnimationEnded(const gfx::Animation *animation) {
202 if (animation == shelf_animation_.get()) {
203 parent_->SetDownloadShelfVisible(shelf_animation_->IsShowing());
204 if (!shelf_animation_->IsShowing())
205 Closed();
209 void DownloadShelfView::Layout() {
210 // Let our base class layout our child views
211 views::View::Layout();
213 // If there is not enough room to show the first download item, show the
214 // "Show all downloads" link to the left to make it more visible that there is
215 // something to see.
216 bool show_link_only = !CanFitFirstDownloadItem();
218 gfx::Size image_size = arrow_image_->GetPreferredSize();
219 gfx::Size close_button_size = close_button_->GetPreferredSize();
220 gfx::Size show_all_size = show_all_view_->GetPreferredSize();
221 int max_download_x =
222 std::max<int>(0, width() - kRightPadding - close_button_size.width() -
223 kCloseAndLinkPadding - show_all_size.width() -
224 kDownloadsTitlePadding - image_size.width() -
225 kDownloadPadding);
226 int next_x = show_link_only ? kLeftPadding :
227 max_download_x + kDownloadPadding;
228 // Align vertically with show_all_view_.
229 arrow_image_->SetBounds(next_x,
230 CenterPosition(image_size.height(), height()),
231 image_size.width(), image_size.height());
232 next_x += image_size.width() + kDownloadsTitlePadding;
233 show_all_view_->SetBounds(next_x,
234 CenterPosition(show_all_size.height(), height()),
235 show_all_size.width(),
236 show_all_size.height());
237 next_x += show_all_size.width() + kCloseAndLinkPadding;
238 // If the window is maximized, we want to expand the hitbox of the close
239 // button to the right and bottom to make it easier to click.
240 bool is_maximized = browser_->window()->IsMaximized();
241 int y = CenterPosition(close_button_size.height(), height());
242 close_button_->SetBounds(next_x, y,
243 is_maximized ? width() - next_x : close_button_size.width(),
244 is_maximized ? height() - y : close_button_size.height());
245 if (show_link_only) {
246 // Let's hide all the items.
247 std::vector<DownloadItemView*>::reverse_iterator ri;
248 for (ri = download_views_.rbegin(); ri != download_views_.rend(); ++ri)
249 (*ri)->SetVisible(false);
250 return;
253 next_x = kLeftPadding;
254 std::vector<DownloadItemView*>::reverse_iterator ri;
255 for (ri = download_views_.rbegin(); ri != download_views_.rend(); ++ri) {
256 gfx::Size view_size = (*ri)->GetPreferredSize();
258 int x = next_x;
260 // Figure out width of item.
261 int item_width = view_size.width();
262 if (new_item_animation_->is_animating() && ri == download_views_.rbegin()) {
263 item_width = static_cast<int>(static_cast<double>(view_size.width()) *
264 new_item_animation_->GetCurrentValue());
267 next_x += item_width;
269 // Make sure our item can be contained within the shelf.
270 if (next_x < max_download_x) {
271 (*ri)->SetVisible(true);
272 (*ri)->SetBounds(x, CenterPosition(view_size.height(), height()),
273 item_width, view_size.height());
274 } else {
275 (*ri)->SetVisible(false);
280 void DownloadShelfView::ViewHierarchyChanged(
281 const ViewHierarchyChangedDetails& details) {
282 View::ViewHierarchyChanged(details);
284 if (details.is_add && (details.child == this)) {
285 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
286 arrow_image_ = new views::ImageView();
287 arrow_image_->SetImage(rb.GetImageSkiaNamed(IDR_DOWNLOADS_FAVICON));
288 AddChildView(arrow_image_);
290 show_all_view_ = new views::Link(
291 l10n_util::GetStringUTF16(IDS_SHOW_ALL_DOWNLOADS));
292 show_all_view_->set_listener(this);
293 AddChildView(show_all_view_);
295 close_button_ = new views::ImageButton(this);
296 close_button_->SetImage(views::CustomButton::STATE_NORMAL,
297 rb.GetImageSkiaNamed(IDR_CLOSE_1));
298 close_button_->SetImage(views::CustomButton::STATE_HOVERED,
299 rb.GetImageSkiaNamed(IDR_CLOSE_1_H));
300 close_button_->SetImage(views::CustomButton::STATE_PRESSED,
301 rb.GetImageSkiaNamed(IDR_CLOSE_1_P));
302 close_button_->SetAccessibleName(
303 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
304 AddChildView(close_button_);
306 UpdateColorsFromTheme();
308 new_item_animation_.reset(new gfx::SlideAnimation(this));
309 new_item_animation_->SetSlideDuration(kNewItemAnimationDurationMs);
311 shelf_animation_.reset(new gfx::SlideAnimation(this));
312 shelf_animation_->SetSlideDuration(kShelfAnimationDurationMs);
316 bool DownloadShelfView::CanFitFirstDownloadItem() {
317 if (download_views_.empty())
318 return true;
320 gfx::Size image_size = arrow_image_->GetPreferredSize();
321 gfx::Size close_button_size = close_button_->GetPreferredSize();
322 gfx::Size show_all_size = show_all_view_->GetPreferredSize();
324 // Let's compute the width available for download items, which is the width
325 // of the shelf minus the "Show all downloads" link, arrow and close button
326 // and the padding.
327 int available_width = width() - kRightPadding - close_button_size.width() -
328 kCloseAndLinkPadding - show_all_size.width() - kDownloadsTitlePadding -
329 image_size.width() - kDownloadPadding - kLeftPadding;
330 if (available_width <= 0)
331 return false;
333 gfx::Size item_size = (*download_views_.rbegin())->GetPreferredSize();
334 return item_size.width() < available_width;
337 void DownloadShelfView::UpdateColorsFromTheme() {
338 if (show_all_view_ && close_button_ && GetThemeProvider()) {
339 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
340 set_background(views::Background::CreateSolidBackground(
341 GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR)));
342 show_all_view_->SetBackgroundColor(background()->get_color());
343 show_all_view_->SetEnabledColor(
344 GetThemeProvider()->GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT));
345 close_button_->SetBackground(
346 GetThemeProvider()->GetColor(ThemeProperties::COLOR_TAB_TEXT),
347 rb.GetImageSkiaNamed(IDR_CLOSE_1),
348 rb.GetImageSkiaNamed(IDR_CLOSE_1_MASK));
352 void DownloadShelfView::OnThemeChanged() {
353 UpdateColorsFromTheme();
356 void DownloadShelfView::LinkClicked(views::Link* source, int event_flags) {
357 chrome::ShowDownloads(browser_);
360 void DownloadShelfView::ButtonPressed(
361 views::Button* button, const ui::Event& event) {
362 Close(USER_ACTION);
365 bool DownloadShelfView::IsShowing() const {
366 return shelf_animation_->IsShowing();
369 bool DownloadShelfView::IsClosing() const {
370 return shelf_animation_->IsClosing();
373 void DownloadShelfView::DoShow() {
374 shelf_animation_->Show();
377 void DownloadShelfView::DoClose(CloseReason reason) {
378 int num_in_progress = 0;
379 for (size_t i = 0; i < download_views_.size(); ++i) {
380 if (download_views_[i]->download()->GetState() == DownloadItem::IN_PROGRESS)
381 ++num_in_progress;
383 RecordDownloadShelfClose(
384 download_views_.size(), num_in_progress, reason == AUTOMATIC);
385 parent_->SetDownloadShelfVisible(false);
386 shelf_animation_->Hide();
389 Browser* DownloadShelfView::browser() const {
390 return browser_;
393 void DownloadShelfView::Closed() {
394 // Don't remove completed downloads if the shelf is just being auto-hidden
395 // rather than explicitly closed by the user.
396 if (is_hidden())
397 return;
398 // When the close animation is complete, remove all completed downloads.
399 size_t i = 0;
400 while (i < download_views_.size()) {
401 DownloadItem* download = download_views_[i]->download();
402 DownloadItem::DownloadState state = download->GetState();
403 bool is_transfer_done = state == DownloadItem::COMPLETE ||
404 state == DownloadItem::CANCELLED ||
405 state == DownloadItem::INTERRUPTED;
406 if (is_transfer_done && !download->IsDangerous()) {
407 RemoveDownloadView(download_views_[i]);
408 } else {
409 // Treat the item as opened when we close. This way if we get shown again
410 // the user need not open this item for the shelf to auto-close.
411 download->SetOpened(true);
412 ++i;
417 bool DownloadShelfView::CanAutoClose() {
418 for (size_t i = 0; i < download_views_.size(); ++i) {
419 if (!download_views_[i]->download()->GetOpened())
420 return false;
422 return true;