创建一个半透明窗口(Qt)
MainWindow::MainWindow(const QJsonObject& obj,QWidget *parent)
: QMainWindow(parent)
{
setWindowFlag(Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground, true);
}
以圆角矩形画半透明背景
void MainWindow::paintEvent(QPaintEvent* event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setBrush(QColor(0, 0, 0, 166));
painter.setPen(Qt::NoPen);
painter.drawRoundedRect(rect(), 4, 4);
}
把窗口嵌入桌面
窗口在桌面背景前面,在桌面图标后面
static HWND workerW;
if (!workerW) {
HWND progman = FindWindow(L"Progman", NULL);
SendMessage(progman, 0x052C, 0xD, 0);
SendMessage(progman, 0x052C, 0xD, 1);
EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL {
HWND defView = FindWindowEx(hwnd, NULL, L"SHELLDLL_DefView", NULL);
if (defView != NULL) {
auto tar = (HWND*)lParam;
*tar = FindWindowEx(NULL, hwnd, L"WorkerW", NULL);
}
return TRUE;
}, (LPARAM)&workerW);
}
auto hwnd = (HWND)winId();
SetParent(hwnd, workerW);
注册输入设备
//WNDPROC oldProc;
auto hwnd = (HWND)winId();
RAWINPUTDEVICE rids[1];
rids[0].usUsagePage = 0x01;
rids[0].usUsage = 0x02;
rids[0].dwFlags = 0x00000100;
rids[0].hwndTarget = hwnd;
RegisterRawInputDevices(rids, 1, sizeof(rids[0]));
oldProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)MainWindow::processMsg);
现在这个窗口的所有消息都会被转发到 WNDPROC oldProc; 这个方法中,
非但如此,现在操作系统的所有鼠标消息,也会被转发到这个方法中,这个方法的签名如下:
LRESULT CALLBACK MainWindow::processMsg(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam){
//过滤没用的消息
//处理系统鼠标消息
//这个方法最后要把消息交给qt的消息处理机制
return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
下面就介绍这个方法
过滤没用的消息
if (uMsg != WM_INPUT ) {
return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
POINT globalPos;
GetCursorPos(&globalPos);
RECT rect;
GetWindowRect(hWnd, &rect);
if (globalPos.x < rect.left || globalPos.y < rect.top ||
globalPos.x > rect.right || globalPos.y > rect.bottom) {
win->onEmbedLeaveWindow();
return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
HWND hwnd = WindowFromPoint(globalPos);
WCHAR className[28];
int len = GetClassName(hwnd, className, 28);
if ((lstrcmp(TEXT("SysListView32"), className) != 0) &&
(lstrcmp(TEXT("WorkerW"), className) != 0) &&
(lstrcmp(TEXT("Progman"), className) != 0)) {
win->onEmbedLeaveWindow();
return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}
- 不是操作系统的鼠标消息不处理 WM_INPUT
- 鼠标位置没在窗口区域内,不处理
- 鼠标所在位置的窗口句柄,不是桌面窗口句柄(SysListView32、WorkerW、Progman)不处理
处理操作系统鼠标消息
auto raw = getRawInput((HRAWINPUT)lParam);
if (raw->header.dwType == RIM_TYPEMOUSE)
{
RAWMOUSE rawMouse = raw->data.mouse;
if (rawMouse.usButtonFlags == RI_MOUSE_WHEEL) //鼠标滚轮消息
{
auto wheelDelta = (short)rawMouse.usButtonData;
win->onEmbedScroll(wheelDelta); //小于0滚动条向下移动
}
else {
switch (rawMouse.ulButtons)
{
case RI_MOUSE_LEFT_BUTTON_DOWN: //鼠标按下
{
win->onEmbedMousePress();
break;
}
default:
{
win->onEmbedMouseMove(); //鼠标移动
break;
}
}
}
}
注意事项
当窗口未嵌入桌面时,不应该注册输入设备。
当窗口嵌入桌面时,Qt的事件处理机制是接不到鼠标消息的,你只能在onEmbed....方法中手动处理。
把窗口从嵌入状态恢复为非嵌入状态,最简单的办法就是把这个窗口关了,重新创建一个。