mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
chrome: Support customization of context menus (see issue #2969)
This commit is contained in:
529
libcef/browser/simple_menu_model_impl.cc
Normal file
529
libcef/browser/simple_menu_model_impl.cc
Normal file
@ -0,0 +1,529 @@
|
||||
// Copyright (c) 2021 The Chromium Embedded Framework Authors.
|
||||
// Portions copyright (c) 2012 The Chromium 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 "libcef/browser/simple_menu_model_impl.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "libcef/browser/thread_util.h"
|
||||
#include "libcef/common/task_runner_impl.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Value based on the documentation on SimpleMenuModel::GetIndexOfCommandId().
|
||||
const int kInvalidIndex = -1;
|
||||
const int kInvalidCommandId = -1;
|
||||
const int kInvalidGroupId = -1;
|
||||
|
||||
cef_menu_item_type_t GetCefItemType(ui::MenuModel::ItemType type) {
|
||||
switch (type) {
|
||||
case ui::MenuModel::TYPE_COMMAND:
|
||||
return MENUITEMTYPE_COMMAND;
|
||||
case ui::MenuModel::TYPE_CHECK:
|
||||
return MENUITEMTYPE_CHECK;
|
||||
case ui::MenuModel::TYPE_RADIO:
|
||||
return MENUITEMTYPE_RADIO;
|
||||
case ui::MenuModel::TYPE_SEPARATOR:
|
||||
return MENUITEMTYPE_SEPARATOR;
|
||||
case ui::MenuModel::TYPE_SUBMENU:
|
||||
return MENUITEMTYPE_SUBMENU;
|
||||
default:
|
||||
return MENUITEMTYPE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CefSimpleMenuModelImpl::CefSimpleMenuModelImpl(
|
||||
ui::SimpleMenuModel* model,
|
||||
ui::SimpleMenuModel::Delegate* delegate,
|
||||
StateDelegate* state_delegate,
|
||||
bool is_owned,
|
||||
bool is_submenu)
|
||||
: supported_thread_id_(base::PlatformThread::CurrentId()),
|
||||
model_(model),
|
||||
delegate_(delegate),
|
||||
state_delegate_(state_delegate),
|
||||
is_owned_(is_owned),
|
||||
is_submenu_(is_submenu) {
|
||||
DCHECK(model_);
|
||||
DCHECK(delegate_);
|
||||
DCHECK(state_delegate_);
|
||||
}
|
||||
|
||||
CefSimpleMenuModelImpl::~CefSimpleMenuModelImpl() {
|
||||
// Detach() must be called before object destruction.
|
||||
DCHECK(!model_);
|
||||
DCHECK(submenumap_.empty());
|
||||
}
|
||||
|
||||
void CefSimpleMenuModelImpl::Detach() {
|
||||
DCHECK(VerifyContext());
|
||||
|
||||
if (!submenumap_.empty()) {
|
||||
auto it = submenumap_.begin();
|
||||
for (; it != submenumap_.end(); ++it) {
|
||||
it->second->Detach();
|
||||
}
|
||||
submenumap_.clear();
|
||||
}
|
||||
|
||||
if (is_owned_)
|
||||
delete model_;
|
||||
model_ = nullptr;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::IsSubMenu() {
|
||||
if (!VerifyContext())
|
||||
return false;
|
||||
return is_submenu_;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::Clear() {
|
||||
if (!VerifyContext())
|
||||
return false;
|
||||
|
||||
model_->Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
int CefSimpleMenuModelImpl::GetCount() {
|
||||
if (!VerifyContext())
|
||||
return 0;
|
||||
|
||||
return model_->GetItemCount();
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::AddSeparator() {
|
||||
if (!VerifyContext())
|
||||
return false;
|
||||
|
||||
model_->AddSeparator(ui::NORMAL_SEPARATOR);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::AddItem(int command_id, const CefString& label) {
|
||||
if (!VerifyContext())
|
||||
return false;
|
||||
|
||||
model_->AddItem(command_id, label);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::AddCheckItem(int command_id,
|
||||
const CefString& label) {
|
||||
if (!VerifyContext())
|
||||
return false;
|
||||
|
||||
model_->AddCheckItem(command_id, label);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::AddRadioItem(int command_id,
|
||||
const CefString& label,
|
||||
int group_id) {
|
||||
if (!VerifyContext())
|
||||
return false;
|
||||
|
||||
model_->AddRadioItem(command_id, label, group_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
CefRefPtr<CefMenuModel> CefSimpleMenuModelImpl::AddSubMenu(
|
||||
int command_id,
|
||||
const CefString& label) {
|
||||
if (!VerifyContext())
|
||||
return nullptr;
|
||||
|
||||
auto new_menu = CreateNewSubMenu(nullptr);
|
||||
model_->AddSubMenu(command_id, label, new_menu->model());
|
||||
return new_menu;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::InsertSeparatorAt(int index) {
|
||||
if (!VerifyContext())
|
||||
return false;
|
||||
|
||||
model_->InsertSeparatorAt(index, ui::NORMAL_SEPARATOR);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::InsertItemAt(int index,
|
||||
int command_id,
|
||||
const CefString& label) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return false;
|
||||
|
||||
model_->InsertItemAt(index, command_id, label);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::InsertCheckItemAt(int index,
|
||||
int command_id,
|
||||
const CefString& label) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return false;
|
||||
|
||||
model_->InsertCheckItemAt(index, command_id, label);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::InsertRadioItemAt(int index,
|
||||
int command_id,
|
||||
const CefString& label,
|
||||
int group_id) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return false;
|
||||
|
||||
model_->InsertRadioItemAt(index, command_id, label, group_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
CefRefPtr<CefMenuModel> CefSimpleMenuModelImpl::InsertSubMenuAt(
|
||||
int index,
|
||||
int command_id,
|
||||
const CefString& label) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return nullptr;
|
||||
|
||||
auto new_menu = CreateNewSubMenu(nullptr);
|
||||
model_->InsertSubMenuAt(index, command_id, label, new_menu->model());
|
||||
return new_menu;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::Remove(int command_id) {
|
||||
return RemoveAt(GetIndexOf(command_id));
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::RemoveAt(int index) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return false;
|
||||
|
||||
auto* sub_menu =
|
||||
static_cast<ui::SimpleMenuModel*>(model_->GetSubmenuModelAt(index));
|
||||
if (sub_menu) {
|
||||
auto it = submenumap_.find(sub_menu);
|
||||
if (it != submenumap_.end()) {
|
||||
it->second->Detach();
|
||||
submenumap_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
model_->RemoveItemAt(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
int CefSimpleMenuModelImpl::GetIndexOf(int command_id) {
|
||||
if (!VerifyContext())
|
||||
return kInvalidIndex;
|
||||
|
||||
return model_->GetIndexOfCommandId(command_id);
|
||||
}
|
||||
|
||||
int CefSimpleMenuModelImpl::GetCommandIdAt(int index) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return kInvalidCommandId;
|
||||
|
||||
return model_->GetCommandIdAt(index);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetCommandIdAt(int index, int command_id) {
|
||||
NOTIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
CefString CefSimpleMenuModelImpl::GetLabel(int command_id) {
|
||||
return GetLabelAt(GetIndexOf(command_id));
|
||||
}
|
||||
|
||||
CefString CefSimpleMenuModelImpl::GetLabelAt(int index) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return CefString();
|
||||
|
||||
return model_->GetLabelAt(index);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetLabel(int command_id, const CefString& label) {
|
||||
return SetLabelAt(GetIndexOf(command_id), label);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetLabelAt(int index, const CefString& label) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return false;
|
||||
|
||||
model_->SetLabel(index, label);
|
||||
return true;
|
||||
}
|
||||
|
||||
CefSimpleMenuModelImpl::MenuItemType CefSimpleMenuModelImpl::GetType(
|
||||
int command_id) {
|
||||
return GetTypeAt(GetIndexOf(command_id));
|
||||
}
|
||||
|
||||
CefSimpleMenuModelImpl::MenuItemType CefSimpleMenuModelImpl::GetTypeAt(
|
||||
int index) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return MENUITEMTYPE_NONE;
|
||||
|
||||
return GetCefItemType(model_->GetTypeAt(index));
|
||||
}
|
||||
|
||||
int CefSimpleMenuModelImpl::GetGroupId(int command_id) {
|
||||
return GetGroupIdAt(GetIndexOf(command_id));
|
||||
}
|
||||
|
||||
int CefSimpleMenuModelImpl::GetGroupIdAt(int index) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return kInvalidGroupId;
|
||||
|
||||
return model_->GetGroupIdAt(index);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetGroupId(int command_id, int group_id) {
|
||||
return SetGroupIdAt(GetIndexOf(command_id), group_id);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetGroupIdAt(int index, int group_id) {
|
||||
NOTIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
CefRefPtr<CefMenuModel> CefSimpleMenuModelImpl::GetSubMenu(int command_id) {
|
||||
return GetSubMenuAt(GetIndexOf(command_id));
|
||||
}
|
||||
|
||||
CefRefPtr<CefMenuModel> CefSimpleMenuModelImpl::GetSubMenuAt(int index) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return nullptr;
|
||||
|
||||
auto* sub_model =
|
||||
static_cast<ui::SimpleMenuModel*>(model_->GetSubmenuModelAt(index));
|
||||
auto it = submenumap_.find(sub_model);
|
||||
if (it != submenumap_.end())
|
||||
return it->second;
|
||||
return CreateNewSubMenu(sub_model);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::IsVisible(int command_id) {
|
||||
return IsVisibleAt(GetIndexOf(command_id));
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::IsVisibleAt(int index) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return false;
|
||||
|
||||
return model_->IsVisibleAt(index);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetVisible(int command_id, bool visible) {
|
||||
return SetVisibleAt(GetIndexOf(command_id), visible);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetVisibleAt(int index, bool visible) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return false;
|
||||
|
||||
model_->SetVisibleAt(index, visible);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::IsEnabled(int command_id) {
|
||||
return IsEnabledAt(GetIndexOf(command_id));
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::IsEnabledAt(int index) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return false;
|
||||
|
||||
return model_->IsEnabledAt(index);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetEnabled(int command_id, bool enabled) {
|
||||
return SetEnabledAt(GetIndexOf(command_id), enabled);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetEnabledAt(int index, bool enabled) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return false;
|
||||
|
||||
model_->SetEnabledAt(index, enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::IsChecked(int command_id) {
|
||||
return IsCheckedAt(GetIndexOf(command_id));
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::IsCheckedAt(int index) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return false;
|
||||
|
||||
return model_->IsItemCheckedAt(index);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetChecked(int command_id, bool checked) {
|
||||
if (!VerifyContext() || command_id == kInvalidIndex)
|
||||
return false;
|
||||
|
||||
state_delegate_->SetChecked(command_id, checked);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetCheckedAt(int index, bool checked) {
|
||||
return SetChecked(GetCommandIdAt(index), checked);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::HasAccelerator(int command_id) {
|
||||
return HasAcceleratorAt(GetIndexOf(command_id));
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::HasAcceleratorAt(int index) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return false;
|
||||
|
||||
ui::Accelerator accelerator;
|
||||
return model_->GetAcceleratorAt(index, &accelerator);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetAccelerator(int command_id,
|
||||
int key_code,
|
||||
bool shift_pressed,
|
||||
bool ctrl_pressed,
|
||||
bool alt_pressed) {
|
||||
if (!VerifyContext() || command_id == kInvalidIndex)
|
||||
return false;
|
||||
|
||||
int modifiers = 0;
|
||||
if (shift_pressed)
|
||||
modifiers |= ui::EF_SHIFT_DOWN;
|
||||
if (ctrl_pressed)
|
||||
modifiers |= ui::EF_CONTROL_DOWN;
|
||||
if (alt_pressed)
|
||||
modifiers |= ui::EF_ALT_DOWN;
|
||||
|
||||
state_delegate_->SetAccelerator(
|
||||
command_id,
|
||||
ui::Accelerator(static_cast<ui::KeyboardCode>(key_code), modifiers));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetAcceleratorAt(int index,
|
||||
int key_code,
|
||||
bool shift_pressed,
|
||||
bool ctrl_pressed,
|
||||
bool alt_pressed) {
|
||||
return SetAccelerator(GetCommandIdAt(index), key_code, shift_pressed,
|
||||
ctrl_pressed, alt_pressed);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::RemoveAccelerator(int command_id) {
|
||||
if (!VerifyContext() || command_id == kInvalidIndex)
|
||||
return false;
|
||||
state_delegate_->SetAccelerator(command_id, base::nullopt);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::RemoveAcceleratorAt(int index) {
|
||||
return RemoveAccelerator(GetCommandIdAt(index));
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::GetAccelerator(int command_id,
|
||||
int& key_code,
|
||||
bool& shift_pressed,
|
||||
bool& ctrl_pressed,
|
||||
bool& alt_pressed) {
|
||||
return GetAcceleratorAt(GetIndexOf(command_id), key_code, shift_pressed,
|
||||
ctrl_pressed, alt_pressed);
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::GetAcceleratorAt(int index,
|
||||
int& key_code,
|
||||
bool& shift_pressed,
|
||||
bool& ctrl_pressed,
|
||||
bool& alt_pressed) {
|
||||
if (!VerifyContext() || !ValidIndex(index))
|
||||
return false;
|
||||
|
||||
ui::Accelerator accel;
|
||||
if (model_->GetAcceleratorAt(index, &accel)) {
|
||||
key_code = accel.key_code();
|
||||
shift_pressed = accel.modifiers() & ui::EF_SHIFT_DOWN;
|
||||
ctrl_pressed = accel.modifiers() & ui::EF_CONTROL_DOWN;
|
||||
alt_pressed = accel.modifiers() & ui::EF_ALT_DOWN;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetColor(int command_id,
|
||||
cef_menu_color_type_t color_type,
|
||||
cef_color_t color) {
|
||||
NOTIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetColorAt(int index,
|
||||
cef_menu_color_type_t color_type,
|
||||
cef_color_t color) {
|
||||
NOTIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::GetColor(int command_id,
|
||||
cef_menu_color_type_t color_type,
|
||||
cef_color_t& color) {
|
||||
NOTIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::GetColorAt(int index,
|
||||
cef_menu_color_type_t color_type,
|
||||
cef_color_t& color) {
|
||||
NOTIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetFontList(int command_id,
|
||||
const CefString& font_list) {
|
||||
NOTIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::SetFontListAt(int index,
|
||||
const CefString& font_list) {
|
||||
NOTIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::VerifyContext() {
|
||||
if (base::PlatformThread::CurrentId() != supported_thread_id_) {
|
||||
// This object should only be accessed from the thread that created it.
|
||||
NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!model_)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CefSimpleMenuModelImpl::ValidIndex(int index) {
|
||||
return index > kInvalidIndex && index < model_->GetItemCount();
|
||||
}
|
||||
|
||||
CefRefPtr<CefSimpleMenuModelImpl> CefSimpleMenuModelImpl::CreateNewSubMenu(
|
||||
ui::SimpleMenuModel* model) {
|
||||
bool is_owned = false;
|
||||
if (!model) {
|
||||
model = new ui::SimpleMenuModel(delegate_);
|
||||
is_owned = true;
|
||||
}
|
||||
|
||||
CefRefPtr<CefSimpleMenuModelImpl> new_impl = new CefSimpleMenuModelImpl(
|
||||
model, delegate_, state_delegate_, is_owned, /*is_submodel=*/true);
|
||||
submenumap_.insert(std::make_pair(model, new_impl));
|
||||
return new_impl;
|
||||
}
|
Reference in New Issue
Block a user