231 lines
6.7 KiB
C++
231 lines
6.7 KiB
C++
|
// 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
|