OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/views/toolbar/browser_actions_container.h" | 5 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h" |
6 | 6 |
7 #include "base/compiler_specific.h" | 7 #include "base/compiler_specific.h" |
8 #include "base/stl_util.h" | 8 #include "base/stl_util.h" |
9 #include "chrome/browser/extensions/extension_action_manager.h" | 9 #include "chrome/browser/extensions/extension_action_manager.h" |
10 #include "chrome/browser/extensions/tab_helper.h" | 10 #include "chrome/browser/extensions/tab_helper.h" |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
53 // TODO(devlin): We should move this to the model, once it supports component | 53 // TODO(devlin): We should move this to the model, once it supports component |
54 // actions. | 54 // actions. |
55 ScopedVector<ToolbarActionViewController> GetToolbarActions( | 55 ScopedVector<ToolbarActionViewController> GetToolbarActions( |
56 extensions::ExtensionToolbarModel* model, | 56 extensions::ExtensionToolbarModel* model, |
57 Browser* browser) { | 57 Browser* browser) { |
58 ScopedVector<ToolbarActionViewController> actions; | 58 ScopedVector<ToolbarActionViewController> actions; |
59 | 59 |
60 // Extension actions come first. | 60 // Extension actions come first. |
61 extensions::ExtensionActionManager* action_manager = | 61 extensions::ExtensionActionManager* action_manager = |
62 extensions::ExtensionActionManager::Get(browser->profile()); | 62 extensions::ExtensionActionManager::Get(browser->profile()); |
63 const extensions::ExtensionList& toolbar_items = model->toolbar_items(); | 63 const extensions::ExtensionList& toolbar_items = model->GetItemOrderForTab( |
64 browser->tab_strip_model()->GetActiveWebContents()); | |
64 for (const scoped_refptr<const Extension>& extension : toolbar_items) { | 65 for (const scoped_refptr<const Extension>& extension : toolbar_items) { |
65 actions.push_back(new ExtensionActionViewController( | 66 actions.push_back(new ExtensionActionViewController( |
66 extension.get(), | 67 extension.get(), |
67 browser, | 68 browser, |
68 action_manager->GetExtensionAction(*extension))); | 69 action_manager->GetExtensionAction(*extension))); |
69 } | 70 } |
70 | 71 |
71 // Component actions come second. | 72 // Component actions come second. |
72 ScopedVector<ToolbarActionViewController> component_actions = | 73 ScopedVector<ToolbarActionViewController> component_actions = |
73 ComponentToolbarActionsFactory::GetInstance()-> | 74 ComponentToolbarActionsFactory::GetInstance()-> |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
118 : initialized_(false), | 119 : initialized_(false), |
119 profile_(browser->profile()), | 120 profile_(browser->profile()), |
120 browser_(browser), | 121 browser_(browser), |
121 main_container_(main_container), | 122 main_container_(main_container), |
122 popup_owner_(NULL), | 123 popup_owner_(NULL), |
123 model_(extensions::ExtensionToolbarModel::Get(browser->profile())), | 124 model_(extensions::ExtensionToolbarModel::Get(browser->profile())), |
124 container_width_(0), | 125 container_width_(0), |
125 resize_area_(NULL), | 126 resize_area_(NULL), |
126 chevron_(NULL), | 127 chevron_(NULL), |
127 suppress_chevron_(false), | 128 suppress_chevron_(false), |
129 suppress_animation_(false), | |
130 suppress_layout_(false), | |
128 resize_amount_(0), | 131 resize_amount_(0), |
129 animation_target_size_(0) { | 132 animation_target_size_(0) { |
130 set_id(VIEW_ID_BROWSER_ACTION_TOOLBAR); | 133 set_id(VIEW_ID_BROWSER_ACTION_TOOLBAR); |
131 if (model_) // |model_| can be NULL in views unittests. | 134 if (model_) // |model_| can be NULL in views unittests. |
132 model_->AddObserver(this); | 135 model_->AddObserver(this); |
133 | 136 |
134 bool overflow_experiment = | 137 bool overflow_experiment = |
135 extensions::FeatureSwitch::extension_action_redesign()->IsEnabled(); | 138 extensions::FeatureSwitch::extension_action_redesign()->IsEnabled(); |
136 DCHECK(!in_overflow_mode() || overflow_experiment); | 139 DCHECK(!in_overflow_mode() || overflow_experiment); |
137 | 140 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
187 BrowserActionView* BrowserActionsContainer::GetViewForExtension( | 190 BrowserActionView* BrowserActionsContainer::GetViewForExtension( |
188 const Extension* extension) { | 191 const Extension* extension) { |
189 for (BrowserActionView* view : browser_action_views_) { | 192 for (BrowserActionView* view : browser_action_views_) { |
190 if (view->view_controller()->GetId() == extension->id()) | 193 if (view->view_controller()->GetId() == extension->id()) |
191 return view; | 194 return view; |
192 } | 195 } |
193 return NULL; | 196 return NULL; |
194 } | 197 } |
195 | 198 |
196 void BrowserActionsContainer::RefreshBrowserActionViews() { | 199 void BrowserActionsContainer::RefreshBrowserActionViews() { |
200 if (browser_action_views_.empty()) | |
201 return; // Nothing to do. | |
202 | |
203 // When we do a bulk-refresh of views (such as when we switch tabs), we don't | |
204 // animate the difference. We only animate when it's a change driven by the | |
205 // action. | |
206 suppress_animation_ = true; | |
Peter Kasting
2014/10/30 22:06:00
Nit: Use base::AutoReset to be safe against recurs
Devlin
2014/10/31 17:44:09
Done.
| |
207 // Don't layout until the end. | |
208 suppress_layout_ = true; | |
209 | |
197 for (BrowserActionView* view : browser_action_views_) | 210 for (BrowserActionView* view : browser_action_views_) |
198 view->UpdateState(); | 211 view->UpdateState(); |
212 | |
213 suppress_layout_ = false; | |
214 ReorderViews(); // Also triggers a layout. | |
215 | |
216 suppress_animation_ = false; | |
199 } | 217 } |
200 | 218 |
201 void BrowserActionsContainer::CreateBrowserActionViews() { | 219 void BrowserActionsContainer::CreateBrowserActionViews() { |
202 DCHECK(browser_action_views_.empty()); | 220 DCHECK(browser_action_views_.empty()); |
203 if (!model_) | 221 if (!model_) |
204 return; | 222 return; |
205 | 223 |
224 // We don't Layout while creating views. Instead, Layout() once at the end. | |
225 suppress_layout_ = true; | |
Peter Kasting
2014/10/30 22:06:00
Nit: AutoReset here too, perhaps
Devlin
2014/10/31 17:44:09
Done.
| |
226 | |
206 ScopedVector<ToolbarActionViewController> actions = | 227 ScopedVector<ToolbarActionViewController> actions = |
207 GetToolbarActions(model_, browser_); | 228 GetToolbarActions(model_, browser_); |
208 for (ToolbarActionViewController* controller : actions) { | 229 for (ToolbarActionViewController* controller : actions) { |
209 BrowserActionView* view = | 230 BrowserActionView* view = |
210 new BrowserActionView(make_scoped_ptr(controller), browser_, this); | 231 new BrowserActionView(make_scoped_ptr(controller), browser_, this); |
211 browser_action_views_.push_back(view); | 232 browser_action_views_.push_back(view); |
212 AddChildView(view); | 233 AddChildView(view); |
213 } | 234 } |
214 actions.weak_clear(); | 235 actions.weak_clear(); |
236 | |
237 suppress_layout_ = false; | |
238 | |
239 Layout(); | |
240 SchedulePaint(); | |
215 } | 241 } |
216 | 242 |
217 void BrowserActionsContainer::DeleteBrowserActionViews() { | 243 void BrowserActionsContainer::DeleteBrowserActionViews() { |
218 HideActivePopup(); | 244 HideActivePopup(); |
219 STLDeleteElements(&browser_action_views_); | 245 STLDeleteElements(&browser_action_views_); |
220 } | 246 } |
221 | 247 |
222 size_t BrowserActionsContainer::VisibleBrowserActions() const { | 248 size_t BrowserActionsContainer::VisibleBrowserActions() const { |
223 size_t visible_actions = 0; | 249 size_t visible_actions = 0; |
224 for (const BrowserActionView* view : browser_action_views_) { | 250 for (const BrowserActionView* view : browser_action_views_) { |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
355 int min_width = std::min(MinimumNonemptyWidth(), IconCountToWidth(-1)); | 381 int min_width = std::min(MinimumNonemptyWidth(), IconCountToWidth(-1)); |
356 return gfx::Size(min_width, IconHeight()); | 382 return gfx::Size(min_width, IconHeight()); |
357 } | 383 } |
358 | 384 |
359 void BrowserActionsContainer::Layout() { | 385 void BrowserActionsContainer::Layout() { |
360 if (browser_action_views_.empty()) { | 386 if (browser_action_views_.empty()) { |
361 SetVisible(false); | 387 SetVisible(false); |
362 return; | 388 return; |
363 } | 389 } |
364 | 390 |
391 if (suppress_layout_) | |
Peter Kasting
2014/10/30 22:05:59
Nit: I think this should happen at the top of the
Devlin
2014/10/31 17:44:09
Done.
| |
392 return; | |
393 | |
365 SetVisible(true); | 394 SetVisible(true); |
366 if (resize_area_) | 395 if (resize_area_) |
367 resize_area_->SetBounds(0, 0, kItemSpacing, height()); | 396 resize_area_->SetBounds(0, 0, kItemSpacing, height()); |
368 | 397 |
369 // If the icons don't all fit, show the chevron (unless suppressed). | 398 // If the icons don't all fit, show the chevron (unless suppressed). |
370 int max_x = GetPreferredSize().width(); | 399 int max_x = GetPreferredSize().width(); |
371 if (IconCountToWidth(-1) > max_x && !suppress_chevron_ && chevron_) { | 400 if (IconCountToWidth(-1) > max_x && !suppress_chevron_ && chevron_) { |
372 chevron_->SetVisible(true); | 401 chevron_->SetVisible(true); |
373 gfx::Size chevron_size(chevron_->GetPreferredSize()); | 402 gfx::Size chevron_size(chevron_->GetPreferredSize()); |
374 max_x -= chevron_size.width() + kChevronSpacing; | 403 max_x -= chevron_size.width() + kChevronSpacing; |
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
630 resize_amount_ = 0; | 659 resize_amount_ = 0; |
631 suppress_chevron_ = false; | 660 suppress_chevron_ = false; |
632 SetChevronVisibility(); | 661 SetChevronVisibility(); |
633 OnBrowserActionVisibilityChanged(); | 662 OnBrowserActionVisibilityChanged(); |
634 | 663 |
635 FOR_EACH_OBSERVER(BrowserActionsContainerObserver, | 664 FOR_EACH_OBSERVER(BrowserActionsContainerObserver, |
636 observers_, | 665 observers_, |
637 OnBrowserActionsContainerAnimationEnded()); | 666 OnBrowserActionsContainerAnimationEnded()); |
638 } | 667 } |
639 | 668 |
640 content::WebContents* BrowserActionsContainer::GetCurrentWebContents() { | 669 content::WebContents* BrowserActionsContainer::GetCurrentWebContents() const { |
641 return browser_->tab_strip_model()->GetActiveWebContents(); | 670 return browser_->tab_strip_model()->GetActiveWebContents(); |
642 } | 671 } |
643 | 672 |
644 extensions::ActiveTabPermissionGranter* | 673 extensions::ActiveTabPermissionGranter* |
645 BrowserActionsContainer::GetActiveTabPermissionGranter() { | 674 BrowserActionsContainer::GetActiveTabPermissionGranter() { |
646 content::WebContents* web_contents = GetCurrentWebContents(); | 675 content::WebContents* web_contents = GetCurrentWebContents(); |
647 if (!web_contents) | 676 if (!web_contents) |
648 return NULL; | 677 return NULL; |
649 return extensions::TabHelper::FromWebContents(web_contents)-> | 678 return extensions::TabHelper::FromWebContents(web_contents)-> |
650 active_tab_permission_granter(); | 679 active_tab_permission_granter(); |
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
873 bool grant_active_tab) { | 902 bool grant_active_tab) { |
874 // Don't override another popup, and only show in the active window. | 903 // Don't override another popup, and only show in the active window. |
875 if (popup_owner_ || !browser_->window()->IsActive()) | 904 if (popup_owner_ || !browser_->window()->IsActive()) |
876 return false; | 905 return false; |
877 | 906 |
878 BrowserActionView* view = GetViewForExtension(extension); | 907 BrowserActionView* view = GetViewForExtension(extension); |
879 return view && view->view_controller()->ExecuteAction(grant_active_tab); | 908 return view && view->view_controller()->ExecuteAction(grant_active_tab); |
880 } | 909 } |
881 | 910 |
882 void BrowserActionsContainer::ToolbarVisibleCountChanged() { | 911 void BrowserActionsContainer::ToolbarVisibleCountChanged() { |
883 if (GetPreferredWidth() != container_width_) | 912 if (GetPreferredWidth() != container_width_) { |
884 Animate(gfx::Tween::EASE_OUT, GetIconCount()); | 913 Animate(gfx::Tween::EASE_OUT, GetIconCount()); |
914 } else if (animation_target_size_ != 0) { | |
915 // It's possible that we're right where we're supposed to be in terms of | |
916 // icon count, but that we're also currently resizing. If this is the case, | |
917 // end the current animation with the current width. | |
918 animation_target_size_ = container_width_; | |
919 resize_animation_->Reset(); | |
920 } | |
885 } | 921 } |
886 | 922 |
887 void BrowserActionsContainer::ToolbarHighlightModeChanged( | 923 void BrowserActionsContainer::ToolbarHighlightModeChanged( |
888 bool is_highlighting) { | 924 bool is_highlighting) { |
889 // The visual highlighting is done in OnPaint(). It's a bit of a pain that | 925 // The visual highlighting is done in OnPaint(). It's a bit of a pain that |
890 // we delete and recreate everything here, but given everything else going on | 926 // we delete and recreate everything here, but given everything else going on |
891 // (the lack of highlight, n more extensions appearing, etc), it's not worth | 927 // (the lack of highlight, n more extensions appearing, etc), it's not worth |
892 // the extra complexity to create and insert only the new extensions. | 928 // the extra complexity to create and insert only the new extensions. |
893 DeleteBrowserActionViews(); | 929 DeleteBrowserActionViews(); |
894 CreateBrowserActionViews(); | 930 CreateBrowserActionViews(); |
895 Animate(gfx::Tween::LINEAR, GetIconCount()); | 931 Animate(gfx::Tween::LINEAR, GetIconCount()); |
896 } | 932 } |
897 | 933 |
934 void BrowserActionsContainer::ToolbarReorderNecessary( | |
935 content::WebContents* web_contents) { | |
936 if (GetCurrentWebContents() == web_contents) | |
937 ReorderViews(); | |
938 } | |
939 | |
898 Browser* BrowserActionsContainer::GetBrowser() { | 940 Browser* BrowserActionsContainer::GetBrowser() { |
899 return browser_; | 941 return browser_; |
900 } | 942 } |
901 | 943 |
902 void BrowserActionsContainer::LoadImages() { | 944 void BrowserActionsContainer::LoadImages() { |
903 if (in_overflow_mode()) | 945 if (in_overflow_mode()) |
904 return; // Overflow mode has neither a chevron nor highlighting. | 946 return; // Overflow mode has neither a chevron nor highlighting. |
905 | 947 |
906 ui::ThemeProvider* tp = GetThemeProvider(); | 948 ui::ThemeProvider* tp = GetThemeProvider(); |
907 if (tp && chevron_) { | 949 if (tp && chevron_) { |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
973 int BrowserActionsContainer::MinimumNonemptyWidth() const { | 1015 int BrowserActionsContainer::MinimumNonemptyWidth() const { |
974 if (!chevron_) | 1016 if (!chevron_) |
975 return ToolbarView::kStandardSpacing; | 1017 return ToolbarView::kStandardSpacing; |
976 return (ToolbarView::kStandardSpacing * 2) + kChevronSpacing + | 1018 return (ToolbarView::kStandardSpacing * 2) + kChevronSpacing + |
977 chevron_->GetPreferredSize().width(); | 1019 chevron_->GetPreferredSize().width(); |
978 } | 1020 } |
979 | 1021 |
980 void BrowserActionsContainer::Animate(gfx::Tween::Type tween_type, | 1022 void BrowserActionsContainer::Animate(gfx::Tween::Type tween_type, |
981 size_t num_visible_icons) { | 1023 size_t num_visible_icons) { |
982 int target_size = IconCountToWidth(num_visible_icons); | 1024 int target_size = IconCountToWidth(num_visible_icons); |
983 if (resize_animation_ && !disable_animations_during_testing_) { | 1025 if (resize_animation_ && !disable_animations_during_testing_ && |
1026 !suppress_animation_) { | |
984 // Animate! We have to set the animation_target_size_ after calling Reset(), | 1027 // Animate! We have to set the animation_target_size_ after calling Reset(), |
985 // because that could end up calling AnimationEnded which clears the value. | 1028 // because that could end up calling AnimationEnded which clears the value. |
986 resize_animation_->Reset(); | 1029 resize_animation_->Reset(); |
987 resize_animation_->SetTweenType(tween_type); | 1030 resize_animation_->SetTweenType(tween_type); |
988 animation_target_size_ = target_size; | 1031 animation_target_size_ = target_size; |
989 resize_animation_->Show(); | 1032 resize_animation_->Show(); |
990 } else { | 1033 } else { |
991 animation_target_size_ = target_size; | 1034 animation_target_size_ = target_size; |
992 AnimationEnded(resize_animation_.get()); | 1035 AnimationEnded(resize_animation_.get()); |
993 } | 1036 } |
994 } | 1037 } |
995 | 1038 |
1039 void BrowserActionsContainer::ReorderViews() { | |
1040 extensions::ExtensionList new_order = | |
1041 model_->GetItemOrderForTab(GetCurrentWebContents()); | |
1042 if (new_order.empty()) | |
1043 return; // Nothing to do. | |
1044 | |
1045 // Run through the views and compare them to the desired order. If something | |
1046 // is out of place, find the correct spot for it. | |
1047 for (size_t i = 0; i < new_order.size() - 1; ++i) { | |
1048 if (new_order[i]->id() != | |
1049 browser_action_views_[i]->view_controller()->GetId()) { | |
1050 // Find where the correct view is (it's guaranteed to be after our current | |
1051 // index, since everything up to this point is correct). | |
Peter Kasting
2014/10/30 22:06:00
Is it conceivable that the two sets of views are n
Devlin
2014/10/31 17:44:09
Hmm...I don't think so. Views are added to the co
| |
1052 size_t j = i + 1; | |
1053 while (new_order[i]->id() != | |
1054 browser_action_views_[j]->view_controller()->GetId()) | |
1055 ++j; | |
1056 std::swap(browser_action_views_[i], browser_action_views_[j]); | |
Peter Kasting
2014/10/30 22:06:00
I wonder if std::rotate() would make more sense th
Devlin
2014/10/31 17:44:09
The reason not to do that is because ReorderViews(
Peter Kasting
2014/10/31 19:01:07
Fine with me.
| |
1057 } | |
1058 } | |
1059 | |
1060 // Our visible browser actions may have changed - re-Layout() and check the | |
1061 // size. | |
1062 ToolbarVisibleCountChanged(); | |
1063 OnBrowserActionVisibilityChanged(); | |
1064 } | |
1065 | |
996 size_t BrowserActionsContainer::GetIconCount() const { | 1066 size_t BrowserActionsContainer::GetIconCount() const { |
997 if (!model_) | 1067 if (!model_) |
998 return 0u; | 1068 return 0u; |
999 | 1069 |
1000 // Find the absolute value for the model's visible count. | 1070 // Find the absolute value for the model's visible count. |
1001 int model_visible_size = model_->GetVisibleIconCount(); | 1071 int model_visible_size = |
1072 model_->GetVisibleIconCountForTab(GetCurrentWebContents()); | |
1002 size_t absolute_model_visible_size = model_visible_size == -1 ? | 1073 size_t absolute_model_visible_size = model_visible_size == -1 ? |
1003 model_->toolbar_items().size() : model_visible_size; | 1074 model_->toolbar_items().size() : model_visible_size; |
1004 | 1075 |
1005 #if !defined(NDEBUG) | 1076 #if !defined(NDEBUG) |
Peter Kasting
2014/10/30 22:06:00
Nit: While here: use DCHECK_IS_ON instead of looki
Devlin
2014/10/31 17:44:09
Good idea.
| |
1006 // Good time for some sanity checks: We should never try to display more | 1077 // Good time for some sanity checks: We should never try to display more |
1007 // icons than we have, and we should always have a view per item in the model. | 1078 // icons than we have, and we should always have a view per item in the model. |
1008 // (The only exception is if this is in initialization.) | 1079 // (The only exception is if this is in initialization.) |
1009 if (initialized_) { | 1080 if (initialized_ && !suppress_layout_) { |
1010 size_t num_extension_actions = 0u; | 1081 size_t num_extension_actions = 0u; |
1011 for (BrowserActionView* view : browser_action_views_) { | 1082 for (BrowserActionView* view : browser_action_views_) { |
1012 // No component action should ever have a valid extension id, so we can | 1083 // No component action should ever have a valid extension id, so we can |
1013 // use this to check the extension amount. | 1084 // use this to check the extension amount. |
1014 // TODO(devlin): Fix this to just check model size when the model also | 1085 // TODO(devlin): Fix this to just check model size when the model also |
1015 // includes component actions. | 1086 // includes component actions. |
1016 if (crx_file::id_util::IdIsValid(view->view_controller()->GetId())) | 1087 if (crx_file::id_util::IdIsValid(view->view_controller()->GetId())) |
1017 ++num_extension_actions; | 1088 ++num_extension_actions; |
1018 } | 1089 } |
1019 DCHECK_LE(absolute_model_visible_size, num_extension_actions); | 1090 DCHECK_LE(absolute_model_visible_size, num_extension_actions); |
1020 DCHECK_EQ(model_->toolbar_items().size(), num_extension_actions); | 1091 DCHECK_EQ(model_->toolbar_items().size(), num_extension_actions); |
1021 } | 1092 } |
1022 #endif | 1093 #endif |
1023 | 1094 |
1024 // The overflow displays any icons not shown by the main bar. | 1095 // The overflow displays any icons not shown by the main bar. |
1025 return in_overflow_mode() ? | 1096 return in_overflow_mode() ? |
1026 model_->toolbar_items().size() - absolute_model_visible_size : | 1097 model_->toolbar_items().size() - absolute_model_visible_size : |
1027 absolute_model_visible_size; | 1098 absolute_model_visible_size; |
1028 } | 1099 } |
OLD | NEW |