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/extension_message_bubble_controller.h"
10 #include "chrome/browser/extensions/extension_toolbar_model.h"
11 #include "chrome/browser/extensions/tab_helper.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/extensions/extension_toolbar_icon_surfacing_bubble_delegate.h"
15 #include "chrome/browser/ui/tabs/tab_strip_model.h"
16 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
17 #include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
18 #include "chrome/browser/ui/view_ids.h"
19 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
20 #include "chrome/browser/ui/views/extensions/extension_message_bubble_view.h"
21 #include "chrome/browser/ui/views/extensions/extension_toolbar_icon_surfacing_bubble_views.h"
22 #include "chrome/browser/ui/views/frame/browser_view.h"
23 #include "chrome/browser/ui/views/toolbar/browser_actions_container_observer.h"
24 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
25 #include "chrome/browser/ui/views/toolbar/wrench_toolbar_button.h"
26 #include "chrome/common/extensions/command.h"
27 #include "chrome/grit/generated_resources.h"
28 #include "extensions/common/feature_switch.h"
29 #include "grit/theme_resources.h"
30 #include "third_party/skia/include/core/SkColor.h"
31 #include "ui/accessibility/ax_view_state.h"
32 #include "ui/base/dragdrop/drag_utils.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/base/nine_image_painter_factory.h"
35 #include "ui/base/resource/resource_bundle.h"
36 #include "ui/base/theme_provider.h"
37 #include "ui/gfx/canvas.h"
38 #include "ui/gfx/geometry/rect.h"
39 #include "ui/resources/grit/ui_resources.h"
40 #include "ui/views/bubble/bubble_delegate.h"
41 #include "ui/views/controls/resize_area.h"
42 #include "ui/views/painter.h"
43 #include "ui/views/widget/widget.h"
47 // Horizontal spacing before the chevron (if visible).
48 const int kChevronSpacing
= ToolbarView::kStandardSpacing
- 2;
52 ////////////////////////////////////////////////////////////////////////////////
53 // BrowserActionsContainer::DropPosition
55 struct BrowserActionsContainer::DropPosition
{
56 DropPosition(size_t row
, size_t icon_in_row
);
58 // The (0-indexed) row into which the action will be dropped.
61 // The (0-indexed) icon in the row before the action will be dropped.
65 BrowserActionsContainer::DropPosition::DropPosition(
66 size_t row
, size_t icon_in_row
)
67 : row(row
), icon_in_row(icon_in_row
) {
70 ////////////////////////////////////////////////////////////////////////////////
71 // BrowserActionsContainer
73 BrowserActionsContainer::BrowserActionsContainer(
75 BrowserActionsContainer
* main_container
)
76 : toolbar_actions_bar_(new ToolbarActionsBar(
80 main_container
->toolbar_actions_bar_
.get() : nullptr)),
82 main_container_(main_container
),
86 suppress_chevron_(false),
87 added_to_view_(false),
90 animation_target_size_(0),
91 active_bubble_(nullptr) {
92 set_id(VIEW_ID_BROWSER_ACTION_TOOLBAR
);
94 bool overflow_experiment
=
95 extensions::FeatureSwitch::extension_action_redesign()->IsEnabled();
96 DCHECK(!in_overflow_mode() || overflow_experiment
);
98 if (!in_overflow_mode()) {
99 resize_animation_
.reset(new gfx::SlideAnimation(this));
100 resize_area_
= new views::ResizeArea(this);
101 AddChildView(resize_area_
);
103 // 'Main' mode doesn't need a chevron overflow when overflow is shown inside
105 if (!overflow_experiment
) {
106 // Since the ChevronMenuButton holds a raw pointer to us, we need to
107 // ensure it doesn't outlive us. Having it owned by the view hierarchy as
108 // a child will suffice.
109 chevron_
= new ChevronMenuButton(this);
110 chevron_
->EnableCanvasFlippingForRTLUI(true);
111 chevron_
->SetAccessibleName(
112 l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS_CHEVRON
));
113 chevron_
->SetVisible(false);
114 AddChildView(chevron_
);
119 BrowserActionsContainer::~BrowserActionsContainer() {
121 active_bubble_
->GetWidget()->Close();
122 // We should synchronously receive the OnWidgetClosing() event, so we should
123 // always have cleared the active bubble by now.
124 DCHECK(!active_bubble_
);
126 FOR_EACH_OBSERVER(BrowserActionsContainerObserver
,
128 OnBrowserActionsContainerDestroyed());
130 toolbar_actions_bar_
->DeleteActions();
131 // All views should be removed as part of ToolbarActionsBar::DeleteActions().
132 DCHECK(toolbar_action_views_
.empty());
135 void BrowserActionsContainer::Init() {
138 // We wait to set the container width until now so that the chevron images
139 // will be loaded. The width calculation needs to know the chevron size.
140 container_width_
= toolbar_actions_bar_
->GetPreferredSize().width();
143 const std::string
& BrowserActionsContainer::GetIdAt(size_t index
) const {
144 return toolbar_action_views_
[index
]->view_controller()->GetId();
147 ToolbarActionView
* BrowserActionsContainer::GetViewForId(
148 const std::string
& id
) {
149 for (ToolbarActionView
* view
: toolbar_action_views_
) {
150 if (view
->view_controller()->GetId() == id
)
156 void BrowserActionsContainer::RefreshToolbarActionViews() {
157 toolbar_actions_bar_
->Update();
160 size_t BrowserActionsContainer::VisibleBrowserActions() const {
161 size_t visible_actions
= 0;
162 for (const ToolbarActionView
* view
: toolbar_action_views_
) {
166 return visible_actions
;
169 size_t BrowserActionsContainer::VisibleBrowserActionsAfterAnimation() const {
171 return VisibleBrowserActions();
173 return toolbar_actions_bar_
->WidthToIconCount(animation_target_size_
);
176 void BrowserActionsContainer::ExecuteExtensionCommand(
177 const extensions::Extension
* extension
,
178 const extensions::Command
& command
) {
179 // Global commands are handled by the ExtensionCommandsGlobalRegistry
181 DCHECK(!command
.global());
182 extension_keybinding_registry_
->ExecuteCommand(extension
->id(),
183 command
.accelerator());
186 bool BrowserActionsContainer::ShownInsideMenu() const {
187 return in_overflow_mode();
190 void BrowserActionsContainer::OnToolbarActionViewDragDone() {
191 FOR_EACH_OBSERVER(BrowserActionsContainerObserver
,
193 OnBrowserActionDragDone());
196 views::MenuButton
* BrowserActionsContainer::GetOverflowReferenceView() {
197 // With traditional overflow, the reference is the chevron. With the
198 // redesign, we use the wrench menu instead.
200 static_cast<views::MenuButton
*>(chevron_
) :
201 static_cast<views::MenuButton
*>(BrowserView::GetBrowserViewForBrowser(
202 browser_
)->toolbar()->app_menu());
205 void BrowserActionsContainer::OnMouseEnteredToolbarActionView() {
206 if (!shown_bubble_
&& !toolbar_action_views_
.empty() &&
207 ExtensionToolbarIconSurfacingBubbleDelegate::ShouldShowForProfile(
208 browser_
->profile())) {
209 ExtensionToolbarIconSurfacingBubble
* bubble
=
210 new ExtensionToolbarIconSurfacingBubble(
211 toolbar_action_views_
[0],
212 make_scoped_ptr(new ExtensionToolbarIconSurfacingBubbleDelegate(
213 browser_
->profile())));
214 views::BubbleDelegateView::CreateBubble(bubble
);
217 shown_bubble_
= true;
220 void BrowserActionsContainer::AddViewForAction(
221 ToolbarActionViewController
* view_controller
,
224 chevron_
->CloseMenu();
226 ToolbarActionView
* view
=
227 new ToolbarActionView(view_controller
, browser_
->profile(), this);
228 toolbar_action_views_
.insert(toolbar_action_views_
.begin() + index
, view
);
229 AddChildViewAt(view
, index
);
232 void BrowserActionsContainer::RemoveViewForAction(
233 ToolbarActionViewController
* action
) {
235 chevron_
->CloseMenu();
237 for (ToolbarActionViews::iterator iter
= toolbar_action_views_
.begin();
238 iter
!= toolbar_action_views_
.end(); ++iter
) {
239 if ((*iter
)->view_controller() == action
) {
241 toolbar_action_views_
.erase(iter
);
247 void BrowserActionsContainer::RemoveAllViews() {
248 STLDeleteElements(&toolbar_action_views_
);
251 void BrowserActionsContainer::Redraw(bool order_changed
) {
252 if (!added_to_view_
) {
253 // We don't want to redraw before the view has been fully added to the
258 std::vector
<ToolbarActionViewController
*> actions
=
259 toolbar_actions_bar_
->GetActions();
261 // Run through the views and compare them to the desired order. If something
262 // is out of place, find the correct spot for it.
263 for (int i
= 0; i
< static_cast<int>(actions
.size()) - 1; ++i
) {
264 if (actions
[i
] != toolbar_action_views_
[i
]->view_controller()) {
265 // Find where the correct view is (it's guaranteed to be after our
266 // current index, since everything up to this point is correct).
268 while (actions
[i
] != toolbar_action_views_
[j
]->view_controller())
270 std::swap(toolbar_action_views_
[i
], toolbar_action_views_
[j
]);
275 if (width() != GetPreferredSize().width() && parent()) {
277 parent()->SchedulePaint();
284 void BrowserActionsContainer::ResizeAndAnimate(
285 gfx::Tween::Type tween_type
,
287 bool suppress_chevron
) {
288 if (resize_animation_
&& !toolbar_actions_bar_
->suppress_animation()) {
289 // Animate! We have to set the animation_target_size_ after calling Reset(),
290 // because that could end up calling AnimationEnded which clears the value.
291 resize_animation_
->Reset();
292 suppress_chevron_
= suppress_chevron
;
293 resize_animation_
->SetTweenType(tween_type
);
294 animation_target_size_
= target_width
;
295 resize_animation_
->Show();
297 animation_target_size_
= target_width
;
298 AnimationEnded(resize_animation_
.get());
302 void BrowserActionsContainer::SetChevronVisibility(bool visible
) {
304 chevron_
->SetVisible(visible
);
307 int BrowserActionsContainer::GetWidth() const {
308 return container_width_
;
311 bool BrowserActionsContainer::IsAnimating() const {
315 void BrowserActionsContainer::StopAnimating() {
316 animation_target_size_
= container_width_
;
317 resize_animation_
->Reset();
320 int BrowserActionsContainer::GetChevronWidth() const {
321 return chevron_
? chevron_
->GetPreferredSize().width() + kChevronSpacing
: 0;
324 void BrowserActionsContainer::OnOverflowedActionWantsToRunChanged(
325 bool overflowed_action_wants_to_run
) {
326 DCHECK(!in_overflow_mode());
327 BrowserView::GetBrowserViewForBrowser(browser_
)->toolbar()->
328 app_menu()->SetOverflowedToolbarActionWantsToRun(
329 overflowed_action_wants_to_run
);
332 void BrowserActionsContainer::ShowExtensionMessageBubble(
333 scoped_ptr
<extensions::ExtensionMessageBubbleController
> controller
,
334 ToolbarActionViewController
* anchor_action
) {
335 // The container shouldn't be asked to show a bubble if it's animating.
336 DCHECK(!animating());
338 views::View
* reference_view
= anchor_action
?
339 static_cast<views::View
*>(GetViewForId(anchor_action
->GetId())) :
340 BrowserView::GetBrowserViewForBrowser(browser_
)->toolbar()->app_menu();
342 extensions::ExtensionMessageBubbleController
* weak_controller
=
344 extensions::ExtensionMessageBubbleView
* bubble
=
345 new extensions::ExtensionMessageBubbleView(
347 views::BubbleBorder::TOP_RIGHT
,
349 views::BubbleDelegateView::CreateBubble(bubble
);
350 active_bubble_
= bubble
;
351 active_bubble_
->GetWidget()->AddObserver(this);
352 weak_controller
->Show(bubble
);
355 void BrowserActionsContainer::OnWidgetClosing(views::Widget
* widget
) {
356 ClearActiveBubble(widget
);
359 void BrowserActionsContainer::OnWidgetDestroying(views::Widget
* widget
) {
360 ClearActiveBubble(widget
);
363 void BrowserActionsContainer::AddObserver(
364 BrowserActionsContainerObserver
* observer
) {
365 observers_
.AddObserver(observer
);
368 void BrowserActionsContainer::RemoveObserver(
369 BrowserActionsContainerObserver
* observer
) {
370 observers_
.RemoveObserver(observer
);
373 gfx::Size
BrowserActionsContainer::GetPreferredSize() const {
374 if (in_overflow_mode())
375 return toolbar_actions_bar_
->GetPreferredSize();
377 // If there are no actions to show, then don't show the container at all.
378 if (toolbar_action_views_
.empty())
381 // We calculate the size of the view by taking the current width and
382 // subtracting resize_amount_ (the latter represents how far the user is
383 // resizing the view or, if animating the snapping, how far to animate it).
384 // But we also clamp it to a minimum size and the maximum size, so that the
385 // container can never shrink too far or take up more space than it needs.
386 // In other words: minimum_width < width - resize < max_width.
387 int preferred_width
= std::min(
388 std::max(toolbar_actions_bar_
->GetMinimumWidth(),
389 container_width_
- resize_amount_
),
390 toolbar_actions_bar_
->GetMaximumWidth());
391 return gfx::Size(preferred_width
, ToolbarActionsBar::IconHeight());
394 int BrowserActionsContainer::GetHeightForWidth(int width
) const {
395 if (in_overflow_mode())
396 toolbar_actions_bar_
->SetOverflowRowWidth(width
);
397 return GetPreferredSize().height();
400 gfx::Size
BrowserActionsContainer::GetMinimumSize() const {
401 return gfx::Size(toolbar_actions_bar_
->GetMinimumWidth(),
402 ToolbarActionsBar::IconHeight());
405 void BrowserActionsContainer::Layout() {
406 if (toolbar_actions_bar_
->suppress_layout())
409 if (toolbar_action_views_
.empty()) {
416 resize_area_
->SetBounds(0, 0, platform_settings().item_spacing
, height());
418 // If the icons don't all fit, show the chevron (unless suppressed).
419 int max_x
= GetPreferredSize().width();
420 if (toolbar_actions_bar_
->IconCountToWidth(-1) > max_x
&&
421 !suppress_chevron_
&& chevron_
) {
422 chevron_
->SetVisible(true);
423 gfx::Size
chevron_size(chevron_
->GetPreferredSize());
424 max_x
-= chevron_size
.width() + kChevronSpacing
;
426 width() - ToolbarView::kStandardSpacing
- chevron_size
.width(),
428 chevron_size
.width(),
429 chevron_size
.height());
430 } else if (chevron_
) {
431 chevron_
->SetVisible(false);
434 // The range of visible icons, from start_index (inclusive) to end_index
436 size_t start_index
= in_overflow_mode() ?
437 toolbar_action_views_
.size() - toolbar_actions_bar_
->GetIconCount() : 0u;
438 // For the main container's last visible icon, we calculate how many icons we
439 // can display with the given width. We add an extra item_spacing because the
440 // last icon doesn't need padding, but we want it to divide easily.
441 size_t end_index
= in_overflow_mode() ?
442 toolbar_action_views_
.size() :
443 (max_x
- platform_settings().left_padding
-
444 platform_settings().right_padding
+
445 platform_settings().item_spacing
) /
446 ToolbarActionsBar::IconWidth(true);
447 // The maximum length for one row of icons.
448 size_t row_length
= in_overflow_mode() ?
449 platform_settings().icons_per_overflow_menu_row
: end_index
;
451 // Now draw the icons for the actions in the available space. Once all the
452 // variables are in place, the layout works equally well for the main and
453 // overflow container.
454 for (size_t i
= 0u; i
< toolbar_action_views_
.size(); ++i
) {
455 ToolbarActionView
* view
= toolbar_action_views_
[i
];
456 if (i
< start_index
|| i
>= end_index
) {
457 view
->SetVisible(false);
459 size_t relative_index
= i
- start_index
;
460 size_t index_in_row
= relative_index
% row_length
;
461 size_t row_index
= relative_index
/ row_length
;
462 view
->SetBounds(platform_settings().left_padding
+
463 index_in_row
* ToolbarActionsBar::IconWidth(true),
464 row_index
* ToolbarActionsBar::IconHeight(),
465 ToolbarActionsBar::IconWidth(false),
466 ToolbarActionsBar::IconHeight());
467 view
->SetVisible(true);
472 void BrowserActionsContainer::OnMouseEntered(const ui::MouseEvent
& event
) {
473 OnMouseEnteredToolbarActionView();
476 bool BrowserActionsContainer::GetDropFormats(
478 std::set
<OSExchangeData::CustomFormat
>* custom_formats
) {
479 return BrowserActionDragData::GetDropFormats(custom_formats
);
482 bool BrowserActionsContainer::AreDropTypesRequired() {
483 return BrowserActionDragData::AreDropTypesRequired();
486 bool BrowserActionsContainer::CanDrop(const OSExchangeData
& data
) {
487 return BrowserActionDragData::CanDrop(data
, browser_
->profile());
490 int BrowserActionsContainer::OnDragUpdated(
491 const ui::DropTargetEvent
& event
) {
492 size_t row_index
= 0;
493 size_t before_icon_in_row
= 0;
494 // If there are no visible actions (such as when dragging an icon to an empty
495 // overflow/main container), then 0, 0 for row, column is correct.
496 if (VisibleBrowserActions() != 0) {
497 // Figure out where to display the indicator. This is a complex calculation:
499 // First, we subtract out the padding to the left of the icon area, which is
500 // ToolbarView::kStandardSpacing. If we're right-to-left, we also mirror the
501 // event.x() so that our calculations are consistent with left-to-right.
502 int offset_into_icon_area
=
503 GetMirroredXInView(event
.x()) - ToolbarView::kStandardSpacing
;
505 // Next, figure out what row we're on. This only matters for overflow mode,
506 // but the calculation is the same for both.
507 row_index
= event
.y() / ToolbarActionsBar::IconHeight();
509 // Sanity check - we should never be on a different row in the main
511 DCHECK(in_overflow_mode() || row_index
== 0);
513 // Next, we determine which icon to place the indicator in front of. We want
514 // to place the indicator in front of icon n when the cursor is between the
515 // midpoints of icons (n - 1) and n. To do this we take the offset into the
516 // icon area and transform it as follows:
521 // |[IC|ON] [IC|ON] [IC|ON]
522 // We want to be before icon 0 for 0 < x <= a, icon 1 for a < x <= b, etc.
523 // Here the "*" represents the offset into the icon area, and since it's
524 // between a and b, we want to return "1".
526 // Transformed "icon area":
529 // |[ICON] |[ICON] |[ICON] |
530 // If we shift both our offset and our divider points later by half an icon
531 // plus one spacing unit, then it becomes very easy to calculate how many
532 // divider points we've passed, because they're the multiples of "one icon
534 int before_icon_unclamped
=
535 (offset_into_icon_area
+ (ToolbarActionsBar::IconWidth(false) / 2) +
536 platform_settings().item_spacing
) / ToolbarActionsBar::IconWidth(true);
538 // We need to figure out how many icons are visible on the relevant row.
539 // In the main container, this will just be the visible actions.
540 int visible_icons_on_row
= VisibleBrowserActionsAfterAnimation();
541 if (in_overflow_mode()) {
542 int icons_per_row
= platform_settings().icons_per_overflow_menu_row
;
543 // If this is the final row of the overflow, then this is the remainder of
544 // visible icons. Otherwise, it's a full row (kIconsPerRow).
545 visible_icons_on_row
=
547 static_cast<size_t>(visible_icons_on_row
/ icons_per_row
) ?
548 visible_icons_on_row
% icons_per_row
: icons_per_row
;
551 // Because the user can drag outside the container bounds, we need to clamp
552 // to the valid range. Note that the maximum allowable value is (num icons),
553 // not (num icons - 1), because we represent the indicator being past the
554 // last icon as being "before the (last + 1) icon".
556 std::min(std::max(before_icon_unclamped
, 0), visible_icons_on_row
);
559 if (!drop_position_
.get() ||
560 !(drop_position_
->row
== row_index
&&
561 drop_position_
->icon_in_row
== before_icon_in_row
)) {
562 drop_position_
.reset(new DropPosition(row_index
, before_icon_in_row
));
566 return ui::DragDropTypes::DRAG_MOVE
;
569 void BrowserActionsContainer::OnDragExited() {
570 drop_position_
.reset();
574 int BrowserActionsContainer::OnPerformDrop(
575 const ui::DropTargetEvent
& event
) {
576 BrowserActionDragData data
;
577 if (!data
.Read(event
.data()))
578 return ui::DragDropTypes::DRAG_NONE
;
580 // Make sure we have the same view as we started with.
581 DCHECK_EQ(GetIdAt(data
.index()), data
.id());
583 size_t i
= drop_position_
->row
*
584 platform_settings().icons_per_overflow_menu_row
+
585 drop_position_
->icon_in_row
;
586 if (in_overflow_mode())
587 i
+= main_container_
->VisibleBrowserActionsAfterAnimation();
588 // |i| now points to the item to the right of the drop indicator*, which is
589 // correct when dragging an icon to the left. When dragging to the right,
590 // however, we want the icon being dragged to get the index of the item to
591 // the left of the drop indicator, so we subtract one.
592 // * Well, it can also point to the end, but not when dragging to the left. :)
593 if (i
> data
.index())
596 ToolbarActionsBar::DragType drag_type
= ToolbarActionsBar::DRAG_TO_SAME
;
597 if (!toolbar_action_views_
[data
.index()]->visible())
598 drag_type
= in_overflow_mode() ? ToolbarActionsBar::DRAG_TO_OVERFLOW
:
599 ToolbarActionsBar::DRAG_TO_MAIN
;
601 toolbar_actions_bar_
->OnDragDrop(data
.index(), i
, drag_type
);
603 OnDragExited(); // Perform clean up after dragging.
604 return ui::DragDropTypes::DRAG_MOVE
;
607 void BrowserActionsContainer::GetAccessibleState(
608 ui::AXViewState
* state
) {
609 state
->role
= ui::AX_ROLE_GROUP
;
610 state
->name
= l10n_util::GetStringUTF16(IDS_ACCNAME_EXTENSIONS
);
613 void BrowserActionsContainer::WriteDragDataForView(View
* sender
,
614 const gfx::Point
& press_pt
,
615 OSExchangeData
* data
) {
618 ToolbarActionViews::iterator iter
= std::find(toolbar_action_views_
.begin(),
619 toolbar_action_views_
.end(),
621 DCHECK(iter
!= toolbar_action_views_
.end());
622 ToolbarActionViewController
* view_controller
= (*iter
)->view_controller();
623 gfx::Size
size(ToolbarActionsBar::IconWidth(false),
624 ToolbarActionsBar::IconHeight());
625 drag_utils::SetDragImageOnDataObject(
626 view_controller
->GetIcon(GetCurrentWebContents(), size
).AsImageSkia(),
627 press_pt
.OffsetFromOrigin(),
629 // Fill in the remaining info.
630 BrowserActionDragData
drag_data(view_controller
->GetId(),
631 iter
- toolbar_action_views_
.begin());
632 drag_data
.Write(browser_
->profile(), data
);
635 int BrowserActionsContainer::GetDragOperationsForView(View
* sender
,
636 const gfx::Point
& p
) {
637 return ui::DragDropTypes::DRAG_MOVE
;
640 bool BrowserActionsContainer::CanStartDragForView(View
* sender
,
641 const gfx::Point
& press_pt
,
642 const gfx::Point
& p
) {
643 // We don't allow dragging while we're highlighting.
644 return !toolbar_actions_bar_
->is_highlighting();
647 void BrowserActionsContainer::OnResize(int resize_amount
, bool done_resizing
) {
648 if (!done_resizing
) {
649 resize_amount_
= resize_amount
;
654 // Up until now we've only been modifying the resize_amount, but now it is
655 // time to set the container size to the size we have resized to, and then
656 // animate to the nearest icon count size if necessary (which may be 0).
658 std::min(std::max(toolbar_actions_bar_
->GetMinimumWidth(),
659 container_width_
- resize_amount
),
660 toolbar_actions_bar_
->GetMaximumWidth());
661 toolbar_actions_bar_
->OnResizeComplete(container_width_
);
664 void BrowserActionsContainer::AnimationProgressed(
665 const gfx::Animation
* animation
) {
666 DCHECK_EQ(resize_animation_
.get(), animation
);
667 resize_amount_
= static_cast<int>(resize_animation_
->GetCurrentValue() *
668 (container_width_
- animation_target_size_
));
672 void BrowserActionsContainer::AnimationCanceled(
673 const gfx::Animation
* animation
) {
674 AnimationEnded(animation
);
677 void BrowserActionsContainer::AnimationEnded(const gfx::Animation
* animation
) {
678 container_width_
= animation_target_size_
;
679 animation_target_size_
= 0;
681 suppress_chevron_
= false;
683 FOR_EACH_OBSERVER(BrowserActionsContainerObserver
,
685 OnBrowserActionsContainerAnimationEnded());
687 toolbar_actions_bar_
->OnAnimationEnded();
690 content::WebContents
* BrowserActionsContainer::GetCurrentWebContents() {
691 return browser_
->tab_strip_model()->GetActiveWebContents();
694 extensions::ActiveTabPermissionGranter
*
695 BrowserActionsContainer::GetActiveTabPermissionGranter() {
696 content::WebContents
* web_contents
= GetCurrentWebContents();
699 return extensions::TabHelper::FromWebContents(web_contents
)->
700 active_tab_permission_granter();
703 void BrowserActionsContainer::OnPaint(gfx::Canvas
* canvas
) {
704 // If the views haven't been initialized yet, wait for the next call to
705 // paint (one will be triggered by entering highlight mode).
706 if (toolbar_actions_bar_
->is_highlighting() &&
707 !toolbar_action_views_
.empty() && !in_overflow_mode()) {
708 extensions::ExtensionToolbarModel::HighlightType highlight_type
=
709 toolbar_actions_bar_
->highlight_type();
710 views::Painter
* painter
=
711 highlight_type
== extensions::ExtensionToolbarModel::HIGHLIGHT_INFO
712 ? info_highlight_painter_
.get()
713 : warning_highlight_painter_
.get();
714 views::Painter::PaintPainterAt(canvas
, painter
, GetLocalBounds());
717 // TODO(sky/glen): Instead of using a drop indicator, animate the icons while
718 // dragging (like we do for tab dragging).
719 if (drop_position_
.get()) {
720 // The two-pixel width drop indicator.
721 static const int kDropIndicatorWidth
= 2;
723 // Convert back to a pixel offset into the container. First find the X
724 // coordinate of the drop icon.
725 int drop_icon_x
= ToolbarView::kStandardSpacing
+
726 (drop_position_
->icon_in_row
* ToolbarActionsBar::IconWidth(true));
727 // Next, find the space before the drop icon. This will either be
728 // left padding or item spacing, depending on whether this is the first
730 // NOTE: Right now, these are the same. But let's do this right for if they
732 int space_before_drop_icon
= drop_position_
->icon_in_row
== 0 ?
733 platform_settings().left_padding
: platform_settings().item_spacing
;
734 // Now place the drop indicator halfway between this and the end of the
735 // previous icon. If there is an odd amount of available space between the
736 // two icons (or the icon and the address bar) after subtracting the drop
737 // indicator width, this calculation puts the extra pixel on the left side
738 // of the indicator, since when the indicator is between the address bar and
739 // the first icon, it looks better closer to the icon.
740 int drop_indicator_x
= drop_icon_x
-
741 ((space_before_drop_icon
+ kDropIndicatorWidth
) / 2);
742 int row_height
= ToolbarActionsBar::IconHeight();
743 int drop_indicator_y
= row_height
* drop_position_
->row
;
744 gfx::Rect
indicator_bounds(drop_indicator_x
,
748 indicator_bounds
.set_x(GetMirroredXForRect(indicator_bounds
));
750 // Color of the drop indicator.
751 static const SkColor kDropIndicatorColor
= SK_ColorBLACK
;
752 canvas
->FillRect(indicator_bounds
, kDropIndicatorColor
);
756 void BrowserActionsContainer::OnThemeChanged() {
760 void BrowserActionsContainer::ViewHierarchyChanged(
761 const ViewHierarchyChangedDetails
& details
) {
762 if (!toolbar_actions_bar_
->enabled())
765 if (details
.is_add
&& details
.child
== this) {
766 if (!in_overflow_mode() && // We only need one keybinding registry.
767 parent()->GetFocusManager()) { // focus manager can be null in tests.
768 extension_keybinding_registry_
.reset(new ExtensionKeybindingRegistryViews(
770 parent()->GetFocusManager(),
771 extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS
,
775 // Initial toolbar button creation and placement in the widget hierarchy.
776 // We do this here instead of in the constructor because adding views
777 // calls Layout on the Toolbar, which needs this object to be constructed
778 // before its Layout function is called.
779 toolbar_actions_bar_
->CreateActions();
781 added_to_view_
= true;
785 void BrowserActionsContainer::LoadImages() {
786 if (in_overflow_mode())
787 return; // Overflow mode has neither a chevron nor highlighting.
789 ui::ThemeProvider
* tp
= GetThemeProvider();
790 if (tp
&& chevron_
) {
791 chevron_
->SetImage(views::Button::STATE_NORMAL
,
792 *tp
->GetImageSkiaNamed(IDR_BROWSER_ACTIONS_OVERFLOW
));
795 const int kInfoImages
[] = IMAGE_GRID(IDR_TOOLBAR_ACTION_HIGHLIGHT
);
796 info_highlight_painter_
.reset(
797 views::Painter::CreateImageGridPainter(kInfoImages
));
798 const int kWarningImages
[] = IMAGE_GRID(IDR_DEVELOPER_MODE_HIGHLIGHT
);
799 warning_highlight_painter_
.reset(
800 views::Painter::CreateImageGridPainter(kWarningImages
));
803 void BrowserActionsContainer::ClearActiveBubble(views::Widget
* widget
) {
804 DCHECK(active_bubble_
);
805 DCHECK_EQ(active_bubble_
->GetWidget(), widget
);
806 widget
->RemoveObserver(this);
807 active_bubble_
= nullptr;