Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(281)

Side by Side Diff: chrome/browser/ui/toolbar/toolbar_actions_bar.cc

Issue 766263003: [Extension Toolbar] Refactor and finish pop out logic for actions (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Sky's + OWNERS Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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/toolbar/toolbar_actions_bar.h" 5 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
6 6
7 #include "base/auto_reset.h" 7 #include "base/auto_reset.h"
8 #include "chrome/browser/extensions/extension_action_manager.h" 8 #include "chrome/browser/extensions/extension_action_manager.h"
9 #include "chrome/browser/profiles/profile.h" 9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/browser/sessions/session_tab_helper.h" 10 #include "chrome/browser/sessions/session_tab_helper.h"
11 #include "chrome/browser/ui/browser.h" 11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/browser_window.h" 12 #include "chrome/browser/ui/browser_window.h"
13 #include "chrome/browser/ui/extensions/extension_action_view_controller.h" 13 #include "chrome/browser/ui/extensions/extension_action_view_controller.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model.h" 14 #include "chrome/browser/ui/tabs/tab_strip_model.h"
15 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
15 #include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h" 16 #include "chrome/browser/ui/toolbar/component_toolbar_actions_factory.h"
16 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h" 17 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
17 #include "chrome/browser/ui/toolbar/toolbar_actions_bar_delegate.h" 18 #include "chrome/browser/ui/toolbar/toolbar_actions_bar_delegate.h"
18 #include "components/crx_file/id_util.h" 19 #include "components/crx_file/id_util.h"
19 #include "extensions/browser/extension_system.h" 20 #include "extensions/browser/extension_system.h"
20 #include "extensions/browser/runtime_data.h" 21 #include "extensions/browser/runtime_data.h"
21 #include "extensions/common/extension.h" 22 #include "extensions/common/extension.h"
22 #include "extensions/common/feature_switch.h" 23 #include "extensions/common/feature_switch.h"
23 #include "grit/theme_resources.h" 24 #include "grit/theme_resources.h"
24 #include "ui/base/resource/resource_bundle.h" 25 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/gfx/image/image_skia.h" 26 #include "ui/gfx/image/image_skia.h"
26 27
27 namespace { 28 namespace {
28 29
30 using WeakToolbarActions = std::vector<ToolbarActionViewController*>;
31
29 #if defined(OS_MACOSX) 32 #if defined(OS_MACOSX)
30 const int kItemSpacing = 2; 33 const int kItemSpacing = 2;
31 const int kLeftPadding = kItemSpacing + 1; 34 const int kLeftPadding = kItemSpacing + 1;
32 const int kRightPadding = 0; 35 const int kRightPadding = 0;
33 const int kOverflowLeftPadding = kItemSpacing; 36 const int kOverflowLeftPadding = kItemSpacing;
34 const int kOverflowRightPadding = kItemSpacing; 37 const int kOverflowRightPadding = kItemSpacing;
35 #else 38 #else
36 // Matches ToolbarView::kStandardSpacing; 39 // Matches ToolbarView::kStandardSpacing;
37 const int kLeftPadding = 3; 40 const int kLeftPadding = 3;
38 const int kRightPadding = kLeftPadding; 41 const int kRightPadding = kLeftPadding;
(...skipping 13 matching lines...) Expand all
52 initialized = true; 55 initialized = true;
53 gfx::ImageSkia* skia = 56 gfx::ImageSkia* skia =
54 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 57 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
55 IDR_BROWSER_ACTION); 58 IDR_BROWSER_ACTION);
56 icon_height = skia->height(); 59 icon_height = skia->height();
57 icon_width = skia->width(); 60 icon_width = skia->width();
58 } 61 }
59 return type == WIDTH ? icon_width : icon_height; 62 return type == WIDTH ? icon_width : icon_height;
60 } 63 }
61 64
65 // Takes a reference vector |reference| of length n, where n is less than or
66 // equal to the length of |to_sort|, and rearranges |to_sort| so that
67 // |to_sort|'s first n elements match the n elements of |reference| (the order
68 // of any remaining elements in |to_sort| is unspecified).
69 // |equal| is used to compare the elements of |to_sort| and |reference|.
70 // This allows us to sort a vector to match another vector of two different
71 // types without needing to construct a more cumbersome comparator class.
72 // |FunctionType| should equate to (something similar to)
73 // bool Equal(const Type1&, const Type2&), but we can't enforce this
74 // because of MSVC compilation limitations.
75 template<typename Type1, typename Type2, typename FunctionType>
76 void SortContainer(std::vector<Type1>* to_sort,
77 const std::vector<Type2>& reference,
78 FunctionType equal) {
79 DCHECK_GE(to_sort->size(), reference.size()) <<
80 "|to_sort| must contain all elements in |reference|.";
81 if (reference.empty())
Thiemo Nagel 2015/11/30 16:52:16 Once the "-1" has been dropped below, this check c
82 return;
83 // Run through the each element and compare it to the reference. If something
84 // is out of place, find the correct spot for it.
85 for (size_t i = 0; i < reference.size() - 1; ++i) {
Thiemo Nagel 2015/11/30 16:52:16 This looks like an off-by-one: As far as I can see
Devlin 2015/11/30 17:07:03 If every element up to the last one is in the prop
Thiemo Nagel 2015/11/30 17:35:42 Thank you, now I see. That might be worth a comme
86 if (!equal(to_sort->at(i), reference[i])) {
Thiemo Nagel 2015/11/30 16:52:17 For efficiency and readability, I'd suggest to rep
Devlin 2015/11/30 17:07:03 Huh. Didn't realize there was a significant diffe
87 // Find the correct index (it's guaranteed to be after our current
88 // index, since everything up to this point is correct), and swap.
89 size_t j = i + 1;
90 while (!equal(to_sort->at(j), reference[i])) {
Thiemo Nagel 2015/11/30 16:52:17 Using "at()" here is a very implicit way of saying
Devlin 2015/11/30 17:07:03 well, in theory, the at() is guaranteed to be safe
Thiemo Nagel 2015/11/30 17:35:42 Yes, in combination with that on line 92.
91 ++j;
92 DCHECK_LE(j, to_sort->size()) <<
93 "Item in |reference| not found in |to_sort|.";
94 }
95 std::swap(to_sort->at(i), to_sort->at(j));
Thiemo Nagel 2015/11/30 16:52:16 "at()" doesn't seem necessary here since both indi
Devlin 2015/11/30 17:07:03 Ditto - didn't realize at() and [] were significan
96 }
97 }
98 }
99
62 } // namespace 100 } // namespace
63 101
64 // static 102 // static
65 bool ToolbarActionsBar::disable_animations_for_testing_ = false; 103 bool ToolbarActionsBar::disable_animations_for_testing_ = false;
66 104
105 // static
106 bool ToolbarActionsBar::pop_out_actions_to_run_ = false;
107
108 // A class to implement an optional tab ordering that "pops out" actions that
109 // want to run on a particular page, as part of our experimentation with how to
110 // best signal that actions like extension page actions want to run.
111 // TODO(devlin): Once we finally settle on the right behavior, determine if
112 // we need this.
113 class ToolbarActionsBar::TabOrderHelper
114 : public TabStripModelObserver {
115 public:
116 TabOrderHelper(ToolbarActionsBar* toolbar,
117 Browser* browser,
118 extensions::ExtensionToolbarModel* model);
119 ~TabOrderHelper() override;
120
121 // Returns the number of extra icons that should appear on the given |tab_id|
122 // because of actions that are going to pop out.
123 size_t GetExtraIconCount(int tab_id);
124
125 // Returns the item order of actions for the tab with the given
126 // |web_contents|.
127 WeakToolbarActions GetActionOrder(content::WebContents* web_contents);
128
129 // Notifies the TabOrderHelper that a given |action| does/doesn't want to run
130 // on the tab indicated by |tab_id|.
131 void SetActionWantsToRun(ToolbarActionViewController* action,
132 int tab_id,
133 bool wants_to_run);
134
135 // Notifies the TabOrderHelper that a given |action| has been removed.
136 void ActionRemoved(ToolbarActionViewController* action);
137
138 // Handles a resize, including updating any actions that want to act and
139 // updating the model.
140 void HandleResize(size_t resized_count, int tab_id);
141
142 // Handles a drag and drop, including updating any actions that want to act
143 // and updating the model.
144 void HandleDragDrop(int dragged,
145 int dropped,
146 ToolbarActionsBar::DragType drag_type,
147 int tab_id);
148
149 private:
150 // TabStripModelObserver:
151 void TabInsertedAt(content::WebContents* web_contents,
152 int index,
153 bool foreground) override;
154 void TabDetachedAt(content::WebContents* web_contents, int index) override;
155 void TabStripModelDeleted() override;
156
157 // The set of tabs for the given action (the key) is currently "popped out".
158 // "Popped out" actions are those that were in the overflow menu normally, but
159 // want to run and are moved to the main bar so the user can see them.
160 std::map<ToolbarActionViewController*, std::set<int>> popped_out_in_tabs_;
161
162 // The set of tab ids that have been checked for whether actions need to be
163 // popped out or not.
164 std::set<int> tabs_checked_for_pop_out_;
165
166 // The owning ToolbarActionsBar.
167 ToolbarActionsBar* toolbar_;
168
169 // The associated toolbar model.
170 extensions::ExtensionToolbarModel* model_;
171
172 // A scoped tab strip observer so we can clean up |tabs_checked_for_popout_|.
173 ScopedObserver<TabStripModel, TabStripModelObserver> tab_strip_observer_;
174
175 DISALLOW_COPY_AND_ASSIGN(TabOrderHelper);
176 };
177
178 ToolbarActionsBar::TabOrderHelper::TabOrderHelper(
179 ToolbarActionsBar* toolbar,
180 Browser* browser,
181 extensions::ExtensionToolbarModel* model)
182 : toolbar_(toolbar),
183 model_(model),
184 tab_strip_observer_(this) {
185 tab_strip_observer_.Add(browser->tab_strip_model());
186 }
187
188 ToolbarActionsBar::TabOrderHelper::~TabOrderHelper() {
189 }
190
191 size_t ToolbarActionsBar::TabOrderHelper::GetExtraIconCount(int tab_id) {
192 size_t extra_icons = 0;
193 const WeakToolbarActions& toolbar_actions = toolbar_->toolbar_actions();
194 for (ToolbarActionViewController* action : toolbar_actions) {
195 auto actions_tabs = popped_out_in_tabs_.find(action);
196 if (actions_tabs != popped_out_in_tabs_.end() &&
197 actions_tabs->second.count(tab_id))
198 ++extra_icons;
199 }
200 return extra_icons;
201 }
202
203 void ToolbarActionsBar::TabOrderHelper::HandleResize(size_t resized_count,
204 int tab_id) {
205 int extra = GetExtraIconCount(tab_id);
206 size_t tab_icon_count = model_->visible_icon_count() + extra;
207 bool reorder_necessary = false;
208 const WeakToolbarActions& toolbar_actions = toolbar_->toolbar_actions();
209 if (resized_count < tab_icon_count) {
210 for (int i = resized_count; i < extra; ++i) {
211 // If an extension that was popped out to act is overflowed, then it
212 // should no longer be popped out, and it also doesn't count for adjusting
213 // the visible count (since it wasn't really out to begin with).
214 if (popped_out_in_tabs_[toolbar_actions[i]].count(tab_id)) {
215 reorder_necessary = true;
216 popped_out_in_tabs_[toolbar_actions[i]].erase(tab_id);
217 ++(resized_count);
218 }
219 }
220 } else {
221 // If the user increases the toolbar size while actions that popped out are
222 // visible, we need to re-arrange the icons in other windows to be
223 // consistent with what the user sees.
224 // That is, if the normal order is A, B, [C, D] (with C and D hidden), C
225 // pops out to act, and then the user increases the size of the toolbar,
226 // the user sees uncovering D (since C is already out). This is what should
227 // happen in all windows.
228 for (size_t i = tab_icon_count; i < resized_count; ++i) {
229 if (toolbar_actions[i]->GetId() !=
230 model_->toolbar_items()[i - extra]->id())
231 model_->MoveExtensionIcon(toolbar_actions[i]->GetId(), i - extra);
232 }
233 }
234
235 resized_count -= extra;
236 model_->SetVisibleIconCount(resized_count);
237 if (reorder_necessary)
238 toolbar_->ReorderActions();
239 }
240
241 void ToolbarActionsBar::TabOrderHelper::HandleDragDrop(
242 int dragged_index,
243 int dropped_index,
244 ToolbarActionsBar::DragType drag_type,
245 int tab_id) {
246 const WeakToolbarActions& toolbar_actions = toolbar_->toolbar_actions();
247 ToolbarActionViewController* action = toolbar_actions[dragged_index];
248 int delta = 0;
249 switch (drag_type) {
250 case ToolbarActionsBar::DRAG_TO_OVERFLOW:
251 // If the user moves an action back into overflow, then we don't adjust
252 // the base visible count, but do stop popping that action out.
253 if (popped_out_in_tabs_[action].count(tab_id))
254 popped_out_in_tabs_[action].erase(tab_id);
255 else
256 delta = -1;
257 break;
258 case ToolbarActionsBar::DRAG_TO_MAIN:
259 delta = 1;
260 break;
261 case ToolbarActionsBar::DRAG_TO_SAME:
262 // If the user moves an action that had popped out to be on the toolbar,
263 // then we treat it as "pinning" the action, and adjust the base visible
264 // count to accommodate.
265 if (popped_out_in_tabs_[action].count(tab_id)) {
266 delta = 1;
267 popped_out_in_tabs_[action].erase(tab_id);
268 }
269 break;
270 }
271
272 // If there are any actions that are in front of the dropped index only
273 // because they were popped out, decrement the dropped index.
274 for (int i = 0; i < dropped_index; ++i) {
275 if (i != dragged_index &&
276 model_->GetIndexForId(toolbar_actions[i]->GetId()) >= dropped_index)
277 --dropped_index;
278 }
279
280 model_->MoveExtensionIcon(action->GetId(), dropped_index);
281
282 if (delta)
283 model_->SetVisibleIconCount(model_->visible_icon_count() + delta);
284 }
285
286 void ToolbarActionsBar::TabOrderHelper::SetActionWantsToRun(
287 ToolbarActionViewController* action,
288 int tab_id,
289 bool wants_to_run) {
290 bool is_overflowed = model_->GetIndexForId(action->GetId()) >=
291 static_cast<int>(model_->visible_icon_count());
292 bool reorder_necessary = false;
293 if (wants_to_run && is_overflowed) {
294 popped_out_in_tabs_[action].insert(tab_id);
295 reorder_necessary = true;
296 } else if (!wants_to_run && popped_out_in_tabs_[action].count(tab_id)) {
297 popped_out_in_tabs_[action].erase(tab_id);
298 reorder_necessary = true;
299 }
300 if (reorder_necessary)
301 toolbar_->ReorderActions();
302 }
303
304 void ToolbarActionsBar::TabOrderHelper::ActionRemoved(
305 ToolbarActionViewController* action) {
306 popped_out_in_tabs_.erase(action);
307 }
308
309 WeakToolbarActions ToolbarActionsBar::TabOrderHelper::GetActionOrder(
310 content::WebContents* web_contents) {
311 WeakToolbarActions toolbar_actions = toolbar_->toolbar_actions();
312 // First, make sure that we've checked any actions that want to run.
313 int tab_id = SessionTabHelper::IdForTab(web_contents);
314 if (!tabs_checked_for_pop_out_.count(tab_id)) {
315 tabs_checked_for_pop_out_.insert(tab_id);
316 for (ToolbarActionViewController* toolbar_action : toolbar_actions) {
317 if (toolbar_action->WantsToRun(web_contents))
318 popped_out_in_tabs_[toolbar_action].insert(tab_id);
319 }
320 }
321
322 // Then, shift any actions that want to run to the front.
323 size_t insert_at = 0;
324 // Rotate any actions that want to run to the boundary between visible and
325 // overflowed actions.
326 for (WeakToolbarActions::iterator iter =
327 toolbar_actions.begin() + model_->visible_icon_count();
328 iter != toolbar_actions.end(); ++iter) {
329 if (popped_out_in_tabs_[(*iter)].count(tab_id)) {
330 std::rotate(toolbar_actions.begin() + insert_at, iter, iter + 1);
331 ++insert_at;
332 }
333 }
334
335 return toolbar_actions;
336 }
337
338 void ToolbarActionsBar::TabOrderHelper::TabInsertedAt(
339 content::WebContents* web_contents,
340 int index,
341 bool foreground) {
342 if (foreground)
343 toolbar_->ReorderActions();
344 }
345
346 void ToolbarActionsBar::TabOrderHelper::TabDetachedAt(
347 content::WebContents* web_contents,
348 int index) {
349 int tab_id = SessionTabHelper::IdForTab(web_contents);
350 for (auto& tabs : popped_out_in_tabs_)
351 tabs.second.erase(tab_id);
352 tabs_checked_for_pop_out_.erase(tab_id);
353 }
354
355 void ToolbarActionsBar::TabOrderHelper::TabStripModelDeleted() {
356 tab_strip_observer_.RemoveAll();
357 }
358
67 ToolbarActionsBar::PlatformSettings::PlatformSettings(bool in_overflow_mode) 359 ToolbarActionsBar::PlatformSettings::PlatformSettings(bool in_overflow_mode)
68 : left_padding(in_overflow_mode ? kOverflowLeftPadding : kLeftPadding), 360 : left_padding(in_overflow_mode ? kOverflowLeftPadding : kLeftPadding),
69 right_padding(in_overflow_mode ? kOverflowRightPadding : kRightPadding), 361 right_padding(in_overflow_mode ? kOverflowRightPadding : kRightPadding),
70 item_spacing(kItemSpacing), 362 item_spacing(kItemSpacing),
71 icons_per_overflow_menu_row(1), 363 icons_per_overflow_menu_row(1),
72 chevron_enabled(!extensions::FeatureSwitch::extension_action_redesign()-> 364 chevron_enabled(!extensions::FeatureSwitch::extension_action_redesign()->
73 IsEnabled()) { 365 IsEnabled()) {
74 } 366 }
75 367
76 ToolbarActionsBar::ToolbarActionsBar(ToolbarActionsBarDelegate* delegate, 368 ToolbarActionsBar::ToolbarActionsBar(ToolbarActionsBarDelegate* delegate,
77 Browser* browser, 369 Browser* browser,
78 bool in_overflow_mode) 370 ToolbarActionsBar* main_bar)
79 : delegate_(delegate), 371 : delegate_(delegate),
80 browser_(browser), 372 browser_(browser),
81 model_(extensions::ExtensionToolbarModel::Get(browser_->profile())), 373 model_(extensions::ExtensionToolbarModel::Get(browser_->profile())),
82 in_overflow_mode_(in_overflow_mode), 374 main_bar_(main_bar),
83 platform_settings_(in_overflow_mode), 375 overflow_bar_(nullptr),
376 platform_settings_(main_bar != nullptr),
84 model_observer_(this), 377 model_observer_(this),
85 tab_strip_observer_(this),
86 suppress_layout_(false), 378 suppress_layout_(false),
87 suppress_animation_(true) { 379 suppress_animation_(true) {
88 if (model_) // |model_| can be null in unittests. 380 if (model_) // |model_| can be null in unittests.
89 model_observer_.Add(model_); 381 model_observer_.Add(model_);
90 tab_strip_observer_.Add(browser_->tab_strip_model()); 382 if (in_overflow_mode())
383 main_bar_->overflow_bar_ = this;
384 else if (pop_out_actions_to_run_)
385 tab_order_helper_.reset(new TabOrderHelper(this, browser_, model_));
91 } 386 }
92 387
93 ToolbarActionsBar::~ToolbarActionsBar() { 388 ToolbarActionsBar::~ToolbarActionsBar() {
94 // We don't just call DeleteActions() here because it makes assumptions about 389 // We don't just call DeleteActions() here because it makes assumptions about
95 // the order of deletion between the views and the ToolbarActionsBar. 390 // the order of deletion between the views and the ToolbarActionsBar.
96 DCHECK(toolbar_actions_.empty()) << 391 DCHECK(toolbar_actions_.empty()) <<
97 "Must call DeleteActions() before destruction."; 392 "Must call DeleteActions() before destruction.";
393 if (in_overflow_mode())
394 main_bar_->overflow_bar_ = nullptr;
395 DCHECK(overflow_bar_ == nullptr) << "Overflow bar cannot outlive main bar";
98 } 396 }
99 397
100 // static 398 // static
101 int ToolbarActionsBar::IconWidth(bool include_padding) { 399 int ToolbarActionsBar::IconWidth(bool include_padding) {
102 return GetIconDimension(WIDTH) + (include_padding ? kItemSpacing : 0); 400 return GetIconDimension(WIDTH) + (include_padding ? kItemSpacing : 0);
103 } 401 }
104 402
105 // static 403 // static
106 int ToolbarActionsBar::IconHeight() { 404 int ToolbarActionsBar::IconHeight() {
107 return GetIconDimension(HEIGHT); 405 return GetIconDimension(HEIGHT);
108 } 406 }
109 407
110 gfx::Size ToolbarActionsBar::GetPreferredSize() const { 408 gfx::Size ToolbarActionsBar::GetPreferredSize() const {
111 int icon_count = GetIconCount(); 409 int icon_count = GetIconCount();
112 if (in_overflow_mode_) { 410 if (in_overflow_mode()) {
113 // In overflow, we always have a preferred size of a full row (even if we 411 // In overflow, we always have a preferred size of a full row (even if we
114 // don't use it), and always of at least one row. The parent may decide to 412 // don't use it), and always of at least one row. The parent may decide to
115 // show us even when empty, e.g. as a drag target for dragging in icons from 413 // show us even when empty, e.g. as a drag target for dragging in icons from
116 // the main container. 414 // the main container.
117 int row_count = ((std::max(0, icon_count - 1)) / 415 int row_count = ((std::max(0, icon_count - 1)) /
118 platform_settings_.icons_per_overflow_menu_row) + 1; 416 platform_settings_.icons_per_overflow_menu_row) + 1;
119 return gfx::Size( 417 return gfx::Size(
120 IconCountToWidth(platform_settings_.icons_per_overflow_menu_row), 418 IconCountToWidth(platform_settings_.icons_per_overflow_menu_row),
121 row_count * IconHeight()); 419 row_count * IconHeight());
122 } 420 }
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
170 // Now we add an extra between-item padding value so the space can be divided 468 // Now we add an extra between-item padding value so the space can be divided
171 // evenly by (size of icon with padding). 469 // evenly by (size of icon with padding).
172 return static_cast<size_t>(std::max( 470 return static_cast<size_t>(std::max(
173 0, available_space + platform_settings_.item_spacing) / IconWidth(true)); 471 0, available_space + platform_settings_.item_spacing) / IconWidth(true));
174 } 472 }
175 473
176 size_t ToolbarActionsBar::GetIconCount() const { 474 size_t ToolbarActionsBar::GetIconCount() const {
177 if (!model_) 475 if (!model_)
178 return 0u; 476 return 0u;
179 477
180 size_t visible_icons = model_->visible_icon_count(); 478 size_t extra_icons = 0;
479 if (tab_order_helper_) {
480 extra_icons = tab_order_helper_->GetExtraIconCount(
481 SessionTabHelper::IdForTab(
482 browser_->tab_strip_model()->GetActiveWebContents()));
483 }
484
485 size_t visible_icons = in_overflow_mode() ?
486 toolbar_actions_.size() - main_bar_->GetIconCount() :
487 model_->visible_icon_count() + extra_icons;
488
181 #if DCHECK_IS_ON 489 #if DCHECK_IS_ON
182 // Good time for some sanity checks: We should never try to display more 490 // Good time for some sanity checks: We should never try to display more
183 // icons than we have, and we should always have a view per item in the model. 491 // icons than we have, and we should always have a view per item in the model.
184 // (The only exception is if this is in initialization.) 492 // (The only exception is if this is in initialization.)
185 if (!toolbar_actions_.empty() && !suppress_layout_ && 493 if (!toolbar_actions_.empty() && !suppress_layout_ &&
186 model_->extensions_initialized()) { 494 model_->extensions_initialized()) {
187 size_t num_extension_actions = 0u; 495 size_t num_extension_actions = 0u;
188 for (ToolbarActionViewController* action : toolbar_actions_) { 496 for (ToolbarActionViewController* action : toolbar_actions_) {
189 // No component action should ever have a valid extension id, so we can 497 // No component action should ever have a valid extension id, so we can
190 // use this to check the extension amount. 498 // use this to check the extension amount.
191 // TODO(devlin): Fix this to just check model size when the model also 499 // TODO(devlin): Fix this to just check model size when the model also
192 // includes component actions. 500 // includes component actions.
193 if (crx_file::id_util::IdIsValid(action->GetId())) 501 if (crx_file::id_util::IdIsValid(action->GetId()))
194 ++num_extension_actions; 502 ++num_extension_actions;
195 } 503 }
196 DCHECK_LE(visible_icons, num_extension_actions); 504 DCHECK_LE(visible_icons, num_extension_actions);
197 DCHECK_EQ(model_->toolbar_items().size(), num_extension_actions); 505 DCHECK_EQ(model_->toolbar_items().size(), num_extension_actions);
198 } 506 }
199 #endif 507 #endif
200 508
201 int tab_id = SessionTabHelper::IdForTab( 509 return visible_icons;
202 browser_->tab_strip_model()->GetActiveWebContents());
203 for (ToolbarActionViewController* action : toolbar_actions_) {
204 auto actions_tabs = popped_out_in_tabs_.find(action);
205 if (actions_tabs != popped_out_in_tabs_.end() &&
206 actions_tabs->second.count(tab_id))
207 ++visible_icons;
208 }
209
210 // The overflow displays any icons not shown by the main bar.
211 return in_overflow_mode_ ?
212 model_->toolbar_items().size() - visible_icons : visible_icons;
213 } 510 }
214 511
215 void ToolbarActionsBar::CreateActions() { 512 void ToolbarActionsBar::CreateActions() {
216 DCHECK(toolbar_actions_.empty()); 513 DCHECK(toolbar_actions_.empty());
217 // We wait for the extension system to be initialized before we add any 514 // We wait for the extension system to be initialized before we add any
218 // actions, as they rely on the extension system to function. 515 // actions, as they rely on the extension system to function.
219 if (!model_ || !model_->extensions_initialized()) 516 if (!model_ || !model_->extensions_initialized())
220 return; 517 return;
221 518
222 { 519 {
(...skipping 19 matching lines...) Expand all
242 ComponentToolbarActionsFactory::GetInstance()-> 539 ComponentToolbarActionsFactory::GetInstance()->
243 GetComponentToolbarActions(); 540 GetComponentToolbarActions();
244 DCHECK(component_actions.empty() || 541 DCHECK(component_actions.empty() ||
245 extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()); 542 extensions::FeatureSwitch::extension_action_redesign()->IsEnabled());
246 toolbar_actions_.insert(toolbar_actions_.end(), 543 toolbar_actions_.insert(toolbar_actions_.end(),
247 component_actions.begin(), 544 component_actions.begin(),
248 component_actions.end()); 545 component_actions.end());
249 component_actions.weak_clear(); 546 component_actions.weak_clear();
250 } 547 }
251 548
252 if (!toolbar_actions_.empty()) 549 if (!toolbar_actions_.empty()) {
253 ReorderActions(); 550 if (in_overflow_mode())
551 CopyActionOrder();
552 else
553 ReorderActions();
554 }
254 555
255 for (size_t i = 0; i < toolbar_actions_.size(); ++i) 556 for (size_t i = 0; i < toolbar_actions_.size(); ++i)
256 delegate_->AddViewForAction(toolbar_actions_[i], i); 557 delegate_->AddViewForAction(toolbar_actions_[i], i);
257 } 558 }
258 559
259 // Once the actions are created, we should animate the changes. 560 // Once the actions are created, we should animate the changes.
260 suppress_animation_ = false; 561 suppress_animation_ = false;
261 } 562 }
262 563
263 void ToolbarActionsBar::DeleteActions() { 564 void ToolbarActionsBar::DeleteActions() {
(...skipping 14 matching lines...) Expand all
278 // Don't layout until the end. 579 // Don't layout until the end.
279 base::AutoReset<bool> layout_resetter(&suppress_layout_, true); 580 base::AutoReset<bool> layout_resetter(&suppress_layout_, true);
280 for (ToolbarActionViewController* action : toolbar_actions_) 581 for (ToolbarActionViewController* action : toolbar_actions_)
281 action->UpdateState(); 582 action->UpdateState();
282 } 583 }
283 584
284 ReorderActions(); // Also triggers a draw. 585 ReorderActions(); // Also triggers a draw.
285 } 586 }
286 587
287 void ToolbarActionsBar::SetOverflowRowWidth(int width) { 588 void ToolbarActionsBar::SetOverflowRowWidth(int width) {
288 DCHECK(in_overflow_mode_); 589 DCHECK(in_overflow_mode());
289 platform_settings_.icons_per_overflow_menu_row = 590 platform_settings_.icons_per_overflow_menu_row =
290 std::max((width - kItemSpacing) / IconWidth(true), 1); 591 std::max((width - kItemSpacing) / IconWidth(true), 1);
291 } 592 }
292 593
293 void ToolbarActionsBar::OnResizeComplete(int width) { 594 void ToolbarActionsBar::OnResizeComplete(int width) {
294 DCHECK(!in_overflow_mode_); // The user can't resize the overflow container. 595 DCHECK(!in_overflow_mode()); // The user can't resize the overflow container.
295 size_t resized_count = WidthToIconCount(width); 596 size_t resized_count = WidthToIconCount(width);
296 size_t tab_icon_count = GetIconCount(); 597 // Save off the desired number of visible icons. We do this now instead of
297 int tab_id = SessionTabHelper::IdForTab(GetCurrentWebContents()); 598 // at the end of the animation so that even if the browser is shut down
298 int delta = tab_icon_count - model_->visible_icon_count(); 599 // while animating, the right value will be restored on next run.
299 bool reorder_necessary = false; 600 if (tab_order_helper_) {
300 if (resized_count < tab_icon_count) { 601 tab_order_helper_->HandleResize(
301 for (int i = resized_count; i < delta; ++i) { 602 resized_count,
302 // If an extension that was popped out to act is overflowed, then it 603 SessionTabHelper::IdForTab(GetCurrentWebContents()));
303 // should no longer be popped out, and it also doesn't count for adjusting
304 // the visible count (since it wasn't really out to begin with).
305 if (popped_out_in_tabs_[toolbar_actions_[i]].count(tab_id)) {
306 reorder_necessary = true;
307 popped_out_in_tabs_[toolbar_actions_[i]].erase(tab_id);
308 ++resized_count;
309 }
310 }
311 } else { 604 } else {
312 // If the user increases the toolbar size while actions that popped out are 605 model_->SetVisibleIconCount(resized_count);
313 // visible, we need to re-arrange the icons in other windows to be
314 // consistent with what the user sees.
315 // That is, if the normal order is A, B, [C, D] (with C and D hidden), C
316 // pops out to act, and then the user increases the size of the toolbar,
317 // the user sees uncovering D (since C is already out). This is what should
318 // happen in all windows.
319 for (size_t i = tab_icon_count; i < resized_count; ++i) {
320 if (toolbar_actions_[i]->GetId() !=
321 model_->toolbar_items()[i - delta]->id())
322 model_->MoveExtensionIcon(toolbar_actions_[i]->GetId(), i - delta);
323 }
324 } 606 }
325 resized_count -= delta;
326 // Save off the desired number of visible icons. We do this now instead of at
327 // the end of the animation so that even if the browser is shut down while
328 // animating, the right value will be restored on next run.
329 model_->SetVisibleIconCount(resized_count);
330 if (reorder_necessary)
331 ReorderActions();
332 } 607 }
333 608
334 void ToolbarActionsBar::OnDragDrop(int dragged_index, 609 void ToolbarActionsBar::OnDragDrop(int dragged_index,
335 int dropped_index, 610 int dropped_index,
336 DragType drag_type) { 611 DragType drag_type) {
337 ToolbarActionViewController* action = toolbar_actions_[dragged_index]; 612 // All drag-and-drop commands should go to the main bar.
338 int tab_id = SessionTabHelper::IdForTab(GetCurrentWebContents()); 613 DCHECK(!in_overflow_mode());
339 int delta = 0; 614
340 switch (drag_type) { 615 if (tab_order_helper_) {
341 case DRAG_TO_OVERFLOW: 616 tab_order_helper_->HandleDragDrop(
342 // If the user moves an action back into overflow, then we don't adjust 617 dragged_index,
343 // the base visible count, but do stop popping that action out. 618 dropped_index,
344 if (popped_out_in_tabs_[action].count(tab_id)) 619 drag_type,
345 popped_out_in_tabs_[action].erase(tab_id); 620 SessionTabHelper::IdForTab(GetCurrentWebContents()));
346 else 621 } else {
347 delta = -1; 622 int delta = 0;
348 break; 623 if (drag_type == DRAG_TO_OVERFLOW)
349 case DRAG_TO_MAIN: 624 delta = -1;
625 else if (drag_type == DRAG_TO_MAIN)
350 delta = 1; 626 delta = 1;
351 break; 627 model_->MoveExtensionIcon(toolbar_actions_[dragged_index]->GetId(),
352 case DRAG_TO_SAME: 628 dropped_index);
353 // If the user moves an action that had popped out to be on the toolbar, 629 if (delta)
354 // then we treat it as "pinning" the action, and adjust the base visible 630 model_->SetVisibleIconCount(model_->visible_icon_count() + delta);
355 // count to accommodate.
356 if (popped_out_in_tabs_[action].count(tab_id)) {
357 delta = 1;
358 popped_out_in_tabs_[action].erase(tab_id);
359 }
360 break;
361 } 631 }
362
363 // If there are any actions that are in front of the dropped index only
364 // because they were popped out, decrement the dropped index.
365 for (int i = 0; i < dropped_index; ++i) {
366 if (i != dragged_index &&
367 model_->GetIndexForId(toolbar_actions_[i]->GetId()) >= dropped_index)
368 --dropped_index;
369 }
370
371 model_->MoveExtensionIcon(action->GetId(), dropped_index);
372
373 if (delta)
374 model_->SetVisibleIconCount(model_->visible_icon_count() + delta);
375 } 632 }
376 633
377 void ToolbarActionsBar::ToolbarExtensionAdded( 634 void ToolbarActionsBar::ToolbarExtensionAdded(
378 const extensions::Extension* extension, 635 const extensions::Extension* extension,
379 int index) { 636 int index) {
380 DCHECK(GetActionForId(extension->id()) == nullptr) << 637 DCHECK(GetActionForId(extension->id()) == nullptr) <<
381 "Asked to add a toolbar action view for an extension that already exists"; 638 "Asked to add a toolbar action view for an extension that already exists";
382 639
383 toolbar_actions_.insert( 640 toolbar_actions_.insert(
384 toolbar_actions_.begin() + index, 641 toolbar_actions_.begin() + index,
(...skipping 25 matching lines...) Expand all
410 void ToolbarActionsBar::ToolbarExtensionRemoved( 667 void ToolbarActionsBar::ToolbarExtensionRemoved(
411 const extensions::Extension* extension) { 668 const extensions::Extension* extension) {
412 ToolbarActions::iterator iter = toolbar_actions_.begin(); 669 ToolbarActions::iterator iter = toolbar_actions_.begin();
413 while (iter != toolbar_actions_.end() && (*iter)->GetId() != extension->id()) 670 while (iter != toolbar_actions_.end() && (*iter)->GetId() != extension->id())
414 ++iter; 671 ++iter;
415 672
416 if (iter == toolbar_actions_.end()) 673 if (iter == toolbar_actions_.end())
417 return; 674 return;
418 675
419 delegate_->RemoveViewForAction(*iter); 676 delegate_->RemoveViewForAction(*iter);
420 popped_out_in_tabs_.erase(*iter); 677 if (tab_order_helper_)
678 tab_order_helper_->ActionRemoved(*iter);
421 toolbar_actions_.erase(iter); 679 toolbar_actions_.erase(iter);
422 680
423 // If the extension is being upgraded we don't want the bar to shrink 681 // If the extension is being upgraded we don't want the bar to shrink
424 // because the icon is just going to get re-added to the same location. 682 // because the icon is just going to get re-added to the same location.
425 if (!extensions::ExtensionSystem::Get(browser_->profile())->runtime_data()-> 683 if (!extensions::ExtensionSystem::Get(browser_->profile())->runtime_data()->
426 IsBeingUpgraded(extension->id())) { 684 IsBeingUpgraded(extension->id())) {
427 if (toolbar_actions_.size() > model_->visible_icon_count()) { 685 if (toolbar_actions_.size() > model_->visible_icon_count()) {
428 // If we have more icons than we can show, then we must not be changing 686 // If we have more icons than we can show, then we must not be changing
429 // the container size (since we either removed an icon from the main 687 // the container size (since we either removed an icon from the main
430 // area and one from the overflow list will have shifted in, or we 688 // area and one from the overflow list will have shifted in, or we
(...skipping 19 matching lines...) Expand all
450 } 708 }
451 709
452 void ToolbarActionsBar::ToolbarExtensionUpdated( 710 void ToolbarActionsBar::ToolbarExtensionUpdated(
453 const extensions::Extension* extension) { 711 const extensions::Extension* extension) {
454 ToolbarActionViewController* action = GetActionForId(extension->id()); 712 ToolbarActionViewController* action = GetActionForId(extension->id());
455 // There might not be a view in cases where we are highlighting or if we 713 // There might not be a view in cases where we are highlighting or if we
456 // haven't fully initialized the actions. 714 // haven't fully initialized the actions.
457 if (action) { 715 if (action) {
458 content::WebContents* web_contents = GetCurrentWebContents(); 716 content::WebContents* web_contents = GetCurrentWebContents();
459 action->UpdateState(); 717 action->UpdateState();
460 bool wants_to_run = action->WantsToRun(web_contents);
461 718
462 // The action may need to be popped in or out of overflow. 719 if (tab_order_helper_) {
463 int index = std::find(toolbar_actions_.begin(), 720 tab_order_helper_->SetActionWantsToRun(
464 toolbar_actions_.end(), 721 action,
465 action) - toolbar_actions_.begin(); 722 SessionTabHelper::IdForTab(web_contents),
466 bool reorder_necessary = false; 723 action->WantsToRun(web_contents));
467 int tab_id = SessionTabHelper::IdForTab(web_contents);
468 if (wants_to_run && static_cast<size_t>(index) >= GetIconCount()) {
469 popped_out_in_tabs_[action].insert(tab_id);
470 reorder_necessary = true;
471 } else if (!wants_to_run && popped_out_in_tabs_[action].count(tab_id)) {
472 popped_out_in_tabs_[action].erase(tab_id);
473 reorder_necessary = true;
474 } 724 }
475 if (reorder_necessary)
476 ReorderActions();
477 } 725 }
478 } 726 }
479 727
480 bool ToolbarActionsBar::ShowExtensionActionPopup( 728 bool ToolbarActionsBar::ShowExtensionActionPopup(
481 const extensions::Extension* extension, 729 const extensions::Extension* extension,
482 bool grant_active_tab) { 730 bool grant_active_tab) {
483 // Don't override another popup, and only show in the active window. 731 // Don't override another popup, and only show in the active window.
484 if (delegate_->IsPopupRunning() || !browser_->window()->IsActive()) 732 if (delegate_->IsPopupRunning() || !browser_->window()->IsActive())
485 return false; 733 return false;
486 734
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
528 // We shouldn't have any actions before the model is initialized. 776 // We shouldn't have any actions before the model is initialized.
529 DCHECK(toolbar_actions_.empty()); 777 DCHECK(toolbar_actions_.empty());
530 CreateActions(); 778 CreateActions();
531 ResizeDelegate(gfx::Tween::EASE_OUT, false); 779 ResizeDelegate(gfx::Tween::EASE_OUT, false);
532 } 780 }
533 781
534 Browser* ToolbarActionsBar::GetBrowser() { 782 Browser* ToolbarActionsBar::GetBrowser() {
535 return browser_; 783 return browser_;
536 } 784 }
537 785
538 void ToolbarActionsBar::TabInsertedAt(content::WebContents* web_contents,
539 int index,
540 bool foreground) {
541 if (foreground)
542 ReorderActions();
543 }
544
545 void ToolbarActionsBar::TabDetachedAt(content::WebContents* web_contents,
546 int index) {
547 int tab_id = SessionTabHelper::IdForTab(web_contents);
548 for (auto& tabs : popped_out_in_tabs_)
549 tabs.second.erase(tab_id);
550 tabs_checked_for_pop_out_.erase(tab_id);
551 }
552
553 void ToolbarActionsBar::TabStripModelDeleted() {
554 tab_strip_observer_.RemoveAll();
555 }
556
557 void ToolbarActionsBar::ReorderActions() { 786 void ToolbarActionsBar::ReorderActions() {
558 if (toolbar_actions_.empty()) 787 if (toolbar_actions_.empty() || in_overflow_mode())
559 return; 788 return;
560 789
561 // First, reset the order to that of the model. Run through the views and 790 // First, reset the order to that of the model.
562 // compare them to the model; if something is out of place, find the correct 791 auto compare = [](ToolbarActionViewController* const& action,
563 // spot for it. 792 const scoped_refptr<const extensions::Extension>& ext) {
564 const extensions::ExtensionList& model_order = model_->toolbar_items(); 793 return action->GetId() == ext->id();
565 for (int i = 0; i < static_cast<int>(model_order.size() - 1); ++i) { 794 };
566 if (model_order[i]->id() != toolbar_actions_[i]->GetId()) { 795 SortContainer(&toolbar_actions_.get(), model_->toolbar_items(), compare);
567 // Find where the correct view is (it's guaranteed to be after our current
568 // index, since everything up to this point is correct).
569 size_t j = i + 1;
570 while (model_order[i]->id() != toolbar_actions_[j]->GetId())
571 ++j;
572 std::swap(toolbar_actions_[i], toolbar_actions_[j]);
573 }
574 }
575 796
576 // Only adjust the order if the model isn't highlighting a particular subset. 797 // Only adjust the order if the model isn't highlighting a particular
577 if (!model_->is_highlighting()) { 798 // subset (and the specialized tab order is enabled).
578 // First, make sure that we've checked any actions that want to run. 799 if (!model_->is_highlighting() && tab_order_helper_) {
579 content::WebContents* web_contents = GetCurrentWebContents(); 800 WeakToolbarActions new_order =
580 int tab_id = SessionTabHelper::IdForTab(web_contents); 801 tab_order_helper_->GetActionOrder(GetCurrentWebContents());
581 if (!tabs_checked_for_pop_out_.count(tab_id)) { 802 auto compare = [](ToolbarActionViewController* const& first,
582 tabs_checked_for_pop_out_.insert(tab_id); 803 ToolbarActionViewController* const& second) {
583 for (ToolbarActionViewController* toolbar_action : toolbar_actions_) { 804 return first == second;
584 if (toolbar_action->WantsToRun(web_contents)) 805 };
585 popped_out_in_tabs_[toolbar_action].insert(tab_id); 806 SortContainer(
586 } 807 &toolbar_actions_.get(), new_order, compare);
587 }
588
589 // Then, shift any actions that want to run to the front.
590 size_t insert_at = 0;
591 // Rotate any actions that want to run to the boundary between visible and
592 // overflowed actions.
593 for (ToolbarActions::iterator iter =
594 toolbar_actions_.begin() + model_->visible_icon_count();
595 iter != toolbar_actions_.end(); ++iter) {
596 if (popped_out_in_tabs_[(*iter)].count(tab_id)) {
597 std::rotate(toolbar_actions_.begin() + insert_at, iter, iter + 1);
598 ++insert_at;
599 }
600 }
601 } 808 }
602 809
603 // Our visible browser actions may have changed - re-Layout() and check the 810 // Our visible browser actions may have changed - re-Layout() and check the
604 // size (if we aren't suppressing the layout). 811 // size (if we aren't suppressing the layout).
605 if (!suppress_layout_) { 812 if (!suppress_layout_) {
606 ResizeDelegate(gfx::Tween::EASE_OUT, false); 813 ResizeDelegate(gfx::Tween::EASE_OUT, false);
607 delegate_->Redraw(true); 814 delegate_->Redraw(true);
608 } 815 }
816
817 if (overflow_bar_)
818 overflow_bar_->CopyActionOrder();
819 }
820
821 void ToolbarActionsBar::CopyActionOrder() {
822 DCHECK(in_overflow_mode());
823 if (!main_bar_->toolbar_actions().empty()) {
824 auto compare = [](ToolbarActionViewController* const& first,
825 ToolbarActionViewController* const& second) {
826 return first->GetId() == second->GetId();
827 };
828 SortContainer(
829 &toolbar_actions_.get(), main_bar_->toolbar_actions(), compare);
830 if (!suppress_layout_) {
831 ResizeDelegate(gfx::Tween::EASE_OUT, false);
832 delegate_->Redraw(true);
833 }
834 }
609 } 835 }
610 836
611 ToolbarActionViewController* ToolbarActionsBar::GetActionForId( 837 ToolbarActionViewController* ToolbarActionsBar::GetActionForId(
612 const std::string& id) { 838 const std::string& id) {
613 for (ToolbarActionViewController* action : toolbar_actions_) { 839 for (ToolbarActionViewController* action : toolbar_actions_) {
614 if (action->GetId() == id) 840 if (action->GetId() == id)
615 return action; 841 return action;
616 } 842 }
617 return nullptr; 843 return nullptr;
618 } 844 }
619 845
620 content::WebContents* ToolbarActionsBar::GetCurrentWebContents() { 846 content::WebContents* ToolbarActionsBar::GetCurrentWebContents() {
621 return browser_->tab_strip_model()->GetActiveWebContents(); 847 return browser_->tab_strip_model()->GetActiveWebContents();
622 } 848 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/toolbar/toolbar_actions_bar.h ('k') | chrome/browser/ui/toolbar/toolbar_actions_bar_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698