Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / views / controls / menu / menu_scroll_view_container.cc
blob451bd1537ad5b4cf5d66b8f552ede0800c1d8b5f
1 // Copyright (c) 2012 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 "ui/views/controls/menu/menu_scroll_view_container.h"
7 #include "third_party/skia/include/core/SkPaint.h"
8 #include "third_party/skia/include/core/SkPath.h"
9 #include "ui/accessibility/ax_view_state.h"
10 #include "ui/gfx/canvas.h"
11 #include "ui/native_theme/native_theme_aura.h"
12 #include "ui/views/border.h"
13 #include "ui/views/bubble/bubble_border.h"
14 #include "ui/views/controls/menu/menu_config.h"
15 #include "ui/views/controls/menu/menu_controller.h"
16 #include "ui/views/controls/menu/menu_item_view.h"
17 #include "ui/views/controls/menu/submenu_view.h"
18 #include "ui/views/round_rect_painter.h"
20 using ui::NativeTheme;
22 namespace views {
24 namespace {
26 static const int kBorderPaddingDueToRoundedCorners = 1;
28 // MenuScrollButton ------------------------------------------------------------
30 // MenuScrollButton is used for the scroll buttons when not all menu items fit
31 // on screen. MenuScrollButton forwards appropriate events to the
32 // MenuController.
34 class MenuScrollButton : public View {
35 public:
36 MenuScrollButton(SubmenuView* host, bool is_up)
37 : host_(host),
38 is_up_(is_up),
39 // Make our height the same as that of other MenuItemViews.
40 pref_height_(MenuItemView::pref_menu_height()) {
43 virtual gfx::Size GetPreferredSize() const OVERRIDE {
44 return gfx::Size(
45 host_->GetMenuItem()->GetMenuConfig().scroll_arrow_height * 2 - 1,
46 pref_height_);
49 virtual bool CanDrop(const OSExchangeData& data) OVERRIDE {
50 DCHECK(host_->GetMenuItem()->GetMenuController());
51 return true; // Always return true so that drop events are targeted to us.
54 virtual void OnDragEntered(const ui::DropTargetEvent& event) OVERRIDE {
55 DCHECK(host_->GetMenuItem()->GetMenuController());
56 host_->GetMenuItem()->GetMenuController()->OnDragEnteredScrollButton(
57 host_, is_up_);
60 virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE {
61 return ui::DragDropTypes::DRAG_NONE;
64 virtual void OnDragExited() OVERRIDE {
65 DCHECK(host_->GetMenuItem()->GetMenuController());
66 host_->GetMenuItem()->GetMenuController()->OnDragExitedScrollButton(host_);
69 virtual int OnPerformDrop(const ui::DropTargetEvent& event) OVERRIDE {
70 return ui::DragDropTypes::DRAG_NONE;
73 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
74 const MenuConfig& config = host_->GetMenuItem()->GetMenuConfig();
76 // The background.
77 gfx::Rect item_bounds(0, 0, width(), height());
78 NativeTheme::ExtraParams extra;
79 extra.menu_item.is_selected = false;
80 GetNativeTheme()->Paint(canvas->sk_canvas(),
81 NativeTheme::kMenuItemBackground,
82 NativeTheme::kNormal, item_bounds, extra);
84 // Then the arrow.
85 int x = width() / 2;
86 int y = (height() - config.scroll_arrow_height) / 2;
88 int x_left = x - config.scroll_arrow_height;
89 int x_right = x + config.scroll_arrow_height;
90 int y_bottom;
92 if (!is_up_) {
93 y_bottom = y;
94 y = y_bottom + config.scroll_arrow_height;
95 } else {
96 y_bottom = y + config.scroll_arrow_height;
98 SkPath path;
99 path.setFillType(SkPath::kWinding_FillType);
100 path.moveTo(SkIntToScalar(x), SkIntToScalar(y));
101 path.lineTo(SkIntToScalar(x_left), SkIntToScalar(y_bottom));
102 path.lineTo(SkIntToScalar(x_right), SkIntToScalar(y_bottom));
103 path.lineTo(SkIntToScalar(x), SkIntToScalar(y));
104 SkPaint paint;
105 paint.setStyle(SkPaint::kFill_Style);
106 paint.setAntiAlias(true);
107 paint.setColor(config.arrow_color);
108 canvas->DrawPath(path, paint);
111 private:
112 // SubmenuView we were created for.
113 SubmenuView* host_;
115 // Direction of the button.
116 bool is_up_;
118 // Preferred height.
119 int pref_height_;
121 DISALLOW_COPY_AND_ASSIGN(MenuScrollButton);
124 } // namespace
126 // MenuScrollView --------------------------------------------------------------
128 // MenuScrollView is a viewport for the SubmenuView. It's reason to exist is so
129 // that ScrollRectToVisible works.
131 // NOTE: It is possible to use ScrollView directly (after making it deal with
132 // null scrollbars), but clicking on a child of ScrollView forces the window to
133 // become active, which we don't want. As we really only need a fraction of
134 // what ScrollView does, so we use a one off variant.
136 class MenuScrollViewContainer::MenuScrollView : public View {
137 public:
138 explicit MenuScrollView(View* child) {
139 AddChildView(child);
142 virtual void ScrollRectToVisible(const gfx::Rect& rect) OVERRIDE {
143 // NOTE: this assumes we only want to scroll in the y direction.
145 // If the rect is already visible, do not scroll.
146 if (GetLocalBounds().Contains(rect))
147 return;
149 // Scroll just enough so that the rect is visible.
150 int dy = 0;
151 if (rect.bottom() > GetLocalBounds().bottom())
152 dy = rect.bottom() - GetLocalBounds().bottom();
153 else
154 dy = rect.y();
156 // Convert rect.y() to view's coordinates and make sure we don't show past
157 // the bottom of the view.
158 View* child = GetContents();
159 child->SetY(-std::max(0, std::min(
160 child->GetPreferredSize().height() - this->height(),
161 dy - child->y())));
164 // Returns the contents, which is the SubmenuView.
165 View* GetContents() {
166 return child_at(0);
169 private:
170 DISALLOW_COPY_AND_ASSIGN(MenuScrollView);
173 // MenuScrollViewContainer ----------------------------------------------------
175 MenuScrollViewContainer::MenuScrollViewContainer(SubmenuView* content_view)
176 : content_view_(content_view),
177 arrow_(BubbleBorder::NONE),
178 bubble_border_(NULL) {
179 scroll_up_button_ = new MenuScrollButton(content_view, true);
180 scroll_down_button_ = new MenuScrollButton(content_view, false);
181 AddChildView(scroll_up_button_);
182 AddChildView(scroll_down_button_);
184 scroll_view_ = new MenuScrollView(content_view);
185 AddChildView(scroll_view_);
187 arrow_ = BubbleBorderTypeFromAnchor(
188 content_view_->GetMenuItem()->GetMenuController()->GetAnchorPosition());
190 if (arrow_ != BubbleBorder::NONE)
191 CreateBubbleBorder();
192 else
193 CreateDefaultBorder();
196 bool MenuScrollViewContainer::HasBubbleBorder() {
197 return arrow_ != BubbleBorder::NONE;
200 void MenuScrollViewContainer::SetBubbleArrowOffset(int offset) {
201 DCHECK(HasBubbleBorder());
202 bubble_border_->set_arrow_offset(offset);
205 void MenuScrollViewContainer::OnPaintBackground(gfx::Canvas* canvas) {
206 if (background()) {
207 View::OnPaintBackground(canvas);
208 return;
211 gfx::Rect bounds(0, 0, width(), height());
212 NativeTheme::ExtraParams extra;
213 const MenuConfig& menu_config = content_view_->GetMenuItem()->GetMenuConfig();
214 extra.menu_background.corner_radius = menu_config.corner_radius;
215 GetNativeTheme()->Paint(canvas->sk_canvas(),
216 NativeTheme::kMenuPopupBackground, NativeTheme::kNormal, bounds, extra);
219 void MenuScrollViewContainer::Layout() {
220 gfx::Insets insets = GetInsets();
221 int x = insets.left();
222 int y = insets.top();
223 int width = View::width() - insets.width();
224 int content_height = height() - insets.height();
225 if (!scroll_up_button_->visible()) {
226 scroll_view_->SetBounds(x, y, width, content_height);
227 scroll_view_->Layout();
228 return;
231 gfx::Size pref = scroll_up_button_->GetPreferredSize();
232 scroll_up_button_->SetBounds(x, y, width, pref.height());
233 content_height -= pref.height();
235 const int scroll_view_y = y + pref.height();
237 pref = scroll_down_button_->GetPreferredSize();
238 scroll_down_button_->SetBounds(x, height() - pref.height() - insets.top(),
239 width, pref.height());
240 content_height -= pref.height();
242 scroll_view_->SetBounds(x, scroll_view_y, width, content_height);
243 scroll_view_->Layout();
246 gfx::Size MenuScrollViewContainer::GetPreferredSize() const {
247 gfx::Size prefsize = scroll_view_->GetContents()->GetPreferredSize();
248 gfx::Insets insets = GetInsets();
249 prefsize.Enlarge(insets.width(), insets.height());
250 return prefsize;
253 void MenuScrollViewContainer::GetAccessibleState(
254 ui::AXViewState* state) {
255 // Get the name from the submenu view.
256 content_view_->GetAccessibleState(state);
258 // Now change the role.
259 state->role = ui::AX_ROLE_MENU_BAR;
260 // Some AT (like NVDA) will not process focus events on menu item children
261 // unless a parent claims to be focused.
262 state->AddStateFlag(ui::AX_STATE_FOCUSED);
265 void MenuScrollViewContainer::OnBoundsChanged(
266 const gfx::Rect& previous_bounds) {
267 gfx::Size content_pref = scroll_view_->GetContents()->GetPreferredSize();
268 scroll_up_button_->SetVisible(content_pref.height() > height());
269 scroll_down_button_->SetVisible(content_pref.height() > height());
270 Layout();
273 void MenuScrollViewContainer::CreateDefaultBorder() {
274 arrow_ = BubbleBorder::NONE;
275 bubble_border_ = NULL;
277 const MenuConfig& menu_config =
278 content_view_->GetMenuItem()->GetMenuConfig();
280 bool use_border = true;
281 int padding = menu_config.corner_radius > 0 ?
282 kBorderPaddingDueToRoundedCorners : 0;
284 #if defined(USE_AURA) && !(defined(OS_LINUX) && !defined(OS_CHROMEOS))
285 if (menu_config.native_theme == ui::NativeThemeAura::instance()) {
286 // In case of NativeThemeAura the border gets drawn with the shadow.
287 // Furthermore no additional padding is wanted.
288 use_border = false;
289 padding = 0;
291 #endif
293 int top = menu_config.menu_vertical_border_size + padding;
294 int left = menu_config.menu_horizontal_border_size + padding;
295 int bottom = menu_config.menu_vertical_border_size + padding;
296 int right = menu_config.menu_horizontal_border_size + padding;
298 if (use_border) {
299 SetBorder(views::Border::CreateBorderPainter(
300 new views::RoundRectPainter(
301 menu_config.native_theme->GetSystemColor(
302 ui::NativeTheme::kColorId_MenuBorderColor),
303 menu_config.corner_radius),
304 gfx::Insets(top, left, bottom, right)));
305 } else {
306 SetBorder(Border::CreateEmptyBorder(top, left, bottom, right));
310 void MenuScrollViewContainer::CreateBubbleBorder() {
311 bubble_border_ = new BubbleBorder(arrow_,
312 BubbleBorder::SMALL_SHADOW,
313 SK_ColorWHITE);
314 SetBorder(scoped_ptr<Border>(bubble_border_));
315 set_background(new BubbleBackground(bubble_border_));
318 BubbleBorder::Arrow MenuScrollViewContainer::BubbleBorderTypeFromAnchor(
319 MenuAnchorPosition anchor) {
320 switch (anchor) {
321 case MENU_ANCHOR_BUBBLE_LEFT:
322 return BubbleBorder::RIGHT_CENTER;
323 case MENU_ANCHOR_BUBBLE_RIGHT:
324 return BubbleBorder::LEFT_CENTER;
325 case MENU_ANCHOR_BUBBLE_ABOVE:
326 return BubbleBorder::BOTTOM_CENTER;
327 case MENU_ANCHOR_BUBBLE_BELOW:
328 return BubbleBorder::TOP_CENTER;
329 default:
330 return BubbleBorder::NONE;
334 } // namespace views