mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
views: Add support for accelerators (issue #2102)
This commit is contained in:
@@ -216,7 +216,8 @@ void CefBrowserPlatformDelegateViews::ViewText(const std::string& text) {
|
||||
|
||||
void CefBrowserPlatformDelegateViews::HandleKeyboardEvent(
|
||||
const content::NativeWebKeyboardEvent& event) {
|
||||
native_delegate_->HandleKeyboardEvent(event);
|
||||
// The BrowserView will handle accelerators.
|
||||
browser_view_->HandleKeyboardEvent(event);
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegateViews::HandleExternalProtocol(const GURL& url) {
|
||||
|
@@ -5,9 +5,14 @@
|
||||
#include "libcef/browser/views/browser_view_impl.h"
|
||||
|
||||
#include "libcef/browser/browser_host_impl.h"
|
||||
#include "libcef/browser/browser_util.h"
|
||||
#include "libcef/browser/context.h"
|
||||
#include "libcef/browser/views/window_impl.h"
|
||||
#include "libcef/browser/thread_util.h"
|
||||
|
||||
#include "content/public/browser/native_web_keyboard_event.h"
|
||||
#include "ui/content_accelerators/accelerator_util.h"
|
||||
|
||||
// static
|
||||
CefRefPtr<CefBrowserView> CefBrowserView::CreateBrowserView(
|
||||
CefRefPtr<CefClient> client,
|
||||
@@ -75,11 +80,45 @@ void CefBrowserViewImpl::BrowserDestroyed(CefBrowserHostImpl* browser) {
|
||||
root_view()->SetWebContents(nullptr);
|
||||
}
|
||||
|
||||
void CefBrowserViewImpl::HandleKeyboardEvent(
|
||||
const content::NativeWebKeyboardEvent& event) {
|
||||
if (!root_view())
|
||||
return;
|
||||
|
||||
views::FocusManager* focus_manager = root_view()->GetFocusManager();
|
||||
if (!focus_manager)
|
||||
return;
|
||||
|
||||
if (HandleAccelerator(event, focus_manager))
|
||||
return;
|
||||
|
||||
// Give the CefWindowDelegate a chance to handle the event.
|
||||
CefRefPtr<CefWindow> window =
|
||||
view_util::GetWindowFor(root_view()->GetWidget());
|
||||
CefWindowImpl* window_impl = static_cast<CefWindowImpl*>(window.get());
|
||||
if (window_impl) {
|
||||
CefKeyEvent cef_event;
|
||||
if (browser_util::GetCefKeyEvent(event, cef_event) &&
|
||||
window_impl->OnKeyEvent(cef_event)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Proceed with default native handling.
|
||||
unhandled_keyboard_event_handler_.HandleKeyboardEvent(event, focus_manager);
|
||||
}
|
||||
|
||||
CefRefPtr<CefBrowser> CefBrowserViewImpl::GetBrowser() {
|
||||
CEF_REQUIRE_VALID_RETURN(nullptr);
|
||||
return browser_;
|
||||
}
|
||||
|
||||
void CefBrowserViewImpl::SetPreferAccelerators(bool prefer_accelerators) {
|
||||
CEF_REQUIRE_VALID_RETURN_VOID();
|
||||
if (root_view())
|
||||
root_view()->set_allow_accelerators(prefer_accelerators);
|
||||
}
|
||||
|
||||
void CefBrowserViewImpl::SetBackgroundColor(cef_color_t color) {
|
||||
CEF_REQUIRE_VALID_RETURN_VOID();
|
||||
ParentClass::SetBackgroundColor(color);
|
||||
@@ -167,3 +206,39 @@ CefBrowserViewView* CefBrowserViewImpl::CreateRootView() {
|
||||
void CefBrowserViewImpl::InitializeRootView() {
|
||||
static_cast<CefBrowserViewView*>(root_view())->Initialize();
|
||||
}
|
||||
|
||||
bool CefBrowserViewImpl::HandleAccelerator(
|
||||
const content::NativeWebKeyboardEvent& event,
|
||||
views::FocusManager* focus_manager) {
|
||||
// Previous calls to TranslateMessage can generate Char events as well as
|
||||
// RawKeyDown events, even if the latter triggered an accelerator. In these
|
||||
// cases, we discard the Char events.
|
||||
if (event.type() == blink::WebInputEvent::Char && ignore_next_char_event_) {
|
||||
ignore_next_char_event_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's necessary to reset this flag, because a RawKeyDown event may not
|
||||
// always generate a Char event.
|
||||
ignore_next_char_event_ = false;
|
||||
|
||||
if (event.type() == blink::WebInputEvent::RawKeyDown) {
|
||||
ui::Accelerator accelerator =
|
||||
ui::GetAcceleratorFromNativeWebKeyboardEvent(event);
|
||||
|
||||
// This is tricky: we want to set ignore_next_char_event_ if
|
||||
// ProcessAccelerator returns true. But ProcessAccelerator might delete
|
||||
// |this| if the accelerator is a "close tab" one. So we speculatively
|
||||
// set the flag and fix it if no event was handled.
|
||||
ignore_next_char_event_ = true;
|
||||
|
||||
if (focus_manager->ProcessAccelerator(accelerator))
|
||||
return true;
|
||||
|
||||
// ProcessAccelerator didn't handle the accelerator, so we know both
|
||||
// that |this| is still valid, and that we didn't want to set the flag.
|
||||
ignore_next_char_event_ = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@@ -14,6 +14,8 @@
|
||||
#include "libcef/browser/views/view_impl.h"
|
||||
#include "libcef/browser/views/browser_view_view.h"
|
||||
|
||||
#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
|
||||
|
||||
class CefBrowserViewImpl :
|
||||
public CefViewImpl<CefBrowserViewView, CefBrowserView,
|
||||
CefBrowserViewDelegate>,
|
||||
@@ -41,8 +43,13 @@ class CefBrowserViewImpl :
|
||||
void BrowserCreated(CefBrowserHostImpl* browser);
|
||||
void BrowserDestroyed(CefBrowserHostImpl* browser);
|
||||
|
||||
// Called to handle accelerators when the event is unhandled by the web
|
||||
// content and the browser client.
|
||||
void HandleKeyboardEvent(const content::NativeWebKeyboardEvent& event);
|
||||
|
||||
// CefBrowserView methods:
|
||||
CefRefPtr<CefBrowser> GetBrowser() override;
|
||||
void SetPreferAccelerators(bool prefer_accelerators) override;
|
||||
|
||||
// CefView methods:
|
||||
CefRefPtr<CefBrowserView> AsBrowserView() override { return this; }
|
||||
@@ -75,10 +82,20 @@ class CefBrowserViewImpl :
|
||||
CefBrowserViewView* CreateRootView() override;
|
||||
void InitializeRootView() override;
|
||||
|
||||
std::unique_ptr<CefBrowserHostImpl::CreateParams> pending_browser_create_params_;
|
||||
// Logic extracted from UnhandledKeyboardEventHandler::HandleKeyboardEvent for
|
||||
// the handling of accelerators. Returns true if the event was handled by the
|
||||
// accelerator.
|
||||
bool HandleAccelerator(const content::NativeWebKeyboardEvent& event,
|
||||
views::FocusManager* focus_manager);
|
||||
|
||||
std::unique_ptr<CefBrowserHostImpl::CreateParams>
|
||||
pending_browser_create_params_;
|
||||
|
||||
CefRefPtr<CefBrowserHostImpl> browser_;
|
||||
|
||||
views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
|
||||
bool ignore_next_char_event_ = false;
|
||||
|
||||
IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefBrowserViewImpl);
|
||||
DISALLOW_COPY_AND_ASSIGN(CefBrowserViewImpl);
|
||||
};
|
||||
|
@@ -35,6 +35,7 @@ CEF_BUTTON_IMPL_T class CefButtonImpl : public CEF_VIEW_IMPL_D {
|
||||
|
||||
// CefView methods:
|
||||
CefRefPtr<CefButton> AsButton() override { return this; }
|
||||
void SetFocusable(bool focusable) override;
|
||||
|
||||
protected:
|
||||
// Create a new implementation object.
|
||||
@@ -68,4 +69,11 @@ CEF_BUTTON_IMPL_T void CEF_BUTTON_IMPL_D::SetAccessibleName(
|
||||
ParentClass::root_view()->SetAccessibleName(name);
|
||||
}
|
||||
|
||||
CEF_BUTTON_IMPL_T void CEF_BUTTON_IMPL_D::SetFocusable(
|
||||
bool focusable) {
|
||||
CEF_REQUIRE_VALID_RETURN_VOID();
|
||||
ParentClass::root_view()->set_request_focus_on_press(focusable);
|
||||
ParentClass::SetFocusable(focusable);
|
||||
}
|
||||
|
||||
#endif // CEF_LIBCEF_BROWSER_VIEWS_BUTTON_IMPL_H_
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "libcef/browser/views/window_impl.h"
|
||||
|
||||
#include "libcef/browser/browser_util.h"
|
||||
#include "libcef/browser/thread_util.h"
|
||||
#include "libcef/browser/views/display_impl.h"
|
||||
#include "libcef/browser/views/fill_layout_impl.h"
|
||||
@@ -17,6 +18,7 @@
|
||||
|
||||
#if defined(USE_AURA)
|
||||
#include "ui/aura/test/ui_controls_factory_aura.h"
|
||||
#include "ui/aura/window.h"
|
||||
#include "ui/base/test/ui_controls_aura.h"
|
||||
#if defined(OS_LINUX)
|
||||
#include "ui/views/test/ui_controls_factory_desktop_aurax11.h"
|
||||
@@ -48,6 +50,58 @@ void InitializeUITesting() {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(USE_AURA)
|
||||
|
||||
// This class forwards KeyEvents to the CefWindowImpl associated with a widget.
|
||||
// This allows KeyEvents to be processed after all other targets.
|
||||
// Events originating from CefBrowserView will instead be delivered via
|
||||
// CefBrowserViewImpl::HandleKeyboardEvent.
|
||||
class CefUnhandledKeyEventHandler : public ui::EventHandler {
|
||||
public:
|
||||
CefUnhandledKeyEventHandler(CefWindowImpl* window_impl,
|
||||
views::Widget* widget)
|
||||
: window_impl_(window_impl),
|
||||
widget_(widget),
|
||||
window_(widget->GetNativeWindow()) {
|
||||
DCHECK(window_);
|
||||
window_->AddPostTargetHandler(this);
|
||||
}
|
||||
|
||||
~CefUnhandledKeyEventHandler() override {
|
||||
window_->RemovePostTargetHandler(this);
|
||||
}
|
||||
|
||||
// Implementation of ui::EventHandler:
|
||||
void OnKeyEvent(ui::KeyEvent* event) override {
|
||||
// Give the FocusManager a chance to handle accelerators first.
|
||||
// Widget::OnKeyEvent would normally call this after all EventHandlers have
|
||||
// had a shot but we don't want to wait.
|
||||
if (widget_->GetFocusManager() &&
|
||||
!widget_->GetFocusManager()->OnKeyEvent(*event)) {
|
||||
event->StopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
CefKeyEvent cef_event;
|
||||
if (browser_util::GetCefKeyEvent(*event, cef_event) &&
|
||||
window_impl_->OnKeyEvent(cef_event)) {
|
||||
event->StopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Members are guaranteed to outlive this object.
|
||||
CefWindowImpl* window_impl_;
|
||||
views::Widget* widget_;
|
||||
|
||||
// |window_| is the event target that is associated with this class.
|
||||
aura::Window* window_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CefUnhandledKeyEventHandler);
|
||||
};
|
||||
|
||||
#endif // defined(USE_AURA)
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
@@ -315,6 +369,12 @@ bool CefWindowImpl::CanWidgetClose() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void CefWindowImpl::OnWindowClosing() {
|
||||
#if defined(USE_AURA)
|
||||
unhandled_key_event_handler_.reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CefWindowImpl::OnWindowViewDeleted() {
|
||||
CancelMenu();
|
||||
|
||||
@@ -337,6 +397,27 @@ void CefWindowImpl::MenuClosed(CefRefPtr<CefMenuModelImpl> source) {
|
||||
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_) {
|
||||
if (entry.second == accelerator)
|
||||
return delegate()->OnAccelerator(this, entry.first);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CefWindowImpl::CanHandleAccelerators() const {
|
||||
if (delegate() && widget_)
|
||||
return widget_->IsActive();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CefWindowImpl::OnKeyEvent(const CefKeyEvent& event) {
|
||||
if (delegate())
|
||||
return delegate()->OnKeyEvent(this, event);
|
||||
return false;
|
||||
}
|
||||
|
||||
void CefWindowImpl::ShowMenu(views::MenuButton* menu_button,
|
||||
CefRefPtr<CefMenuModel> menu_model,
|
||||
const CefPoint& screen_point,
|
||||
@@ -474,6 +555,67 @@ void CefWindowImpl::SendMouseEvents(cef_mouse_button_type_t button,
|
||||
ui_controls::SendMouseEvents(type, state);
|
||||
}
|
||||
|
||||
void CefWindowImpl::SetAccelerator(int command_id,
|
||||
int key_code,
|
||||
bool shift_pressed,
|
||||
bool ctrl_pressed,
|
||||
bool alt_pressed) {
|
||||
CEF_REQUIRE_VALID_RETURN_VOID();
|
||||
if (!widget_)
|
||||
return;
|
||||
|
||||
AcceleratorMap::const_iterator it = accelerator_map_.find(command_id);
|
||||
if (it != accelerator_map_.end())
|
||||
RemoveAccelerator(command_id);
|
||||
|
||||
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;
|
||||
ui::Accelerator accelerator(static_cast<ui::KeyboardCode>(key_code),
|
||||
modifiers);
|
||||
|
||||
accelerator_map_.insert(std::make_pair(command_id, accelerator));
|
||||
|
||||
views::FocusManager* focus_manager = widget_->GetFocusManager();
|
||||
DCHECK(focus_manager);
|
||||
focus_manager->RegisterAccelerator(
|
||||
accelerator, ui::AcceleratorManager::kNormalPriority, this);
|
||||
}
|
||||
|
||||
void CefWindowImpl::RemoveAccelerator(int command_id) {
|
||||
CEF_REQUIRE_VALID_RETURN_VOID();
|
||||
if (!widget_)
|
||||
return;
|
||||
|
||||
AcceleratorMap::iterator it = accelerator_map_.find(command_id);
|
||||
if (it == accelerator_map_.end())
|
||||
return;
|
||||
|
||||
ui::Accelerator accelerator = it->second;
|
||||
|
||||
accelerator_map_.erase(it);
|
||||
|
||||
views::FocusManager* focus_manager = widget_->GetFocusManager();
|
||||
DCHECK(focus_manager);
|
||||
focus_manager->UnregisterAccelerator(accelerator, this);
|
||||
}
|
||||
|
||||
void CefWindowImpl::RemoveAllAccelerators() {
|
||||
CEF_REQUIRE_VALID_RETURN_VOID();
|
||||
if (!widget_)
|
||||
return;
|
||||
|
||||
accelerator_map_.clear();
|
||||
|
||||
views::FocusManager* focus_manager = widget_->GetFocusManager();
|
||||
DCHECK(focus_manager);
|
||||
focus_manager->UnregisterAccelerators(this);
|
||||
}
|
||||
|
||||
CefWindowImpl::CefWindowImpl(CefRefPtr<CefWindowDelegate> delegate)
|
||||
: ParentClass(delegate),
|
||||
widget_(nullptr),
|
||||
@@ -495,6 +637,11 @@ void CefWindowImpl::CreateWidget() {
|
||||
widget_ = root_view()->GetWidget();
|
||||
DCHECK(widget_);
|
||||
|
||||
#if defined(USE_AURA)
|
||||
unhandled_key_event_handler_ =
|
||||
base::MakeUnique<CefUnhandledKeyEventHandler>(this, widget_);
|
||||
#endif
|
||||
|
||||
// The Widget and root View are owned by the native window. Therefore don't
|
||||
// keep an owned reference.
|
||||
std::unique_ptr<views::View> view_ptr = view_util::PassOwnership(this);
|
||||
|
@@ -6,6 +6,8 @@
|
||||
#define CEF_LIBCEF_BROWSER_VIEWS_WINDOW_IMPL_H_
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "include/views/cef_window.h"
|
||||
#include "include/views/cef_window_delegate.h"
|
||||
|
||||
@@ -13,6 +15,7 @@
|
||||
#include "libcef/browser/views/panel_impl.h"
|
||||
#include "libcef/browser/views/window_view.h"
|
||||
|
||||
#include "ui/base/accelerators/accelerator.h"
|
||||
#include "ui/views/controls/menu/menu_runner.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
|
||||
@@ -23,7 +26,8 @@ class MenuButton;
|
||||
class CefWindowImpl :
|
||||
public CefPanelImpl<CefWindowView, CefWindow, CefWindowDelegate>,
|
||||
public CefWindowView::Delegate,
|
||||
public CefMenuModelImpl::Observer {
|
||||
public CefMenuModelImpl::Observer,
|
||||
public ui::AcceleratorTarget {
|
||||
public:
|
||||
typedef CefPanelImpl<CefWindowView, CefWindow, CefWindowDelegate> ParentClass;
|
||||
|
||||
@@ -70,6 +74,13 @@ class CefWindowImpl :
|
||||
void SendMouseEvents(cef_mouse_button_type_t button,
|
||||
bool mouse_down,
|
||||
bool mouse_up) override;
|
||||
void SetAccelerator(int command_id,
|
||||
int key_code,
|
||||
bool shift_pressed,
|
||||
bool ctrl_pressed,
|
||||
bool alt_pressed) override;
|
||||
void RemoveAccelerator(int command_id) override;
|
||||
void RemoveAllAccelerators() override;
|
||||
|
||||
// CefViewAdapter methods:
|
||||
void Detach() override;
|
||||
@@ -91,6 +102,7 @@ class CefWindowImpl :
|
||||
|
||||
// CefWindowView::Delegate methods:
|
||||
bool CanWidgetClose() override;
|
||||
void OnWindowClosing() override;
|
||||
void OnWindowViewDeleted() override;
|
||||
|
||||
// CefMenuModelImpl::Observer methods:
|
||||
@@ -101,6 +113,14 @@ class CefWindowImpl :
|
||||
void GetDebugInfo(base::DictionaryValue* info,
|
||||
bool include_children) override;
|
||||
|
||||
// ui::AcceleratorTarget methods:
|
||||
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
|
||||
bool CanHandleAccelerators() const override;
|
||||
|
||||
// Called for key events that have not been handled by other controls in the
|
||||
// window. Returns true if the event was handled.
|
||||
bool OnKeyEvent(const CefKeyEvent& event);
|
||||
|
||||
void ShowMenu(views::MenuButton* menu_button,
|
||||
CefRefPtr<CefMenuModel> menu_model,
|
||||
const CefPoint& screen_point,
|
||||
@@ -128,6 +148,15 @@ class CefWindowImpl :
|
||||
CefRefPtr<CefMenuModelImpl> menu_model_;
|
||||
std::unique_ptr<views::MenuRunner> menu_runner_;
|
||||
|
||||
// Map of command_id to accelerator.
|
||||
typedef std::map<int, ui::Accelerator> AcceleratorMap;
|
||||
AcceleratorMap accelerator_map_;
|
||||
|
||||
#if defined(USE_AURA)
|
||||
// Native widget's handler to receive events after the event target.
|
||||
std::unique_ptr<ui::EventHandler> unhandled_key_event_handler_;
|
||||
#endif
|
||||
|
||||
IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefWindowImpl);
|
||||
DISALLOW_COPY_AND_ASSIGN(CefWindowImpl);
|
||||
};
|
||||
|
@@ -368,6 +368,10 @@ gfx::ImageSkia CefWindowView::GetWindowAppIcon() {
|
||||
GetForced1xScaleRepresentation(GetDisplay().device_scale_factor());
|
||||
}
|
||||
|
||||
void CefWindowView::WindowClosing() {
|
||||
window_delegate_->OnWindowClosing();
|
||||
}
|
||||
|
||||
views::View* CefWindowView::GetContentsView() {
|
||||
// |this| will be the "Contents View" hosted by the Widget via ClientView and
|
||||
// RootView.
|
||||
|
@@ -29,6 +29,9 @@ class CefWindowView :
|
||||
// Returns true to signal that the Widget can be closed.
|
||||
virtual bool CanWidgetClose() = 0;
|
||||
|
||||
// Called when the underlying platform window is closing.
|
||||
virtual void OnWindowClosing() = 0;
|
||||
|
||||
// Called when the WindowView is about to be deleted.
|
||||
virtual void OnWindowViewDeleted() = 0;
|
||||
|
||||
@@ -58,6 +61,7 @@ class CefWindowView :
|
||||
base::string16 GetWindowTitle() const override;
|
||||
gfx::ImageSkia GetWindowIcon() override;
|
||||
gfx::ImageSkia GetWindowAppIcon() override;
|
||||
void WindowClosing() override;
|
||||
views::View* GetContentsView() override;
|
||||
views::ClientView* CreateClientView(views::Widget* widget) override;
|
||||
views::NonClientFrameView* CreateNonClientFrameView(
|
||||
|
Reference in New Issue
Block a user