Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / ui / views / toolbar / browser_actions_container.cc
blobb86b08413c2a0ffa01566426b1bb203f4f2f0ad4
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
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
7 #include "base/compiler_specific.h"
8 #include "base/stl_util.h"
9 #include "chrome/browser/extensions/tab_helper.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/tabs/tab_strip_model.h"
13 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
14 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
15 #include "chrome/browser/ui/view_ids.h"
16 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
17 #include "chrome/browser/ui/views/extensions/extension_toolbar_icon_surfacing_bubble_views.h"
18 #include "chrome/browser/ui/views/frame/browser_view.h"
19 #include "chrome/browser/ui/views/toolbar/browser_actions_container_observer.h"
20 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
21 #include "chrome/browser/ui/views/toolbar/wrench_toolbar_button.h"
22 #include "chrome/common/extensions/command.h"
23 #include "chrome/grit/generated_resources.h"
24 #include "extensions/common/feature_switch.h"
25 #include "grit/theme_resources.h"
26 #include "third_party/skia/include/core/SkColor.h"
27 #include "ui/accessibility/ax_view_state.h"
28 #include "ui/base/dragdrop/drag_utils.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/base/nine_image_painter_factory.h"
31 #include "ui/base/resource/resource_bundle.h"
32 #include "ui/base/theme_provider.h"
33 #include "ui/gfx/canvas.h"
34 #include "ui/gfx/geometry/rect.h"
35 #include "ui/resources/grit/ui_resources.h"
36 #include "ui/views/controls/resize_area.h"
37 #include "ui/views/painter.h"
38 #include "ui/views/widget/widget.h"
40 namespace {
42 // Horizontal spacing before the chevron (if visible).
43 const int kChevronSpacing = ToolbarView::kStandardSpacing - 2;
45 } // namespace
47 ////////////////////////////////////////////////////////////////////////////////
48 // BrowserActionsContainer::DropPosition
50 struct BrowserActionsContainer::DropPosition {
51 DropPosition(size_t row, size_t icon_in_row);
53 // The (0-indexed) row into which the action will be dropped.
54 size_t row;
56 // The (0-indexed) icon in the row before the action will be dropped.
57 size_t icon_in_row;
60 BrowserActionsContainer::DropPosition::DropPosition(
61 size_t row, size_t icon_in_row)
62 : row(row), icon_in_row(icon_in_row) {
65 ////////////////////////////////////////////////////////////////////////////////
66 // BrowserActionsContainer
68 BrowserActionsContainer::BrowserActionsContainer(
69 Browser* browser,
70 BrowserActionsContainer* main_container)
71 : toolbar_actions_bar_(new ToolbarActionsBar(
72 this,
73 browser,
74 main_container ?
75 main_container->toolbar_actions_bar_.get() : nullptr)),
76 browser_(browser),
77 main_container_(main_container),
78 popup_owner_(NULL),
79 container_width_(0),
80 resize_area_(NULL),
81 chevron_(NULL),
82 suppress_chevron_(false),
83 added_to_view_(false),
84 shown_bubble_(false),
85 resize_amount_(0),
86 animation_target_size_(0) {
87 set_id(VIEW_ID_BROWSER_ACTION_TOOLBAR);
89 bool overflow_experiment =
90 extensions::FeatureSwitch::extension_action_redesign()->IsEnabled();
91 DCHECK(!in_overflow_mode() || overflow_experiment);
93 if (!in_overflow_mode()) {
94 resize_animation_.reset(new gfx::SlideAnimation(this));
95 resize_area_ = new views::ResizeArea(this);
96 AddChildView(resize_area_);
98 // 'Main' mode doesn't need a chevron overflow when overflow is shown inside
99 // the Chrome menu.
100 if (!overflow_experiment) {
101 // Since the ChevronMenuButton holds a raw pointer to us, we need to
102 // ensure it doesn't outlive us. Having it owned by the view hierarchy as
103 // a child will suffice.
104 chevron_ = new ChevronMenuButton(this);
105 chevron_->EnableCanvasFlippingForRTLUI(true);
106 chevron_->SetAccessibleName(
107 l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS_CHEVRON));
108 chevron_->SetVisible(false);
109 AddChildView(chevron_);
114 BrowserActionsContainer::~BrowserActionsContainer() {
115 FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
116 observers_,
117 OnBrowserActionsContainerDestroyed());
119 toolbar_actions_bar_->DeleteActions();
120 // All views should be removed as part of ToolbarActionsBar::DeleteActions().
121 DCHECK(toolbar_action_views_.empty());
124 void BrowserActionsContainer::Init() {
125 LoadImages();
127 // We wait to set the container width until now so that the chevron images
128 // will be loaded. The width calculation needs to know the chevron size.
129 container_width_ = toolbar_actions_bar_->GetPreferredSize().width();
132 const std::string& BrowserActionsContainer::GetIdAt(size_t index) const {
133 return toolbar_action_views_[index]->view_controller()->GetId();
136 ToolbarActionView* BrowserActionsContainer::GetViewForId(
137 const std::string& id) {
138 for (ToolbarActionView* view : toolbar_action_views_) {
139 if (view->view_controller()->GetId() == id)
140 return view;
142 return nullptr;
145 void BrowserActionsContainer::RefreshToolbarActionViews() {
146 toolbar_actions_bar_->Update();
149 size_t BrowserActionsContainer::VisibleBrowserActions() const {
150 size_t visible_actions = 0;
151 for (const ToolbarActionView* view : toolbar_action_views_) {
152 if (view->visible())
153 ++visible_actions;
155 return visible_actions;
158 size_t BrowserActionsContainer::VisibleBrowserActionsAfterAnimation() const {
159 if (!animating())
160 return VisibleBrowserActions();
162 return toolbar_actions_bar_->WidthToIconCount(animation_target_size_);
165 void BrowserActionsContainer::ExecuteExtensionCommand(
166 const extensions::Extension* extension,
167 const extensions::Command& command) {
168 // Global commands are handled by the ExtensionCommandsGlobalRegistry
169 // instance.
170 DCHECK(!command.global());
171 extension_keybinding_registry_->ExecuteCommand(extension->id(),
172 command.accelerator());
175 bool BrowserActionsContainer::ShownInsideMenu() const {
176 return in_overflow_mode();
179 void BrowserActionsContainer::OnToolbarActionViewDragDone() {
180 FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
181 observers_,
182 OnBrowserActionDragDone());
185 views::MenuButton* BrowserActionsContainer::GetOverflowReferenceView() {
186 // With traditional overflow, the reference is the chevron. With the
187 // redesign, we use the wrench menu instead.
188 return chevron_ ?
189 static_cast<views::MenuButton*>(chevron_) :
190 static_cast<views::MenuButton*>(BrowserView::GetBrowserViewForBrowser(
191 browser_)->toolbar()->app_menu());
194 void BrowserActionsContainer::SetPopupOwner(ToolbarActionView* popup_owner) {
195 // We should never be setting a popup owner when one already exists, and
196 // never unsetting one when one wasn't set.
197 DCHECK((!popup_owner_ && popup_owner) ||
198 (popup_owner_ && !popup_owner));
199 popup_owner_ = popup_owner;
202 void BrowserActionsContainer::HideActivePopup() {
203 if (popup_owner_)
204 popup_owner_->view_controller()->HidePopup();
207 ToolbarActionView* BrowserActionsContainer::GetMainViewForAction(
208 ToolbarActionView* view) {
209 if (!in_overflow_mode())
210 return view; // This is the main view.
212 // The overflow container and main container each have the same views and
213 // view indices, so we can return the view of the index that |view| has in
214 // this container.
215 ToolbarActionViews::const_iterator iter =
216 std::find(toolbar_action_views_.begin(),
217 toolbar_action_views_.end(),
218 view);
219 DCHECK(iter != toolbar_action_views_.end());
220 size_t index = iter - toolbar_action_views_.begin();
221 return main_container_->toolbar_action_views_[index];
224 void BrowserActionsContainer::AddViewForAction(
225 ToolbarActionViewController* view_controller,
226 size_t index) {
227 if (chevron_)
228 chevron_->CloseMenu();
230 view_controller->GetActionName();
231 ToolbarActionView* view =
232 new ToolbarActionView(view_controller, browser_->profile(), this);
233 toolbar_action_views_.insert(toolbar_action_views_.begin() + index, view);
234 AddChildViewAt(view, index);
237 void BrowserActionsContainer::RemoveViewForAction(
238 ToolbarActionViewController* action) {
239 if (chevron_)
240 chevron_->CloseMenu();
242 for (ToolbarActionViews::iterator iter = toolbar_action_views_.begin();
243 iter != toolbar_action_views_.end(); ++iter) {
244 if ((*iter)->view_controller() == action) {
245 delete *iter;
246 toolbar_action_views_.erase(iter);
247 break;
252 void BrowserActionsContainer::RemoveAllViews() {
253 HideActivePopup();
254 STLDeleteElements(&toolbar_action_views_);
257 void BrowserActionsContainer::Redraw(bool order_changed) {
258 if (!added_to_view_) {
259 // We don't want to redraw before the view has been fully added to the
260 // hierarchy.
261 return;
264 std::vector<ToolbarActionViewController*> actions =
265 toolbar_actions_bar_->toolbar_actions();
266 if (order_changed) {
267 // Run through the views and compare them to the desired order. If something
268 // is out of place, find the correct spot for it.
269 for (int i = 0; i < static_cast<int>(actions.size()) - 1; ++i) {
270 if (actions[i] != toolbar_action_views_[i]->view_controller()) {
271 // Find where the correct view is (it's guaranteed to be after our
272 // current index, since everything up to this point is correct).
273 int j = i + 1;
274 while (actions[i] != toolbar_action_views_[j]->view_controller())
275 ++j;
276 std::swap(toolbar_action_views_[i], toolbar_action_views_[j]);
281 if (width() != GetPreferredSize().width() && parent()) {
282 parent()->Layout();
283 parent()->SchedulePaint();
284 } else {
285 Layout();
286 SchedulePaint();
290 void BrowserActionsContainer::ResizeAndAnimate(
291 gfx::Tween::Type tween_type,
292 int target_width,
293 bool suppress_chevron) {
294 if (resize_animation_ && !toolbar_actions_bar_->suppress_animation()) {
295 // Animate! We have to set the animation_target_size_ after calling Reset(),
296 // because that could end up calling AnimationEnded which clears the value.
297 resize_animation_->Reset();
298 suppress_chevron_ = suppress_chevron;
299 resize_animation_->SetTweenType(tween_type);
300 animation_target_size_ = target_width;
301 resize_animation_->Show();
302 } else {
303 animation_target_size_ = target_width;
304 AnimationEnded(resize_animation_.get());
308 void BrowserActionsContainer::SetChevronVisibility(bool visible) {
309 if (chevron_)
310 chevron_->SetVisible(visible);
313 int BrowserActionsContainer::GetWidth() const {
314 return container_width_;
317 bool BrowserActionsContainer::IsAnimating() const {
318 return animating();
321 void BrowserActionsContainer::StopAnimating() {
322 animation_target_size_ = container_width_;
323 resize_animation_->Reset();
326 int BrowserActionsContainer::GetChevronWidth() const {
327 return chevron_ ? chevron_->GetPreferredSize().width() + kChevronSpacing : 0;
330 bool BrowserActionsContainer::IsPopupRunning() const {
331 return popup_owner_ != nullptr;
334 void BrowserActionsContainer::OnOverflowedActionWantsToRunChanged(
335 bool overflowed_action_wants_to_run) {
336 DCHECK(!in_overflow_mode());
337 BrowserView::GetBrowserViewForBrowser(browser_)->toolbar()->
338 app_menu()->SetOverflowedToolbarActionWantsToRun(
339 overflowed_action_wants_to_run);
342 void BrowserActionsContainer::AddObserver(
343 BrowserActionsContainerObserver* observer) {
344 observers_.AddObserver(observer);
347 void BrowserActionsContainer::RemoveObserver(
348 BrowserActionsContainerObserver* observer) {
349 observers_.RemoveObserver(observer);
352 gfx::Size BrowserActionsContainer::GetPreferredSize() const {
353 if (in_overflow_mode())
354 return toolbar_actions_bar_->GetPreferredSize();
356 // If there are no actions to show, then don't show the container at all.
357 if (toolbar_action_views_.empty())
358 return gfx::Size();
360 // We calculate the size of the view by taking the current width and
361 // subtracting resize_amount_ (the latter represents how far the user is
362 // resizing the view or, if animating the snapping, how far to animate it).
363 // But we also clamp it to a minimum size and the maximum size, so that the
364 // container can never shrink too far or take up more space than it needs.
365 // In other words: minimum_width < width - resize < max_width.
366 int preferred_width = std::min(
367 std::max(toolbar_actions_bar_->GetMinimumWidth(),
368 container_width_ - resize_amount_),
369 toolbar_actions_bar_->GetMaximumWidth());
370 return gfx::Size(preferred_width, ToolbarActionsBar::IconHeight());
373 int BrowserActionsContainer::GetHeightForWidth(int width) const {
374 if (in_overflow_mode())
375 toolbar_actions_bar_->SetOverflowRowWidth(width);
376 return GetPreferredSize().height();
379 gfx::Size BrowserActionsContainer::GetMinimumSize() const {
380 return gfx::Size(toolbar_actions_bar_->GetMinimumWidth(),
381 ToolbarActionsBar::IconHeight());
384 void BrowserActionsContainer::Layout() {
385 if (toolbar_actions_bar_->suppress_layout())
386 return;
388 if (toolbar_action_views_.empty()) {
389 SetVisible(false);
390 return;
393 SetVisible(true);
394 if (resize_area_)
395 resize_area_->SetBounds(0, 0, platform_settings().item_spacing, height());
397 // If the icons don't all fit, show the chevron (unless suppressed).
398 int max_x = GetPreferredSize().width();
399 if (toolbar_actions_bar_->IconCountToWidth(-1) > max_x &&
400 !suppress_chevron_ && chevron_) {
401 chevron_->SetVisible(true);
402 gfx::Size chevron_size(chevron_->GetPreferredSize());
403 max_x -= chevron_size.width() + kChevronSpacing;
404 chevron_->SetBounds(
405 width() - ToolbarView::kStandardSpacing - chevron_size.width(),
407 chevron_size.width(),
408 chevron_size.height());
409 } else if (chevron_) {
410 chevron_->SetVisible(false);
413 // The range of visible icons, from start_index (inclusive) to end_index
414 // (exclusive).
415 size_t start_index = in_overflow_mode() ?
416 main_container_->toolbar_actions_bar_->GetIconCount() : 0u;
417 // For the main container's last visible icon, we calculate how many icons we
418 // can display with the given width. We add an extra item_spacing because the
419 // last icon doesn't need padding, but we want it to divide easily.
420 size_t end_index = in_overflow_mode() ?
421 toolbar_action_views_.size() :
422 (max_x - platform_settings().left_padding -
423 platform_settings().right_padding +
424 platform_settings().item_spacing) /
425 ToolbarActionsBar::IconWidth(true);
426 // The maximum length for one row of icons.
427 size_t row_length = in_overflow_mode() ?
428 platform_settings().icons_per_overflow_menu_row : end_index;
430 // Now draw the icons for the actions in the available space. Once all the
431 // variables are in place, the layout works equally well for the main and
432 // overflow container.
433 for (size_t i = 0u; i < toolbar_action_views_.size(); ++i) {
434 ToolbarActionView* view = toolbar_action_views_[i];
435 if (i < start_index || i >= end_index) {
436 view->SetVisible(false);
437 } else {
438 size_t relative_index = i - start_index;
439 size_t index_in_row = relative_index % row_length;
440 size_t row_index = relative_index / row_length;
441 view->SetBounds(platform_settings().left_padding +
442 index_in_row * ToolbarActionsBar::IconWidth(true),
443 row_index * ToolbarActionsBar::IconHeight(),
444 ToolbarActionsBar::IconWidth(false),
445 ToolbarActionsBar::IconHeight());
446 view->SetVisible(true);
451 void BrowserActionsContainer::OnMouseEntered(const ui::MouseEvent& event) {
452 if (!shown_bubble_ && !toolbar_action_views_.empty() &&
453 toolbar_actions_bar_->ShouldShowInfoBubble()) {
454 ExtensionToolbarIconSurfacingBubble* bubble =
455 new ExtensionToolbarIconSurfacingBubble(toolbar_action_views_[0],
456 toolbar_actions_bar_.get());
457 views::BubbleDelegateView::CreateBubble(bubble);
458 bubble->GetWidget()->Show();
460 shown_bubble_ = true;
463 bool BrowserActionsContainer::GetDropFormats(
464 int* formats,
465 std::set<OSExchangeData::CustomFormat>* custom_formats) {
466 return BrowserActionDragData::GetDropFormats(custom_formats);
469 bool BrowserActionsContainer::AreDropTypesRequired() {
470 return BrowserActionDragData::AreDropTypesRequired();
473 bool BrowserActionsContainer::CanDrop(const OSExchangeData& data) {
474 return BrowserActionDragData::CanDrop(data, browser_->profile());
477 int BrowserActionsContainer::OnDragUpdated(
478 const ui::DropTargetEvent& event) {
479 size_t row_index = 0;
480 size_t before_icon_in_row = 0;
481 // If there are no visible actions (such as when dragging an icon to an empty
482 // overflow/main container), then 0, 0 for row, column is correct.
483 if (VisibleBrowserActions() != 0) {
484 // Figure out where to display the indicator. This is a complex calculation:
486 // First, we subtract out the padding to the left of the icon area, which is
487 // ToolbarView::kStandardSpacing. If we're right-to-left, we also mirror the
488 // event.x() so that our calculations are consistent with left-to-right.
489 int offset_into_icon_area =
490 GetMirroredXInView(event.x()) - ToolbarView::kStandardSpacing;
492 // Next, figure out what row we're on. This only matters for overflow mode,
493 // but the calculation is the same for both.
494 row_index = event.y() / ToolbarActionsBar::IconHeight();
496 // Sanity check - we should never be on a different row in the main
497 // container.
498 DCHECK(in_overflow_mode() || row_index == 0);
500 // Next, we determine which icon to place the indicator in front of. We want
501 // to place the indicator in front of icon n when the cursor is between the
502 // midpoints of icons (n - 1) and n. To do this we take the offset into the
503 // icon area and transform it as follows:
505 // Real icon area:
506 // 0 a * b c
507 // | | | |
508 // |[IC|ON] [IC|ON] [IC|ON]
509 // We want to be before icon 0 for 0 < x <= a, icon 1 for a < x <= b, etc.
510 // Here the "*" represents the offset into the icon area, and since it's
511 // between a and b, we want to return "1".
513 // Transformed "icon area":
514 // 0 a * b c
515 // | | | |
516 // |[ICON] |[ICON] |[ICON] |
517 // If we shift both our offset and our divider points later by half an icon
518 // plus one spacing unit, then it becomes very easy to calculate how many
519 // divider points we've passed, because they're the multiples of "one icon
520 // plus padding".
521 int before_icon_unclamped =
522 (offset_into_icon_area + (ToolbarActionsBar::IconWidth(false) / 2) +
523 platform_settings().item_spacing) / ToolbarActionsBar::IconWidth(true);
525 // We need to figure out how many icons are visible on the relevant row.
526 // In the main container, this will just be the visible actions.
527 int visible_icons_on_row = VisibleBrowserActionsAfterAnimation();
528 if (in_overflow_mode()) {
529 int icons_per_row = platform_settings().icons_per_overflow_menu_row;
530 // If this is the final row of the overflow, then this is the remainder of
531 // visible icons. Otherwise, it's a full row (kIconsPerRow).
532 visible_icons_on_row =
533 row_index ==
534 static_cast<size_t>(visible_icons_on_row / icons_per_row) ?
535 visible_icons_on_row % icons_per_row : icons_per_row;
538 // Because the user can drag outside the container bounds, we need to clamp
539 // to the valid range. Note that the maximum allowable value is (num icons),
540 // not (num icons - 1), because we represent the indicator being past the
541 // last icon as being "before the (last + 1) icon".
542 before_icon_in_row =
543 std::min(std::max(before_icon_unclamped, 0), visible_icons_on_row);
546 if (!drop_position_.get() ||
547 !(drop_position_->row == row_index &&
548 drop_position_->icon_in_row == before_icon_in_row)) {
549 drop_position_.reset(new DropPosition(row_index, before_icon_in_row));
550 SchedulePaint();
553 return ui::DragDropTypes::DRAG_MOVE;
556 void BrowserActionsContainer::OnDragExited() {
557 drop_position_.reset();
558 SchedulePaint();
561 int BrowserActionsContainer::OnPerformDrop(
562 const ui::DropTargetEvent& event) {
563 BrowserActionDragData data;
564 if (!data.Read(event.data()))
565 return ui::DragDropTypes::DRAG_NONE;
567 // Make sure we have the same view as we started with.
568 DCHECK_EQ(GetIdAt(data.index()), data.id());
570 size_t i = drop_position_->row *
571 platform_settings().icons_per_overflow_menu_row +
572 drop_position_->icon_in_row;
573 if (in_overflow_mode())
574 i += main_container_->VisibleBrowserActionsAfterAnimation();
575 // |i| now points to the item to the right of the drop indicator*, which is
576 // correct when dragging an icon to the left. When dragging to the right,
577 // however, we want the icon being dragged to get the index of the item to
578 // the left of the drop indicator, so we subtract one.
579 // * Well, it can also point to the end, but not when dragging to the left. :)
580 if (i > data.index())
581 --i;
583 ToolbarActionsBar::DragType drag_type = ToolbarActionsBar::DRAG_TO_SAME;
584 if (!toolbar_action_views_[data.index()]->visible())
585 drag_type = in_overflow_mode() ? ToolbarActionsBar::DRAG_TO_OVERFLOW :
586 ToolbarActionsBar::DRAG_TO_MAIN;
588 toolbar_actions_bar_->OnDragDrop(data.index(), i, drag_type);
590 OnDragExited(); // Perform clean up after dragging.
591 return ui::DragDropTypes::DRAG_MOVE;
594 void BrowserActionsContainer::GetAccessibleState(
595 ui::AXViewState* state) {
596 state->role = ui::AX_ROLE_GROUP;
597 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS);
600 void BrowserActionsContainer::WriteDragDataForView(View* sender,
601 const gfx::Point& press_pt,
602 OSExchangeData* data) {
603 DCHECK(data);
605 ToolbarActionViews::iterator iter = std::find(toolbar_action_views_.begin(),
606 toolbar_action_views_.end(),
607 sender);
608 DCHECK(iter != toolbar_action_views_.end());
609 ToolbarActionViewController* view_controller = (*iter)->view_controller();
610 drag_utils::SetDragImageOnDataObject(
611 view_controller->GetIconWithBadge(),
612 press_pt.OffsetFromOrigin(),
613 data);
614 // Fill in the remaining info.
615 BrowserActionDragData drag_data(view_controller->GetId(),
616 iter - toolbar_action_views_.begin());
617 drag_data.Write(browser_->profile(), data);
620 int BrowserActionsContainer::GetDragOperationsForView(View* sender,
621 const gfx::Point& p) {
622 return ui::DragDropTypes::DRAG_MOVE;
625 bool BrowserActionsContainer::CanStartDragForView(View* sender,
626 const gfx::Point& press_pt,
627 const gfx::Point& p) {
628 // We don't allow dragging while we're highlighting.
629 return !toolbar_actions_bar_->is_highlighting();
632 void BrowserActionsContainer::OnResize(int resize_amount, bool done_resizing) {
633 if (!done_resizing) {
634 resize_amount_ = resize_amount;
635 Redraw(false);
636 return;
639 // Up until now we've only been modifying the resize_amount, but now it is
640 // time to set the container size to the size we have resized to, and then
641 // animate to the nearest icon count size if necessary (which may be 0).
642 container_width_ =
643 std::min(std::max(toolbar_actions_bar_->GetMinimumWidth(),
644 container_width_ - resize_amount),
645 toolbar_actions_bar_->GetMaximumWidth());
646 toolbar_actions_bar_->OnResizeComplete(container_width_);
649 void BrowserActionsContainer::AnimationProgressed(
650 const gfx::Animation* animation) {
651 DCHECK_EQ(resize_animation_.get(), animation);
652 resize_amount_ = static_cast<int>(resize_animation_->GetCurrentValue() *
653 (container_width_ - animation_target_size_));
654 Redraw(false);
657 void BrowserActionsContainer::AnimationCanceled(
658 const gfx::Animation* animation) {
659 AnimationEnded(animation);
662 void BrowserActionsContainer::AnimationEnded(const gfx::Animation* animation) {
663 container_width_ = animation_target_size_;
664 animation_target_size_ = 0;
665 resize_amount_ = 0;
666 suppress_chevron_ = false;
667 Redraw(false);
668 FOR_EACH_OBSERVER(BrowserActionsContainerObserver,
669 observers_,
670 OnBrowserActionsContainerAnimationEnded());
673 content::WebContents* BrowserActionsContainer::GetCurrentWebContents() {
674 return browser_->tab_strip_model()->GetActiveWebContents();
677 extensions::ActiveTabPermissionGranter*
678 BrowserActionsContainer::GetActiveTabPermissionGranter() {
679 content::WebContents* web_contents = GetCurrentWebContents();
680 if (!web_contents)
681 return NULL;
682 return extensions::TabHelper::FromWebContents(web_contents)->
683 active_tab_permission_granter();
686 gfx::NativeView BrowserActionsContainer::TestGetPopup() {
687 return popup_owner_ ?
688 popup_owner_->view_controller()->GetPopupNativeView() :
689 NULL;
692 void BrowserActionsContainer::OnPaint(gfx::Canvas* canvas) {
693 // If the views haven't been initialized yet, wait for the next call to
694 // paint (one will be triggered by entering highlight mode).
695 if (toolbar_actions_bar_->is_highlighting() &&
696 !toolbar_action_views_.empty() &&
697 highlight_painter_) {
698 views::Painter::PaintPainterAt(
699 canvas, highlight_painter_.get(), GetLocalBounds());
702 // TODO(sky/glen): Instead of using a drop indicator, animate the icons while
703 // dragging (like we do for tab dragging).
704 if (drop_position_.get()) {
705 // The two-pixel width drop indicator.
706 static const int kDropIndicatorWidth = 2;
708 // Convert back to a pixel offset into the container. First find the X
709 // coordinate of the drop icon.
710 int drop_icon_x = ToolbarView::kStandardSpacing +
711 (drop_position_->icon_in_row * ToolbarActionsBar::IconWidth(true));
712 // Next, find the space before the drop icon. This will either be
713 // left padding or item spacing, depending on whether this is the first
714 // icon.
715 // NOTE: Right now, these are the same. But let's do this right for if they
716 // ever aren't.
717 int space_before_drop_icon = drop_position_->icon_in_row == 0 ?
718 platform_settings().left_padding : platform_settings().item_spacing;
719 // Now place the drop indicator halfway between this and the end of the
720 // previous icon. If there is an odd amount of available space between the
721 // two icons (or the icon and the address bar) after subtracting the drop
722 // indicator width, this calculation puts the extra pixel on the left side
723 // of the indicator, since when the indicator is between the address bar and
724 // the first icon, it looks better closer to the icon.
725 int drop_indicator_x = drop_icon_x -
726 ((space_before_drop_icon + kDropIndicatorWidth) / 2);
727 int row_height = ToolbarActionsBar::IconHeight();
728 int drop_indicator_y = row_height * drop_position_->row;
729 gfx::Rect indicator_bounds(drop_indicator_x,
730 drop_indicator_y,
731 kDropIndicatorWidth,
732 row_height);
733 indicator_bounds.set_x(GetMirroredXForRect(indicator_bounds));
735 // Color of the drop indicator.
736 static const SkColor kDropIndicatorColor = SK_ColorBLACK;
737 canvas->FillRect(indicator_bounds, kDropIndicatorColor);
741 void BrowserActionsContainer::OnThemeChanged() {
742 LoadImages();
745 void BrowserActionsContainer::ViewHierarchyChanged(
746 const ViewHierarchyChangedDetails& details) {
747 if (!toolbar_actions_bar_->enabled())
748 return;
750 if (details.is_add && details.child == this) {
751 if (!in_overflow_mode() && // We only need one keybinding registry.
752 parent()->GetFocusManager()) { // focus manager can be null in tests.
753 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews(
754 browser_->profile(),
755 parent()->GetFocusManager(),
756 extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS,
757 this));
760 // Initial toolbar button creation and placement in the widget hierarchy.
761 // We do this here instead of in the constructor because adding views
762 // calls Layout on the Toolbar, which needs this object to be constructed
763 // before its Layout function is called.
764 toolbar_actions_bar_->CreateActions();
766 added_to_view_ = true;
770 void BrowserActionsContainer::LoadImages() {
771 if (in_overflow_mode())
772 return; // Overflow mode has neither a chevron nor highlighting.
774 ui::ThemeProvider* tp = GetThemeProvider();
775 if (tp && chevron_) {
776 chevron_->SetImage(views::Button::STATE_NORMAL,
777 *tp->GetImageSkiaNamed(IDR_BROWSER_ACTIONS_OVERFLOW));
780 const int kImages[] = IMAGE_GRID(IDR_DEVELOPER_MODE_HIGHLIGHT);
781 highlight_painter_.reset(views::Painter::CreateImageGridPainter(kImages));