OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/views/browser_actions_container.h" | 5 #include "chrome/browser/views/browser_actions_container.h" |
6 | 6 |
7 #include "app/gfx/canvas.h" | 7 #include "app/gfx/canvas.h" |
8 #include "app/resource_bundle.h" | 8 #include "app/resource_bundle.h" |
9 #include "app/slide_animation.h" | 9 #include "app/slide_animation.h" |
10 #include "base/stl_util-inl.h" | 10 #include "base/stl_util-inl.h" |
(...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
278 | 278 |
279 //////////////////////////////////////////////////////////////////////////////// | 279 //////////////////////////////////////////////////////////////////////////////// |
280 // BrowserActionsContainer | 280 // BrowserActionsContainer |
281 | 281 |
282 BrowserActionsContainer::BrowserActionsContainer( | 282 BrowserActionsContainer::BrowserActionsContainer( |
283 Profile* profile, ToolbarView* toolbar) | 283 Profile* profile, ToolbarView* toolbar) |
284 : profile_(profile), | 284 : profile_(profile), |
285 toolbar_(toolbar), | 285 toolbar_(toolbar), |
286 popup_(NULL), | 286 popup_(NULL), |
287 popup_button_(NULL), | 287 popup_button_(NULL), |
| 288 model_(NULL), |
288 resize_gripper_(NULL), | 289 resize_gripper_(NULL), |
289 chevron_(NULL), | 290 chevron_(NULL), |
290 suppress_chevron_(false), | 291 suppress_chevron_(false), |
291 resize_amount_(0), | 292 resize_amount_(0), |
292 animation_target_size_(0), | 293 animation_target_size_(0), |
293 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { | 294 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { |
| 295 SetID(VIEW_ID_BROWSER_ACTION_TOOLBAR); |
| 296 |
294 ExtensionsService* extension_service = profile->GetExtensionsService(); | 297 ExtensionsService* extension_service = profile->GetExtensionsService(); |
295 if (!extension_service) // The |extension_service| can be NULL in Incognito. | 298 if (!extension_service) // The |extension_service| can be NULL in Incognito. |
296 return; | 299 return; |
297 | 300 |
298 registrar_.Add(this, NotificationType::EXTENSION_LOADED, | |
299 Source<Profile>(profile_)); | |
300 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, | |
301 Source<Profile>(profile_)); | |
302 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED, | |
303 Source<Profile>(profile_)); | |
304 registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, | 301 registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE, |
305 Source<Profile>(profile_)); | 302 Source<Profile>(profile_)); |
306 | 303 |
| 304 model_ = extension_service->toolbar_model(); |
| 305 model_->AddObserver(this); |
| 306 |
307 resize_animation_.reset(new SlideAnimation(this)); | 307 resize_animation_.reset(new SlideAnimation(this)); |
308 | |
309 resize_gripper_ = new views::ResizeGripper(this); | 308 resize_gripper_ = new views::ResizeGripper(this); |
310 resize_gripper_->SetVisible(false); | 309 resize_gripper_->SetVisible(false); |
311 AddChildView(resize_gripper_); | 310 AddChildView(resize_gripper_); |
312 | 311 |
313 // TODO(glen): Come up with a new bitmap for the chevron. | 312 // TODO(glen): Come up with a new bitmap for the chevron. |
314 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | 313 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
315 SkBitmap* chevron_image = rb.GetBitmapNamed(IDR_BOOKMARK_BAR_CHEVRONS); | 314 SkBitmap* chevron_image = rb.GetBitmapNamed(IDR_BOOKMARK_BAR_CHEVRONS); |
316 chevron_ = new views::MenuButton(NULL, std::wstring(), this, false); | 315 chevron_ = new views::MenuButton(NULL, std::wstring(), this, false); |
317 chevron_->SetVisible(false); | 316 chevron_->SetVisible(false); |
318 chevron_->SetIcon(*chevron_image); | 317 chevron_->SetIcon(*chevron_image); |
319 // Chevron contains >> that should point left in LTR locales. | 318 // Chevron contains >> that should point left in LTR locales. |
320 chevron_->EnableCanvasFlippingForRTLUI(true); | 319 chevron_->EnableCanvasFlippingForRTLUI(true); |
321 AddChildView(chevron_); | 320 AddChildView(chevron_); |
322 | 321 |
323 int predefined_width = | 322 int predefined_width = |
324 profile_->GetPrefs()->GetInteger(prefs::kBrowserActionContainerWidth); | 323 profile_->GetPrefs()->GetInteger(prefs::kBrowserActionContainerWidth); |
325 container_size_ = gfx::Size(predefined_width, kButtonSize); | 324 container_size_ = gfx::Size(predefined_width, kButtonSize); |
326 | |
327 SetID(VIEW_ID_BROWSER_ACTION_TOOLBAR); | |
328 } | 325 } |
329 | 326 |
330 BrowserActionsContainer::~BrowserActionsContainer() { | 327 BrowserActionsContainer::~BrowserActionsContainer() { |
| 328 if (model_) |
| 329 model_->RemoveObserver(this); |
331 CloseOverflowMenu(); | 330 CloseOverflowMenu(); |
332 HidePopup(); | 331 HidePopup(); |
333 DeleteBrowserActionViews(); | 332 DeleteBrowserActionViews(); |
334 } | 333 } |
335 | 334 |
336 // Static. | 335 // Static. |
337 void BrowserActionsContainer::RegisterUserPrefs(PrefService* prefs) { | 336 void BrowserActionsContainer::RegisterUserPrefs(PrefService* prefs) { |
338 prefs->RegisterIntegerPref(prefs::kBrowserActionContainerWidth, 0); | 337 prefs->RegisterIntegerPref(prefs::kBrowserActionContainerWidth, 0); |
339 } | 338 } |
340 | 339 |
(...skipping 15 matching lines...) Expand all Loading... |
356 } | 355 } |
357 | 356 |
358 return NULL; | 357 return NULL; |
359 } | 358 } |
360 | 359 |
361 void BrowserActionsContainer::RefreshBrowserActionViews() { | 360 void BrowserActionsContainer::RefreshBrowserActionViews() { |
362 for (size_t i = 0; i < browser_action_views_.size(); ++i) | 361 for (size_t i = 0; i < browser_action_views_.size(); ++i) |
363 browser_action_views_[i]->button()->UpdateState(); | 362 browser_action_views_[i]->button()->UpdateState(); |
364 } | 363 } |
365 | 364 |
366 void BrowserActionsContainer::AddBrowserAction(Extension* extension) { | |
367 #if defined(DEBUG) | |
368 for (size_t i = 0; i < browser_action_views_.size(); ++i) { | |
369 DCHECK(browser_action_views_[i]->button()->extension() != extension) << | |
370 "Asked to add a browser action view for an extension that already " | |
371 "exists."; | |
372 } | |
373 #endif | |
374 if (!extension->browser_action()) | |
375 return; | |
376 | |
377 // Before we change anything, determine the number of visible browser actions. | |
378 size_t visible_actions = VisibleBrowserActions(); | |
379 | |
380 // Add the new browser action to the vector and the view hierarchy. | |
381 BrowserActionView* view = new BrowserActionView(extension, this); | |
382 browser_action_views_.push_back(view); | |
383 AddChildView(view); | |
384 | |
385 // For details on why we do the following see the class comments in the | |
386 // header. | |
387 | |
388 // Determine if we need to increase (we only do that if the container was | |
389 // showing all icons before the addition of this icon). We use -1 because | |
390 // we don't want to count the view that we just added. | |
391 if (visible_actions < browser_action_views_.size() - 1) { | |
392 // Some icons were hidden, don't increase the size of the container. | |
393 OnBrowserActionVisibilityChanged(); | |
394 } else { | |
395 // Container was at max, increase the size of it by one icon. | |
396 animation_target_size_ = IconCountToWidth(visible_actions + 1); | |
397 | |
398 // We don't want the chevron to appear while we animate. See documentation | |
399 // in the header for why we do this. | |
400 suppress_chevron_ = !chevron_->IsVisible(); | |
401 | |
402 // Animate! | |
403 resize_animation_->Reset(); | |
404 resize_animation_->SetTweenType(SlideAnimation::NONE); | |
405 resize_animation_->Show(); | |
406 } | |
407 } | |
408 | |
409 void BrowserActionsContainer::RemoveBrowserAction(Extension* extension) { | |
410 if (!extension->browser_action()) | |
411 return; | |
412 | |
413 if (popup_ && popup_->host()->extension() == extension) { | |
414 HidePopup(); | |
415 } | |
416 | |
417 // Before we change anything, determine the number of visible browser actions. | |
418 int visible_actions = VisibleBrowserActions(); | |
419 | |
420 for (std::vector<BrowserActionView*>::iterator iter = | |
421 browser_action_views_.begin(); iter != browser_action_views_.end(); | |
422 ++iter) { | |
423 if ((*iter)->button()->extension() == extension) { | |
424 RemoveChildView(*iter); | |
425 delete *iter; | |
426 browser_action_views_.erase(iter); | |
427 | |
428 // For details on why we do the following see the class comments in the | |
429 // header. | |
430 | |
431 // Calculate the target size we'll animate to (end state). This might be | |
432 // the same size (if the icon we are removing is in the overflow bucket | |
433 // and there are other icons there). We don't decrement visible_actions | |
434 // because we want the container to stay the same size (clamping will take | |
435 // care of shrinking the container if there aren't enough icons to show). | |
436 animation_target_size_ = | |
437 ClampToNearestIconCount(IconCountToWidth(visible_actions)); | |
438 | |
439 // Animate! | |
440 resize_animation_->Reset(); | |
441 resize_animation_->SetTweenType(SlideAnimation::EASE_OUT); | |
442 resize_animation_->Show(); | |
443 return; | |
444 } | |
445 } | |
446 } | |
447 | |
448 void BrowserActionsContainer::CloseOverflowMenu() { | 365 void BrowserActionsContainer::CloseOverflowMenu() { |
449 // Close the overflow menu if open (and the context menu off of that). | 366 // Close the overflow menu if open (and the context menu off of that). |
450 if (overflow_menu_.get()) | 367 if (overflow_menu_.get()) |
451 overflow_menu_->CancelMenu(); | 368 overflow_menu_->CancelMenu(); |
452 } | 369 } |
453 | 370 |
454 void BrowserActionsContainer::DeleteBrowserActionViews() { | 371 void BrowserActionsContainer::DeleteBrowserActionViews() { |
455 if (!browser_action_views_.empty()) { | 372 if (!browser_action_views_.empty()) { |
456 for (size_t i = 0; i < browser_action_views_.size(); ++i) | 373 for (size_t i = 0; i < browser_action_views_.size(); ++i) |
457 RemoveChildView(browser_action_views_[i]); | 374 RemoveChildView(browser_action_views_[i]); |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
630 DetachableToolbarView::PaintVerticalDivider( | 547 DetachableToolbarView::PaintVerticalDivider( |
631 canvas, x, height(), kDividerVerticalPadding, | 548 canvas, x, height(), kDividerVerticalPadding, |
632 DetachableToolbarView::kEdgeDividerColor, | 549 DetachableToolbarView::kEdgeDividerColor, |
633 DetachableToolbarView::kMiddleDividerColor, | 550 DetachableToolbarView::kMiddleDividerColor, |
634 GetThemeProvider()->GetColor(BrowserThemeProvider::COLOR_TOOLBAR)); | 551 GetThemeProvider()->GetColor(BrowserThemeProvider::COLOR_TOOLBAR)); |
635 } | 552 } |
636 | 553 |
637 void BrowserActionsContainer::ViewHierarchyChanged(bool is_add, | 554 void BrowserActionsContainer::ViewHierarchyChanged(bool is_add, |
638 views::View* parent, | 555 views::View* parent, |
639 views::View* child) { | 556 views::View* child) { |
| 557 // No extensions (e.g., incognito). |
| 558 if (!model_) |
| 559 return; |
| 560 |
640 if (is_add && child == this) { | 561 if (is_add && child == this) { |
| 562 // Initial toolbar button creation and placement in the widget hierarchy. |
641 // We do this here instead of in the constructor because AddBrowserAction | 563 // We do this here instead of in the constructor because AddBrowserAction |
642 // calls Layout on the Toolbar, which needs this object to be constructed | 564 // calls Layout on the Toolbar, which needs this object to be constructed |
643 // before its Layout function is called. | 565 // before its Layout function is called. |
644 ExtensionsService* extension_service = profile_->GetExtensionsService(); | 566 for (ExtensionList::iterator iter = model_->begin(); |
645 if (!extension_service) | 567 iter != model_->end(); ++iter) { |
646 return; // The |extension_service| can be NULL in Incognito. | 568 BrowserActionView* view = new BrowserActionView(*iter, this); |
647 for (size_t i = 0; i < extension_service->extensions()->size(); ++i) | 569 browser_action_views_.push_back(view); |
648 AddBrowserAction(extension_service->extensions()->at(i)); | 570 AddChildView(view); |
| 571 } |
649 } | 572 } |
650 } | 573 } |
651 | 574 |
652 void BrowserActionsContainer::Observe(NotificationType type, | 575 void BrowserActionsContainer::Observe(NotificationType type, |
653 const NotificationSource& source, | 576 const NotificationSource& source, |
654 const NotificationDetails& details) { | 577 const NotificationDetails& details) { |
655 switch (type.value) { | 578 switch (type.value) { |
656 case NotificationType::EXTENSION_LOADED: | |
657 CloseOverflowMenu(); | |
658 AddBrowserAction(Details<Extension>(details).ptr()); | |
659 OnBrowserActionVisibilityChanged(); | |
660 break; | |
661 | |
662 case NotificationType::EXTENSION_UNLOADED: | |
663 case NotificationType::EXTENSION_UNLOADED_DISABLED: | |
664 CloseOverflowMenu(); | |
665 RemoveBrowserAction(Details<Extension>(details).ptr()); | |
666 OnBrowserActionVisibilityChanged(); | |
667 break; | |
668 | |
669 case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE: | 579 case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE: |
670 // If we aren't the host of the popup, then disregard the notification. | 580 // If we aren't the host of the popup, then disregard the notification. |
671 if (!popup_ || Details<ExtensionHost>(popup_->host()) != details) | 581 if (!popup_ || Details<ExtensionHost>(popup_->host()) != details) |
672 return; | 582 return; |
673 | 583 |
674 HidePopup(); | 584 HidePopup(); |
675 break; | 585 break; |
676 | 586 |
677 default: | 587 default: |
678 NOTREACHED() << "Unexpected notification"; | 588 NOTREACHED() << "Unexpected notification"; |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
758 } else { | 668 } else { |
759 // A negative |pixels| count indicates caller wants to know the max width | 669 // A negative |pixels| count indicates caller wants to know the max width |
760 // that fits all icons; | 670 // that fits all icons; |
761 icon_count = browser_action_views_.size(); | 671 icon_count = browser_action_views_.size(); |
762 } | 672 } |
763 | 673 |
764 int returning = extras + (icon_count * icon_width); | 674 int returning = extras + (icon_count * icon_width); |
765 return returning; | 675 return returning; |
766 } | 676 } |
767 | 677 |
| 678 void BrowserActionsContainer::BrowserActionAdded(Extension* extension, |
| 679 int index) { |
| 680 #if defined(DEBUG) |
| 681 for (size_t i = 0; i < browser_action_views_.size(); ++i) { |
| 682 DCHECK(browser_action_views_[i]->button()->extension() != extension) << |
| 683 "Asked to add a browser action view for an extension that already " |
| 684 "exists."; |
| 685 } |
| 686 #endif |
| 687 |
| 688 CloseOverflowMenu(); |
| 689 |
| 690 // Before we change anything, determine the number of visible browser actions. |
| 691 size_t visible_actions = VisibleBrowserActions(); |
| 692 |
| 693 // Add the new browser action to the vector and the view hierarchy. |
| 694 BrowserActionView* view = new BrowserActionView(extension, this); |
| 695 browser_action_views_.push_back(view); |
| 696 AddChildView(index, view); |
| 697 |
| 698 // For details on why we do the following see the class comments in the |
| 699 // header. |
| 700 |
| 701 // Determine if we need to increase (we only do that if the container was |
| 702 // showing all icons before the addition of this icon). We use -1 because |
| 703 // we don't want to count the view that we just added. |
| 704 if (visible_actions < browser_action_views_.size() - 1) { |
| 705 // Some icons were hidden, don't increase the size of the container. |
| 706 OnBrowserActionVisibilityChanged(); |
| 707 } else { |
| 708 // Container was at max, increase the size of it by one icon. |
| 709 animation_target_size_ = IconCountToWidth(visible_actions + 1); |
| 710 |
| 711 // We don't want the chevron to appear while we animate. See documentation |
| 712 // in the header for why we do this. |
| 713 suppress_chevron_ = !chevron_->IsVisible(); |
| 714 |
| 715 // Animate! |
| 716 resize_animation_->Reset(); |
| 717 resize_animation_->SetTweenType(SlideAnimation::NONE); |
| 718 resize_animation_->Show(); |
| 719 } |
| 720 } |
| 721 |
| 722 void BrowserActionsContainer::BrowserActionRemoved(Extension* extension) { |
| 723 CloseOverflowMenu(); |
| 724 |
| 725 if (popup_ && popup_->host()->extension() == extension) |
| 726 HidePopup(); |
| 727 |
| 728 // Before we change anything, determine the number of visible browser actions. |
| 729 size_t visible_actions = VisibleBrowserActions(); |
| 730 |
| 731 for (std::vector<BrowserActionView*>::iterator iter = |
| 732 browser_action_views_.begin(); iter != browser_action_views_.end(); |
| 733 ++iter) { |
| 734 if ((*iter)->button()->extension() == extension) { |
| 735 RemoveChildView(*iter); |
| 736 delete *iter; |
| 737 browser_action_views_.erase(iter); |
| 738 |
| 739 // For details on why we do the following see the class comments in the |
| 740 // header. |
| 741 |
| 742 // Calculate the target size we'll animate to (end state). This might be |
| 743 // the same size (if the icon we are removing is in the overflow bucket |
| 744 // and there are other icons there). We don't decrement visible_actions |
| 745 // because we want the container to stay the same size (clamping will take |
| 746 // care of shrinking the container if there aren't enough icons to show). |
| 747 animation_target_size_ = |
| 748 ClampToNearestIconCount(IconCountToWidth(visible_actions)); |
| 749 |
| 750 // Animate! |
| 751 resize_animation_->Reset(); |
| 752 resize_animation_->SetTweenType(SlideAnimation::EASE_OUT); |
| 753 resize_animation_->Show(); |
| 754 return; |
| 755 } |
| 756 } |
| 757 } |
| 758 |
768 int BrowserActionsContainer::WidthOfNonIconArea() const { | 759 int BrowserActionsContainer::WidthOfNonIconArea() const { |
769 int chevron_size = (chevron_->IsVisible()) ? | 760 int chevron_size = (chevron_->IsVisible()) ? |
770 chevron_->GetPreferredSize().width() : 0; | 761 chevron_->GetPreferredSize().width() : 0; |
771 return resize_gripper_->GetPreferredSize().width() + kHorizontalPadding + | 762 return resize_gripper_->GetPreferredSize().width() + kHorizontalPadding + |
772 chevron_size + kChevronRightMargin + kDividerHorizontalMargin; | 763 chevron_size + kChevronRightMargin + kDividerHorizontalMargin; |
773 } | 764 } |
774 | 765 |
775 int BrowserActionsContainer::IconCountToWidth(int icons) const { | 766 int BrowserActionsContainer::IconCountToWidth(int icons) const { |
776 DCHECK(icons >= 0); | 767 DCHECK(icons >= 0); |
777 if (icons == 0) | 768 if (icons == 0) |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
835 void BrowserActionsContainer::AnimationEnded(const Animation* animation) { | 826 void BrowserActionsContainer::AnimationEnded(const Animation* animation) { |
836 container_size_.set_width(animation_target_size_); | 827 container_size_.set_width(animation_target_size_); |
837 animation_target_size_ = 0; | 828 animation_target_size_ = 0; |
838 resize_amount_ = 0; | 829 resize_amount_ = 0; |
839 OnBrowserActionVisibilityChanged(); | 830 OnBrowserActionVisibilityChanged(); |
840 suppress_chevron_ = false; | 831 suppress_chevron_ = false; |
841 | 832 |
842 profile_->GetPrefs()->SetInteger(prefs::kBrowserActionContainerWidth, | 833 profile_->GetPrefs()->SetInteger(prefs::kBrowserActionContainerWidth, |
843 container_size_.width()); | 834 container_size_.width()); |
844 } | 835 } |
OLD | NEW |