diff --git a/shell/browser/ui/win/notify_icon.cc b/shell/browser/ui/win/notify_icon.cc index 3c3c17cf35636..15f3e252c6be4 100644 --- a/shell/browser/ui/win/notify_icon.cc +++ b/shell/browser/ui/win/notify_icon.cc @@ -18,6 +18,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/image/image.h" #include "ui/views/controls/menu/menu_runner.h" +#include "ui/views/widget/widget.h" namespace { @@ -48,7 +49,11 @@ NotifyIcon::NotifyIcon(NotifyIconHost* host, HWND window, UINT message, GUID guid) - : host_(host), icon_id_(id), window_(window), message_id_(message) { + : host_(host), + icon_id_(id), + window_(window), + message_id_(message), + weak_factory_(this) { guid_ = guid; is_using_guid_ = guid != GUID_DEFAULT; NOTIFYICONDATA icon_data; @@ -208,10 +213,26 @@ void NotifyIcon::PopUpContextMenu(const gfx::Point& pos, if (pos.IsOrigin()) rect.set_origin(display::Screen::GetScreen()->GetCursorScreenPoint()); - menu_runner_.reset( - new views::MenuRunner(menu_model != nullptr ? menu_model : menu_model_, - views::MenuRunner::HAS_MNEMONICS)); - menu_runner_->RunMenuAt(nullptr, nullptr, rect, + // Create a widget for the menu, otherwise we get no keyboard events, which + // is required for accessibility. + widget_.reset(new views::Widget()); + views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); + params.ownership = + views::Widget::InitParams::Ownership::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(0, 0, 0, 0); + params.force_software_compositing = true; + params.z_order = ui::ZOrderLevel::kFloatingUIElement; + + widget_->Init(std::move(params)); + + widget_->Show(); + widget_->Activate(); + menu_runner_.reset(new views::MenuRunner( + menu_model != nullptr ? menu_model : menu_model_, + views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS, + base::BindRepeating(&NotifyIcon::OnContextMenuClosed, + weak_factory_.GetWeakPtr()))); + menu_runner_->RunMenuAt(widget_.get(), NULL, rect, views::MenuAnchorPosition::kTopLeft, ui::MENU_SOURCE_MOUSE); } @@ -252,4 +273,8 @@ void NotifyIcon::InitIconData(NOTIFYICONDATA* icon_data) { } } +void NotifyIcon::OnContextMenuClosed() { + widget_->Close(); +} + } // namespace electron diff --git a/shell/browser/ui/win/notify_icon.h b/shell/browser/ui/win/notify_icon.h index 37b4b4c81b7ea..3589a86ca4196 100644 --- a/shell/browser/ui/win/notify_icon.h +++ b/shell/browser/ui/win/notify_icon.h @@ -14,6 +14,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "base/win/scoped_gdi_object.h" #include "shell/browser/ui/tray_icon.h" #include "shell/browser/ui/win/notify_icon_host.h" @@ -24,7 +25,8 @@ class Point; namespace views { class MenuRunner; -} +class Widget; +} // namespace views namespace electron { @@ -73,6 +75,7 @@ class NotifyIcon : public TrayIcon { private: void InitIconData(NOTIFYICONDATA* icon_data); + void OnContextMenuClosed(); // The tray that owns us. Weak. NotifyIconHost* host_; @@ -101,6 +104,12 @@ class NotifyIcon : public TrayIcon { // Context menu associated with this icon (if any). std::unique_ptr menu_runner_; + // Temporary widget for the context menu, needed for keyboard event capture. + std::unique_ptr widget_; + + // WeakPtrFactory for CloseClosure safety. + base::WeakPtrFactory weak_factory_; + DISALLOW_COPY_AND_ASSIGN(NotifyIcon); };