views: Support top menu switching with mouse and arrow keys (issue #2102)

- cefclient: Add example ViewsMenuBar implementation and show top menu
  by default (specify `--hide-top-menu` to hide it).
This commit is contained in:
Marshall Greenblatt
2017-02-22 13:05:27 -05:00
parent fbc8b983d5
commit 6eaf11f07a
42 changed files with 1055 additions and 152 deletions

View File

@ -299,6 +299,8 @@
'tests/cefclient/browser/root_window_win.h',
'tests/cefclient/browser/temp_window_win.cc',
'tests/cefclient/browser/temp_window_win.h',
'tests/cefclient/browser/views_menu_bar.cc',
'tests/cefclient/browser/views_menu_bar.h',
'tests/cefclient/browser/views_window.cc',
'tests/cefclient/browser/views_window.h',
'tests/cefclient/browser/window_test_runner_views.cc',
@ -350,6 +352,8 @@
'tests/cefclient/browser/root_window_views.h',
'tests/cefclient/browser/temp_window_x11.cc',
'tests/cefclient/browser/temp_window_x11.h',
'tests/cefclient/browser/views_menu_bar.cc',
'tests/cefclient/browser/views_menu_bar.h',
'tests/cefclient/browser/views_window.cc',
'tests/cefclient/browser/views_window.h',
'tests/cefclient/browser/window_test_runner_gtk.cc',

View File

@ -58,6 +58,11 @@ typedef struct _cef_menu_model_t {
///
cef_base_ref_counted_t base;
///
// Returns true (1) if this menu is a submenu.
///
int (CEF_CALLBACK *is_sub_menu)(struct _cef_menu_model_t* self);
///
// Clears the menu. Returns true (1) on success.
///

View File

@ -65,6 +65,30 @@ typedef struct _cef_menu_model_delegate_t {
struct _cef_menu_model_t* menu_model, int command_id,
cef_event_flags_t event_flags);
///
// Called when the user moves the mouse outside the menu and over the owning
// window.
///
void (CEF_CALLBACK *mouse_outside_menu)(
struct _cef_menu_model_delegate_t* self,
struct _cef_menu_model_t* menu_model, const cef_point_t* screen_point);
///
// Called on unhandled open submenu keyboard commands. |is_rtl| will be true
// (1) if the menu is displaying a right-to-left language.
///
void (CEF_CALLBACK *unhandled_open_submenu)(
struct _cef_menu_model_delegate_t* self,
struct _cef_menu_model_t* menu_model, int is_rtl);
///
// Called on unhandled close submenu keyboard commands. |is_rtl| will be true
// (1) if the menu is displaying a right-to-left language.
///
void (CEF_CALLBACK *unhandled_close_submenu)(
struct _cef_menu_model_delegate_t* self,
struct _cef_menu_model_t* menu_model, int is_rtl);
///
// The menu is about to show.
///

View File

@ -62,6 +62,12 @@ typedef struct _cef_button_delegate_t {
///
void (CEF_CALLBACK *on_button_pressed)(struct _cef_button_delegate_t* self,
struct _cef_button_t* button);
///
// Called when the state of |button| changes.
///
void (CEF_CALLBACK *on_button_state_changed)(
struct _cef_button_delegate_t* self, struct _cef_button_t* button);
} cef_button_delegate_t;

View File

@ -68,6 +68,12 @@ typedef struct _cef_menu_button_t {
void (CEF_CALLBACK *show_menu)(struct _cef_menu_button_t* self,
struct _cef_menu_model_t* menu_model, const cef_point_t* screen_point,
cef_menu_anchor_position_t anchor_position);
///
// Show the menu for this button. Results in a call to
// cef_menu_button_delegate_t::on_menu_button_pressed().
///
void (CEF_CALLBACK *trigger_menu)(struct _cef_menu_button_t* self);
} cef_menu_button_t;

View File

@ -59,6 +59,12 @@ class CefMenuModel : public virtual CefBaseRefCounted {
static CefRefPtr<CefMenuModel> CreateMenuModel(
CefRefPtr<CefMenuModelDelegate> delegate);
///
// Returns true if this menu is a submenu.
///
/*--cef()--*/
virtual bool IsSubMenu() =0;
///
// Clears the menu. Returns true on success.
///

View File

@ -59,6 +59,30 @@ class CefMenuModelDelegate : public virtual CefBaseRefCounted {
int command_id,
cef_event_flags_t event_flags) =0;
///
// Called when the user moves the mouse outside the menu and over the owning
// window.
///
/*--cef()--*/
virtual void MouseOutsideMenu(CefRefPtr<CefMenuModel> menu_model,
const CefPoint& screen_point) {}
///
// Called on unhandled open submenu keyboard commands. |is_rtl| will be true
// if the menu is displaying a right-to-left language.
///
/*--cef()--*/
virtual void UnhandledOpenSubmenu(CefRefPtr<CefMenuModel> menu_model,
bool is_rtl) {}
///
// Called on unhandled close submenu keyboard commands. |is_rtl| will be true
// if the menu is displaying a right-to-left language.
///
/*--cef()--*/
virtual void UnhandledCloseSubmenu(CefRefPtr<CefMenuModel> menu_model,
bool is_rtl) {}
///
// The menu is about to show.
///

View File

@ -54,6 +54,12 @@ class CefButtonDelegate : public CefViewDelegate {
///
/*--cef()--*/
virtual void OnButtonPressed(CefRefPtr<CefButton> button) =0;
///
// Called when the state of |button| changes.
///
/*--cef()--*/
virtual void OnButtonStateChanged(CefRefPtr<CefButton> button) {};
};
#endif // CEF_INCLUDE_VIEWS_CEF_BUTTON_DELEGATE_H_

View File

@ -78,6 +78,13 @@ class CefMenuButton : public CefLabelButton {
virtual void ShowMenu(CefRefPtr<CefMenuModel> menu_model,
const CefPoint& screen_point,
cef_menu_anchor_position_t anchor_position) =0;
///
// Show the menu for this button. Results in a call to
// CefMenuButtonDelegate::OnMenuButtonPressed().
///
/*--cef()--*/
virtual void TriggerMenu() =0;
};
#endif // CEF_INCLUDE_VIEWS_CEF_MENU_BUTTON_H_

View File

@ -113,6 +113,7 @@ class CefWindowDelegate : public CefPanelDelegate {
/*--cef()--*/
virtual bool OnKeyEvent(CefRefPtr<CefWindow> window,
const CefKeyEvent& event) { return false; }
};
#endif // CEF_INCLUDE_VIEWS_CEF_WINDOW_DELEGATE_H_

View File

@ -99,7 +99,7 @@ CefMenuManager::CefMenuManager(CefBrowserHostImpl* browser,
weak_ptr_factory_(this) {
DCHECK(web_contents());
DCHECK(runner_.get());
model_ = new CefMenuModelImpl(this, nullptr);
model_ = new CefMenuModelImpl(this, nullptr, false);
}
CefMenuManager::~CefMenuManager() {
@ -289,7 +289,8 @@ void CefMenuManager::MenuClosed(CefRefPtr<CefMenuModelImpl> source) {
web_contents()->NotifyContextMenuClosed(params_.custom_context);
}
bool CefMenuManager::FormatLabel(base::string16& label) {
bool CefMenuManager::FormatLabel(CefRefPtr<CefMenuModelImpl> source,
base::string16& label) {
return runner_->FormatLabel(label);
}

View File

@ -46,7 +46,8 @@ class CefMenuManager : public CefMenuModelImpl::Delegate,
cef_event_flags_t event_flags) override;
void MenuWillShow(CefRefPtr<CefMenuModelImpl> source) override;
void MenuClosed(CefRefPtr<CefMenuModelImpl> source) override;
bool FormatLabel(base::string16& label) override;
bool FormatLabel(CefRefPtr<CefMenuModelImpl> source,
base::string16& label) override;
void ExecuteCommandCallback(int command_id,
cef_event_flags_t event_flags);

View File

@ -14,6 +14,7 @@
#include "base/message_loop/message_loop.h"
#include "content/public/common/menu_item.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/gfx/geometry/point.h"
namespace {
@ -138,6 +139,18 @@ class CefSimpleMenuModel : public ui::MenuModel {
return NULL;
}
void MouseOutsideMenu(const gfx::Point& screen_point) override {
impl_->MouseOutsideMenu(screen_point);
}
void UnhandledOpenSubmenu(bool is_rtl) override {
impl_->UnhandledOpenSubmenu(is_rtl);
}
void UnhandledCloseSubmenu(bool is_rtl) override {
impl_->UnhandledCloseSubmenu(is_rtl);
}
void MenuWillShow() override {
impl_->MenuWillShow();
}
@ -173,7 +186,7 @@ CefRefPtr<CefMenuModel> CefMenuModel::CreateMenuModel(
return nullptr;
CefRefPtr<CefMenuModelImpl> menu_model =
new CefMenuModelImpl(nullptr, delegate);
new CefMenuModelImpl(nullptr, delegate, false);
return menu_model;
}
@ -219,10 +232,12 @@ struct CefMenuModelImpl::Item {
CefMenuModelImpl::CefMenuModelImpl(
Delegate* delegate,
CefRefPtr<CefMenuModelDelegate> menu_model_delegate)
CefRefPtr<CefMenuModelDelegate> menu_model_delegate,
bool is_submenu)
: supported_thread_id_(base::PlatformThread::CurrentId()),
delegate_(delegate),
menu_model_delegate_(menu_model_delegate) {
menu_model_delegate_(menu_model_delegate),
is_submenu_(is_submenu) {
DCHECK(delegate_ || menu_model_delegate_);
model_.reset(new CefSimpleMenuModel(this));
}
@ -230,6 +245,12 @@ CefMenuModelImpl::CefMenuModelImpl(
CefMenuModelImpl::~CefMenuModelImpl() {
}
bool CefMenuModelImpl::IsSubMenu() {
if (!VerifyContext())
return false;
return is_submenu_;
}
bool CefMenuModelImpl::Clear() {
if (!VerifyContext())
return false;
@ -284,7 +305,7 @@ CefRefPtr<CefMenuModel> CefMenuModelImpl::AddSubMenu(int command_id,
return NULL;
Item item(MENUITEMTYPE_SUBMENU, command_id, label, -1);
item.submenu_ = new CefMenuModelImpl(delegate_, menu_model_delegate_);
item.submenu_ = new CefMenuModelImpl(delegate_, menu_model_delegate_, true);
AppendItem(item);
return item.submenu_.get();
}
@ -331,7 +352,7 @@ CefRefPtr<CefMenuModel> CefMenuModelImpl::InsertSubMenuAt(
return NULL;
Item item(MENUITEMTYPE_SUBMENU, command_id, label, -1);
item.submenu_ = new CefMenuModelImpl(delegate_, menu_model_delegate_);
item.submenu_ = new CefMenuModelImpl(delegate_, menu_model_delegate_, true);
InsertItemAt(item, index);
return item.submenu_.get();
}
@ -646,6 +667,39 @@ void CefMenuModelImpl::ActivatedAt(int index, cef_event_flags_t event_flags) {
menu_model_delegate_->ExecuteCommand(this, command_id, event_flags);
}
void CefMenuModelImpl::MouseOutsideMenu(const gfx::Point& screen_point) {
if (!VerifyContext())
return;
// Allow the callstack to unwind before notifying the delegate since it may
// result in the menu being destroyed.
base::MessageLoop::current()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&CefMenuModelImpl::OnMouseOutsideMenu, this, screen_point));
}
void CefMenuModelImpl::UnhandledOpenSubmenu(bool is_rtl) {
if (!VerifyContext())
return;
// Allow the callstack to unwind before notifying the delegate since it may
// result in the menu being destroyed.
base::MessageLoop::current()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&CefMenuModelImpl::OnUnhandledOpenSubmenu, this, is_rtl));
}
void CefMenuModelImpl::UnhandledCloseSubmenu(bool is_rtl) {
if (!VerifyContext())
return;
// Allow the callstack to unwind before notifying the delegate since it may
// result in the menu being destroyed.
base::MessageLoop::current()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&CefMenuModelImpl::OnUnhandledCloseSubmenu, this, is_rtl));
}
void CefMenuModelImpl::MenuWillShow() {
if (!VerifyContext())
return;
@ -654,14 +708,15 @@ void CefMenuModelImpl::MenuWillShow() {
delegate_->MenuWillShow(this);
if (menu_model_delegate_)
menu_model_delegate_->MenuWillShow(this);
for (auto& observer : observers_)
observer.MenuWillShow(this);
}
void CefMenuModelImpl::MenuWillClose() {
if (!VerifyContext())
return;
if (!auto_notify_menu_closed_)
return;
// Due to how menus work on the different platforms, ActivatedAt will be
// called after this. It's more convenient for the delegate to be called
// afterwards, though, so post a task.
@ -673,7 +728,7 @@ void CefMenuModelImpl::MenuWillClose() {
base::string16 CefMenuModelImpl::GetFormattedLabelAt(int index) {
base::string16 label = GetLabelAt(index).ToString16();
if (delegate_)
delegate_->FormatLabel(label);
delegate_->FormatLabel(this, label);
if (menu_model_delegate_) {
CefString new_label = label;
if (menu_model_delegate_->FormatLabel(this, new_label))
@ -699,18 +754,6 @@ bool CefMenuModelImpl::VerifyRefCount() {
return true;
}
void CefMenuModelImpl::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void CefMenuModelImpl::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
bool CefMenuModelImpl::HasObserver(Observer* observer) const {
return observers_.HasObserver(observer);
}
void CefMenuModelImpl::AddMenuItem(const content::MenuItem& menu_item) {
const int command_id = static_cast<int>(menu_item.action);
@ -746,6 +789,11 @@ void CefMenuModelImpl::AddMenuItem(const content::MenuItem& menu_item) {
}
}
void CefMenuModelImpl::NotifyMenuClosed() {
DCHECK(!auto_notify_menu_closed_);
OnMenuClosed();
}
void CefMenuModelImpl::AppendItem(const Item& item) {
ValidateItem(item);
items_.push_back(item);
@ -772,13 +820,34 @@ void CefMenuModelImpl::ValidateItem(const Item& item) {
#endif
}
void CefMenuModelImpl::OnMouseOutsideMenu(const gfx::Point& screen_point) {
if (delegate_)
delegate_->MouseOutsideMenu(this, screen_point);
if (menu_model_delegate_) {
menu_model_delegate_->MouseOutsideMenu(this,
CefPoint(screen_point.x(), screen_point.y()));
}
}
void CefMenuModelImpl::OnUnhandledOpenSubmenu(bool is_rtl) {
if (delegate_)
delegate_->UnhandledOpenSubmenu(this, is_rtl);
if (menu_model_delegate_)
menu_model_delegate_->UnhandledOpenSubmenu(this, is_rtl);
}
void CefMenuModelImpl::OnUnhandledCloseSubmenu(bool is_rtl) {
if (delegate_)
delegate_->UnhandledCloseSubmenu(this, is_rtl);
if (menu_model_delegate_)
menu_model_delegate_->UnhandledCloseSubmenu(this, is_rtl);
}
void CefMenuModelImpl::OnMenuClosed() {
if (delegate_)
delegate_->MenuClosed(this);
if (menu_model_delegate_)
menu_model_delegate_->MenuClosed(this);
for (auto& observer : observers_)
observer.MenuClosed(this);
}
bool CefMenuModelImpl::VerifyContext() {

View File

@ -12,7 +12,6 @@
#include "include/cef_menu_model.h"
#include "include/cef_menu_model_delegate.h"
#include "base/observer_list.h"
#include "base/threading/platform_thread.h"
#include "ui/base/models/menu_model.h"
@ -30,38 +29,41 @@ class CefMenuModelImpl : public CefMenuModel {
int command_id,
cef_event_flags_t event_flags) =0;
// Notifies the delegate that the menu is about to show.
// Called when the user moves the mouse outside the menu and over the owning
// window.
virtual void MouseOutsideMenu(CefRefPtr<CefMenuModelImpl> source,
const gfx::Point& screen_point) {}
// Called on unhandled open/close submenu keyboard commands. |is_rtl| will
// be true if the menu is displaying a right-to-left language.
virtual void UnhandledOpenSubmenu(CefRefPtr<CefMenuModelImpl> source,
bool is_rtl) {}
virtual void UnhandledCloseSubmenu(CefRefPtr<CefMenuModelImpl> source,
bool is_rtl) {}
// Called when the menu is about to show.
virtual void MenuWillShow(CefRefPtr<CefMenuModelImpl> source) =0;
// Notifies the delegate that the menu has closed.
// Called when the menu has closed.
virtual void MenuClosed(CefRefPtr<CefMenuModelImpl> source) =0;
// Allows the delegate to modify a menu item label before it's displayed.
virtual bool FormatLabel(base::string16& label) =0;
virtual bool FormatLabel(CefRefPtr<CefMenuModelImpl> source,
base::string16& label) =0;
protected:
virtual ~Delegate() {}
};
class Observer {
public:
// Notifies the delegate that the menu is about to show.
virtual void MenuWillShow(CefRefPtr<CefMenuModelImpl> source) {};
// Notifies the delegate that the menu has closed.
virtual void MenuClosed(CefRefPtr<CefMenuModelImpl> source) {};
protected:
virtual ~Observer() {}
};
// Either |delegate| or |menu_model_delegate| must be non-nullptr.
// If |delegate| is non-nullptr it must outlive this class.
CefMenuModelImpl(Delegate* delegate,
CefRefPtr<CefMenuModelDelegate> menu_model_delegate);
CefRefPtr<CefMenuModelDelegate> menu_model_delegate,
bool is_submenu);
~CefMenuModelImpl() override;
// CefMenuModel methods.
bool IsSubMenu() override;
bool Clear() override;
int GetCount() override;
bool AddSeparator() override;
@ -124,6 +126,9 @@ class CefMenuModelImpl : public CefMenuModel {
// Callbacks from the ui::MenuModel implementation.
void ActivatedAt(int index, cef_event_flags_t event_flags);
void MouseOutsideMenu(const gfx::Point& screen_point);
void UnhandledOpenSubmenu(bool is_rtl);
void UnhandledCloseSubmenu(bool is_rtl);
void MenuWillShow();
void MenuWillClose();
base::string16 GetFormattedLabelAt(int index);
@ -131,21 +136,20 @@ class CefMenuModelImpl : public CefMenuModel {
// Verify that only a single reference exists to all CefMenuModelImpl objects.
bool VerifyRefCount();
// Manage observer objects. The observer must either outlive this object or
// remove itself before destruction.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
bool HasObserver(Observer* observer) const;
// Helper for adding custom menu items originating from the renderer process.
void AddMenuItem(const content::MenuItem& menu_item);
ui::MenuModel* model() { return model_.get(); }
ui::MenuModel* model() const { return model_.get(); }
// Used when created via CefMenuManager.
Delegate* delegate() { return delegate_; }
Delegate* delegate() const { return delegate_; }
void set_delegate(Delegate* delegate) { delegate_ = delegate; }
// Used for menus run via CefWindowImpl::ShowMenu to provide more accurate
// menu close notification.
void set_auto_notify_menu_closed(bool val) { auto_notify_menu_closed_ = val; }
void NotifyMenuClosed();
private:
struct Item;
@ -156,7 +160,10 @@ class CefMenuModelImpl : public CefMenuModel {
void InsertItemAt(const Item& item, int index);
void ValidateItem(const Item& item);
// Notify the delegate that the menu is closed.
// Notify the delegate asynchronously.
void OnMouseOutsideMenu(const gfx::Point& screen_point);
void OnUnhandledOpenSubmenu(bool is_rtl);
void OnUnhandledCloseSubmenu(bool is_rtl);
void OnMenuClosed();
// Verify that the object is being accessed from the correct thread.
@ -170,11 +177,12 @@ class CefMenuModelImpl : public CefMenuModel {
// Used when created via CefMenuModel::CreateMenuModel().
CefRefPtr<CefMenuModelDelegate> menu_model_delegate_;
const bool is_submenu_;
ItemVector items_;
std::unique_ptr<ui::MenuModel> model_;
// Observers that want to be notified of changes to this object.
base::ObserverList<Observer> observers_;
bool auto_notify_menu_closed_ = true;
IMPLEMENT_REFCOUNTING(CefMenuModelImpl);
DISALLOW_COPY_AND_ASSIGN(CefMenuModelImpl);

View File

@ -40,10 +40,19 @@ CEF_BUTTON_VIEW_T class CefButtonView : public CEF_VIEW_VIEW_D {
return button;
}
// views::CustomButton methods:
void StateChanged() override;
// views::ButtonListener methods:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
};
CEF_BUTTON_VIEW_T void CEF_BUTTON_VIEW_D::StateChanged() {
ParentClass::StateChanged();
if (ParentClass::cef_delegate())
ParentClass::cef_delegate()->OnButtonStateChanged(GetCefButton());
}
CEF_BUTTON_VIEW_T void CEF_BUTTON_VIEW_D::ButtonPressed(
views::Button* sender, const ui::Event& event) {
if (ParentClass::cef_delegate())

View File

@ -50,6 +50,11 @@ void CefMenuButtonImpl::ShowMenu(CefRefPtr<CefMenuModel> menu_model,
}
}
void CefMenuButtonImpl::TriggerMenu() {
CEF_REQUIRE_VALID_RETURN_VOID();
root_view()->Activate(nullptr);
}
CefMenuButtonImpl::CefMenuButtonImpl(CefRefPtr<CefMenuButtonDelegate> delegate)
: ParentClass(delegate) {
DCHECK(delegate);

View File

@ -32,6 +32,7 @@ class CefMenuButtonImpl :
void ShowMenu(CefRefPtr<CefMenuModel> menu_model,
const CefPoint& screen_point,
cef_menu_anchor_position_t anchor_position) override;
void TriggerMenu() override;
// CefLabelButton methods:
CefRefPtr<CefMenuButton> AsMenuButton() override { return this; }

View File

@ -390,13 +390,6 @@ void CefWindowImpl::OnWindowViewDeleted() {
Detach();
}
void CefWindowImpl::MenuClosed(CefRefPtr<CefMenuModelImpl> source) {
DCHECK_EQ(menu_model_, source);
menu_model_->RemoveObserver(this);
menu_model_ = nullptr;
menu_runner_.reset(nullptr);
}
// Will only be called if CanHandleAccelerators() returns true.
bool CefWindowImpl::AcceleratorPressed(const ui::Accelerator& accelerator) {
for (const auto& entry : accelerator_map_) {
@ -433,12 +426,16 @@ void CefWindowImpl::ShowMenu(views::MenuButton* menu_button,
return;
menu_model_ = menu_model_impl;
menu_model_->AddObserver(this);
// We'll send the MenuClosed notification manually for better accuracy.
menu_model_->set_auto_notify_menu_closed(false);
menu_runner_.reset(
new views::MenuRunner(menu_model_impl->model(),
menu_button ? views::MenuRunner::HAS_MNEMONICS :
views::MenuRunner::CONTEXT_MENU));
views::MenuRunner::ASYNC |
(menu_button ? views::MenuRunner::HAS_MNEMONICS :
views::MenuRunner::CONTEXT_MENU),
base::Bind(&CefWindowImpl::MenuClosed, this)));
views::MenuRunner::RunResult result = menu_runner_->RunMenuAt(
widget_,
@ -449,6 +446,12 @@ void CefWindowImpl::ShowMenu(views::MenuButton* menu_button,
ALLOW_UNUSED_LOCAL(result);
}
void CefWindowImpl::MenuClosed() {
menu_model_->NotifyMenuClosed();
menu_model_ = nullptr;
menu_runner_.reset(nullptr);
}
void CefWindowImpl::CancelMenu() {
CEF_REQUIRE_VALID_RETURN_VOID();
if (menu_runner_)

View File

@ -26,7 +26,6 @@ class MenuButton;
class CefWindowImpl :
public CefPanelImpl<CefWindowView, CefWindow, CefWindowDelegate>,
public CefWindowView::Delegate,
public CefMenuModelImpl::Observer,
public ui::AcceleratorTarget {
public:
typedef CefPanelImpl<CefWindowView, CefWindow, CefWindowDelegate> ParentClass;
@ -105,9 +104,6 @@ class CefWindowImpl :
void OnWindowClosing() override;
void OnWindowViewDeleted() override;
// CefMenuModelImpl::Observer methods:
void MenuClosed(CefRefPtr<CefMenuModelImpl> source) override;
// CefViewAdapter methods:
std::string GetDebugType() override { return "Window"; }
void GetDebugInfo(base::DictionaryValue* info,
@ -125,6 +121,7 @@ class CefWindowImpl :
CefRefPtr<CefMenuModel> menu_model,
const CefPoint& screen_point,
cef_menu_anchor_position_t anchor_position);
void MenuClosed();
private:
// Create a new implementation object.

View File

@ -38,6 +38,20 @@ namespace {
// MEMBER FUNCTIONS - Body may be edited by hand.
int CEF_CALLBACK menu_model_is_sub_menu(struct _cef_menu_model_t* self) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Execute
bool _retval = CefMenuModelCppToC::Get(self)->IsSubMenu();
// Return type: bool
return _retval;
}
int CEF_CALLBACK menu_model_clear(struct _cef_menu_model_t* self) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
@ -986,6 +1000,7 @@ int CEF_CALLBACK menu_model_get_accelerator_at(struct _cef_menu_model_t* self,
// CONSTRUCTOR - Do not edit by hand.
CefMenuModelCppToC::CefMenuModelCppToC() {
GetStruct()->is_sub_menu = menu_model_is_sub_menu;
GetStruct()->clear = menu_model_clear;
GetStruct()->get_count = menu_model_get_count;
GetStruct()->add_separator = menu_model_add_separator;

View File

@ -38,6 +38,70 @@ void CEF_CALLBACK menu_model_delegate_execute_command(
event_flags);
}
void CEF_CALLBACK menu_model_delegate_mouse_outside_menu(
struct _cef_menu_model_delegate_t* self, cef_menu_model_t* menu_model,
const cef_point_t* screen_point) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: menu_model; type: refptr_diff
DCHECK(menu_model);
if (!menu_model)
return;
// Verify param: screen_point; type: simple_byref_const
DCHECK(screen_point);
if (!screen_point)
return;
// Translate param: screen_point; type: simple_byref_const
CefPoint screen_pointVal = screen_point?*screen_point:CefPoint();
// Execute
CefMenuModelDelegateCppToC::Get(self)->MouseOutsideMenu(
CefMenuModelCToCpp::Wrap(menu_model),
screen_pointVal);
}
void CEF_CALLBACK menu_model_delegate_unhandled_open_submenu(
struct _cef_menu_model_delegate_t* self, cef_menu_model_t* menu_model,
int is_rtl) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: menu_model; type: refptr_diff
DCHECK(menu_model);
if (!menu_model)
return;
// Execute
CefMenuModelDelegateCppToC::Get(self)->UnhandledOpenSubmenu(
CefMenuModelCToCpp::Wrap(menu_model),
is_rtl?true:false);
}
void CEF_CALLBACK menu_model_delegate_unhandled_close_submenu(
struct _cef_menu_model_delegate_t* self, cef_menu_model_t* menu_model,
int is_rtl) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: menu_model; type: refptr_diff
DCHECK(menu_model);
if (!menu_model)
return;
// Execute
CefMenuModelDelegateCppToC::Get(self)->UnhandledCloseSubmenu(
CefMenuModelCToCpp::Wrap(menu_model),
is_rtl?true:false);
}
void CEF_CALLBACK menu_model_delegate_menu_will_show(
struct _cef_menu_model_delegate_t* self, cef_menu_model_t* menu_model) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
@ -108,6 +172,11 @@ int CEF_CALLBACK menu_model_delegate_format_label(
CefMenuModelDelegateCppToC::CefMenuModelDelegateCppToC() {
GetStruct()->execute_command = menu_model_delegate_execute_command;
GetStruct()->mouse_outside_menu = menu_model_delegate_mouse_outside_menu;
GetStruct()->unhandled_open_submenu =
menu_model_delegate_unhandled_open_submenu;
GetStruct()->unhandled_close_submenu =
menu_model_delegate_unhandled_close_submenu;
GetStruct()->menu_will_show = menu_model_delegate_menu_will_show;
GetStruct()->menu_closed = menu_model_delegate_menu_closed;
GetStruct()->format_label = menu_model_delegate_format_label;

View File

@ -37,6 +37,23 @@ void CEF_CALLBACK button_delegate_on_button_pressed(
CefButtonCToCpp::Wrap(button));
}
void CEF_CALLBACK button_delegate_on_button_state_changed(
struct _cef_button_delegate_t* self, cef_button_t* button) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: button; type: refptr_diff
DCHECK(button);
if (!button)
return;
// Execute
CefButtonDelegateCppToC::Get(self)->OnButtonStateChanged(
CefButtonCToCpp::Wrap(button));
}
cef_size_t CEF_CALLBACK button_delegate_get_preferred_size(
struct _cef_view_delegate_t* self, cef_view_t* view) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
@ -215,6 +232,8 @@ void CEF_CALLBACK button_delegate_on_blur(struct _cef_view_delegate_t* self,
CefButtonDelegateCppToC::CefButtonDelegateCppToC() {
GetStruct()->on_button_pressed = button_delegate_on_button_pressed;
GetStruct()->on_button_state_changed =
button_delegate_on_button_state_changed;
GetStruct()->base.get_preferred_size = button_delegate_get_preferred_size;
GetStruct()->base.get_minimum_size = button_delegate_get_minimum_size;
GetStruct()->base.get_maximum_size = button_delegate_get_maximum_size;

View File

@ -81,6 +81,17 @@ void CEF_CALLBACK menu_button_show_menu(struct _cef_menu_button_t* self,
anchor_position);
}
void CEF_CALLBACK menu_button_trigger_menu(struct _cef_menu_button_t* self) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Execute
CefMenuButtonCppToC::Get(self)->TriggerMenu();
}
cef_menu_button_t* CEF_CALLBACK menu_button_as_menu_button(
struct _cef_label_button_t* self) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
@ -1186,6 +1197,7 @@ int CEF_CALLBACK menu_button_convert_point_from_view(struct _cef_view_t* self,
CefMenuButtonCppToC::CefMenuButtonCppToC() {
GetStruct()->show_menu = menu_button_show_menu;
GetStruct()->trigger_menu = menu_button_trigger_menu;
GetStruct()->base.as_menu_button = menu_button_as_menu_button;
GetStruct()->base.set_text = menu_button_set_text;
GetStruct()->base.get_text = menu_button_get_text;

View File

@ -64,6 +64,25 @@ void CEF_CALLBACK menu_button_delegate_on_button_pressed(
CefButtonCToCpp::Wrap(button));
}
void CEF_CALLBACK menu_button_delegate_on_button_state_changed(
struct _cef_button_delegate_t* self, cef_button_t* button) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return;
// Verify param: button; type: refptr_diff
DCHECK(button);
if (!button)
return;
// Execute
CefMenuButtonDelegateCppToC::Get(
reinterpret_cast<cef_menu_button_delegate_t*>(
self))->OnButtonStateChanged(
CefButtonCToCpp::Wrap(button));
}
cef_size_t CEF_CALLBACK menu_button_delegate_get_preferred_size(
struct _cef_view_delegate_t* self, cef_view_t* view) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
@ -244,6 +263,8 @@ CefMenuButtonDelegateCppToC::CefMenuButtonDelegateCppToC() {
GetStruct()->on_menu_button_pressed =
menu_button_delegate_on_menu_button_pressed;
GetStruct()->base.on_button_pressed = menu_button_delegate_on_button_pressed;
GetStruct()->base.on_button_state_changed =
menu_button_delegate_on_button_state_changed;
GetStruct()->base.base.get_preferred_size =
menu_button_delegate_get_preferred_size;
GetStruct()->base.base.get_minimum_size =

View File

@ -36,6 +36,20 @@ CefRefPtr<CefMenuModel> CefMenuModel::CreateMenuModel(
// VIRTUAL METHODS - Body may be edited by hand.
bool CefMenuModelCToCpp::IsSubMenu() {
cef_menu_model_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, is_sub_menu))
return false;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
int _retval = _struct->is_sub_menu(_struct);
// Return type: bool
return _retval?true:false;
}
bool CefMenuModelCToCpp::Clear() {
cef_menu_model_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, clear))

View File

@ -31,6 +31,7 @@ class CefMenuModelCToCpp
CefMenuModelCToCpp();
// CefMenuModel methods.
bool IsSubMenu() OVERRIDE;
bool Clear() OVERRIDE;
int GetCount() OVERRIDE;
bool AddSeparator() OVERRIDE;

View File

@ -37,6 +37,63 @@ void CefMenuModelDelegateCToCpp::ExecuteCommand(
event_flags);
}
void CefMenuModelDelegateCToCpp::MouseOutsideMenu(
CefRefPtr<CefMenuModel> menu_model, const CefPoint& screen_point) {
cef_menu_model_delegate_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, mouse_outside_menu))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: menu_model; type: refptr_diff
DCHECK(menu_model.get());
if (!menu_model.get())
return;
// Execute
_struct->mouse_outside_menu(_struct,
CefMenuModelCppToC::Wrap(menu_model),
&screen_point);
}
void CefMenuModelDelegateCToCpp::UnhandledOpenSubmenu(
CefRefPtr<CefMenuModel> menu_model, bool is_rtl) {
cef_menu_model_delegate_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, unhandled_open_submenu))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: menu_model; type: refptr_diff
DCHECK(menu_model.get());
if (!menu_model.get())
return;
// Execute
_struct->unhandled_open_submenu(_struct,
CefMenuModelCppToC::Wrap(menu_model),
is_rtl);
}
void CefMenuModelDelegateCToCpp::UnhandledCloseSubmenu(
CefRefPtr<CefMenuModel> menu_model, bool is_rtl) {
cef_menu_model_delegate_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, unhandled_close_submenu))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: menu_model; type: refptr_diff
DCHECK(menu_model.get());
if (!menu_model.get())
return;
// Execute
_struct->unhandled_close_submenu(_struct,
CefMenuModelCppToC::Wrap(menu_model),
is_rtl);
}
void CefMenuModelDelegateCToCpp::MenuWillShow(
CefRefPtr<CefMenuModel> menu_model) {
cef_menu_model_delegate_t* _struct = GetStruct();

View File

@ -35,6 +35,12 @@ class CefMenuModelDelegateCToCpp
// CefMenuModelDelegate methods.
void ExecuteCommand(CefRefPtr<CefMenuModel> menu_model, int command_id,
cef_event_flags_t event_flags) override;
void MouseOutsideMenu(CefRefPtr<CefMenuModel> menu_model,
const CefPoint& screen_point) override;
void UnhandledOpenSubmenu(CefRefPtr<CefMenuModel> menu_model,
bool is_rtl) override;
void UnhandledCloseSubmenu(CefRefPtr<CefMenuModel> menu_model,
bool is_rtl) override;
void MenuWillShow(CefRefPtr<CefMenuModel> menu_model) override;
void MenuClosed(CefRefPtr<CefMenuModel> menu_model) override;
bool FormatLabel(CefRefPtr<CefMenuModel> menu_model,

View File

@ -35,6 +35,24 @@ void CefButtonDelegateCToCpp::OnButtonPressed(CefRefPtr<CefButton> button) {
CefButtonCppToC::Wrap(button));
}
void CefButtonDelegateCToCpp::OnButtonStateChanged(
CefRefPtr<CefButton> button) {
cef_button_delegate_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, on_button_state_changed))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: button; type: refptr_diff
DCHECK(button.get());
if (!button.get())
return;
// Execute
_struct->on_button_state_changed(_struct,
CefButtonCppToC::Wrap(button));
}
CefSize CefButtonDelegateCToCpp::GetPreferredSize(CefRefPtr<CefView> view) {
cef_view_delegate_t* _struct = reinterpret_cast<cef_view_delegate_t*>(
GetStruct());

View File

@ -34,6 +34,7 @@ class CefButtonDelegateCToCpp
// CefButtonDelegate methods.
void OnButtonPressed(CefRefPtr<CefButton> button) override;
void OnButtonStateChanged(CefRefPtr<CefButton> button) override;
// CefViewDelegate methods.
CefSize GetPreferredSize(CefRefPtr<CefView> view) override;

View File

@ -73,6 +73,17 @@ void CefMenuButtonCToCpp::ShowMenu(CefRefPtr<CefMenuModel> menu_model,
anchor_position);
}
void CefMenuButtonCToCpp::TriggerMenu() {
cef_menu_button_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, trigger_menu))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Execute
_struct->trigger_menu(_struct);
}
CefRefPtr<CefMenuButton> CefMenuButtonCToCpp::AsMenuButton() {
cef_label_button_t* _struct = reinterpret_cast<cef_label_button_t*>(GetStruct(
));

View File

@ -34,6 +34,7 @@ class CefMenuButtonCToCpp
void ShowMenu(CefRefPtr<CefMenuModel> menu_model,
const CefPoint& screen_point,
cef_menu_anchor_position_t anchor_position) OVERRIDE;
void TriggerMenu() OVERRIDE;
// CefLabelButton methods.
CefRefPtr<CefMenuButton> AsMenuButton() OVERRIDE;

View File

@ -55,6 +55,25 @@ void CefMenuButtonDelegateCToCpp::OnButtonPressed(CefRefPtr<CefButton> button) {
CefButtonCppToC::Wrap(button));
}
void CefMenuButtonDelegateCToCpp::OnButtonStateChanged(
CefRefPtr<CefButton> button) {
cef_button_delegate_t* _struct = reinterpret_cast<cef_button_delegate_t*>(
GetStruct());
if (CEF_MEMBER_MISSING(_struct, on_button_state_changed))
return;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: button; type: refptr_diff
DCHECK(button.get());
if (!button.get())
return;
// Execute
_struct->on_button_state_changed(_struct,
CefButtonCppToC::Wrap(button));
}
CefSize CefMenuButtonDelegateCToCpp::GetPreferredSize(CefRefPtr<CefView> view) {
cef_view_delegate_t* _struct = reinterpret_cast<cef_view_delegate_t*>(
GetStruct());

View File

@ -38,6 +38,7 @@ class CefMenuButtonDelegateCToCpp
// CefButtonDelegate methods.
void OnButtonPressed(CefRefPtr<CefButton> button) override;
void OnButtonStateChanged(CefRefPtr<CefButton> button) override;
// CefViewDelegate methods.
CefSize GetPreferredSize(CefRefPtr<CefView> view) override;

View File

@ -327,4 +327,10 @@ patches = [
'name': 'plugin_info_webkit_2015',
'path': '../third_party/WebKit/',
},
{
# Expose callbacks for mouse/keyboard events that trigger menu switching.
# https://bitbucket.org/chromiumembedded/cef/issues/2102
'name': 'views_menu_2102',
'path': '../',
},
]

View File

@ -0,0 +1,147 @@
diff --git ui/base/models/menu_model.h ui/base/models/menu_model.h
index 0755f27..72db677 100644
--- ui/base/models/menu_model.h
+++ ui/base/models/menu_model.h
@@ -15,6 +15,7 @@
namespace gfx {
class FontList;
class Image;
+class Point;
}
namespace ui {
@@ -115,6 +116,15 @@ class UI_BASE_EXPORT MenuModel {
// |event_flags| is a bit mask of ui::EventFlags.
virtual void ActivatedAt(int index, int event_flags);
+ // Called when the user moves the mouse outside the menu and over the owning
+ // window.
+ virtual void MouseOutsideMenu(const gfx::Point& screen_point) {}
+
+ // Called on unhandled open/close submenu keyboard commands. |is_rtl| will be
+ // true if the menu is displaying a right-to-left language.
+ virtual void UnhandledOpenSubmenu(bool is_rtl) {}
+ virtual void UnhandledCloseSubmenu(bool is_rtl) {}
+
// Called when the menu is about to be shown.
virtual void MenuWillShow() {}
diff --git ui/views/controls/menu/menu_controller.cc ui/views/controls/menu/menu_controller.cc
index 79ff77c..a0582c0 100644
--- ui/views/controls/menu/menu_controller.cc
+++ ui/views/controls/menu/menu_controller.cc
@@ -2254,8 +2254,13 @@ MenuItemView* MenuController::FindNextSelectableMenuItem(
void MenuController::OpenSubmenuChangeSelectionIfCan() {
MenuItemView* item = pending_state_.item;
- if (!item->HasSubmenu() || !item->enabled())
+ if (!item->HasSubmenu() || !item->enabled() || !item->GetParentMenuItem()) {
+ MenuItemView* submenu_item =
+ item->GetParentMenuItem() ? item->GetParentMenuItem() : item;
+ submenu_item->GetDelegate()->OnUnhandledOpenSubmenu(submenu_item,
+ base::i18n::IsRTL());
return;
+ }
MenuItemView* to_select = NULL;
if (item->GetSubmenu()->GetMenuItemCount() > 0)
to_select = FindInitialSelectableMenuItem(item, INCREMENT_SELECTION_DOWN);
@@ -2270,8 +2275,10 @@ void MenuController::OpenSubmenuChangeSelectionIfCan() {
void MenuController::CloseSubmenu() {
MenuItemView* item = state_.item;
DCHECK(item);
- if (!item->GetParentMenuItem())
+ if (!item->GetParentMenuItem()) {
+ item->GetDelegate()->OnUnhandledCloseSubmenu(item, base::i18n::IsRTL());
return;
+ }
if (item->HasSubmenu() && item->GetSubmenu()->IsShowing())
SetSelection(item, SELECTION_UPDATE_IMMEDIATELY);
else if (item->GetParentMenuItem()->GetParentMenuItem())
diff --git ui/views/controls/menu/menu_delegate.h ui/views/controls/menu/menu_delegate.h
index 3b7cb7f..3ad68d0 100644
--- ui/views/controls/menu/menu_delegate.h
+++ ui/views/controls/menu/menu_delegate.h
@@ -229,6 +229,11 @@ class VIEWS_EXPORT MenuDelegate {
bool* has_mnemonics,
MenuButton** button);
+ // Called on unhandled open/close submenu keyboard commands. |is_rtl| will be
+ // true if the menu is displaying a right-to-left language.
+ virtual void OnUnhandledOpenSubmenu(MenuItemView* menu, bool is_rtl) {}
+ virtual void OnUnhandledCloseSubmenu(MenuItemView* menu, bool is_rtl) {}
+
// Returns the max width menus can grow to be.
virtual int GetMaxWidthForMenu(MenuItemView* menu);
diff --git ui/views/controls/menu/menu_model_adapter.cc ui/views/controls/menu/menu_model_adapter.cc
index bc04dcb..b2ec114 100644
--- ui/views/controls/menu/menu_model_adapter.cc
+++ ui/views/controls/menu/menu_model_adapter.cc
@@ -245,6 +245,49 @@ void MenuModelAdapter::SelectionChanged(MenuItemView* menu) {
NOTREACHED();
}
+MenuItemView* MenuModelAdapter::GetSiblingMenu(MenuItemView* menu,
+ const gfx::Point& screen_point,
+ MenuAnchorPosition* anchor,
+ bool* has_mnemonics,
+ MenuButton** button) {
+ // Look up the menu model for this menu.
+ const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator =
+ menu_map_.find(menu);
+ if (map_iterator != menu_map_.end()) {
+ map_iterator->second->MouseOutsideMenu(screen_point);
+ return nullptr;
+ }
+
+ NOTREACHED();
+ return nullptr;
+}
+
+void MenuModelAdapter::OnUnhandledOpenSubmenu(MenuItemView* menu,
+ bool is_rtl) {
+ // Look up the menu model for this menu.
+ const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator =
+ menu_map_.find(menu);
+ if (map_iterator != menu_map_.end()) {
+ map_iterator->second->UnhandledOpenSubmenu(is_rtl);
+ return;
+ }
+
+ NOTREACHED();
+}
+
+void MenuModelAdapter::OnUnhandledCloseSubmenu(MenuItemView* menu,
+ bool is_rtl) {
+ // Look up the menu model for this menu.
+ const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator =
+ menu_map_.find(menu);
+ if (map_iterator != menu_map_.end()) {
+ map_iterator->second->UnhandledCloseSubmenu(is_rtl);
+ return;
+ }
+
+ NOTREACHED();
+}
+
void MenuModelAdapter::WillShowMenu(MenuItemView* menu) {
// Look up the menu model for this menu.
const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator =
diff --git ui/views/controls/menu/menu_model_adapter.h ui/views/controls/menu/menu_model_adapter.h
index c9799da..c7ecca6 100644
--- ui/views/controls/menu/menu_model_adapter.h
+++ ui/views/controls/menu/menu_model_adapter.h
@@ -76,6 +76,13 @@ class VIEWS_EXPORT MenuModelAdapter : public MenuDelegate {
bool IsCommandVisible(int id) const override;
bool IsItemChecked(int id) const override;
void SelectionChanged(MenuItemView* menu) override;
+ MenuItemView* GetSiblingMenu(MenuItemView* menu,
+ const gfx::Point& screen_point,
+ MenuAnchorPosition* anchor,
+ bool* has_mnemonics,
+ MenuButton** button) override;
+ void OnUnhandledOpenSubmenu(MenuItemView* menu, bool is_rtl) override;
+ void OnUnhandledCloseSubmenu(MenuItemView* menu, bool is_rtl) override;
void WillShowMenu(MenuItemView* menu) override;
void WillHideMenu(MenuItemView* menu) override;
void OnMenuClosed(MenuItemView* menu, MenuRunner::RunResult result) override;

View File

@ -0,0 +1,230 @@
// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefclient/browser/views_menu_bar.h"
#include "include/views/cef_box_layout.h"
#include "include/views/cef_window.h"
namespace client {
namespace {
const int kMenuBarGroupId = 100;
} // namespace
ViewsMenuBar::ViewsMenuBar(Delegate* delegate,
int menu_id_start)
: delegate_(delegate),
id_start_(menu_id_start),
id_next_(menu_id_start),
last_nav_with_keyboard_(false) {
DCHECK(delegate_);
DCHECK_GT(id_start_, 0);
}
bool ViewsMenuBar::HasMenuId(int menu_id) const {
return menu_id >= id_start_ && menu_id < id_next_;
}
CefRefPtr<CefPanel> ViewsMenuBar::GetMenuPanel() {
EnsureMenuPanel();
return panel_;
}
CefRefPtr<CefMenuModel> ViewsMenuBar::CreateMenuModel(const std::string& label,
int* menu_id) {
EnsureMenuPanel();
// Assign the new menu ID.
const int new_menu_id = id_next_++;
if (menu_id)
*menu_id = new_menu_id;
// Create the new MenuModel.
CefRefPtr<CefMenuModel> model = CefMenuModel::CreateMenuModel(this);
models_.push_back(model);
// Create the new MenuButton.
CefRefPtr<CefMenuButton> button =
CefMenuButton::CreateMenuButton(this, label, false, false);
button->SetID(new_menu_id);
// Assign a group ID to allow focus traversal between MenuButtons using the
// arrow keys when the menu is not displayed.
button->SetGroupID(kMenuBarGroupId);
// Add the new MenuButton to the Planel.
panel_->AddChildView(button);
return model;
}
CefRefPtr<CefMenuModel> ViewsMenuBar::GetMenuModel(int menu_id) const {
if (HasMenuId(menu_id))
return models_[menu_id - id_start_];
return NULL;
}
void ViewsMenuBar::SetMenuFocusable(bool focusable) {
if (!panel_)
return;
for (int id = id_start_; id < id_next_; ++id)
panel_->GetViewForID(id)->SetFocusable(focusable);
if (focusable) {
// Give focus to the first MenuButton.
panel_->GetViewForID(id_start_)->RequestFocus();
}
}
void ViewsMenuBar::Reset() {
panel_ = NULL;
models_.clear();
id_next_ = id_start_;
}
void ViewsMenuBar::OnMenuButtonPressed(CefRefPtr<CefMenuButton> menu_button,
const CefPoint& screen_point) {
CefRefPtr<CefMenuModel> menu_model = GetMenuModel(menu_button->GetID());
// Adjust menu position left by button width.
CefPoint point = screen_point;
point.x -= menu_button->GetBounds().width - 4;
// Keep track of the current |last_nav_with_keyboard_| status and restore it
// after displaying the new menu.
bool cur_last_nav_with_keyboard = last_nav_with_keyboard_;
// May result in the previous menu being closed, in which case MenuClosed will
// be called before the new menu is displayed.
menu_button->ShowMenu(menu_model, point, CEF_MENU_ANCHOR_TOPLEFT);
last_nav_with_keyboard_ = cur_last_nav_with_keyboard;
}
void ViewsMenuBar::ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
int command_id,
cef_event_flags_t event_flags) {
delegate_->MenuBarExecuteCommand(menu_model, command_id, event_flags);
}
void ViewsMenuBar::MouseOutsideMenu(CefRefPtr<CefMenuModel> menu_model,
const CefPoint& screen_point) {
DCHECK(panel_);
// Retrieve the Window hosting the Panel.
CefRefPtr<CefWindow> window = panel_->GetWindow();
DCHECK(window);
// Convert the point from screen to window coordinates.
CefPoint window_point = screen_point;
if (!window->ConvertPointFromScreen(window_point))
return;
CefRect panel_bounds = panel_->GetBounds();
if (last_nav_with_keyboard_) {
// The user navigated last using the keyboard. Don't change menus using
// mouse movements until the mouse exits and re-enters the Panel.
if (panel_bounds.Contains(window_point))
return;
last_nav_with_keyboard_ = false;
}
// Check that the point is inside the Panel.
if (!panel_bounds.Contains(window_point))
return;
const int active_menu_id = GetActiveMenuId();
// Determine which MenuButton is under the specified point.
for (int id = id_start_; id < id_next_; ++id) {
// Skip the currently active MenuButton.
if (id == active_menu_id)
continue;
CefRefPtr<CefView> button = panel_->GetViewForID(id);
CefRect button_bounds = button->GetBounds();
if (button_bounds.Contains(window_point)) {
// Trigger the hovered MenuButton.
TriggerMenuButton(button);
break;
}
}
}
void ViewsMenuBar::UnhandledOpenSubmenu(CefRefPtr<CefMenuModel> menu_model,
bool is_rtl) {
TriggerNextMenu(is_rtl ? 1 : -1);
}
void ViewsMenuBar::UnhandledCloseSubmenu(CefRefPtr<CefMenuModel> menu_model,
bool is_rtl) {
TriggerNextMenu(is_rtl ? -1 : 1);
}
void ViewsMenuBar::MenuClosed(CefRefPtr<CefMenuModel> menu_model) {
// Reset |last_nav_with_keyboard_| status whenever the main menu closes.
if (!menu_model->IsSubMenu() && last_nav_with_keyboard_)
last_nav_with_keyboard_ = false;
}
void ViewsMenuBar::EnsureMenuPanel() {
if (panel_)
return;
panel_ = CefPanel::CreatePanel(NULL);
// Use a horizontal box layout.
CefBoxLayoutSettings top_panel_layout_settings;
top_panel_layout_settings.horizontal = true;
panel_->SetToBoxLayout(top_panel_layout_settings);
}
int ViewsMenuBar::GetActiveMenuId() {
DCHECK(panel_);
for (int id = id_start_; id < id_next_; ++id) {
CefRefPtr<CefButton> button = panel_->GetViewForID(id)->AsButton();
if (button->GetState() == CEF_BUTTON_STATE_PRESSED)
return id;
}
return -1;
}
void ViewsMenuBar::TriggerNextMenu(int offset) {
DCHECK(panel_);
const int active_menu_id = GetActiveMenuId();
const int menu_count = id_next_ - id_start_;
const int active_menu_index = active_menu_id - id_start_;
// Compute the modulus to avoid negative values.
int next_menu_index = (active_menu_index + offset) % menu_count;
if (next_menu_index < 0)
next_menu_index += menu_count;
// Cancel the existing menu. MenuClosed may be called.
panel_->GetWindow()->CancelMenu();
// Set status indicating that we navigated using the keyboard.
last_nav_with_keyboard_ = true;
// Show the new menu.
TriggerMenuButton(panel_->GetViewForID(id_start_ + next_menu_index));
}
void ViewsMenuBar::TriggerMenuButton(CefRefPtr<CefView> button) {
CefRefPtr<CefMenuButton> menu_button =
button->AsButton()->AsLabelButton()->AsMenuButton();
if (menu_button->IsFocusable())
menu_button->RequestFocus();
menu_button->TriggerMenu();
}
} // namespace client

View File

@ -0,0 +1,116 @@
// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_MENU_BAR_H_
#define CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_MENU_BAR_H_
#pragma once
#include <string>
#include <vector>
#include "include/cef_menu_model.h"
#include "include/cef_menu_model_delegate.h"
#include "include/views/cef_menu_button.h"
#include "include/views/cef_menu_button_delegate.h"
#include "include/views/cef_panel.h"
namespace client {
// Implements a menu bar which is composed of CefMenuButtons positioned in a
// row with automatic switching between them via mouse/keyboard. All methods
// must be called on the browser process UI thread.
class ViewsMenuBar : public CefMenuButtonDelegate,
public CefMenuModelDelegate {
public:
// Delegate methods will be called on the browser process UI thread.
class Delegate {
public:
// Called when a menu command is selected.
virtual void MenuBarExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
int command_id,
cef_event_flags_t event_flags) =0;
protected:
virtual ~Delegate() {}
};
// |delegate| must outlive this object.
// |menu_id_start| is the ID for the first CefMenuButton in the bar. An ID
// range starting with |menu_id_start| and extending for a reasonable distance
// should be reserved in the client for MenuBar usage.
ViewsMenuBar(Delegate* delegate,
int menu_id_start);
// Returns true if |menu_id| exists in the menu bar.
bool HasMenuId(int menu_id) const;
// Returns the CefPanel that represents the menu bar.
CefRefPtr<CefPanel> GetMenuPanel();
// Create a new menu with the specified |label|. If |menu_id| is non-NULL it
// will be populated with the new menu ID.
CefRefPtr<CefMenuModel> CreateMenuModel(const std::string& label,
int* menu_id);
// Returns the menu with the specified |menu_id|, or NULL if no such menu
// exists.
CefRefPtr<CefMenuModel> GetMenuModel(int menu_id) const;
// Assign or remove focus from the menu bar.
// Focus is assigned to the menu bar by ViewsWindow::OnKeyEvent when the ALT
// key is pressed. Focus is removed from the menu bar by ViewsWindow::OnFocus
// when a control not in the menu bar gains focus.
void SetMenuFocusable(bool focusable);
// Reset menu bar state.
void Reset();
protected:
// CefButtonDelegate methods:
void OnButtonPressed(CefRefPtr<CefButton> button) OVERRIDE {}
// CefMenuButtonDelegate methods:
void OnMenuButtonPressed(CefRefPtr<CefMenuButton> menu_button,
const CefPoint& screen_point) OVERRIDE;
// CefMenuModelDelegate methods:
void ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
int command_id,
cef_event_flags_t event_flags) OVERRIDE;
void MouseOutsideMenu(CefRefPtr<CefMenuModel> menu_model,
const CefPoint& screen_point) OVERRIDE;
void UnhandledOpenSubmenu(CefRefPtr<CefMenuModel> menu_model,
bool is_rtl) OVERRIDE;
void UnhandledCloseSubmenu(CefRefPtr<CefMenuModel> menu_model,
bool is_rtl) OVERRIDE;
void MenuClosed(CefRefPtr<CefMenuModel> menu_model) OVERRIDE;
private:
// Creates the menu panel if it doesn't already exist.
void EnsureMenuPanel();
// Returns the ID for the currently active menu, or -1 if no menu is currently
// active.
int GetActiveMenuId();
// Triggers the menu at the specified |offset| from the currently active menu.
void TriggerNextMenu(int offset);
// Triggers the specified MenuButton |button|.
void TriggerMenuButton(CefRefPtr<CefView> button);
Delegate* delegate_; // Not owned by this object.
const int id_start_;
int id_next_;
CefRefPtr<CefPanel> panel_;
std::vector<CefRefPtr<CefMenuModel> > models_;
bool last_nav_with_keyboard_;
IMPLEMENT_REFCOUNTING(ViewsMenuBar);
DISALLOW_COPY_AND_ASSIGN(ViewsMenuBar);
};
} // namespace client
#endif // CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_MENU_BAR_H_

View File

@ -34,8 +34,10 @@ enum ControlIds {
ID_RELOAD_BUTTON,
ID_URL_TEXTFIELD,
ID_MENU_BUTTON,
ID_TOP_FILE_MENU_BUTTON,
ID_TOP_TEST_MENU_BUTTON,
// Reserved range of top menu button IDs.
ID_TOP_MENU_FIRST,
ID_TOP_MENU_LAST = ID_TOP_MENU_FIRST + 10,
};
typedef std::vector<CefRefPtr<CefLabelButton> > LabelButtons;
@ -301,31 +303,10 @@ void ViewsWindow::OnMenuButtonPressed(CefRefPtr<CefMenuButton> menu_button,
const CefPoint& screen_point) {
CEF_REQUIRE_UI_THREAD();
DCHECK(with_controls_);
DCHECK_EQ(ID_MENU_BUTTON, menu_button->GetID());
CefRefPtr<CefMenuModel> menu_model;
cef_menu_anchor_position_t position = CEF_MENU_ANCHOR_TOPLEFT;
switch (menu_button->GetID()) {
case ID_MENU_BUTTON:
menu_model = button_menu_model_;
position = CEF_MENU_ANCHOR_TOPRIGHT;
break;
case ID_TOP_FILE_MENU_BUTTON:
menu_model = file_menu_model_;
break;
case ID_TOP_TEST_MENU_BUTTON:
menu_model = test_menu_model_;
break;
}
CefPoint point = screen_point;
if (position == CEF_MENU_ANCHOR_TOPLEFT) {
// Adjust menu position left by button width.
point.x -= menu_button->GetBounds().width - 4;
}
if (menu_model)
menu_button->ShowMenu(menu_model, point, position);
menu_button->ShowMenu(button_menu_model_, screen_point,
CEF_MENU_ANCHOR_TOPRIGHT);
}
void ViewsWindow::ExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
@ -430,8 +411,10 @@ void ViewsWindow::OnWindowDestroyed(CefRefPtr<CefWindow> window) {
browser_view_ = NULL;
button_menu_model_ = NULL;
file_menu_model_ = NULL;
test_menu_model_ = NULL;
if (top_menu_bar_) {
top_menu_bar_->Reset();
top_menu_bar_ = NULL;
}
window_ = NULL;
}
@ -492,17 +475,21 @@ void ViewsWindow::OnFocus(CefRefPtr<CefView> view) {
// When focus leaves the menu buttons make them unfocusable.
if (menu_has_focus_) {
const int view_id = view->GetID();
if (with_top_menu_) {
if (view_id != ID_TOP_FILE_MENU_BUTTON &&
view_id != ID_TOP_TEST_MENU_BUTTON) {
if (top_menu_bar_) {
if (!top_menu_bar_->HasMenuId(view_id))
SetMenuFocusable(false);
}
} else if (view_id != ID_MENU_BUTTON) {
SetMenuFocusable(false);
}
}
}
void ViewsWindow::MenuBarExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
int command_id,
cef_event_flags_t event_flags) {
ExecuteCommand(menu_model, command_id, event_flags);
}
ViewsWindow::ViewsWindow(Delegate* delegate,
CefRefPtr<CefBrowserView> browser_view)
: delegate_(delegate),
@ -515,7 +502,9 @@ ViewsWindow::ViewsWindow(Delegate* delegate,
CefRefPtr<CefCommandLine> command_line =
CefCommandLine::GetGlobalCommandLine();
frameless_ = command_line->HasSwitch(switches::kHideFrame);
with_top_menu_ = command_line->HasSwitch(switches::kShowTopMenu);
if (!command_line->HasSwitch(switches::kHideTopMenu))
top_menu_bar_ = new ViewsMenuBar(this, ID_TOP_MENU_FIRST);
}
void ViewsWindow::SetBrowserView(CefRefPtr<CefBrowserView> browser_view) {
@ -534,26 +523,13 @@ void ViewsWindow::CreateMenuModel() {
AddTestMenuItems(test_menu);
AddFileMenuItems(button_menu_model_, 1);
if (with_top_menu_) {
// Create the top menu model.
file_menu_model_ = CefMenuModel::CreateMenuModel(this);
AddFileMenuItems(file_menu_model_, 0);
test_menu_model_ = CefMenuModel::CreateMenuModel(this);
AddTestMenuItems(test_menu_model_);
if (top_menu_bar_) {
// Add the menus to the top menu bar.
AddFileMenuItems(top_menu_bar_->CreateMenuModel("File", NULL), 0);
AddTestMenuItems(top_menu_bar_->CreateMenuModel("Tests", NULL));
}
}
CefRefPtr<CefMenuButton> ViewsWindow::CreateTopMenuButton(
const std::string& label, int id) {
CefRefPtr<CefMenuButton> button =
CefMenuButton::CreateMenuButton(this, label, false, false);
button->SetID(id);
// Assign a group ID to allow focus traversal between top menu buttons using
// the arrow keys.
button->SetGroupID(1);
return button;
}
CefRefPtr<CefLabelButton> ViewsWindow::CreateBrowseButton(
const std::string& label,
int id) {
@ -570,25 +546,8 @@ void ViewsWindow::AddControls() {
CreateMenuModel();
CefRefPtr<CefPanel> top_menu_panel;
if (with_top_menu_) {
// Create the top menu buttons.
CefRefPtr<CefMenuButton> top_file_menu_button =
CreateTopMenuButton("File", ID_TOP_FILE_MENU_BUTTON);
CefRefPtr<CefMenuButton> top_test_menu_button =
CreateTopMenuButton("Test", ID_TOP_TEST_MENU_BUTTON);
// Create the top menu panel.
top_menu_panel = CefPanel::CreatePanel(NULL);
// Use a horizontal box layout for |top_panel|.
CefBoxLayoutSettings top_panel_layout_settings;
top_panel_layout_settings.horizontal = true;
CefRefPtr<CefBoxLayout> top_panel_layout =
top_menu_panel->SetToBoxLayout(top_panel_layout_settings);
top_menu_panel->AddChildView(top_file_menu_button);
top_menu_panel->AddChildView(top_test_menu_button);
}
if (top_menu_bar_)
top_menu_panel = top_menu_bar_->GetMenuPanel();
// Create the browse buttons.
LabelButtons browse_buttons;
@ -677,14 +636,8 @@ void ViewsWindow::SetMenuFocusable(bool focusable) {
if (!window_ || !with_controls_)
return;
if (with_top_menu_) {
window_->GetViewForID(ID_TOP_FILE_MENU_BUTTON)->SetFocusable(focusable);
window_->GetViewForID(ID_TOP_TEST_MENU_BUTTON)->SetFocusable(focusable);
if (focusable) {
// Give focus to top file menu button.
window_->GetViewForID(ID_TOP_FILE_MENU_BUTTON)->RequestFocus();
}
if (top_menu_bar_) {
top_menu_bar_->SetMenuFocusable(focusable);
} else {
window_->GetViewForID(ID_MENU_BUTTON)->SetFocusable(focusable);

View File

@ -19,6 +19,7 @@
#include "include/views/cef_textfield_delegate.h"
#include "include/views/cef_window.h"
#include "include/views/cef_window_delegate.h"
#include "tests/cefclient/browser/views_menu_bar.h"
namespace client {
@ -29,7 +30,8 @@ class ViewsWindow : public CefBrowserViewDelegate,
public CefMenuButtonDelegate,
public CefMenuModelDelegate,
public CefTextfieldDelegate,
public CefWindowDelegate {
public CefWindowDelegate,
public ViewsMenuBar::Delegate {
public:
// Delegate methods will be called on the browser process UI thread.
class Delegate {
@ -120,6 +122,11 @@ class ViewsWindow : public CefBrowserViewDelegate,
CefSize GetMinimumSize(CefRefPtr<CefView> view) OVERRIDE;
void OnFocus(CefRefPtr<CefView> view) OVERRIDE;
// ViewsMenuBar::Delegate methods:
void MenuBarExecuteCommand(CefRefPtr<CefMenuModel> menu_model,
int command_id,
cef_event_flags_t event_flags) OVERRIDE;
private:
// |delegate| is guaranteed to outlive this object.
// |browser_view| may be NULL, in which case SetBrowserView() will be called.
@ -130,8 +137,6 @@ class ViewsWindow : public CefBrowserViewDelegate,
// Create controls.
void CreateMenuModel();
CefRefPtr<CefMenuButton> CreateTopMenuButton(const std::string& label,
int id);
CefRefPtr<CefLabelButton> CreateBrowseButton(const std::string& label,
int id);
@ -154,12 +159,10 @@ class ViewsWindow : public CefBrowserViewDelegate,
CefRefPtr<CefBrowserView> browser_view_;
bool frameless_;
bool with_controls_;
bool with_top_menu_;
CefRefPtr<CefWindow> window_;
CefRefPtr<CefMenuModel> button_menu_model_;
CefRefPtr<CefMenuModel> file_menu_model_;
CefRefPtr<CefMenuModel> test_menu_model_;
CefRefPtr<ViewsMenuBar> top_menu_bar_;
bool menu_has_focus_;
CefSize minimum_window_size_;

View File

@ -34,7 +34,7 @@ const char kFilterURL[] = "filter-url";
const char kUseViews[] = "use-views";
const char kHideFrame[] = "hide-frame";
const char kHideControls[] = "hide-controls";
const char kShowTopMenu[] = "show-top-menu";
const char kHideTopMenu[] = "hide-top-menu";
const char kWidevineCdmPath[] = "widevine-cdm-path";
const char kSslClientCertificate[] = "ssl-client-certificate";

View File

@ -28,7 +28,7 @@ extern const char kFilterURL[];
extern const char kUseViews[];
extern const char kHideFrame[];
extern const char kHideControls[];
extern const char kShowTopMenu[];
extern const char kHideTopMenu[];
extern const char kWidevineCdmPath[];
extern const char kSslClientCertificate[];