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

Side by Side Diff: chrome/browser/ui/views/browser_actions_container.cc

Issue 71743002: [Toolbar Views] Move toolbar files to a new subdirectory. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 1 month 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 | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/ui/views/browser_actions_container.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/stl_util.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/extensions/extension_system.h"
12 #include "chrome/browser/extensions/extension_util.h"
13 #include "chrome/browser/extensions/tab_helper.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/sessions/session_tab_helper.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_window.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/browser/ui/view_ids.h"
20 #include "chrome/browser/ui/views/browser_action_view.h"
21 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
22 #include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views .h"
23 #include "chrome/browser/ui/views/extensions/extension_popup.h"
24 #include "chrome/browser/ui/views/toolbar_view.h"
25 #include "chrome/common/pref_names.h"
26 #include "grit/generated_resources.h"
27 #include "grit/theme_resources.h"
28 #include "grit/ui_resources.h"
29 #include "ui/base/accessibility/accessible_view_state.h"
30 #include "ui/base/dragdrop/drag_utils.h"
31 #include "ui/base/l10n/l10n_util.h"
32 #include "ui/base/resource/resource_bundle.h"
33 #include "ui/base/theme_provider.h"
34 #include "ui/gfx/animation/slide_animation.h"
35 #include "ui/gfx/canvas.h"
36 #include "ui/views/controls/resize_area.h"
37 #include "ui/views/metrics.h"
38 #include "ui/views/widget/widget.h"
39
40 using extensions::Extension;
41
42 namespace {
43
44 // Horizontal spacing between most items in the container, as well as after the
45 // last item or chevron (if visible).
46 const int kItemSpacing = ToolbarView::kStandardSpacing;
47
48 // Horizontal spacing before the chevron (if visible).
49 const int kChevronSpacing = kItemSpacing - 2;
50
51 } // namespace
52
53 // static
54 bool BrowserActionsContainer::disable_animations_during_testing_ = false;
55
56 ////////////////////////////////////////////////////////////////////////////////
57 // BrowserActionsContainer
58
59 BrowserActionsContainer::BrowserActionsContainer(Browser* browser,
60 View* owner_view)
61 : profile_(browser->profile()),
62 browser_(browser),
63 owner_view_(owner_view),
64 popup_(NULL),
65 popup_button_(NULL),
66 model_(NULL),
67 container_width_(0),
68 chevron_(NULL),
69 overflow_menu_(NULL),
70 suppress_chevron_(false),
71 resize_amount_(0),
72 animation_target_size_(0),
73 drop_indicator_position_(-1),
74 task_factory_(this),
75 show_menu_task_factory_(this) {
76 set_id(VIEW_ID_BROWSER_ACTION_TOOLBAR);
77
78 model_ = ExtensionToolbarModel::Get(browser->profile());
79 if (model_)
80 model_->AddObserver(this);
81
82 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews(
83 browser->profile(),
84 owner_view->GetFocusManager(),
85 extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS,
86 this));
87
88 resize_animation_.reset(new gfx::SlideAnimation(this));
89 resize_area_ = new views::ResizeArea(this);
90 AddChildView(resize_area_);
91
92 chevron_ = new views::MenuButton(NULL, string16(), this, false);
93 chevron_->set_border(NULL);
94 chevron_->EnableCanvasFlippingForRTLUI(true);
95 chevron_->SetAccessibleName(
96 l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS_CHEVRON));
97 chevron_->SetVisible(false);
98 AddChildView(chevron_);
99 }
100
101 BrowserActionsContainer::~BrowserActionsContainer() {
102 if (overflow_menu_)
103 overflow_menu_->set_observer(NULL);
104 if (model_)
105 model_->RemoveObserver(this);
106 StopShowFolderDropMenuTimer();
107 if (popup_)
108 popup_->GetWidget()->RemoveObserver(this);
109 HidePopup();
110 DeleteBrowserActionViews();
111 }
112
113 void BrowserActionsContainer::Init() {
114 LoadImages();
115
116 // We wait to set the container width until now so that the chevron images
117 // will be loaded. The width calculation needs to know the chevron size.
118 if (model_ &&
119 !profile_->GetPrefs()->HasPrefPath(prefs::kExtensionToolbarSize)) {
120 // Migration code to the new VisibleIconCount pref.
121 // TODO(mpcomplete): remove this after users are upgraded to 5.0.
122 int predefined_width =
123 profile_->GetPrefs()->GetInteger(prefs::kBrowserActionContainerWidth);
124 if (predefined_width != 0)
125 model_->SetVisibleIconCount(WidthToIconCount(predefined_width));
126 }
127 if (model_ && model_->extensions_initialized())
128 SetContainerWidth();
129 }
130
131 BrowserActionView* BrowserActionsContainer::GetBrowserActionView(
132 ExtensionAction* action) {
133 for (BrowserActionViews::iterator i(browser_action_views_.begin());
134 i != browser_action_views_.end(); ++i) {
135 if ((*i)->button()->browser_action() == action)
136 return *i;
137 }
138 return NULL;
139 }
140
141 void BrowserActionsContainer::RefreshBrowserActionViews() {
142 for (size_t i = 0; i < browser_action_views_.size(); ++i)
143 browser_action_views_[i]->button()->UpdateState();
144 }
145
146 void BrowserActionsContainer::CreateBrowserActionViews() {
147 DCHECK(browser_action_views_.empty());
148 if (!model_)
149 return;
150
151 const extensions::ExtensionList& toolbar_items = model_->toolbar_items();
152 for (extensions::ExtensionList::const_iterator i(toolbar_items.begin());
153 i != toolbar_items.end(); ++i) {
154 if (!ShouldDisplayBrowserAction(i->get()))
155 continue;
156
157 BrowserActionView* view = new BrowserActionView(i->get(), browser_, this);
158 browser_action_views_.push_back(view);
159 AddChildView(view);
160 }
161 }
162
163 void BrowserActionsContainer::DeleteBrowserActionViews() {
164 HidePopup();
165 STLDeleteElements(&browser_action_views_);
166 }
167
168 size_t BrowserActionsContainer::VisibleBrowserActions() const {
169 size_t visible_actions = 0;
170 for (size_t i = 0; i < browser_action_views_.size(); ++i) {
171 if (browser_action_views_[i]->visible())
172 ++visible_actions;
173 }
174 return visible_actions;
175 }
176
177 gfx::Size BrowserActionsContainer::GetPreferredSize() {
178 if (browser_action_views_.empty())
179 return gfx::Size(ToolbarView::kStandardSpacing, 0);
180
181 // We calculate the size of the view by taking the current width and
182 // subtracting resize_amount_ (the latter represents how far the user is
183 // resizing the view or, if animating the snapping, how far to animate it).
184 // But we also clamp it to a minimum size and the maximum size, so that the
185 // container can never shrink too far or take up more space than it needs. In
186 // other words: ContainerMinSize() < width() - resize < ClampTo(MAX).
187 int clamped_width = std::min(
188 std::max(ContainerMinSize(), container_width_ - resize_amount_),
189 IconCountToWidth(-1, false));
190 return gfx::Size(clamped_width, 0);
191 }
192
193 void BrowserActionsContainer::Layout() {
194 if (browser_action_views_.empty()) {
195 SetVisible(false);
196 return;
197 }
198
199 SetVisible(true);
200 resize_area_->SetBounds(0, ToolbarView::kVertSpacing, kItemSpacing,
201 IconHeight());
202
203 // If the icons don't all fit, show the chevron (unless suppressed).
204 int max_x = GetPreferredSize().width();
205 if ((IconCountToWidth(-1, false) > max_x) && !suppress_chevron_) {
206 chevron_->SetVisible(true);
207 gfx::Size chevron_size(chevron_->GetPreferredSize());
208 max_x -=
209 ToolbarView::kStandardSpacing + chevron_size.width() + kChevronSpacing;
210 chevron_->SetBounds(
211 width() - ToolbarView::kStandardSpacing - chevron_size.width(),
212 ToolbarView::kVertSpacing, chevron_size.width(), chevron_size.height());
213 } else {
214 chevron_->SetVisible(false);
215 }
216
217 // Now draw the icons for the browser actions in the available space.
218 int icon_width = IconWidth(false);
219 for (size_t i = 0; i < browser_action_views_.size(); ++i) {
220 BrowserActionView* view = browser_action_views_[i];
221 int x = ToolbarView::kStandardSpacing + (i * IconWidth(true));
222 if (x + icon_width <= max_x) {
223 view->SetBounds(x, 0, icon_width, height());
224 view->SetVisible(true);
225 } else {
226 view->SetVisible(false);
227 }
228 }
229 }
230
231 bool BrowserActionsContainer::GetDropFormats(
232 int* formats,
233 std::set<OSExchangeData::CustomFormat>* custom_formats) {
234 custom_formats->insert(BrowserActionDragData::GetBrowserActionCustomFormat());
235
236 return true;
237 }
238
239 bool BrowserActionsContainer::AreDropTypesRequired() {
240 return true;
241 }
242
243 bool BrowserActionsContainer::CanDrop(const OSExchangeData& data) {
244 BrowserActionDragData drop_data;
245 return drop_data.Read(data) ? drop_data.IsFromProfile(profile_) : false;
246 }
247
248 void BrowserActionsContainer::OnDragEntered(
249 const ui::DropTargetEvent& event) {
250 }
251
252 int BrowserActionsContainer::OnDragUpdated(
253 const ui::DropTargetEvent& event) {
254 // First check if we are above the chevron (overflow) menu.
255 if (GetEventHandlerForPoint(event.location()) == chevron_) {
256 if (!show_menu_task_factory_.HasWeakPtrs() && !overflow_menu_)
257 StartShowFolderDropMenuTimer();
258 return ui::DragDropTypes::DRAG_MOVE;
259 }
260 StopShowFolderDropMenuTimer();
261
262 // Figure out where to display the indicator. This is a complex calculation:
263
264 // First, we figure out how much space is to the left of the icon area, so we
265 // can calculate the true offset into the icon area.
266 int width_before_icons = ToolbarView::kStandardSpacing +
267 (base::i18n::IsRTL() ?
268 (chevron_->GetPreferredSize().width() + kChevronSpacing) : 0);
269 int offset_into_icon_area = event.x() - width_before_icons;
270
271 // Next, we determine which icon to place the indicator in front of. We want
272 // to place the indicator in front of icon n when the cursor is between the
273 // midpoints of icons (n - 1) and n. To do this we take the offset into the
274 // icon area and transform it as follows:
275 //
276 // Real icon area:
277 // 0 a * b c
278 // | | | |
279 // |[IC|ON] [IC|ON] [IC|ON]
280 // We want to be before icon 0 for 0 < x <= a, icon 1 for a < x <= b, etc.
281 // Here the "*" represents the offset into the icon area, and since it's
282 // between a and b, we want to return "1".
283 //
284 // Transformed "icon area":
285 // 0 a * b c
286 // | | | |
287 // |[ICON] |[ICON] |[ICON] |
288 // If we shift both our offset and our divider points later by half an icon
289 // plus one spacing unit, then it becomes very easy to calculate how many
290 // divider points we've passed, because they're the multiples of "one icon
291 // plus padding".
292 int before_icon_unclamped = (offset_into_icon_area + (IconWidth(false) / 2) +
293 kItemSpacing) / IconWidth(true);
294
295 // Because the user can drag outside the container bounds, we need to clamp to
296 // the valid range. Note that the maximum allowable value is (num icons), not
297 // (num icons - 1), because we represent the indicator being past the last
298 // icon as being "before the (last + 1) icon".
299 int before_icon = std::min(std::max(before_icon_unclamped, 0),
300 static_cast<int>(VisibleBrowserActions()));
301
302 // Now we convert back to a pixel offset into the container. We want to place
303 // the center of the drop indicator at the midpoint of the space before our
304 // chosen icon.
305 SetDropIndicator(width_before_icons + (before_icon * IconWidth(true)) -
306 (kItemSpacing / 2));
307
308 return ui::DragDropTypes::DRAG_MOVE;
309 }
310
311 void BrowserActionsContainer::OnDragExited() {
312 StopShowFolderDropMenuTimer();
313 drop_indicator_position_ = -1;
314 SchedulePaint();
315 }
316
317 int BrowserActionsContainer::OnPerformDrop(
318 const ui::DropTargetEvent& event) {
319 BrowserActionDragData data;
320 if (!data.Read(event.data()))
321 return ui::DragDropTypes::DRAG_NONE;
322
323 // Make sure we have the same view as we started with.
324 DCHECK_EQ(browser_action_views_[data.index()]->button()->extension()->id(),
325 data.id());
326 DCHECK(model_);
327
328 size_t i = 0;
329 for (; i < browser_action_views_.size(); ++i) {
330 int view_x = browser_action_views_[i]->GetMirroredBounds().x();
331 if (!browser_action_views_[i]->visible() ||
332 (base::i18n::IsRTL() ? (view_x < drop_indicator_position_) :
333 (view_x >= drop_indicator_position_))) {
334 // We have reached the end of the visible icons or found one that has a
335 // higher x position than the drop point.
336 break;
337 }
338 }
339
340 // |i| now points to the item to the right of the drop indicator*, which is
341 // correct when dragging an icon to the left. When dragging to the right,
342 // however, we want the icon being dragged to get the index of the item to
343 // the left of the drop indicator, so we subtract one.
344 // * Well, it can also point to the end, but not when dragging to the left. :)
345 if (i > data.index())
346 --i;
347
348 if (profile_->IsOffTheRecord())
349 i = model_->IncognitoIndexToOriginal(i);
350
351 model_->MoveBrowserAction(
352 browser_action_views_[data.index()]->button()->extension(), i);
353
354 OnDragExited(); // Perform clean up after dragging.
355 return ui::DragDropTypes::DRAG_MOVE;
356 }
357
358 void BrowserActionsContainer::GetAccessibleState(
359 ui::AccessibleViewState* state) {
360 state->role = ui::AccessibilityTypes::ROLE_GROUPING;
361 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS);
362 }
363
364 void BrowserActionsContainer::OnMenuButtonClicked(views::View* source,
365 const gfx::Point& point) {
366 if (source == chevron_) {
367 overflow_menu_ = new BrowserActionOverflowMenuController(
368 this, browser_, chevron_, browser_action_views_,
369 VisibleBrowserActions());
370 overflow_menu_->set_observer(this);
371 overflow_menu_->RunMenu(GetWidget(), false);
372 }
373 }
374
375 void BrowserActionsContainer::WriteDragDataForView(View* sender,
376 const gfx::Point& press_pt,
377 OSExchangeData* data) {
378 DCHECK(data);
379
380 for (size_t i = 0; i < browser_action_views_.size(); ++i) {
381 BrowserActionButton* button = browser_action_views_[i]->button();
382 if (button == sender) {
383 // Set the dragging image for the icon.
384 gfx::ImageSkia badge(browser_action_views_[i]->GetIconWithBadge());
385 drag_utils::SetDragImageOnDataObject(badge, button->size(),
386 press_pt.OffsetFromOrigin(),
387 data);
388
389 // Fill in the remaining info.
390 BrowserActionDragData drag_data(
391 browser_action_views_[i]->button()->extension()->id(), i);
392 drag_data.Write(profile_, data);
393 break;
394 }
395 }
396 }
397
398 int BrowserActionsContainer::GetDragOperationsForView(View* sender,
399 const gfx::Point& p) {
400 return ui::DragDropTypes::DRAG_MOVE;
401 }
402
403 bool BrowserActionsContainer::CanStartDragForView(View* sender,
404 const gfx::Point& press_pt,
405 const gfx::Point& p) {
406 return true;
407 }
408
409 void BrowserActionsContainer::OnResize(int resize_amount, bool done_resizing) {
410 if (!done_resizing) {
411 resize_amount_ = resize_amount;
412 OnBrowserActionVisibilityChanged();
413 return;
414 }
415
416 // Up until now we've only been modifying the resize_amount, but now it is
417 // time to set the container size to the size we have resized to, and then
418 // animate to the nearest icon count size if necessary (which may be 0).
419 int max_width = IconCountToWidth(-1, false);
420 container_width_ =
421 std::min(std::max(0, container_width_ - resize_amount), max_width);
422 SaveDesiredSizeAndAnimate(gfx::Tween::EASE_OUT,
423 WidthToIconCount(container_width_));
424 }
425
426 void BrowserActionsContainer::AnimationProgressed(
427 const gfx::Animation* animation) {
428 DCHECK_EQ(resize_animation_.get(), animation);
429 resize_amount_ = static_cast<int>(resize_animation_->GetCurrentValue() *
430 (container_width_ - animation_target_size_));
431 OnBrowserActionVisibilityChanged();
432 }
433
434 void BrowserActionsContainer::AnimationEnded(const gfx::Animation* animation) {
435 container_width_ = animation_target_size_;
436 animation_target_size_ = 0;
437 resize_amount_ = 0;
438 OnBrowserActionVisibilityChanged();
439 suppress_chevron_ = false;
440 }
441
442 void BrowserActionsContainer::NotifyMenuDeleted(
443 BrowserActionOverflowMenuController* controller) {
444 DCHECK_EQ(overflow_menu_, controller);
445 overflow_menu_ = NULL;
446 }
447
448 void BrowserActionsContainer::OnWidgetDestroying(views::Widget* widget) {
449 DCHECK_EQ(popup_->GetWidget(), widget);
450 popup_->GetWidget()->RemoveObserver(this);
451 popup_ = NULL;
452 // |popup_button_| is NULL if the extension has been removed.
453 if (popup_button_) {
454 popup_button_->SetButtonNotPushed();
455 popup_button_ = NULL;
456 }
457 }
458
459 void BrowserActionsContainer::InspectPopup(ExtensionAction* action) {
460 BrowserActionView* view = GetBrowserActionView(action);
461 ShowPopup(view->button(), ExtensionPopup::SHOW_AND_INSPECT, true);
462 }
463
464 int BrowserActionsContainer::GetCurrentTabId() const {
465 content::WebContents* active_tab =
466 browser_->tab_strip_model()->GetActiveWebContents();
467 if (!active_tab)
468 return -1;
469
470 return SessionTabHelper::FromWebContents(active_tab)->session_id().id();
471 }
472
473 void BrowserActionsContainer::OnBrowserActionExecuted(
474 BrowserActionButton* button) {
475 ShowPopup(button, ExtensionPopup::SHOW, true);
476 }
477
478 void BrowserActionsContainer::OnBrowserActionVisibilityChanged() {
479 SetVisible(!browser_action_views_.empty());
480 owner_view_->Layout();
481 owner_view_->SchedulePaint();
482 }
483
484 gfx::Point BrowserActionsContainer::GetViewContentOffset() const {
485 return gfx::Point(0, ToolbarView::kVertSpacing);
486 }
487
488 extensions::ActiveTabPermissionGranter*
489 BrowserActionsContainer::GetActiveTabPermissionGranter() {
490 content::WebContents* web_contents =
491 browser_->tab_strip_model()->GetActiveWebContents();
492 if (!web_contents)
493 return NULL;
494 return extensions::TabHelper::FromWebContents(web_contents)->
495 active_tab_permission_granter();
496 }
497
498 void BrowserActionsContainer::MoveBrowserAction(const std::string& extension_id,
499 size_t new_index) {
500 ExtensionService* service =
501 extensions::ExtensionSystem::Get(profile_)->extension_service();
502 if (service) {
503 const Extension* extension = service->GetExtensionById(extension_id, false);
504 model_->MoveBrowserAction(extension, new_index);
505 SchedulePaint();
506 }
507 }
508
509 void BrowserActionsContainer::HidePopup() {
510 // Remove this as an observer and clear |popup_| and |popup_button_| here,
511 // since we might change them before OnWidgetDestroying() gets called.
512 if (popup_) {
513 popup_->GetWidget()->RemoveObserver(this);
514 popup_->GetWidget()->Close();
515 popup_ = NULL;
516 }
517 if (popup_button_) {
518 popup_button_->SetButtonNotPushed();
519 popup_button_ = NULL;
520 }
521 }
522
523 void BrowserActionsContainer::TestExecuteBrowserAction(int index) {
524 BrowserActionButton* button = browser_action_views_[index]->button();
525 OnBrowserActionExecuted(button);
526 }
527
528 void BrowserActionsContainer::TestSetIconVisibilityCount(size_t icons) {
529 model_->SetVisibleIconCount(icons);
530 chevron_->SetVisible(icons < browser_action_views_.size());
531 container_width_ = IconCountToWidth(icons, chevron_->visible());
532 Layout();
533 SchedulePaint();
534 }
535
536 void BrowserActionsContainer::OnPaint(gfx::Canvas* canvas) {
537 // TODO(sky/glen): Instead of using a drop indicator, animate the icons while
538 // dragging (like we do for tab dragging).
539 if (drop_indicator_position_ > -1) {
540 // The two-pixel width drop indicator.
541 static const int kDropIndicatorWidth = 2;
542 gfx::Rect indicator_bounds(
543 drop_indicator_position_ - (kDropIndicatorWidth / 2),
544 ToolbarView::kVertSpacing, kDropIndicatorWidth, IconHeight());
545
546 // Color of the drop indicator.
547 static const SkColor kDropIndicatorColor = SK_ColorBLACK;
548 canvas->FillRect(indicator_bounds, kDropIndicatorColor);
549 }
550 }
551
552 void BrowserActionsContainer::OnThemeChanged() {
553 LoadImages();
554 }
555
556 void BrowserActionsContainer::ViewHierarchyChanged(
557 const ViewHierarchyChangedDetails& details) {
558 // No extensions (e.g., incognito).
559 if (!model_)
560 return;
561
562 if (details.is_add && details.child == this) {
563 // Initial toolbar button creation and placement in the widget hierarchy.
564 // We do this here instead of in the constructor because AddBrowserAction
565 // calls Layout on the Toolbar, which needs this object to be constructed
566 // before its Layout function is called.
567 CreateBrowserActionViews();
568 }
569 }
570
571 // static
572 int BrowserActionsContainer::IconWidth(bool include_padding) {
573 static bool initialized = false;
574 static int icon_width = 0;
575 if (!initialized) {
576 initialized = true;
577 icon_width = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
578 IDR_BROWSER_ACTION)->width();
579 }
580 return icon_width + (include_padding ? kItemSpacing : 0);
581 }
582
583 // static
584 int BrowserActionsContainer::IconHeight() {
585 static bool initialized = false;
586 static int icon_height = 0;
587 if (!initialized) {
588 initialized = true;
589 icon_height = ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
590 IDR_BROWSER_ACTION)->height();
591 }
592 return icon_height;
593 }
594
595 void BrowserActionsContainer::BrowserActionAdded(const Extension* extension,
596 int index) {
597 #if defined(DEBUG)
598 for (size_t i = 0; i < browser_action_views_.size(); ++i) {
599 DCHECK(browser_action_views_[i]->button()->extension() != extension) <<
600 "Asked to add a browser action view for an extension that already "
601 "exists.";
602 }
603 #endif
604 CloseOverflowMenu();
605
606 if (!ShouldDisplayBrowserAction(extension))
607 return;
608
609 size_t visible_actions = VisibleBrowserActions();
610
611 // Add the new browser action to the vector and the view hierarchy.
612 if (profile_->IsOffTheRecord())
613 index = model_->OriginalIndexToIncognito(index);
614 BrowserActionView* view = new BrowserActionView(extension, browser_, this);
615 browser_action_views_.insert(browser_action_views_.begin() + index, view);
616 AddChildViewAt(view, index);
617
618 // If we are still initializing the container, don't bother animating.
619 if (!model_->extensions_initialized())
620 return;
621
622 // Enlarge the container if it was already at maximum size and we're not in
623 // the middle of upgrading.
624 if ((model_->GetVisibleIconCount() < 0) &&
625 !extensions::ExtensionSystem::Get(profile_)->extension_service()->
626 IsBeingUpgraded(extension)) {
627 suppress_chevron_ = true;
628 SaveDesiredSizeAndAnimate(gfx::Tween::LINEAR, visible_actions + 1);
629 } else {
630 // Just redraw the (possibly modified) visible icon set.
631 OnBrowserActionVisibilityChanged();
632 }
633 }
634
635 void BrowserActionsContainer::BrowserActionRemoved(const Extension* extension) {
636 CloseOverflowMenu();
637
638 if (popup_ && popup_->host()->extension() == extension)
639 HidePopup();
640
641 size_t visible_actions = VisibleBrowserActions();
642 for (BrowserActionViews::iterator i(browser_action_views_.begin());
643 i != browser_action_views_.end(); ++i) {
644 if ((*i)->button()->extension() == extension) {
645 delete *i;
646 browser_action_views_.erase(i);
647
648 // If the extension is being upgraded we don't want the bar to shrink
649 // because the icon is just going to get re-added to the same location.
650 if (extensions::ExtensionSystem::Get(profile_)->extension_service()->
651 IsBeingUpgraded(extension))
652 return;
653
654 if (browser_action_views_.size() > visible_actions) {
655 // If we have more icons than we can show, then we must not be changing
656 // the container size (since we either removed an icon from the main
657 // area and one from the overflow list will have shifted in, or we
658 // removed an entry directly from the overflow list).
659 OnBrowserActionVisibilityChanged();
660 } else {
661 // Either we went from overflow to no-overflow, or we shrunk the no-
662 // overflow container by 1. Either way the size changed, so animate.
663 chevron_->SetVisible(false);
664 SaveDesiredSizeAndAnimate(gfx::Tween::EASE_OUT,
665 browser_action_views_.size());
666 }
667 return;
668 }
669 }
670 }
671
672 void BrowserActionsContainer::BrowserActionMoved(const Extension* extension,
673 int index) {
674 if (!ShouldDisplayBrowserAction(extension))
675 return;
676
677 if (profile_->IsOffTheRecord())
678 index = model_->OriginalIndexToIncognito(index);
679
680 DCHECK(index >= 0 && index < static_cast<int>(browser_action_views_.size()));
681
682 DeleteBrowserActionViews();
683 CreateBrowserActionViews();
684 Layout();
685 SchedulePaint();
686 }
687
688 bool BrowserActionsContainer::BrowserActionShowPopup(
689 const extensions::Extension* extension) {
690 // Do not override other popups and only show in active window. The window
691 // must also have a toolbar, otherwise it should not be showing popups.
692 // TODO(justinlin): Remove toolbar check when http://crbug.com/308645 is
693 // fixed.
694 if (popup_ ||
695 !browser_->window()->IsActive() ||
696 !browser_->window()->IsToolbarVisible()) {
697 return false;
698 }
699
700 for (BrowserActionViews::iterator it = browser_action_views_.begin();
701 it != browser_action_views_.end(); ++it) {
702 BrowserActionButton* button = (*it)->button();
703 if (button && button->extension() == extension)
704 return ShowPopup(button, ExtensionPopup::SHOW, false);
705 }
706 return false;
707 }
708
709 void BrowserActionsContainer::ModelLoaded() {
710 SetContainerWidth();
711 }
712
713 void BrowserActionsContainer::LoadImages() {
714 ui::ThemeProvider* tp = GetThemeProvider();
715 chevron_->SetIcon(*tp->GetImageSkiaNamed(IDR_BROWSER_ACTIONS_OVERFLOW));
716 chevron_->SetHoverIcon(*tp->GetImageSkiaNamed(
717 IDR_BROWSER_ACTIONS_OVERFLOW_H));
718 chevron_->SetPushedIcon(*tp->GetImageSkiaNamed(
719 IDR_BROWSER_ACTIONS_OVERFLOW_P));
720 }
721
722 void BrowserActionsContainer::SetContainerWidth() {
723 int visible_actions = model_->GetVisibleIconCount();
724 if (visible_actions < 0) // All icons should be visible.
725 visible_actions = model_->toolbar_items().size();
726 chevron_->SetVisible(
727 static_cast<size_t>(visible_actions) < model_->toolbar_items().size());
728 container_width_ = IconCountToWidth(visible_actions, chevron_->visible());
729 }
730
731 void BrowserActionsContainer::CloseOverflowMenu() {
732 if (overflow_menu_)
733 overflow_menu_->CancelMenu();
734 }
735
736 void BrowserActionsContainer::StopShowFolderDropMenuTimer() {
737 show_menu_task_factory_.InvalidateWeakPtrs();
738 }
739
740 void BrowserActionsContainer::StartShowFolderDropMenuTimer() {
741 base::MessageLoop::current()->PostDelayedTask(
742 FROM_HERE,
743 base::Bind(&BrowserActionsContainer::ShowDropFolder,
744 show_menu_task_factory_.GetWeakPtr()),
745 base::TimeDelta::FromMilliseconds(views::GetMenuShowDelay()));
746 }
747
748 void BrowserActionsContainer::ShowDropFolder() {
749 DCHECK(!overflow_menu_);
750 SetDropIndicator(-1);
751 overflow_menu_ = new BrowserActionOverflowMenuController(
752 this, browser_, chevron_, browser_action_views_, VisibleBrowserActions());
753 overflow_menu_->set_observer(this);
754 overflow_menu_->RunMenu(GetWidget(), true);
755 }
756
757 void BrowserActionsContainer::SetDropIndicator(int x_pos) {
758 if (drop_indicator_position_ != x_pos) {
759 drop_indicator_position_ = x_pos;
760 SchedulePaint();
761 }
762 }
763
764 int BrowserActionsContainer::IconCountToWidth(int icons,
765 bool display_chevron) const {
766 if (icons < 0)
767 icons = browser_action_views_.size();
768 if ((icons == 0) && !display_chevron)
769 return ToolbarView::kStandardSpacing;
770 int icons_size =
771 (icons == 0) ? 0 : ((icons * IconWidth(true)) - kItemSpacing);
772 int chevron_size = display_chevron ?
773 (kChevronSpacing + chevron_->GetPreferredSize().width()) : 0;
774 return ToolbarView::kStandardSpacing + icons_size + chevron_size +
775 ToolbarView::kStandardSpacing;
776 }
777
778 size_t BrowserActionsContainer::WidthToIconCount(int pixels) const {
779 // Check for widths large enough to show the entire icon set.
780 if (pixels >= IconCountToWidth(-1, false))
781 return browser_action_views_.size();
782
783 // We need to reserve space for the resize area, chevron, and the spacing on
784 // either side of the chevron.
785 int available_space = pixels - ToolbarView::kStandardSpacing -
786 chevron_->GetPreferredSize().width() - kChevronSpacing -
787 ToolbarView::kStandardSpacing;
788 // Now we add an extra between-item padding value so the space can be divided
789 // evenly by (size of icon with padding).
790 return static_cast<size_t>(
791 std::max(0, available_space + kItemSpacing) / IconWidth(true));
792 }
793
794 int BrowserActionsContainer::ContainerMinSize() const {
795 return ToolbarView::kStandardSpacing + kChevronSpacing +
796 chevron_->GetPreferredSize().width() + ToolbarView::kStandardSpacing;
797 }
798
799 void BrowserActionsContainer::SaveDesiredSizeAndAnimate(
800 gfx::Tween::Type tween_type,
801 size_t num_visible_icons) {
802 // Save off the desired number of visible icons. We do this now instead of at
803 // the end of the animation so that even if the browser is shut down while
804 // animating, the right value will be restored on next run.
805 // NOTE: Don't save the icon count in incognito because there may be fewer
806 // icons in that mode. The result is that the container in a normal window is
807 // always at least as wide as in an incognito window.
808 if (!profile_->IsOffTheRecord())
809 model_->SetVisibleIconCount(num_visible_icons);
810
811 int target_size = IconCountToWidth(num_visible_icons,
812 num_visible_icons < browser_action_views_.size());
813 if (!disable_animations_during_testing_) {
814 // Animate! We have to set the animation_target_size_ after calling Reset(),
815 // because that could end up calling AnimationEnded which clears the value.
816 resize_animation_->Reset();
817 resize_animation_->SetTweenType(tween_type);
818 animation_target_size_ = target_size;
819 resize_animation_->Show();
820 } else {
821 animation_target_size_ = target_size;
822 AnimationEnded(resize_animation_.get());
823 }
824 }
825
826 bool BrowserActionsContainer::ShouldDisplayBrowserAction(
827 const Extension* extension) {
828 // Only display incognito-enabled extensions while in incognito mode.
829 return
830 (!profile_->IsOffTheRecord() ||
831 extension_util::IsIncognitoEnabled(
832 extension->id(),
833 extensions::ExtensionSystem::Get(profile_)->extension_service()));
834 }
835
836 bool BrowserActionsContainer::ShowPopup(
837 BrowserActionButton* button,
838 ExtensionPopup::ShowAction show_action,
839 bool should_grant) {
840 const Extension* extension = button->extension();
841 GURL popup_url;
842 if (model_->ExecuteBrowserAction(
843 extension, browser_, &popup_url, should_grant) !=
844 ExtensionToolbarModel::ACTION_SHOW_POPUP) {
845 return false;
846 }
847
848 // If we're showing the same popup, just hide it and return.
849 bool same_showing = popup_ && button == popup_button_;
850
851 // Always hide the current popup, even if it's not the same.
852 // Only one popup should be visible at a time.
853 HidePopup();
854
855 if (same_showing)
856 return false;
857
858 // We can get the execute event for browser actions that are not visible,
859 // since buttons can be activated from the overflow menu (chevron). In that
860 // case we show the popup as originating from the chevron.
861 View* reference_view = button->parent()->visible() ? button : chevron_;
862 views::BubbleBorder::Arrow arrow = base::i18n::IsRTL() ?
863 views::BubbleBorder::TOP_LEFT : views::BubbleBorder::TOP_RIGHT;
864 popup_ = ExtensionPopup::ShowPopup(popup_url,
865 browser_,
866 reference_view,
867 arrow,
868 show_action);
869 popup_->GetWidget()->AddObserver(this);
870 popup_button_ = button;
871
872 // Only set button as pushed if it was triggered by a user click.
873 if (should_grant)
874 popup_button_->SetButtonPushed();
875 return true;
876 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698