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.
4
5 #include "ui/views/controls/menu/menu_host.h"
6
7 #include "base/auto_reset.h"
8 #include "base/debug/trace_event.h"
9 #include "ui/events/gestures/gesture_recognizer.h"
10 #include "ui/gfx/path.h"
11 #include "ui/native_theme/native_theme.h"
12 #include "ui/views/controls/menu/menu_controller.h"
13 #include "ui/views/controls/menu/menu_host_root_view.h"
14 #include "ui/views/controls/menu/menu_item_view.h"
15 #include "ui/views/controls/menu/menu_scroll_view_container.h"
16 #include "ui/views/controls/menu/submenu_view.h"
17 #include "ui/views/round_rect_painter.h"
18 #include "ui/views/widget/native_widget_private.h"
19 #include "ui/views/widget/widget.h"
20
21 #if defined(USE_AURA)
22 #include "ui/views/corewm/shadow_types.h"
23 #endif
24
25 namespace views {
26
27 ////////////////////////////////////////////////////////////////////////////////
28 // MenuHost, public:
29
MenuHost(SubmenuView * submenu)30 MenuHost::MenuHost(SubmenuView* submenu)
31 : submenu_(submenu),
32 destroying_(false),
33 ignore_capture_lost_(false) {
34 set_auto_release_capture(false);
35 }
36
~MenuHost()37 MenuHost::~MenuHost() {
38 }
39
InitMenuHost(Widget * parent,const gfx::Rect & bounds,View * contents_view,bool do_capture)40 void MenuHost::InitMenuHost(Widget* parent,
41 const gfx::Rect& bounds,
42 View* contents_view,
43 bool do_capture) {
44 TRACE_EVENT0("views", "MenuHost::InitMenuHost");
45 Widget::InitParams params(Widget::InitParams::TYPE_MENU);
46 const MenuController* menu_controller =
47 submenu_->GetMenuItem()->GetMenuController();
48 const MenuConfig& menu_config = submenu_->GetMenuItem()->GetMenuConfig();
49 bool rounded_border = menu_controller && menu_config.corner_radius > 0;
50 bool bubble_border = submenu_->GetScrollViewContainer() &&
51 submenu_->GetScrollViewContainer()->HasBubbleBorder();
52 params.has_dropshadow = !bubble_border;
53 params.opacity = (bubble_border || rounded_border) ?
54 Widget::InitParams::TRANSLUCENT_WINDOW :
55 Widget::InitParams::OPAQUE_WINDOW;
56 params.parent = parent ? parent->GetNativeView() : NULL;
57 params.bounds = bounds;
58 Init(params);
59
60 #if defined(USE_AURA)
61 if (bubble_border)
62 SetShadowType(GetNativeView(), views::corewm::SHADOW_TYPE_NONE);
63 #endif
64
65 SetContentsView(contents_view);
66 if (bubble_border || rounded_border)
67 SetOpacity(0);
68 ShowMenuHost(do_capture);
69 }
70
IsMenuHostVisible()71 bool MenuHost::IsMenuHostVisible() {
72 return IsVisible();
73 }
74
ShowMenuHost(bool do_capture)75 void MenuHost::ShowMenuHost(bool do_capture) {
76 // Doing a capture may make us get capture lost. Ignore it while we're in the
77 // process of showing.
78 base::AutoReset<bool> reseter(&ignore_capture_lost_, true);
79 Show();
80 if (do_capture) {
81 #if defined(USE_AURA)
82 // Cancel existing touches, so we don't miss some touch release/cancel
83 // events due to the menu taking capture.
84 ui::GestureRecognizer::Get()->TransferEventsTo(GetNativeWindow(), NULL);
85 #endif // USE_AURA
86 native_widget_private()->SetCapture();
87 }
88 }
89
HideMenuHost()90 void MenuHost::HideMenuHost() {
91 ignore_capture_lost_ = true;
92 ReleaseMenuHostCapture();
93 Hide();
94 ignore_capture_lost_ = false;
95 }
96
DestroyMenuHost()97 void MenuHost::DestroyMenuHost() {
98 HideMenuHost();
99 destroying_ = true;
100 static_cast<MenuHostRootView*>(GetRootView())->ClearSubmenu();
101 Close();
102 }
103
SetMenuHostBounds(const gfx::Rect & bounds)104 void MenuHost::SetMenuHostBounds(const gfx::Rect& bounds) {
105 SetBounds(bounds);
106 }
107
ReleaseMenuHostCapture()108 void MenuHost::ReleaseMenuHostCapture() {
109 if (native_widget_private()->HasCapture())
110 native_widget_private()->ReleaseCapture();
111 }
112
113 ////////////////////////////////////////////////////////////////////////////////
114 // MenuHost, Widget overrides:
115
CreateRootView()116 internal::RootView* MenuHost::CreateRootView() {
117 return new MenuHostRootView(this, submenu_);
118 }
119
OnMouseCaptureLost()120 void MenuHost::OnMouseCaptureLost() {
121 if (destroying_ || ignore_capture_lost_)
122 return;
123 MenuController* menu_controller =
124 submenu_->GetMenuItem()->GetMenuController();
125 if (menu_controller && !menu_controller->drag_in_progress())
126 menu_controller->CancelAll();
127 Widget::OnMouseCaptureLost();
128 }
129
OnNativeWidgetDestroyed()130 void MenuHost::OnNativeWidgetDestroyed() {
131 if (!destroying_) {
132 // We weren't explicitly told to destroy ourselves, which means the menu was
133 // deleted out from under us (the window we're parented to was closed). Tell
134 // the SubmenuView to drop references to us.
135 submenu_->MenuHostDestroyed();
136 }
137 Widget::OnNativeWidgetDestroyed();
138 }
139
OnOwnerClosing()140 void MenuHost::OnOwnerClosing() {
141 if (destroying_)
142 return;
143
144 MenuController* menu_controller =
145 submenu_->GetMenuItem()->GetMenuController();
146 if (menu_controller && !menu_controller->drag_in_progress())
147 menu_controller->CancelAll();
148 }
149
150 } // namespace views
151