这个范例是用Windows API实现的,因为根据我的测试,用纯Qt实现会有一些缺点。无边框窗口是普通窗口通过响应WM_NCCALCSIZE消息将边框去掉的窗口,并不是Qt中把窗口设置一个FramelessWindowHint属性然后添加窗口内容。这二者的效果和对系统操作(比如把窗口拖到屏幕最上方然后松开自动最大化窗口)的支持差别很大。我的代码是根据GitHub上的开源项目BorderlessWindow写的。它是基于Windows API写的,而我这个例子是基于Qt+Windows API写的,实现代码并不相同。BorderlessWindow原始项目可以参考以下链接:
- 知乎问答链接:https://www.zhihu.com/question/346558582
- GitHub上的源代码:GitHub - melak47/BorderlessWindow: basic win32 example of a borderless window (with aero shadows)
下面是普通显示和最大化显示的窗口效果图。我在窗口中加了很少的内容,如果你要是用可以很方便地修改它:
上代码,头文件(需要#include "qt_windows.h"):
class MDiyWindow : public QWidget { Q_OBJECT public: MDiyWindow(QWidget *parent = nullptr); ~MDiyWindow(); private: bool nativeEvent(const QByteArray &eventType, void *message, long *result) override; long hitTest(HWND hWnd, POINT cursor) const; bool isCompositionEnabled(); void adjustRectIfMaximized(HWND window, RECT& rect); bool isMaximized(HWND hwnd); private slots: void on_pbClose_clicked(); void groupButtonClicked(int id); private: Ui::MDiyWindowClass ui; };
CPP文件:
#include "windowsx.h" #include "dwmapi.h" #pragma comment(lib, "dwmapi.lib") MDiyWindow::MDiyWindow(QWidget *parent) : QWidget(parent) { ui.setupUi(this); setWindowFlag(Qt::Window); setWindowTitle(u8"无边框窗口测试"); setGeometry(400, 400, 800, 500); HWND hWnd = (HWND)winId(); SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE); QButtonGroup* group = new QButtonGroup(this); group->addButton(ui.pbPage1, 0); group->addButton(ui.pbPage2, 1); group->addButton(ui.pbPage3, 2); connect(group, QOverload<int>::of(&QButtonGroup::buttonClicked), this, &MDiyWindow::groupButtonClicked); ui.hlMainTool->setAlignment(ui.pbClose, Qt::AlignTop); ui.pbPage1->setChecked(true); } MDiyWindow::~MDiyWindow() { } void MDiyWindow::groupButtonClicked(int id) { ui.swPanels->setCurrentIndex(id); } void MDiyWindow::on_pbClose_clicked() { close(); } bool MDiyWindow::nativeEvent(const QByteArray &eventType, void *message, long* result) { MSG* msg = (MSG*)message; switch (msg->message) { case WM_NCCALCSIZE: if (msg->wParam == TRUE) { NCCALCSIZE_PARAMS* params = reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam); adjustRectIfMaximized(msg->hwnd, params->rgrc[0]); *result = 0; return true; } break; case WM_NCHITTEST: *result = hitTest(msg->hwnd, { GET_X_LPARAM(msg->lParam), GET_Y_LPARAM(msg->lParam) }); return true; case WM_NCACTIVATE: if (!isCompositionEnabled()) { *result = TRUE; /* 防止显示Windows基本主题的边框 */ return true; } break; default: break; } return false; } bool MDiyWindow::isCompositionEnabled() { BOOL composition_enabled = FALSE; if (DwmIsCompositionEnabled(&composition_enabled) == S_OK) { return composition_enabled; } return false; } bool MDiyWindow::isMaximized(HWND hwnd) { WINDOWPLACEMENT placement; if (GetWindowPlacement(hwnd, &placement)) { return placement.showCmd == SW_MAXIMIZE; } return false; } void MDiyWindow::adjustRectIfMaximized(HWND window, RECT& rect) { if (!isMaximized(window)) { return; } auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL); if (!monitor) { return; } MONITORINFO monitor_info = { 0 }; monitor_info.cbSize = sizeof(monitor_info); if (GetMonitorInfo(monitor, &monitor_info)) { rect.left = monitor_info.rcWork.left - 6; rect.top = monitor_info.rcWork.top - 6; rect.right = monitor_info.rcWork.right + 6; rect.bottom = monitor_info.rcWork.bottom + 6; } } long MDiyWindow::hitTest(HWND hWnd, POINT cursor) const { const POINT border { GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER), GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER) }; RECT window; if (!GetWindowRect(hWnd, &window)) { return HTNOWHERE; } QWidget* child = childAt(mapFromGlobal({ cursor.x, cursor.y })); if (child) { return HTCLIENT; } enum RegionMask { CLIENT = 0b0000, LEFT = 0b0001, RIGHT = 0b0010, TOP = 0b0100, BOTTOM = 0b1000, }; const auto result = LEFT * (cursor.x < (window.left + border.x)) | RIGHT * (cursor.x >= (window.right - border.x)) | TOP * (cursor.y < (window.top + border.y)) | BOTTOM * (cursor.y >= (window.bottom - border.y)); switch (result) { case LEFT: return HTLEFT; case RIGHT: return HTRIGHT; case TOP: return HTTOP; case BOTTOM: return HTBOTTOM; case TOP | LEFT: return HTTOPLEFT; case TOP | RIGHT: return HTTOPRIGHT; case BOTTOM | LEFT: return HTBOTTOMLEFT; case BOTTOM | RIGHT: return HTBOTTOMRIGHT; case CLIENT: return HTCAPTION; default: // 其它 break; } return HTNOWHERE; }
UI文件:
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MDiyWindowClass</class> <widget class="QWidget" name="MDiyWindowClass"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>600</width> <height>400</height> </rect> </property> <property name="windowTitle"> <string>MDiyWindow</string> </property> <property name="styleSheet"> <string notr="true">#MDiyWindowClass { background-color: rgb(220, 220, 255); } #pbPage1, #pbPage2, #pbPage3 { background-color: rgb(223, 223, 223); border-bottom: 3px solid #dfdfdf; border-top: none; border-left: none; border-right: none; } #pbPage1:checked, #pbPage2:checked, #pbPage3:checked { background-color: rgb(233, 233, 233); border-bottom: 3px solid #ff5f00; border-top: none; border-left: none; border-right: none; } #swPanels { background-color: rgb(240, 240, 240); } </string> </property> <layout class="QGridLayout" name="gridLayout"> <property name="leftMargin"> <number>6</number> </property> <property name="topMargin"> <number>6</number> </property> <property name="rightMargin"> <number>6</number> </property> <property name="bottomMargin"> <number>6</number> </property> <property name="verticalSpacing"> <number>0</number> </property> <item row="0" column="0"> <layout class="QHBoxLayout" name="hlMainTool"> <property name="spacing"> <number>0</number> </property> <item> <widget class="QPushButton" name="pbPage1"> <property name="minimumSize"> <size> <width>64</width> <height>26</height> </size> </property> <property name="text"> <string>第1页</string> </property> <property name="checkable"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QPushButton" name="pbPage2"> <property name="minimumSize"> <size> <width>64</width> <height>26</height> </size> </property> <property name="text"> <string>第2页</string> </property> <property name="checkable"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QPushButton" name="pbPage3"> <property name="minimumSize"> <size> <width>64</width> <height>26</height> </size> </property> <property name="text"> <string>第3页</string> </property> <property name="checkable"> <bool>true</bool> </property> </widget> </item> <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="pbClose"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>22</width> <height>22</height> </size> </property> <property name="maximumSize"> <size> <width>22</width> <height>22</height> </size> </property> <property name="text"> <string>X</string> </property> </widget> </item> </layout> </item> <item row="1" column="0"> <widget class="QStackedWidget" name="swPanels"> <property name="frameShape"> <enum>QFrame::StyledPanel</enum> </property> <widget class="QWidget" name="page"> <widget class="QLabel" name="label_3"> <property name="geometry"> <rect> <x>10</x> <y>10</y> <width>81</width> <height>21</height> </rect> </property> <property name="text"> <string>第1页</string> </property> </widget> </widget> <widget class="QWidget" name="page_2"> <widget class="QLabel" name="label_2"> <property name="geometry"> <rect> <x>10</x> <y>10</y> <width>81</width> <height>21</height> </rect> </property> <property name="text"> <string>第2页</string> </property> </widget> </widget> <widget class="QWidget" name="page_3"> <widget class="QLabel" name="label"> <property name="geometry"> <rect> <x>10</x> <y>10</y> <width>81</width> <height>21</height> </rect> </property> <property name="text"> <string>第3页</string> </property> </widget> </widget> </widget> </item> </layout> </widget> <layoutdefault spacing="6" margin="11"/> <resources/> <connections/> </ui>
标签:case,return,Qt,边框,window,窗口,MDiyWindow,border,monitor From: https://www.cnblogs.com/mengxiangdu/p/16976079.html