Add support for draggable regions (issue #1645).

Regions are defined using the '-webkit-app-region: drag/no-drag'
CSS property and passed to the CefDragHandler::
OnDraggableRegionsChanged callback.
This commit is contained in:
Felix Bruns
2015-04-24 15:48:32 +02:00
committed by Marshall Greenblatt
parent ead921a3f6
commit c5b8b8b9c8
27 changed files with 581 additions and 3 deletions

View File

@@ -68,4 +68,10 @@ void BrowserWindow::OnSetLoadingState(bool isLoading,
delegate_->OnSetLoadingState(isLoading, canGoBack, canGoForward);
}
void BrowserWindow::OnSetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) {
REQUIRE_MAIN_THREAD();
delegate_->OnSetDraggableRegions(regions);
}
} // namespace client

View File

@@ -39,6 +39,10 @@ class BrowserWindow : public ClientHandler::Delegate {
bool canGoBack,
bool canGoForward) = 0;
// Set the draggable regions.
virtual void OnSetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) = 0;
protected:
virtual ~Delegate() {}
};
@@ -101,6 +105,8 @@ class BrowserWindow : public ClientHandler::Delegate {
void OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) OVERRIDE;
void OnSetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) OVERRIDE;
Delegate* delegate_;
CefRefPtr<CefBrowser> browser_;

View File

@@ -290,6 +290,14 @@ bool ClientHandler::OnDragEnter(CefRefPtr<CefBrowser> browser,
return false;
}
void ClientHandler::OnDraggableRegionsChanged(
CefRefPtr<CefBrowser> browser,
const std::vector<CefDraggableRegion>& regions) {
CEF_REQUIRE_UI_THREAD();
NotifyDraggableRegions(regions);
}
bool ClientHandler::OnRequestGeolocationPermission(
CefRefPtr<CefBrowser> browser,
const CefString& requesting_url,
@@ -676,6 +684,19 @@ void ClientHandler::NotifyLoadingState(bool isLoading,
delegate_->OnSetLoadingState(isLoading, canGoBack, canGoForward);
}
void ClientHandler::NotifyDraggableRegions(
const std::vector<CefDraggableRegion>& regions) {
if (!CURRENTLY_ON_MAIN_THREAD()) {
// Execute this method on the main thread.
MAIN_POST_CLOSURE(
base::Bind(&ClientHandler::NotifyDraggableRegions, this, regions));
return;
}
if (delegate_)
delegate_->OnSetDraggableRegions(regions);
}
void ClientHandler::BuildTestMenu(CefRefPtr<CefMenuModel> model) {
if (model->GetCount() > 0)
model->AddSeparator();

View File

@@ -57,6 +57,10 @@ class ClientHandler : public CefClient,
bool canGoBack,
bool canGoForward) = 0;
// Set the draggable regions.
virtual void OnSetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) = 0;
protected:
virtual ~Delegate() {}
};
@@ -152,6 +156,10 @@ class ClientHandler : public CefClient,
CefRefPtr<CefDragData> dragData,
CefDragHandler::DragOperationsMask mask) OVERRIDE;
void OnDraggableRegionsChanged(
CefRefPtr<CefBrowser> browser,
const std::vector<CefDraggableRegion>& regions) OVERRIDE;
// CefGeolocationHandler methods
bool OnRequestGeolocationPermission(
CefRefPtr<CefBrowser> browser,
@@ -263,6 +271,8 @@ class ClientHandler : public CefClient,
void NotifyLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward);
void NotifyDraggableRegions(
const std::vector<CefDraggableRegion>& regions);
// Test context menu creation.
void BuildTestMenu(CefRefPtr<CefMenuModel> model);
@@ -288,7 +298,7 @@ class ClientHandler : public CefClient,
// Handles the browser side of query routing. The renderer side is handled
// in client_renderer.cc.
CefRefPtr<CefMessageRouterBrowserSide> message_router_;
// MAIN THREAD MEMBERS
// The following members will only be accessed on the main thread. This will
// be the same as the CEF UI thread except when using multi-threaded message

View File

@@ -387,6 +387,12 @@ void RootWindowGtk::OnSetLoadingState(bool isLoading,
}
}
void RootWindowGtk::OnSetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) {
REQUIRE_MAIN_THREAD();
// TODO(cef): Implement support for draggable regions on this platform.
}
void RootWindowGtk::NotifyDestroyedIfDone() {
// Notify once both the window and the browser have been destroyed.
if (window_destroyed_ && browser_destroyed_)

View File

@@ -58,6 +58,8 @@ class RootWindowGtk : public RootWindow,
void OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) OVERRIDE;
void OnSetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) OVERRIDE;
void NotifyDestroyedIfDone();

View File

@@ -64,6 +64,8 @@ class RootWindowMac : public RootWindow,
void OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) OVERRIDE;
void OnSetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) OVERRIDE;
void NotifyDestroyedIfDone();

View File

@@ -576,6 +576,12 @@ void RootWindowMac::OnSetAddress(const std::string& url) {
}
}
void RootWindowMac::OnSetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) {
REQUIRE_MAIN_THREAD();
// TODO(cef): Implement support for draggable regions on this platform.
}
void RootWindowMac::OnSetTitle(const std::string& title) {
REQUIRE_MAIN_THREAD();

View File

@@ -5,6 +5,7 @@
#include "cefclient/browser/root_window_win.h"
#include "include/base/cef_bind.h"
#include "include/base/cef_build.h"
#include "include/cef_app.h"
#include "cefclient/browser/browser_window_osr_win.h"
#include "cefclient/browser/browser_window_std_win.h"
@@ -51,6 +52,7 @@ RootWindowWin::RootWindowWin()
start_rect_(),
initialized_(false),
hwnd_(NULL),
draggable_region_(NULL),
back_hwnd_(NULL),
forward_hwnd_(NULL),
reload_hwnd_(NULL),
@@ -66,11 +68,16 @@ RootWindowWin::RootWindowWin()
window_destroyed_(false),
browser_destroyed_(false) {
find_buff_[0] = 0;
// Create a HRGN representing the draggable window area.
draggable_region_ = ::CreateRectRgn(0, 0, 0, 0);
}
RootWindowWin::~RootWindowWin() {
REQUIRE_MAIN_THREAD();
::DeleteObject(draggable_region_);
// The window and browser should already have been destroyed.
DCHECK(window_destroyed_);
DCHECK(browser_destroyed_);
@@ -513,6 +520,21 @@ LRESULT CALLBACK RootWindowWin::RootWndProc(HWND hWnd, UINT message,
return 0; // Cancel the close.
break;
case WM_NCHITTEST: {
LRESULT hit = DefWindowProc(hWnd, message, wParam, lParam);
if (hit == HTCLIENT) {
POINTS points = MAKEPOINTS(lParam);
POINT point = { points.x, points.y };
::ScreenToClient(hWnd, &point);
if (::PtInRegion(self->draggable_region_, point.x, point.y)) {
// If cursor is inside a draggable region return HTCAPTION to allow
// dragging.
return HTCAPTION;
}
}
return hit;
}
case WM_NCDESTROY:
// Clear the reference to |self|.
SetUserDataPtr(hWnd, NULL);
@@ -764,6 +786,111 @@ void RootWindowWin::OnSetLoadingState(bool isLoading,
}
}
namespace {
LPCWSTR kParentWndProc = L"CefParentWndProc";
LPCWSTR kDraggableRegion = L"CefDraggableRegion";
LRESULT CALLBACK SubclassedWindowProc(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
WNDPROC hParentWndProc = reinterpret_cast<WNDPROC>(
::GetPropW(hWnd, kParentWndProc));
HRGN hRegion = reinterpret_cast<HRGN>(
::GetPropW(hWnd, kDraggableRegion));
if (message == WM_NCHITTEST) {
LRESULT hit = CallWindowProc(
hParentWndProc, hWnd, message, wParam, lParam);
if (hit == HTCLIENT) {
POINTS points = MAKEPOINTS(lParam);
POINT point = { points.x, points.y };
::ScreenToClient(hWnd, &point);
if (::PtInRegion(hRegion, point.x, point.y)) {
// Let the parent window handle WM_NCHITTEST by returning HTTRANSPARENT
// in child windows.
return HTTRANSPARENT;
}
}
return hit;
}
return CallWindowProc(hParentWndProc, hWnd, message, wParam, lParam);
}
void SubclassWindow(HWND hWnd, HRGN hRegion) {
HANDLE hParentWndProc = ::GetPropW(hWnd, kParentWndProc);
if (hParentWndProc) {
return;
}
SetLastError(0);
LONG_PTR hOldWndProc = SetWindowLongPtr(
hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SubclassedWindowProc));
if (hOldWndProc == 0 && GetLastError() != ERROR_SUCCESS) {
return;
}
::SetPropW(hWnd, kParentWndProc, reinterpret_cast<HANDLE>(hOldWndProc));
::SetPropW(hWnd, kDraggableRegion, reinterpret_cast<HANDLE>(hRegion));
}
void UnSubclassWindow(HWND hWnd) {
LONG_PTR hParentWndProc = reinterpret_cast<LONG_PTR>(
::GetPropW(hWnd, kParentWndProc));
if (hParentWndProc) {
LONG_PTR hPreviousWndProc =
SetWindowLongPtr(hWnd, GWLP_WNDPROC, hParentWndProc);
ALLOW_UNUSED_LOCAL(hPreviousWndProc);
DCHECK_EQ(hPreviousWndProc,
reinterpret_cast<LONG_PTR>(SubclassedWindowProc));
}
::RemovePropW(hWnd, kParentWndProc);
::RemovePropW(hWnd, kDraggableRegion);
}
BOOL CALLBACK SubclassWindowsProc(HWND hwnd, LPARAM lParam) {
SubclassWindow(hwnd, reinterpret_cast<HRGN>(lParam));
return TRUE;
}
BOOL CALLBACK UnSubclassWindowsProc(HWND hwnd, LPARAM lParam) {
UnSubclassWindow(hwnd);
return TRUE;
}
} // namespace
void RootWindowWin::OnSetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) {
REQUIRE_MAIN_THREAD();
// Reset draggable region.
::SetRectRgn(draggable_region_, 0, 0, 0, 0);
// Determine new draggable region.
std::vector<CefDraggableRegion>::const_iterator it = regions.begin();
for (;it != regions.end(); ++it) {
HRGN region = ::CreateRectRgn(
it->bounds.x, it->bounds.y,
it->bounds.x + it->bounds.width,
it->bounds.y + it->bounds.height);
::CombineRgn(
draggable_region_, draggable_region_, region,
it->draggable ? RGN_OR : RGN_DIFF);
::DeleteObject(region);
}
// Subclass child window procedures in order to do hit-testing.
// This will be a no-op, if it is already subclassed.
if (hwnd_) {
WNDENUMPROC proc = !regions.empty() ?
SubclassWindowsProc : UnSubclassWindowsProc;
::EnumChildWindows(
hwnd_, proc, reinterpret_cast<LPARAM>(draggable_region_));
}
}
void RootWindowWin::NotifyDestroyedIfDone() {
// Notify once both the window and the browser have been destroyed.
if (window_destroyed_ && browser_destroyed_)

View File

@@ -90,6 +90,8 @@ class RootWindowWin : public RootWindow,
void OnSetLoadingState(bool isLoading,
bool canGoBack,
bool canGoForward) OVERRIDE;
void OnSetDraggableRegions(
const std::vector<CefDraggableRegion>& regions) OVERRIDE;
void NotifyDestroyedIfDone();
@@ -106,6 +108,9 @@ class RootWindowWin : public RootWindow,
// Main window.
HWND hwnd_;
// Draggable region.
HRGN draggable_region_;
// Buttons.
HWND back_hwnd_;
HWND forward_hwnd_;
@@ -115,7 +120,7 @@ class RootWindowWin : public RootWindow,
// URL text field.
HWND edit_hwnd_;
WNDPROC edit_wndproc_old_;
// Find dialog.
HWND find_hwnd_;
UINT find_message_id_;