diff options
author | skuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-30 11:50:00 +0000 |
---|---|---|
committer | skuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-30 11:50:00 +0000 |
commit | 5df65ae5d9f66cb8d5e8ccae7c77b25acb65f149 (patch) | |
tree | 5b650db46670e94f207df891384b82efc2ef7d0c /ui/views | |
parent | fd2d063449e1160db3545fdccf3dc7f64855d590 (diff) | |
download | chromium_src-5df65ae5d9f66cb8d5e8ccae7c77b25acb65f149.zip chromium_src-5df65ae5d9f66cb8d5e8ccae7c77b25acb65f149.tar.gz chromium_src-5df65ae5d9f66cb8d5e8ccae7c77b25acb65f149.tar.bz2 |
Adding new bubble border to menus in ChromeOS
BUG=169297
TEST=visual
Review URL: https://chromiumcodereview.appspot.com/12092008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@179601 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/views')
-rw-r--r-- | ui/views/controls/menu/menu_controller.cc | 93 | ||||
-rw-r--r-- | ui/views/controls/menu/menu_controller.h | 9 | ||||
-rw-r--r-- | ui/views/controls/menu/menu_host.cc | 25 | ||||
-rw-r--r-- | ui/views/controls/menu/menu_item_view.cc | 9 | ||||
-rw-r--r-- | ui/views/controls/menu/menu_item_view.h | 12 | ||||
-rw-r--r-- | ui/views/controls/menu/menu_runner.cc | 5 | ||||
-rw-r--r-- | ui/views/controls/menu/menu_runner.h | 2 | ||||
-rw-r--r-- | ui/views/controls/menu/menu_scroll_view_container.cc | 86 | ||||
-rw-r--r-- | ui/views/controls/menu/menu_scroll_view_container.h | 23 |
9 files changed, 235 insertions, 29 deletions
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc index cdeb37b..cabe9b2 100644 --- a/ui/views/controls/menu/menu_controller.cc +++ b/ui/views/controls/menu/menu_controller.cc @@ -56,6 +56,9 @@ namespace views { namespace { +// The spacing offset for the bubble tip. +const int kBubbleTipSize = 10; + // Returns true if the mnemonic of |menu| matches key. bool MatchesMnemonic(MenuItemView* menu, char16 key) { return menu->GetMnemonic() == key; @@ -1104,7 +1107,9 @@ void MenuController::UpdateInitialLocation( } // Reverse anchor position for RTL languages. - if (base::i18n::IsRTL()) { + if (base::i18n::IsRTL() && + (position == MenuItemView::TOPRIGHT || + position == MenuItemView::TOPLEFT)) { pending_state_.anchor = position == MenuItemView::TOPRIGHT ? MenuItemView::TOPLEFT : MenuItemView::TOPRIGHT; } else { @@ -1193,6 +1198,9 @@ bool MenuController::ShowSiblingMenu(SubmenuView* source, did_capture_ = false; gfx::Point screen_menu_loc; View::ConvertPointToScreen(button, &screen_menu_loc); + + // It is currently not possible to show a submenu recursively in a bubble. + DCHECK(!MenuItemView::IsBubble(anchor)); // Subtract 1 from the height to make the popup flush with the button border. UpdateInitialLocation(gfx::Rect(screen_menu_loc.x(), screen_menu_loc.y(), button->width(), button->height() - 1), @@ -1456,7 +1464,8 @@ void MenuController::OpenMenuImpl(MenuItemView* item, bool show) { bool prefer_leading = state_.open_leading.empty() ? true : state_.open_leading.back(); bool resulting_direction; - gfx::Rect bounds = + gfx::Rect bounds = MenuItemView::IsBubble(state_.anchor) ? + CalculateBubbleMenuBounds(item, prefer_leading, &resulting_direction) : CalculateMenuBounds(item, prefer_leading, &resulting_direction); state_.open_leading.push_back(resulting_direction); bool do_capture = (!did_capture_ && blocking_run_); @@ -1720,6 +1729,86 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item, return gfx::Rect(x, y, pref.width(), pref.height()); } +gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item, + bool prefer_leading, + bool* is_leading) { + DCHECK(item); + DCHECK(!item->GetParentMenuItem()); + + // Assume we can honor prefer_leading. + *is_leading = prefer_leading; + + SubmenuView* submenu = item->GetSubmenu(); + DCHECK(submenu); + + gfx::Size pref = submenu->GetScrollViewContainer()->GetPreferredSize(); + const gfx::Rect& owner_bounds = pending_state_.initial_bounds; + + // First the size gets reduced to the possible space. + if (!state_.monitor_bounds.IsEmpty()) { + int max_width = state_.monitor_bounds.width(); + int max_height = state_.monitor_bounds.height(); + // In case of bubbles, the maximum width is limited by the space + // between the display corner and the target area + the tip size. + if (state_.anchor == MenuItemView::BUBBLE_LEFT) { + max_width = owner_bounds.x() - state_.monitor_bounds.x() + + kBubbleTipSize; + } else if (state_.anchor == MenuItemView::BUBBLE_RIGHT) { + max_width = state_.monitor_bounds.right() - owner_bounds.right() + + kBubbleTipSize; + } else if (state_.anchor == MenuItemView::BUBBLE_ABOVE) { + max_height = owner_bounds.y() - state_.monitor_bounds.y() + + kBubbleTipSize; + } else if (state_.anchor == MenuItemView::BUBBLE_BELOW) { + max_height = state_.monitor_bounds.bottom() - owner_bounds.bottom() + + kBubbleTipSize; + } + // The space for the menu to cover should never get empty. + DCHECK_GE(max_width, kBubbleTipSize); + DCHECK_GE(max_height, kBubbleTipSize); + pref.set_width(std::min(pref.width(), max_width)); + pref.set_height(std::min(pref.height(), max_height)); + } + // Also make sure that the menu does not go too wide. + pref.set_width(std::min(pref.width(), + item->GetDelegate()->GetMaxWidthForMenu(item))); + + int x, y; + if (state_.anchor == MenuItemView::BUBBLE_ABOVE || + state_.anchor == MenuItemView::BUBBLE_BELOW) { + if (state_.anchor == MenuItemView::BUBBLE_ABOVE) + y = owner_bounds.y() - pref.height() + kBubbleTipSize; + else + y = owner_bounds.bottom() - kBubbleTipSize; + + x = owner_bounds.CenterPoint().x() - pref.width() / 2; + int x_old = x; + if (x < state_.monitor_bounds.x()) { + x = state_.monitor_bounds.x(); + } else if (x + pref.width() > state_.monitor_bounds.right()) { + x = state_.monitor_bounds.right() - pref.width(); + } + submenu->GetScrollViewContainer()->SetBubbleArrowOffset( + pref.width() / 2 - x + x_old); + } else { + if (state_.anchor == MenuItemView::BUBBLE_RIGHT) + x = owner_bounds.right() - kBubbleTipSize; + else + x = owner_bounds.x() - pref.width() + kBubbleTipSize; + + y = owner_bounds.CenterPoint().y() - pref.height() / 2; + int y_old = y; + if (y < state_.monitor_bounds.y()) { + y = state_.monitor_bounds.y(); + } else if (y + pref.height() > state_.monitor_bounds.bottom()) { + y = state_.monitor_bounds.bottom() - pref.height(); + } + submenu->GetScrollViewContainer()->SetBubbleArrowOffset( + pref.height() / 2 - y + y_old); + } + return gfx::Rect(x, y, pref.width(), pref.height()); +} + // static int MenuController::MenuDepth(MenuItemView* item) { return item ? (MenuDepth(item->GetParentMenuItem()) + 1) : 0; diff --git a/ui/views/controls/menu/menu_controller.h b/ui/views/controls/menu/menu_controller.h index 1e5985c3..66caa36 100644 --- a/ui/views/controls/menu/menu_controller.h +++ b/ui/views/controls/menu/menu_controller.h @@ -86,6 +86,9 @@ class VIEWS_EXPORT MenuController // Whether or not drag operation is in progress. bool drag_in_progress() const { return drag_in_progress_; } + // Get the anchor position wich is used to show this menu. + MenuItemView::AnchorPosition GetAnchorPosition() { return state_.anchor; } + // Cancels the current Run. See ExitType for a description of what happens // with the various parameters. void Cancel(ExitType type); @@ -376,6 +379,12 @@ class VIEWS_EXPORT MenuController bool prefer_leading, bool* is_leading); + // Calculates the bubble bounds of the menu to show. is_leading is set to + // match the direction the menu opened in. + gfx::Rect CalculateBubbleMenuBounds(MenuItemView* item, + bool prefer_leading, + bool* is_leading); + // Returns the depth of the menu. static int MenuDepth(MenuItemView* item); diff --git a/ui/views/controls/menu/menu_host.cc b/ui/views/controls/menu/menu_host.cc index 8c2374b..a5a6e7c 100644 --- a/ui/views/controls/menu/menu_host.cc +++ b/ui/views/controls/menu/menu_host.cc @@ -9,11 +9,16 @@ #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/controls/menu/menu_host_root_view.h" #include "ui/views/controls/menu/menu_item_view.h" +#include "ui/views/controls/menu/menu_scroll_view_container.h" #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/round_rect_painter.h" #include "ui/views/widget/native_widget_private.h" #include "ui/views/widget/widget.h" +#if defined(USE_AURA) +#include "ui/views/corewm/shadow_types.h" +#endif + namespace views { //////////////////////////////////////////////////////////////////////////////// @@ -33,19 +38,25 @@ void MenuHost::InitMenuHost(Widget* parent, View* contents_view, bool do_capture) { Widget::InitParams params(Widget::InitParams::TYPE_MENU); - params.has_dropshadow = true; - params.parent = parent ? parent->GetNativeView() : NULL; - params.bounds = bounds; - const MenuController* menu_controller = submenu_->GetMenuItem()->GetMenuController(); const MenuConfig& menu_config = submenu_->GetMenuItem()->GetMenuConfig(); - if (menu_controller && menu_config.corner_radius > 0) - params.transparent = true; + bool rounded_border = menu_controller && menu_config.corner_radius > 0; + bool bubble_border = submenu_->GetScrollViewContainer() && + submenu_->GetScrollViewContainer()->HasBubbleBorder(); + params.has_dropshadow = !bubble_border; + params.transparent = bubble_border || rounded_border; + params.parent = parent ? parent->GetNativeView() : NULL; + params.bounds = bounds; Init(params); +#if defined(USE_AURA) + if (bubble_border) + SetShadowType(GetNativeView(), views::corewm::SHADOW_TYPE_NONE); +#endif + SetContentsView(contents_view); - if (menu_controller && menu_config.corner_radius > 0) + if (bubble_border || rounded_border) SetOpacity(0); ShowMenuHost(do_capture); } diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc index 787f402..0c66f17 100644 --- a/ui/views/controls/menu/menu_item_view.cc +++ b/ui/views/controls/menu/menu_item_view.cc @@ -22,6 +22,7 @@ #include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/controls/menu/menu_image_util.h" +#include "ui/views/controls/menu/menu_scroll_view_container.h" #include "ui/views/controls/menu/menu_separator.h" #include "ui/views/controls/menu/submenu_view.h" #include "ui/views/widget/widget.h" @@ -181,6 +182,14 @@ void MenuItemView::GetAccessibleState(ui::AccessibleViewState* state) { } // static +bool MenuItemView::IsBubble(MenuItemView::AnchorPosition anchor) { + return anchor == MenuItemView::BUBBLE_LEFT || + anchor == MenuItemView::BUBBLE_RIGHT || + anchor == MenuItemView::BUBBLE_ABOVE || + anchor == MenuItemView::BUBBLE_BELOW; +} + +// static string16 MenuItemView::GetAccessibleNameForMenuItem( const string16& item_text, const string16& accelerator_text) { string16 accessible_name = item_text; diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h index 7fc4e21..0afcdf5 100644 --- a/ui/views/controls/menu/menu_item_view.h +++ b/ui/views/controls/menu/menu_item_view.h @@ -90,10 +90,17 @@ class VIEWS_EXPORT MenuItemView : public View { // Where the menu should be anchored to for non-RTL languages. The // opposite position will be used if base::i18n:IsRTL() is true. + // The BUBBLE flags are used when the menu should get enclosed by a bubble. + // Note that BUBBLE flags should only be used with menus which have no + // children. enum AnchorPosition { TOPLEFT, TOPRIGHT, - BOTTOMCENTER + BOTTOMCENTER, + BUBBLE_LEFT, + BUBBLE_RIGHT, + BUBBLE_ABOVE, + BUBBLE_BELOW }; // Where the menu should be drawn, above or below the bounds (when @@ -140,6 +147,9 @@ class VIEWS_EXPORT MenuItemView : public View { // X-coordinate of where the label starts. static int label_start() { return label_start_; } + // Returns if a given |anchor| is a bubble or not. + static bool IsBubble(MenuItemView::AnchorPosition anchor); + // Returns the accessible name to be used with screen readers. Mnemonics are // removed and the menu item accelerator text is appended. static string16 GetAccessibleNameForMenuItem( diff --git a/ui/views/controls/menu/menu_runner.cc b/ui/views/controls/menu/menu_runner.cc index 704300e..3545fc4 100644 --- a/ui/views/controls/menu/menu_runner.cc +++ b/ui/views/controls/menu/menu_runner.cc @@ -288,7 +288,10 @@ MenuRunner::RunResult MenuRunner::RunMenuAt(Widget* parent, display_change_listener_.reset( internal::DisplayChangeListener::Create(parent, this)); } - if ((types & MenuRunner::CONTEXT_MENU) && parent && parent->GetCurrentEvent()) + if ((types & MenuRunner::CONTEXT_MENU) && + parent && + parent->GetCurrentEvent() && + !MenuItemView::IsBubble(anchor)) anchor = parent->GetCurrentEvent()->IsGestureEvent() ? MenuItemView::BOTTOMCENTER : MenuItemView::TOPLEFT; diff --git a/ui/views/controls/menu/menu_runner.h b/ui/views/controls/menu/menu_runner.h index 72d2efc..7752708 100644 --- a/ui/views/controls/menu/menu_runner.h +++ b/ui/views/controls/menu/menu_runner.h @@ -82,6 +82,8 @@ class VIEWS_EXPORT MenuRunner { // MENU_DELETED the method is returning because the MenuRunner was deleted. // Typically callers should NOT do any processing if this returns // MENU_DELETED. + // If |anchor| uses a |BUBBLE_..| type, the bounds will get determined by + // using |bounds| as the thing to point at in screen coordinates. RunResult RunMenuAt(Widget* parent, MenuButton* button, const gfx::Rect& bounds, diff --git a/ui/views/controls/menu/menu_scroll_view_container.cc b/ui/views/controls/menu/menu_scroll_view_container.cc index a1534da..503e214 100644 --- a/ui/views/controls/menu/menu_scroll_view_container.cc +++ b/ui/views/controls/menu/menu_scroll_view_container.cc @@ -10,6 +10,7 @@ #include "ui/gfx/canvas.h" #include "ui/native_theme/native_theme.h" #include "ui/views/border.h" +#include "ui/views/bubble/bubble_border.h" #include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/controls/menu/menu_item_view.h" @@ -165,7 +166,9 @@ class MenuScrollViewContainer::MenuScrollView : public View { // MenuScrollViewContainer ---------------------------------------------------- MenuScrollViewContainer::MenuScrollViewContainer(SubmenuView* content_view) - : content_view_(content_view) { + : content_view_(content_view), + arrow_location_(BubbleBorder::NONE), + bubble_border_(NULL) { scroll_up_button_ = new MenuScrollButton(content_view, true); scroll_down_button_ = new MenuScrollButton(content_view, false); AddChildView(scroll_up_button_); @@ -174,25 +177,22 @@ MenuScrollViewContainer::MenuScrollViewContainer(SubmenuView* content_view) scroll_view_ = new MenuScrollView(content_view); AddChildView(scroll_view_); - const MenuConfig& menu_config = - content_view_->GetMenuItem()->GetMenuConfig(); + arrow_location_ = BubbleBorderTypeFromAnchor( + content_view_->GetMenuItem()->GetMenuController()->GetAnchorPosition()); - int padding = menu_config.corner_radius > 0 ? - kBorderPaddingDueToRoundedCorners : 0; - int top = menu_config.menu_vertical_border_size + padding; - int left = menu_config.menu_horizontal_border_size + padding; - int bottom = menu_config.menu_vertical_border_size + padding; - int right = menu_config.menu_horizontal_border_size + padding; + if (arrow_location_ != BubbleBorder::NONE) + CreateBubbleBorder(); + else + CreateDefaultBorder(); +} - if (NativeTheme::IsNewMenuStyleEnabled()) { - set_border(views::Border::CreateBorderPainter( - new views::RoundRectPainter(menu_config.native_theme->GetSystemColor( - ui::NativeTheme::kColorId_MenuBorderColor), - menu_config.corner_radius), - gfx::Insets(top, left, bottom, right))); - } else { - set_border(Border::CreateEmptyBorder(top, left, bottom, right)); - } +bool MenuScrollViewContainer::HasBubbleBorder() { + return arrow_location_ != BubbleBorder::NONE; +} + +void MenuScrollViewContainer::SetBubbleArrowOffset(int offset) { + DCHECK(HasBubbleBorder()); + bubble_border_->set_arrow_offset(offset); } void MenuScrollViewContainer::OnPaintBackground(gfx::Canvas* canvas) { @@ -263,4 +263,54 @@ void MenuScrollViewContainer::OnBoundsChanged( Layout(); } +void MenuScrollViewContainer::CreateDefaultBorder() { + arrow_location_ = BubbleBorder::NONE; + bubble_border_ = NULL; + + const MenuConfig& menu_config = + content_view_->GetMenuItem()->GetMenuConfig(); + + int padding = menu_config.corner_radius > 0 ? + kBorderPaddingDueToRoundedCorners : 0; + int top = menu_config.menu_vertical_border_size + padding; + int left = menu_config.menu_horizontal_border_size + padding; + int bottom = menu_config.menu_vertical_border_size + padding; + int right = menu_config.menu_horizontal_border_size + padding; + + if (NativeTheme::IsNewMenuStyleEnabled()) { + set_border(views::Border::CreateBorderPainter( + new views::RoundRectPainter(menu_config.native_theme->GetSystemColor( + ui::NativeTheme::kColorId_MenuBorderColor), + menu_config.corner_radius), + gfx::Insets(top, left, bottom, right))); + } else { + set_border(Border::CreateEmptyBorder(top, left, bottom, right)); + } +} + +void MenuScrollViewContainer::CreateBubbleBorder() { + bubble_border_ = new BubbleBorder( + arrow_location_, + BubbleBorder::NO_SHADOW); + set_border(bubble_border_); + set_background(new BubbleBackground(bubble_border_)); +} + +BubbleBorder::ArrowLocation +MenuScrollViewContainer::BubbleBorderTypeFromAnchor( + MenuItemView::AnchorPosition anchor) { + switch (anchor) { + case views::MenuItemView::BUBBLE_LEFT: + return BubbleBorder::RIGHT_CENTER; + case views::MenuItemView::BUBBLE_RIGHT: + return BubbleBorder::LEFT_CENTER; + case views::MenuItemView::BUBBLE_ABOVE: + return BubbleBorder::BOTTOM_CENTER; + case views::MenuItemView::BUBBLE_BELOW: + return BubbleBorder::TOP_CENTER; + default: + return BubbleBorder::NONE; + } +} + } // namespace views diff --git a/ui/views/controls/menu/menu_scroll_view_container.h b/ui/views/controls/menu/menu_scroll_view_container.h index 209d8f9..68762d6 100644 --- a/ui/views/controls/menu/menu_scroll_view_container.h +++ b/ui/views/controls/menu/menu_scroll_view_container.h @@ -6,6 +6,8 @@ #define UI_VIEWS_CONTROLS_MENU_MENU_SCROLL_VIEW_CONTAINER_H_ #include "ui/views/view.h" +#include "ui/views/bubble/bubble_border.h" +#include "ui/views/controls/menu/menu_item_view.h" namespace views { @@ -22,6 +24,12 @@ class MenuScrollViewContainer : public View { View* scroll_down_button() const { return scroll_down_button_; } View* scroll_up_button() const { return scroll_up_button_; } + // External function to check if the bubble border is usd. + bool HasBubbleBorder(); + + // Offsets the Arrow from the default location. + void SetBubbleArrowOffset(int offset); + // View overrides. virtual void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE; virtual void Layout() OVERRIDE; @@ -33,6 +41,15 @@ class MenuScrollViewContainer : public View { virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; private: + // Create the default border. + void CreateDefaultBorder(); + + // Create the bubble border. + void CreateBubbleBorder(); + + BubbleBorder::ArrowLocation BubbleBorderTypeFromAnchor( + MenuItemView::AnchorPosition anchor); + class MenuScrollView; // The scroll buttons. @@ -45,6 +62,12 @@ class MenuScrollViewContainer : public View { // The content view. SubmenuView* content_view_; + // If set the currently set border is a bubble border. + BubbleBorder::ArrowLocation arrow_location_; + + // The currently set border. + BubbleBorder* bubble_border_; + DISALLOW_COPY_AND_ASSIGN(MenuScrollViewContainer); }; |