将CEF官方Demo中的tests/cefclient/browser/root_window_win.cc下列代码拷贝到自己的离屏渲染项目中,并做修改。
核心原理则是通过OnDraggableRegionsChanged函数获得当前可拖拽区域,再通过劫持窗口消息,实现窗口拖拽。
kDraggableControl是一个自己扩展的属性,用来存储当前的控件指针,这样在劫持窗口消息的时候可以获取到当前控件的信息。使用__try的目的是防止程序释放顺序错误导致控件优先释放掉,导致程序崩溃。
当在WM_NCHITTEST消息中,如果是在拖拽区域内,则返回HTCAPTION,这样就可以实现窗口拖拽了。
namespace
{
LPCWSTR kParentWndProc = L"CefParentWndProc";
LPCWSTR kDraggableRegion = L"CefDraggableRegion";
LPCWSTR kDraggableControl = L"CefDraggableControl";
LRESULT CALLBACK SubClassedWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
const auto hParentWndProc = reinterpret_cast<WNDPROC>(::GetPropW(hWnd, kParentWndProc));
const auto hRegion = static_cast<HRGN>(::GetPropW(hWnd, kDraggableRegion));
const auto pControl = static_cast<SCefBrowserBaseEx *>(::GetPropW(hWnd, kDraggableControl));
if (message == WM_NCHITTEST)
{
const LRESULT hit = CallWindowProc(hParentWndProc, hWnd, message, wParam, lParam);
if (hit == HTCLIENT)
{
CRect rect;
__try
{
rect = pControl->GetClientRect();
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return hit;
}
const auto [x, y] = MAKEPOINTS(lParam);
POINT point = { x - rect.left, y - rect.top };
::ScreenToClient(hWnd, &point);
if (::PtInRegion(hRegion, point.x, point.y))
{
return HTCAPTION;
}
}
return hit;
}
return CallWindowProc(hParentWndProc, hWnd, message, wParam, lParam);
}
void SubclassWindow(HWND hWnd, HRGN hRegion, const SCefBrowserBaseEx *control)
{
if (::GetPropW(hWnd, kParentWndProc))
{
return;
}
SetLastError(0);
const 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));
::SetPropW(hWnd, kDraggableControl, (HANDLE)control);
}
void UnSubclassWindow(const HWND hWnd)
{
if (const auto hParentWndProc = reinterpret_cast<LONG_PTR>(::GetPropW(hWnd, kParentWndProc)))
{
const auto hPreviousWndProc = SetWindowLongPtr(hWnd, GWLP_WNDPROC, hParentWndProc);
ALLOW_UNUSED_LOCAL(hPreviousWndProc);
DCHECK_EQ(hPreviousWndProc, reinterpret_cast<LONG_PTR>(SubClassedWindowProc));
}
::RemovePropW(hWnd, kParentWndProc);
::RemovePropW(hWnd, kDraggableRegion);
::RemovePropW(hWnd, kDraggableControl);
}
BOOL CALLBACK SubclassWindowsProc(const HWND hWnd, HRGN hRegion, const SCefBrowserBaseEx *control)
{
SubclassWindow(hWnd, hRegion, control);
return TRUE;
}
BOOL CALLBACK UnSubclassWindowsProc(const HWND hWnd, HRGN hRegion, const SCefBrowserBaseEx *control)
{
UnSubclassWindow(hWnd);
return TRUE;
}
} // namespace 支持-webkit-app-region CSS属性
void SCefBrowserBaseEx::OnDraggableRegionsChanged(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const std::vector<CefDraggableRegion> ®ions)
{
::SetRectRgn(m_draggableRegion, 0, 0, 0, 0);
std::vector<CefDraggableRegion>::const_iterator it = regions.begin();
for (; it != regions.end(); ++it)
{
const HRGN region = ::CreateRectRgn(it->bounds.x, it->bounds.y, it->bounds.x + it->bounds.width, it->bounds.y + it->bounds.height);
::CombineRgn(m_draggableRegion, m_draggableRegion, region, it->draggable ? RGN_OR : RGN_DIFF);
::DeleteObject(region);
}
if (const auto hWnd = GetHostHwnd())
{
const auto proc = !regions.empty() ? SubclassWindowsProc : UnSubclassWindowsProc;
proc(hWnd, m_draggableRegion, this);
}
}
Comments