基于 QMainWindow 实现的效果很好的 Qt 无边框窗口,支持 Windows 和 OS X 系统。在 Windows 上,支持窗口阴影、Aero 效果等;在 OS X 上,支持原生窗口样式,比如窗口圆角、窗口阴影、三个系统按钮(关闭、最小化、最大化)等。
window效果
OSX
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fmi8s2C4-1629970950506)(https://gitee.com/zlozl5566/drawing-bed-1/raw/master/20210826174006.gif)]
#ifndef CFRAMELESSWINDOW_H
#define CFRAMELESSWINDOW_H
#include "qsystemdetection.h"
#include
#include
//A nice frameless window for both Windows and OS X
//Author: Bringer-of-Light
//Github: https://github.com/Bringer-of-Light/Qt-Nice-Frameless-Window
// Usage: use "CFramelessWindow" as base class instead of "QMainWindow", and enjoy
#ifdef Q_OS_WIN
#include
#include
#include
#include
class CFramelessWindow : public QMainWindow
{
Q_OBJECT
public:
explicit CFramelessWindow(QWidget *parent = 0);
public:
//设置是否可以通过鼠标调整窗口大小
//if resizeable is set to false, then the window can not be resized by mouse
//but still can be resized programtically
void setResizeable(bool resizeable=true);
bool isResizeable(){return m_bResizeable;}
//设置可调整大小区域的宽度,在此区域内,可以使用鼠标调整窗口大小
//set border width, inside this aera, window can be resized by mouse
void setResizeableAreaWidth(int width = 5);
protected:
//设置一个标题栏widget,此widget会被当做标题栏对待
//set a widget which will be treat as SYSTEM titlebar
void setTitleBar(QWidget* titlebar);
//在标题栏控件内,也可以有子控件如标签控件“label1”,此label1遮盖了标题栏,导致不能通过label1拖动窗口
//要解决此问题,使用addIgnoreWidget(label1)
//generally, we can add widget say "label1" on titlebar, and it will cover the titlebar under it
//as a result, we can not drag and move the MainWindow with this "label1" again
//we can fix this by add "label1" to a ignorelist, just call addIgnoreWidget(label1)
void addIgnoreWidget(QWidget* widget);
bool nativeEvent(const QByteArray &eventType, void *message, long *result);
private slots:
void onTitleBarDestroyed();
public:
void setContentsMargins(const QMargins &margins);
void setContentsMargins(int left, int top, int right, int bottom);
QMargins contentsMargins() const;
QRect contentsRect() const;
void getContentsMargins(int *left, int *top, int *right, int *bottom) const;
public slots:
void showFullScreen();
private:
QWidget* m_titlebar;
QList<QWidget*> m_whiteList;
int m_borderWidth;
QMargins m_margins;
QMargins m_frames;
bool m_bJustMaximized;
bool m_bResizeable;
};
#elif defined Q_OS_MAC
#include
#include
#include
class CFramelessWindow : public QMainWindow
{
Q_OBJECT
public:
explicit CFramelessWindow(QWidget *parent = 0);
private:
void initUI();
public:
//设置可拖动区域的高度,在此区域内,可以通过鼠标拖动窗口, 0表示整个窗口都可拖动
//In draggable area, window can be moved by mouse, (height = 0) means that the whole window is draggable
void setDraggableAreaHeight(int height = 0);
//只有OS X10.10及以后系统,才支持OS X原生样式包括:三个系统按钮、窗口圆角、窗口阴影
//类初始化完成后,可以通过此函数查看是否已经启用了原生样式。如果未启动,需要自定义关闭按钮、最小化按钮、最大化按钮
//Native style(three system button/ round corner/ drop shadow) works only on OS X 10.10 or later
//after init, we should check whether NativeStyle is OK with this function
//if NOT ok, we should implement close button/ min button/ max button ourself
bool isNativeStyleOK() {return m_bNativeSystemBtn;}
//如果设置setCloseBtnQuit(false),那么点击关闭按钮后,程序不会退出,而是会隐藏,只有在OS X 10.10 及以后系统中有效
//if setCloseBtnQuit(false), then when close button is clicked, the application will hide itself instead of quit
//be carefull, after you set this to false, you can NOT change it to true again
//this function should be called inside of the constructor function of derived classes, and can NOT be called more than once
//only works for OS X 10.10 or later
void setCloseBtnQuit(bool bQuit = true);
//启用或禁用关闭按钮,只有在isNativeStyleOK()返回true的情况下才有效
//enable or disable Close button, only worked if isNativeStyleOK() returns true
void setCloseBtnEnabled(bool bEnable = true);
//启用或禁用最小化按钮,只有在isNativeStyleOK()返回true的情况下才有效
//enable or disable Miniaturize button, only worked if isNativeStyleOK() returns true
void setMinBtnEnabled(bool bEnable = true);
//启用或禁用zoom(最大化)按钮,只有在isNativeStyleOK()返回true的情况下才有效
//enable or disable Zoom button(fullscreen button), only worked if isNativeStyleOK() returns true
void setZoomBtnEnabled(bool bEnable = true);
bool isCloseBtnEnabled() {return m_bIsCloseBtnEnabled;}
bool isMinBtnEnabled() {return m_bIsMinBtnEnabled;}
bool isZoomBtnEnabled() {return m_bIsZoomBtnEnabled;}
protected:
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
private:
int m_draggableHeight;
bool m_bWinMoving;
bool m_bMousePressed;
QPoint m_MousePos;
QPoint m_WindowPos;
bool m_bCloseBtnQuit;
bool m_bNativeSystemBtn;
bool m_bIsCloseBtnEnabled, m_bIsMinBtnEnabled, m_bIsZoomBtnEnabled;
//===============================================
//TODO
//下面的代码是试验性质的
//tentative code
//窗口从全屏状态恢复正常大小时,标题栏又会出现,原因未知。
//默认情况下,系统的最大化按钮(zoom button)是进入全屏,为了避免标题栏重新出现的问题,
//以上代码已经重新定义了系统zoom button的行为,是其功能变为最大化而不是全屏
//以下代码尝试,每次窗口从全屏状态恢复正常大小时,都再次进行设置,以消除标题栏
//after the window restore from fullscreen mode, the titlebar will show again, it looks like a BUG
//on OS X 10.10 and later, click the system green button (zoom button) will make the app become fullscreen
//so we have override it's action to "maximized" in the CFramelessWindow Constructor function
//but we may try something else such as delete the titlebar again and again...
private:
bool m_bTitleBarVisible;
void setTitlebarVisible(bool bTitlebarVisible = false);
bool isTitlebarVisible() {return m_bTitleBarVisible;}
private slots:
void onRestoreFromFullScreen();
signals:
void restoreFromFullScreen();
protected:
void resizeEvent(QResizeEvent *event);
};
#endif
#endif // CFRAMELESSWINDOW_H
#include "framelesswindow.h"
#include
#include
#include
#ifdef Q_OS_WIN
#include
#include
#include
#include
#include // Fixes error C2504: 'IUnknown' : base class undefined
#include
#include
#pragma comment (lib,"Dwmapi.lib") // Adds missing library, fixes error LNK2019: unresolved external symbol __imp__DwmExtendFrameIntoClientArea
#pragma comment (lib,"user32.lib")
CFramelessWindow::CFramelessWindow(QWidget *parent)
: QMainWindow(parent),
m_titlebar(Q_NULLPTR),
m_borderWidth(5),
m_bJustMaximized(false),
m_bResizeable(true)
{
// setWindowFlag(Qt::Window,true);
// setWindowFlag(Qt::FramelessWindowHint, true);
// setWindowFlag(Qt::WindowSystemMenuHint, true);
// setWindowFlag() is not avaliable before Qt v5.9, so we should use setWindowFlags instead
setWindowFlags(windowFlags() | Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
setResizeable(m_bResizeable);
}
void CFramelessWindow::setResizeable(bool resizeable)
{
bool visible = isVisible();
m_bResizeable = resizeable;
if (m_bResizeable){
setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
// setWindowFlag(Qt::WindowMaximizeButtonHint);
//此行代码可以带回Aero效果,同时也带回了标题栏和边框,在nativeEvent()会再次去掉标题栏
//
//this line will get titlebar/thick frame/Aero back, which is exactly what we want
//we will get rid of titlebar and thick frame again in nativeEvent() later
HWND hwnd = (HWND)this->winId();
DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION);
}else{
setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint);
// setWindowFlag(Qt::WindowMaximizeButtonHint,false);
HWND hwnd = (HWND)this->winId();
DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
::SetWindowLong(hwnd, GWL_STYLE, style & ~WS_MAXIMIZEBOX & ~WS_CAPTION);
}
//保留一个像素的边框宽度,否则系统不会绘制边框阴影
//
//we better left 1 piexl width of border untouch, so OS can draw nice shadow around it
const MARGINS shadow = { 1, 1, 1, 1 };
DwmExtendFrameIntoClientArea(HWND(winId()), &shadow);
setVisible(visible);
}
void CFramelessWindow::setResizeableAreaWidth(int width)
{
if (1 > width) width = 1;
m_borderWidth = width;
}
void CFramelessWindow::setTitleBar(QWidget* titlebar)
{
m_titlebar = titlebar;
if (!titlebar) return;
connect(titlebar, SIGNAL(destroyed(QObject*)), this, SLOT(onTitleBarDestroyed()));
}
void CFramelessWindow::onTitleBarDestroyed()
{
if (m_titlebar == QObject::sender())
{
m_titlebar = Q_NULLPTR;
}
}
void CFramelessWindow::addIgnoreWidget(QWidget* widget)
{
if (!widget) return;
if (m_whiteList.contains(widget)) return;
m_whiteList.append(widget);
}
bool CFramelessWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
//Workaround for known bug -> check Qt forum : https://forum.qt.io/topic/93141/qtablewidget-itemselectionchanged/13
#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
MSG* msg = *reinterpret_cast<MSG**>(message);
#else
//MSG* msg = reinterpret_cast(message);
const auto msg = static_cast<LPMSG>(message);
#endif
if (!msg->hwnd) {
// Why sometimes the window handle is null? Is it designed to be?
// Anyway, we should skip it in this case.
return false;
}
switch (msg->message)
{
case WM_NCCALCSIZE:
{
// Windows是根据这个消息的返回值来设置窗口的客户区(窗口中真正显示的内容)
// 和非客户区(标题栏、窗口边框、菜单栏和状态栏等Windows系统自行提供的部分
// ,不过对于Qt来说,除了标题栏和窗口边框,非客户区基本也都是自绘的)的范
// 围的,lParam里存放的就是新客户区的几何区域,默认是整个窗口的大小,正常
// 的程序需要修改这个参数,告知系统窗口的客户区和非客户区的范围(一般来说可
// 以完全交给Windows,让其自行处理,使用默认的客户区和非客户区),因此如果
// 我们不修改lParam,就可以使客户区充满整个窗口,从而去掉标题栏和窗口边框
// (因为这些东西都被客户区给盖住了。但边框阴影也会因此而丢失,不过我们会使
// 用其他方式将其带回,请参考其他消息的处理,此处不过多提及)。但有个情况要
// 特别注意,那就是窗口最大化后,窗口的实际尺寸会比屏幕的尺寸大一点,从而使
// 用户看不到窗口的边界,这样用户就不能在窗口最大化后调整窗口的大小了(虽然
// 这个做法听起来特别奇怪,但Windows确实就是这样做的),因此如果我们要自行
// 处理窗口的非客户区,就要在窗口最大化后,将窗口边框的宽度和高度(一般是相
// 等的)从客户区裁剪掉,否则我们窗口所显示的内容就会超出屏幕边界,显示不全。
// 如果用户开启了任务栏自动隐藏,在窗口最大化后,还要考虑任务栏的位置。因为
// 如果窗口最大化后,其尺寸和屏幕尺寸相等(因为任务栏隐藏了,所以窗口最大化
// 后其实是充满了整个屏幕,变相的全屏了),Windows会认为窗口已经进入全屏的
// 状态,从而导致自动隐藏的任务栏无法弹出。要避免这个状况,就要使窗口的尺寸
// 小于屏幕尺寸。我下面的做法参考了火狐、Chromium和Windows Terminal
// 如果没有开启任务栏自动隐藏,是不存在这个问题的,所以要先进行判断。
// 一般情况下,*result设置为0(相当于DefWindowProc的返回值为0)就可以了,
// 根据MSDN的说法,返回0意为此消息已经被程序自行处理了,让Windows跳过此消
// 息,否则Windows会添加对此消息的默认处理,对于当前这个消息而言,就意味着
// 标题栏和窗口边框又会回来,这当然不是我们想要的结果。根据MSDN,当wParam
// 为FALSE时,只能返回0,但当其为TRUE时,可以返回0,也可以返回一个WVR_常
// 量。根据Chromium的注释,当存在非客户区时,如果返回WVR_REDRAW会导致子
// 窗口/子控件出现奇怪的bug(自绘控件错位),并且Lucas在Windows 10
// 上成功复现,说明这个bug至今都没有解决。我查阅了大量资料,发现唯一的解决
// 方案就是返回0。但如果不存在非客户区,且wParam为TRUE,最好返回
// WVR_REDRAW,否则窗口在调整大小可能会产生严重的闪烁现象。
// 虽然对大多数消息来说,返回0都代表让Windows忽略此消息,但实际上不同消息
// 能接受的返回值是不一样的,请注意自行查阅MSDN。
// Sent when the size and position of a window's client area must be
// calculated. By processing this message, an application can
// control the content of the window's client area when the size or
// position of the window changes. If wParam is TRUE, lParam points
// to an NCCALCSIZE_PARAMS structure that contains information an
// application can use to calculate the new size and position of the
// client rectangle. If wParam is FALSE, lParam points to a RECT
// structure. On entry, the structure contains the proposed window
// rectangle for the window. On exit, the structure should contain
// the screen coordinates of the corresponding window client area.
// The client area is the window's content area, the non-client area
// is the area which is provided by the system, such as the title
// bar, the four window borders, the frame shadow, the menu bar, the
// status bar, the scroll bar, etc. But for Qt, it draws most of the
// window area (client + non-client) itself. We now know that the
// title bar and the window frame is in the non-client area and we
// can set the scope of the client area in this message, so we can
// remove the title bar and the window frame by let the non-client
// area be covered by the client area (because we can't really get
// rid of the non-client area, it will always be there, all we can
// do is to hide it) , which means we should let the client area's
// size the same with the whole window's size. So there is no room
// for the non-client area and then the user won't be able to see it
// again. But how to achieve this? Very easy, just leave lParam (the
// re-calculated client area) untouched. But of course you can
// modify lParam, then the non-client area will be seen and the
// window borders and the window frame will show up. However, things
// are quite different when you try to modify the top margin of the
// client area. DWM will always draw the whole title bar no matter
// what margin value you set for the top, unless you don't modify it
// and remove the whole top area (the title bar + the one pixel
// height window border). This can be confirmed in Windows
// Terminal's source code, you can also try yourself to verify
// it. So things will become quite complicated if you want to
// preserve the four window borders. So we just remove the whole
// window frame, otherwise the code will become much more complex.
if (msg->wParam == FALSE) {
*result = 0;
return true;
}
if(::IsZoomed(msg->hwnd)) {
*result = WVR_REDRAW;
return true;
} else {
*result = 0;
return true;
}
// NCCALCSIZE_PARAMS& params = *reinterpret_cast(msg->lParam);
//if (params.rgrc[0].top != 0)
// params.rgrc[0].top -= 1;
// //this kills the window frame and title bar we added with WS_THICKFRAME and WS_CAPTION
// *result = WVR_REDRAW;
// return true;
}
case WM_NCHITTEST:
{
// 原生Win32窗口只有顶边是在窗口内部resize的,其余三边都是在窗口
// 外部进行resize的,其原理是,WS_THICKFRAME这个窗口样式会在窗
// 口的左、右和底边添加三个透明的resize区域,这三个区域在正常状态
// 下是完全不可见的,它们由DWM负责绘制和控制。这些区域的宽度等于
// (SM_CXSIZEFRAME + SM_CXPADDEDBORDER),高度等于
// (SM_CYSIZEFRAME + SM_CXPADDEDBORDER),在100%缩放时,均等
// 于8像素。它们属于窗口区域的一部分,但不属于客户区,而是属于非客
// 户区,因此GetWindowRect获取的区域中是包含这三个resize区域的,
// 而GetClientRect获取的区域是不包含它们的。当把
// DWMWA_EXTENDED_FRAME_BOUNDS作为参数调用
// DwmGetWindowAttribute时,也能获取到一个窗口大小,这个大小介
// 于前面两者之间,暂时不知道这个数据的意义及其作用。我们在
// WM_NCCALCSIZE消息的处理中,已经把整个窗口都设置为客户区了,也
// 就是说,我们的窗口已经没有非客户区了,因此那三个透明的resize区
// 域,此刻也已经成为窗口客户区的一部分了,从而变得不透明了。所以
// 现在的resize,看起来像是在窗口内部resize,是因为原本透明的地方
// 现在变得不透明了,实际上,单纯从范围上来看,现在我们resize的地方,
// 就是普通窗口的边框外部,那三个透明区域的范围。
// 因此,如果我们把边框完全去掉(就是我们正在做的事情),resize就
// 会看起来是在内部进行,这个问题通过常规方法非常难以解决。我测试过
// QQ和钉钉的窗口,它们的窗口就是在外部resize,但实际上它们是通过
// 把窗口实际的内容,嵌入到一个完全透明的但尺寸要大一圈的窗口中实现
// 的,虽然看起来效果还行,但在我看来不是正途。而且我之所以能发现,
// 也是由于这种方法在很多情况下会露馅,比如窗口未响应卡住或贴边的时
// 候,能明显看到窗口周围多出来一圈边界。我曾经尝试再把那三个区域弄
// 透明,但无一例外都会破坏DWM绘制的边框阴影,因此只好作罢。
// As you may have found, if you use this code, the resize areas
// will be inside the frameless window, however, a normal Win32
// window can be resized outside of it. Here is the reason: the
// WS_THICKFRAME window style will cause a window has three
// transparent areas beside the window's left, right and bottom
// edge. Their width or height is eight pixels if the window is not
// scaled. In most cases, they are totally invisible. It's DWM's
// responsibility to draw and control them. They exist to let the
// user resize the window, visually outside of it. They are in the
// window area, but not the client area, so they are in the
// non-client area actually. But we have turned the whole window
// area into client area in WM_NCCALCSIZE, so the three transparent
// resize areas also become a part of the client area and thus they
// become visible. When we resize the window, it looks like we are
// resizing inside of it, however, that's because the transparent
// resize areas are visible now, we ARE resizing outside of the
// window actually. But I don't know how to make them become
// transparent again without breaking the frame shadow drawn by DWM.
// If you really want to solve it, you can try to embed your window
// into a larger transparent window and draw the frame shadow
// yourself. As what we have said in WM_NCCALCSIZE, you can only
// remove the top area of the window, this will let us be able to
// resize outside of the window and don't need much process in this
// message, it looks like a perfect plan, however, the top border is
// missing due to the whole top area is removed, and it's very hard
// to bring it back because we have to use a trick in WM_PAINT
// (learned from Windows Terminal), but no matter what we do in
// WM_PAINT, it will always break the backing store mechanism of Qt,
// so actually we can't do it. And it's very difficult to do such
// things in NativeEventFilters as well. What's worse, if we really
// do this, the four window borders will become white and they look
// horrible in dark mode. This solution only supports Windows 10
// because the border width on Win10 is only one pixel, however it's
// eight pixels on Windows 7 so preserving the three window borders
// looks terrible on old systems. I'm testing this solution in
// another branch, if you are interested in it, you can give it a
// try.
*result = 0;
const LONG border_width = m_borderWidth;
RECT winrect;
GetWindowRect(HWND(winId()), &winrect);
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);
if(m_bResizeable)
{
bool resizeWidth = minimumWidth() != maximumWidth();
bool resizeHeight = minimumHeight() != maximumHeight();
if(resizeWidth)
{
//left border
if (x >= winrect.left && x < winrect.left + border_width)
{
*result = HTLEFT;
}
//right border
if (x < winrect.right && x >= winrect.right - border_width)
{
*result = HTRIGHT;
}
}
if(resizeHeight)
{
//bottom border
if (y < winrect.bottom && y >= winrect.bottom - border_width)
{
*result = HTBOTTOM;
}
//top border
if (y >= winrect.top && y < winrect.top + border_width)
{
*result = HTTOP;
}
}
if(resizeWidth && resizeHeight)
{
//bottom left corner
if (x >= winrect.left && x < winrect.left + border_width &&
y < winrect.bottom && y >= winrect.bottom - border_width)
{
*result = HTBOTTOMLEFT;
}
//bottom right corner
if (x < winrect.right && x >= winrect.right - border_width &&
y < winrect.bottom && y >= winrect.bottom - border_width)
{
*result = HTBOTTOMRIGHT;
}
//top left corner
if (x >= winrect.left && x < winrect.left + border_width &&
y >= winrect.top && y < winrect.top + border_width)
{
*result = HTTOPLEFT;
}
//top right corner
if (x < winrect.right && x >= winrect.right - border_width &&
y >= winrect.top && y < winrect.top + border_width)
{
*result = HTTOPRIGHT;
}
}
}
if (0!=*result) return true;
//*result仍然等于0,说明鼠标位置不在上述的边框范围内,则还有可能处于标题栏范围内
//标题栏效果(win7 及以上):
//1. 双击标题栏,窗口在最大化、正常化之间切换
//2. 拖动标题栏时,可以移动窗口,移动时如果鼠标触碰到桌面顶端,则最大化窗口等类似效果
//*result still equals 0, that means the cursor locate OUTSIDE the frame area
//but it may locate in titlebar area
if (!m_titlebar) return false;
//support highdpi
double dpr = this->devicePixelRatioF();
QPoint pos = m_titlebar->mapFromGlobal(QPoint(x/dpr,y/dpr));
if (!m_titlebar->rect().contains(pos)) return false;
QWidget* child = m_titlebar->childAt(pos);
if (!child)
{
*result = HTCAPTION;
return true;
}else{
if (m_whiteList.contains(child))
{
*result = HTCAPTION;
return true;
}
}
return false;
} //end case WM_NCHITTEST
case WM_GETMINMAXINFO:
{
if (::IsZoomed(msg->hwnd)) {
RECT frame = { 0, 0, 0, 0 };
AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0);
//record frame area data
double dpr = this->devicePixelRatioF();
m_frames.setLeft(abs(frame.left)/dpr+0.5);
m_frames.setTop(abs(frame.bottom)/dpr+0.5);
m_frames.setRight(abs(frame.right)/dpr+0.5);
m_frames.setBottom(abs(frame.bottom)/dpr+0.5);
QMainWindow::setContentsMargins(m_frames.left()+m_margins.left(), \
m_frames.top()+m_margins.top(), \
m_frames.right()+m_margins.right(), \
m_frames.bottom()+m_margins.bottom());
m_bJustMaximized = true;
}else {
if (m_bJustMaximized)
{
QMainWindow::setContentsMargins(m_margins);
m_frames = QMargins();
m_bJustMaximized = false;
}
}
return false;
}
default:
return QMainWindow::nativeEvent(eventType, message, result);
}
}
void CFramelessWindow::setContentsMargins(const QMargins &margins)
{
QMainWindow::setContentsMargins(margins+m_frames);
m_margins = margins;
}
void CFramelessWindow::setContentsMargins(int left, int top, int right, int bottom)
{
QMainWindow::setContentsMargins(left+m_frames.left(),\
top+m_frames.top(), \
right+m_frames.right(), \
bottom+m_frames.bottom());
m_margins.setLeft(left);
m_margins.setTop(top);
m_margins.setRight(right);
m_margins.setBottom(bottom);
}
QMargins CFramelessWindow::contentsMargins() const
{
QMargins margins = QMainWindow::contentsMargins();
margins -= m_frames;
return margins;
}
void CFramelessWindow::getContentsMargins(int *left, int *top, int *right, int *bottom) const
{
QMainWindow::getContentsMargins(left,top,right,bottom);
if (!(left&&top&&right&&bottom)) return;
if (isMaximized())
{
*left -= m_frames.left();
*top -= m_frames.top();
*right -= m_frames.right();
*bottom -= m_frames.bottom();
}
}
QRect CFramelessWindow::contentsRect() const
{
QRect rect = QMainWindow::contentsRect();
int width = rect.width();
int height = rect.height();
rect.setLeft(rect.left() - m_frames.left());
rect.setTop(rect.top() - m_frames.top());
rect.setWidth(width);
rect.setHeight(height);
return rect;
}
void CFramelessWindow::showFullScreen()
{
if (isMaximized())
{
QMainWindow::setContentsMargins(m_margins);
m_frames = QMargins();
}
QMainWindow::showFullScreen();
}
#endif //Q_OS_WIN
使用方法,就是QWidget 继承 CFramelessWindow类
class MainWindow : public CFramelessWindow
{
}
完整代码:
demo下载地址:
https://download.csdn.net/download/ZLOZL/21569440?spm=1001.2014.3001.5503
QtCustomWindow/src at master · liamlife/QtCustomWindow (github.com)