首页 > 其他分享 >Qt实现无边框窗口

Qt实现无边框窗口

时间:2022-12-12 15:11:53浏览次数:54  
标签:case return Qt 边框 window 窗口 MDiyWindow border monitor

这个范例是用Windows API实现的,因为根据我的测试,用纯Qt实现会有一些缺点。无边框窗口是普通窗口通过响应WM_NCCALCSIZE消息将边框去掉的窗口,并不是Qt中把窗口设置一个FramelessWindowHint属性然后添加窗口内容。这二者的效果和对系统操作(比如把窗口拖到屏幕最上方然后松开自动最大化窗口)的支持差别很大。我的代码是根据GitHub上的开源项目BorderlessWindow写的。它是基于Windows API写的,而我这个例子是基于Qt+Windows API写的,实现代码并不相同。BorderlessWindow原始项目可以参考以下链接:

下面是普通显示和最大化显示的窗口效果图。我在窗口中加了很少的内容,如果你要是用可以很方便地修改它:

上代码,头文件(需要#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

相关文章