前言
我们今天实现一个无边框,带窗口阴影,可拖拽、伸缩的窗口。
第一步
去除qt窗口的边框、工具栏等属性。我们的窗口的类Window,继承于QWidget这个基础的控件类,然后直接在构造函数内一句函数搞定:
Window::Window(const QSize &initSize, QWidget *parent) : QWidget(parent){// 设置窗口大小 resize(initSize);// 去除边框 setWindowFlags(Qt::Window |Qt::framelessWindowHint);}
在主程序内直接show这个窗口类:
int main(int argc, char **argv){ QApplication app(argc, argv); Window *mainWindow = new Window(QSize(994,650); mainWindow->show(); return app.exec();}
效果如下(桌面背景为白色):
第二步,给它套个阴影
Window::Window(const QSize &initSize, QWidget *parent) : QWidget(parent){ resize(initSize); setWindowFlags(Qt::Window |Qt::framelessWindowHint); HWND hwnd = reinterpret_cast(winId()); LONG style = static_cast( WS_POPUP | WS_CAPTION | (true ? WS_MINIMIZEBOX : 0) | (true ? WS_MAXIMIZEBOX : 0) | WS_THICKframe | WS_CLIPCHILDREN ); ::SetWindowLongPtr(hwnd, GWL_STYLE, style); const MARGINS shadow = {1, 1, 1, 1}; DwmExtendframeIntoClientArea(hwnd, &shadow);}
效果如下:
第三步,加上实现可拖拽、伸缩效果
原理是重写nativeEvent函数,处理windows下的鼠标事件,其实也就是把窗口边界的事件转化为Windows的窗口事件
bool Window::nativeEvent(const QByteArray &eventType, void *message, long *result){if (eventType != "windows_generic_MSG")return false;MSG* msg = reinterpret_cast(message);QWidget* widget = QWidget::find(reinterpret_cast(msg->hwnd));if (!widget)return false;switch (msg->message) {case WM_NCCALCSIZE:{*result = 0;return true;}case WM_NCHITTEST:{const LONG borderWidth = 9;RECT winrect;::GetWindowRect(msg->hwnd, &winrect);long x = GET_X_LPARAM(msg->lParam);long y = GET_Y_LPARAM(msg->lParam);if (m_resizeable && m_freeSizeable){// bottom leftif (x >= winrect.left && x < winrect.left + borderWidth &&y < winrect.bottom && y >= winrect.bottom - borderWidth){*result = HTBOTTOMLEFT;return true;}// bottom rightif (x < winrect.right && x >= winrect.right - borderWidth &&y < winrect.bottom && y >= winrect.bottom - borderWidth){*result = HTBOTTOMRIGHT;return true;}// top leftif (x >= winrect.left && x < winrect.left + borderWidth &&y >= winrect.top && y < winrect.top + borderWidth){*result = HTTOPLEFT;return true;}// top rightif (x < winrect.right && x >= winrect.right - borderWidth &&y >= winrect.top && y < winrect.top + borderWidth){*result = HTTOPRIGHT;return true;}// leftif (x >= winrect.left && x < winrect.left + borderWidth){*result = HTLEFT;return true;}// rightif (x < winrect.right && x >= winrect.right - borderWidth){*result = HTRIGHT;return true;}// bottomif (y < winrect.bottom && y >= winrect.bottom - borderWidth){*result = HTBOTTOM;return true;}// topif (y >= winrect.top && y < winrect.top + borderWidth){*result = HTTOP;return true;}}// 高分屏double dpr = this->devicePixelRatioF();QPoint pos = mapFromGlobal(QPoint(static_cast(x/dpr), static_cast(y/dpr)));// 全区域移动,前提是窗口内没有子控件,有的话需要加入m_ignoreListif (m_fullAreaMoveable){if (!rect().contains(pos)) return false;QWidget *curParent = this;while (1){QWidget* child = curParent->childAt(pos);if (!child){*result = HTCAPTION;return true;}else{// 当鼠标在m_ignoreList列表的窗口子控件内时才能拖动窗口if (m_ignoreList.contains(child)){curParent = child;continue;}elsebreak;}}break;}// 标题栏移动if (!m_titleBar) return false;if (!m_titleBar->rect().contains(pos)) return false;QWidget* child = m_titleBar->childAt(pos);if (!child){*result = HTCAPTION;return true;}return false;}case WM_GETMINMAXINFO:{// 最大化窗口部件位置修正if (::IsZoomed(msg->hwnd)){RECT frame = { 0, 0, 0, 0 };AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0);frame.left = abs(frame.left);frame.top = abs(frame.bottom);widget->setContentsMargins(frame.left, frame.top, frame.right, frame.bottom);}else{widget->setContentsMargins(0, 0, 0, 0);}// 限定最小及最大窗口reinterpret_cast(msg->lParam)->ptMinTrackSize.x = minimumWidth();reinterpret_cast(msg->lParam)->ptMinTrackSize.y = minimumHeight();reinterpret_cast(msg->lParam)->ptMaxTrackSize.x = maximumWidth();reinterpret_cast(msg->lParam)->ptMaxTrackSize.y = maximumHeight();*result = ::DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);return true;}default:break;}return QWidget::nativeEvent(eventType, message, result);}// 加入全局拖拽时的控件忽略列表void Window::AddToMoveableIgnoreList(QWidget *widget){m_ignoreList.append(widget);}// 使能全局拖拽void Window::SetFullAreaMoveable(bool enable){m_fullAreaMoveable = enable;}
这样调用窗口
int main(int argc, char **argv){ QApplication app(argc, argv); Window *mainWindow = new Window(QSize(994, 650)); mainWindow->SetFullAreaMoveable(true); mainWindow->SetResizeable(true); mainWindow->show(); return app.exec();}
效果如下:
如有疑问欢迎交流