summaryrefslogtreecommitdiffstats
path: root/ui/views
diff options
context:
space:
mode:
authorskuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-30 11:50:00 +0000
committerskuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-30 11:50:00 +0000
commit5df65ae5d9f66cb8d5e8ccae7c77b25acb65f149 (patch)
tree5b650db46670e94f207df891384b82efc2ef7d0c /ui/views
parentfd2d063449e1160db3545fdccf3dc7f64855d590 (diff)
downloadchromium_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.cc93
-rw-r--r--ui/views/controls/menu/menu_controller.h9
-rw-r--r--ui/views/controls/menu/menu_host.cc25
-rw-r--r--ui/views/controls/menu/menu_item_view.cc9
-rw-r--r--ui/views/controls/menu/menu_item_view.h12
-rw-r--r--ui/views/controls/menu/menu_runner.cc5
-rw-r--r--ui/views/controls/menu/menu_runner.h2
-rw-r--r--ui/views/controls/menu/menu_scroll_view_container.cc86
-rw-r--r--ui/views/controls/menu/menu_scroll_view_container.h23
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);
};