blob: dbecdff1659cb389a2591f2b175f526cba07fb8d [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_TOOLBAR_BROWSER_ACTIONS_CONTAINER_H_
#define CHROME_BROWSER_UI_VIEWS_TOOLBAR_BROWSER_ACTIONS_CONTAINER_H_
#include <stddef.h>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/optional.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_bar.h"
#include "chrome/browser/ui/toolbar/toolbar_actions_bar_delegate.h"
#include "chrome/browser/ui/views/toolbar/toolbar_action_view.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/animation/tween.h"
#include "ui/views/controls/resize_area_delegate.h"
#include "ui/views/drag_controller.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget_observer.h"
namespace views {
class BubbleDialogDelegateView;
class ResizeArea;
class Separator;
}
// The BrowserActionsContainer is a container view, responsible for drawing the
// toolbar action icons (including extension icons and icons for component
// toolbar actions). It comes in two flavors, a main container (when residing on
// the toolbar) and an overflow container (that resides in the main application
// menu, aka the Chrome menu).
//
// When in 'main' mode, the container supports the full functionality of a
// BrowserActionContainer, but in 'overflow' mode the container is effectively
// just an overflow for the 'main' toolbar (shows only the icons that the main
// toolbar does not) and as such does not have an overflow itself. The overflow
// container also does not support resizing. Since the main container only shows
// icons in the Chrome toolbar, it is limited to a single row of icons. The
// overflow container, however, is allowed to display icons in multiple rows.
//
// The main container is placed flush against the omnibox and hot dog menu,
// whereas the overflow container is placed within the hot dog menu. The
// layout is similar to this:
// rI_I_Is
// Where the letters are as follows:
// r: An invisible resize area. This is
// GetLayoutConstant(TOOLBAR_STANDARD_SPACING) pixels wide and directly
// adjacent to the omnibox. Only shown for the main container.
// I: An icon. This has a width of 28.
// _: ToolbarActionsBar::PlatformSettings::item_spacing pixels of empty space.
// s: GetLayoutConstant(TOOLBAR_STANDARD_SPACING) pixels of empty space
// (before the app menu).
// The reason the container contains the trailing space "s", rather than having
// it be handled by the parent view, is so that when the user starts
// dragging an icon around, we have the space to draw the ultimate drop
// indicator. (Otherwise, we'd be trying to draw it into the padding beyond our
// right edge, and it wouldn't appear.)
//
// The BrowserActionsContainer in 'main' mode follows a few rules, in terms of
// user experience:
//
// 1) The container can never grow beyond the space needed to show all icons
// (hereby referred to as the max width).
// 2) The container can never shrink below the space needed to show just the
// initial padding (ignoring the case where there are no icons to show, in which
// case the container won't be visible anyway).
// 3) The container snaps into place (to the pixel count that fits the visible
// icons) to make sure there is no wasted space at the edges of the container.
// 4) If the user adds or removes icons (read: installs/uninstalls browser
// actions) we grow and shrink the container as needed - but ONLY if the
// container was at max width to begin with.
// 5) If the container is NOT at max width (has an overflow menu), we respect
// that size when adding and removing icons and DON'T grow/shrink the container.
// This means that new icons (which always appear at the far right) will show up
// in the overflow.
//
// Resizing the BrowserActionsContainer:
//
// The ResizeArea view sends OnResize messages to the BrowserActionsContainer
// class as the user drags it. This modifies the value for |resize_amount_|.
// That indicates to the container that a resize is in progress and is used to
// calculate the size in GetPreferredSize(), though that function never exceeds
// the defined minimum and maximum size of the container.
//
// When the user releases the mouse (ends the resize), we calculate a target
// size for the container (animation_target_size_), clamp that value to the
// containers min and max and then animate from the *current* position (that the
// user has dragged the view to) to the target size.
//
// Animating the BrowserActionsContainer:
//
// Animations are used when snapping the container to a value that fits all
// visible icons. This can be triggered when the user finishes resizing the
// container or when Browser Actions are added/removed.
//
// We always animate from the current width (container_width_) to the target
// size (animation_target_size_), using |resize_amount| to keep track of the
// animation progress.
//
////////////////////////////////////////////////////////////////////////////////
class BrowserActionsContainer : public views::View,
public ToolbarActionsBarDelegate,
public views::ResizeAreaDelegate,
public gfx::AnimationDelegate,
public ToolbarActionView::Delegate,
public views::WidgetObserver {
public:
class Delegate {
public:
// Returns the view of the toolbar actions overflow menu to use as a
// reference point for a popup when this view isn't visible.
virtual views::MenuButton* GetOverflowReferenceView() = 0;
// Returns the maximum width the browser actions container can have. An
// empty value means there is no maximum.
virtual base::Optional<int> GetMaxBrowserActionsWidth() const = 0;
// Creates a ToolbarActionsBar for the BrowserActionsContainer to use.
virtual std::unique_ptr<ToolbarActionsBar> CreateToolbarActionsBar(
ToolbarActionsBarDelegate* delegate,
Browser* browser,
ToolbarActionsBar* main_bar) const = 0;
};
// Constructs a BrowserActionContainer for a particular |browser| object. For
// documentation of |main_container|, see class comments.
//
// |interactive| determines whether the bar can be dragged to resize it or do
// drag and drop.
BrowserActionsContainer(Browser* browser,
BrowserActionsContainer* main_container,
Delegate* delegate,
bool interactive = true);
~BrowserActionsContainer() override;
// Get the number of toolbar actions being displayed.
size_t num_toolbar_actions() const { return toolbar_action_views_.size(); }
// Returns the browser this container is associated with.
Browser* browser() const { return browser_; }
Delegate* delegate() { return delegate_; }
ToolbarActionsBar* toolbar_actions_bar() {
return toolbar_actions_bar_.get();
}
// Get a particular toolbar action view.
ToolbarActionView* GetToolbarActionViewAt(int index) {
return toolbar_action_views_[index].get();
}
// Whether we are performing resize animation on the container.
bool animating() const {
return resize_animation_ && resize_animation_->is_animating();
}
// Returns the ID of the action represented by the view at |index|.
std::string GetIdAt(size_t index) const;
// Returns the ToolbarActionView* associated with the given |extension|, or
// nullptr if none exists.
ToolbarActionView* GetViewForId(const std::string& id);
// Update the views to reflect the state of the toolbar actions.
void RefreshToolbarActionViews();
// Returns how many actions are currently visible. If the intent is to find
// how many are visible once the container finishes animation, see
// VisibleBrowserActionsAfterAnimation() below.
size_t VisibleBrowserActions() const;
// Returns how many actions will be visible once the container finishes
// animating to a new size, or (if not animating) the currently visible icons.
size_t VisibleBrowserActionsAfterAnimation() const;
// Sets the color for the separator if present, called after construction and
// on theme changes.
void SetSeparatorColor(SkColor color);
// Overridden from views::View:
gfx::Size CalculatePreferredSize() const override;
int GetHeightForWidth(int width) const override;
gfx::Size GetMinimumSize() const override;
void Layout() override;
bool GetDropFormats(int* formats,
std::set<ui::ClipboardFormatType>* format_types) override;
bool AreDropTypesRequired() override;
bool CanDrop(const ui::OSExchangeData& data) override;
int OnDragUpdated(const ui::DropTargetEvent& event) override;
void OnDragExited() override;
int OnPerformDrop(const ui::DropTargetEvent& event) override;
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
// Overridden from views::DragController:
void WriteDragDataForView(View* sender,
const gfx::Point& press_pt,
ui::OSExchangeData* data) override;
int GetDragOperationsForView(View* sender, const gfx::Point& p) override;
bool CanStartDragForView(View* sender,
const gfx::Point& press_pt,
const gfx::Point& p) override;
// Overridden from views::ResizeAreaDelegate:
void OnResize(int resize_amount, bool done_resizing) override;
// Overridden from gfx::AnimationDelegate:
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override;
void AnimationEnded(const gfx::Animation* animation) override;
// Overridden from ToolbarActionView::Delegate:
content::WebContents* GetCurrentWebContents() override;
bool ShownInsideMenu() const override;
void OnToolbarActionViewDragDone() override;
views::MenuButton* GetOverflowReferenceView() override;
gfx::Size GetToolbarActionSize() override;
// ToolbarActionsBarDelegate:
void AddViewForAction(ToolbarActionViewController* action,
size_t index) override;
void RemoveViewForAction(ToolbarActionViewController* action) override;
void RemoveAllViews() override;
void Redraw(bool order_changed) override;
void ResizeAndAnimate(gfx::Tween::Type tween_type,
int target_width) override;
int GetWidth(GetWidthTime get_width_time) const override;
bool IsAnimating() const override;
void StopAnimating() override;
void ShowToolbarActionBubble(
std::unique_ptr<ToolbarActionsBarBubbleDelegate> controller) override;
bool CloseOverflowMenuIfOpen() override;
// views::WidgetObserver:
void OnWidgetClosing(views::Widget* widget) override;
void OnWidgetDestroying(views::Widget* widget) override;
views::BubbleDialogDelegateView* active_bubble() { return active_bubble_; }
static views::FlexRule GetFlexRule();
protected:
// Overridden from views::View:
void ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) override;
void OnPaint(gfx::Canvas* canvas) override;
private:
// A struct representing the position at which an action will be dropped.
struct DropPosition;
// Clears the |active_bubble_|, and unregisters the container as an observer.
void ClearActiveBubble(views::Widget* widget);
// Utility functions for going from/to width and icon counts.
size_t WidthToIconCount(int width) const;
int GetWidthForIconCount(size_t num_icons) const;
int GetWidthWithAllActionsVisible() const;
// Get index of the drag-drop position.
size_t GetDropPositionIndex() const;
// Returns the preferred width given the limit of |max_width|. (Unlike most
// views, since we don't want to show part of an icon or a large space after
// the omnibox, this is probably *not* |max_width|).
int GetWidthForMaxWidth(int max_width) const;
// Width allocated for the resize handle, |resize_area_|. 0 when it should not
// be shown.
int GetResizeAreaWidth() const;
// Width of the separator and surrounding padding. 0 when the separator should
// not be shown.
int GetSeparatorAreaWidth() const;
const ToolbarActionsBar::PlatformSettings& platform_settings() const {
return toolbar_actions_bar_->platform_settings();
}
Delegate* const delegate_ = nullptr;
// The controlling ToolbarActionsBar, which handles most non-view logic.
std::unique_ptr<ToolbarActionsBar> toolbar_actions_bar_;
// Child toolbar action buttons.
std::vector<std::unique_ptr<ToolbarActionView>> toolbar_action_views_;
// The Browser object the container is associated with.
Browser* const browser_;
// The main container we are serving as overflow for, or NULL if this
// class is the the main container. See class comments for details on
// the difference between main and overflow.
BrowserActionsContainer* main_container_;
// The resize area for the container.
views::ResizeArea* resize_area_ = nullptr;
// Separator at the end of browser actions to highlight that these actions are
// different from built-in toolbar actions.
views::Separator* separator_ = nullptr;
// The animation that happens when the container snaps to place.
std::unique_ptr<gfx::SlideAnimation> resize_animation_;
// True if the container has been added to the parent view.
bool added_to_view_ = false;
// When the container is resizing, this is the width at which it started.
base::Optional<int> resize_starting_width_;
// This is used while the user is resizing (and when the animations are in
// progress) to know how wide the delta is between the current state and what
// we should draw.
int resize_amount_ = 0;
// Keeps track of the absolute pixel width the container should have when we
// are done animating.
int animation_target_size_ = 0;
// The DropPosition for the current drag-and-drop operation, or NULL if there
// is none.
std::unique_ptr<DropPosition> drop_position_;
// Whether the container can be interacted with (e.g drag/drop, resize).
const bool interactive_ = true;
// The extension bubble that is actively showing, if any.
views::BubbleDialogDelegateView* active_bubble_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainer);
};
#endif // CHROME_BROWSER_UI_VIEWS_TOOLBAR_BROWSER_ACTIONS_CONTAINER_H_