2023-02-26 23:47:07 +08:00
|
|
|
|
#include "FramelessView.h"
|
|
|
|
|
#include <QGuiApplication>
|
|
|
|
|
#include <QQuickItem>
|
|
|
|
|
#include <QScreen>
|
|
|
|
|
#include <QWindow>
|
2023-03-09 11:50:40 +08:00
|
|
|
|
#include <FluTheme.h>
|
2023-03-28 11:53:25 +08:00
|
|
|
|
#include <QTimer>
|
|
|
|
|
|
|
|
|
|
#include <dwmapi.h>
|
|
|
|
|
#include <windowsx.h>
|
|
|
|
|
#pragma comment(lib, "Dwmapi.lib")
|
|
|
|
|
#pragma comment(lib, "User32.lib")
|
|
|
|
|
|
|
|
|
|
static bool isMaxWin(QWindow* win)
|
|
|
|
|
{
|
|
|
|
|
return win->windowState() == Qt::WindowMaximized;
|
|
|
|
|
}
|
|
|
|
|
static bool isFullWin(QQuickView* win)
|
|
|
|
|
{
|
|
|
|
|
return win->windowState() == Qt::WindowFullScreen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static long hitTest(RECT winrect, long x, long y, int borderWidth)
|
|
|
|
|
{
|
|
|
|
|
if ((x >= winrect.left) && (x < winrect.left + borderWidth) && (y >= winrect.top) && (y < winrect.top + borderWidth))
|
|
|
|
|
{
|
|
|
|
|
return HTTOPLEFT;
|
|
|
|
|
}
|
|
|
|
|
else if (x < winrect.right && x >= winrect.right - borderWidth && y >= winrect.top && y < winrect.top + borderWidth)
|
|
|
|
|
{
|
|
|
|
|
return HTTOPRIGHT;
|
|
|
|
|
}
|
|
|
|
|
else if (x >= winrect.left && x < winrect.left + borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth)
|
|
|
|
|
{
|
|
|
|
|
return HTBOTTOMLEFT;
|
|
|
|
|
}
|
|
|
|
|
else if (x < winrect.right && x >= winrect.right - borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth)
|
|
|
|
|
{
|
|
|
|
|
return HTBOTTOMRIGHT;
|
|
|
|
|
}
|
|
|
|
|
else if (x >= winrect.left && x < winrect.left + borderWidth)
|
|
|
|
|
{
|
|
|
|
|
return HTLEFT;
|
|
|
|
|
}
|
|
|
|
|
else if (x < winrect.right && x >= winrect.right - borderWidth)
|
|
|
|
|
{
|
|
|
|
|
return HTRIGHT;
|
|
|
|
|
}
|
|
|
|
|
else if (y >= winrect.top && y < winrect.top + borderWidth)
|
|
|
|
|
{
|
|
|
|
|
return HTTOP;
|
|
|
|
|
}
|
|
|
|
|
else if (y < winrect.bottom && y >= winrect.bottom - borderWidth)
|
|
|
|
|
{
|
|
|
|
|
return HTBOTTOM;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-07 00:05:27 +08:00
|
|
|
|
|
2023-03-02 18:21:43 +08:00
|
|
|
|
class FramelessViewPrivate
|
2023-02-26 23:47:07 +08:00
|
|
|
|
{
|
2023-03-02 18:21:43 +08:00
|
|
|
|
public:
|
|
|
|
|
bool m_isMax = false;
|
|
|
|
|
bool m_isFull = false;
|
|
|
|
|
bool m_deleteLater = false;
|
2023-03-28 11:53:25 +08:00
|
|
|
|
bool m_isFirst = true;
|
2023-03-02 18:21:43 +08:00
|
|
|
|
QQuickItem *m_titleItem = nullptr;
|
2023-02-26 23:47:07 +08:00
|
|
|
|
};
|
2023-03-09 11:50:40 +08:00
|
|
|
|
|
2023-03-02 18:21:43 +08:00
|
|
|
|
FramelessView::FramelessView(QWindow *parent) : Super(parent), d(new FramelessViewPrivate)
|
2023-02-26 23:47:07 +08:00
|
|
|
|
{
|
2023-03-28 11:53:25 +08:00
|
|
|
|
setFlags( Qt::Window | Qt::FramelessWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
|
|
|
|
|
setResizeMode(SizeRootObjectToView);
|
2023-02-26 23:47:07 +08:00
|
|
|
|
setIsMax(windowState() == Qt::WindowMaximized);
|
|
|
|
|
setIsFull(windowState() == Qt::WindowFullScreen);
|
|
|
|
|
connect(this, &QWindow::windowStateChanged, this, [&](Qt::WindowState state) {
|
|
|
|
|
(void)state;
|
|
|
|
|
setIsMax(windowState() == Qt::WindowMaximized);
|
|
|
|
|
setIsFull(windowState() == Qt::WindowFullScreen);
|
|
|
|
|
});
|
2023-03-09 11:50:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-26 23:47:07 +08:00
|
|
|
|
FramelessView::~FramelessView()
|
|
|
|
|
{
|
|
|
|
|
delete d;
|
|
|
|
|
}
|
2023-03-09 11:50:40 +08:00
|
|
|
|
|
2023-03-02 18:21:43 +08:00
|
|
|
|
void FramelessView::showEvent(QShowEvent *e)
|
2023-02-26 23:47:07 +08:00
|
|
|
|
{
|
2023-03-28 11:53:25 +08:00
|
|
|
|
static const MARGINS shadow_state[2] { { 0, 0, 0, 0 }, { 1, 1, 1, 1 } };
|
|
|
|
|
::DwmExtendFrameIntoClientArea((HWND)(winId()), &shadow_state[true]);
|
2023-03-02 18:21:43 +08:00
|
|
|
|
Super::showEvent(e);
|
2023-03-28 11:53:25 +08:00
|
|
|
|
if(d->m_isFirst){
|
|
|
|
|
QTimer::singleShot(150,this,[=](){ setFlag(Qt::FramelessWindowHint,false); });
|
|
|
|
|
}
|
|
|
|
|
setFlag(Qt::FramelessWindowHint,false);
|
2023-02-26 23:47:07 +08:00
|
|
|
|
}
|
2023-03-09 11:50:40 +08:00
|
|
|
|
|
2023-03-02 18:21:43 +08:00
|
|
|
|
QRect FramelessView::calcCenterGeo(const QRect &screenGeo, const QSize &normalSize)
|
2023-02-26 23:47:07 +08:00
|
|
|
|
{
|
|
|
|
|
int w = normalSize.width();
|
|
|
|
|
int h = normalSize.height();
|
|
|
|
|
int x = screenGeo.x() + (screenGeo.width() - w) / 2;
|
|
|
|
|
int y = screenGeo.y() + (screenGeo.height() - h) / 2;
|
2023-03-02 18:21:43 +08:00
|
|
|
|
if (screenGeo.width() < w) {
|
2023-02-26 23:47:07 +08:00
|
|
|
|
x = screenGeo.x();
|
|
|
|
|
w = screenGeo.width();
|
|
|
|
|
}
|
2023-03-02 18:21:43 +08:00
|
|
|
|
if (screenGeo.height() < h) {
|
2023-02-26 23:47:07 +08:00
|
|
|
|
y = screenGeo.y();
|
|
|
|
|
h = screenGeo.height();
|
|
|
|
|
}
|
|
|
|
|
return { x, y, w, h };
|
|
|
|
|
}
|
2023-03-09 11:50:40 +08:00
|
|
|
|
|
2023-02-26 23:47:07 +08:00
|
|
|
|
void FramelessView::moveToScreenCenter()
|
|
|
|
|
{
|
|
|
|
|
auto geo = calcCenterGeo(screen()->availableGeometry(), size());
|
2023-03-02 18:21:43 +08:00
|
|
|
|
if (minimumWidth() > geo.width() || minimumHeight() > geo.height()) {
|
2023-02-26 23:47:07 +08:00
|
|
|
|
setMinimumSize(geo.size());
|
|
|
|
|
}
|
|
|
|
|
setGeometry(geo);
|
|
|
|
|
update();
|
|
|
|
|
}
|
2023-03-09 11:50:40 +08:00
|
|
|
|
|
2023-02-26 23:47:07 +08:00
|
|
|
|
void FramelessView::closeDeleteLater(){
|
|
|
|
|
d->m_deleteLater = true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-02 18:21:43 +08:00
|
|
|
|
bool FramelessView::isMax() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_isMax;
|
|
|
|
|
}
|
2023-03-09 11:50:40 +08:00
|
|
|
|
|
2023-03-02 18:21:43 +08:00
|
|
|
|
bool FramelessView::isFull() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_isFull;
|
|
|
|
|
}
|
2023-03-09 11:50:40 +08:00
|
|
|
|
|
2023-03-02 18:21:43 +08:00
|
|
|
|
QQuickItem *FramelessView::titleItem() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_titleItem;
|
|
|
|
|
}
|
2023-03-09 11:50:40 +08:00
|
|
|
|
|
2023-02-26 23:47:07 +08:00
|
|
|
|
void FramelessView::setIsMax(bool isMax)
|
|
|
|
|
{
|
|
|
|
|
if (d->m_isMax == isMax)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
d->m_isMax = isMax;
|
|
|
|
|
emit isMaxChanged(d->m_isMax);
|
|
|
|
|
}
|
2023-03-09 11:50:40 +08:00
|
|
|
|
|
2023-02-26 23:47:07 +08:00
|
|
|
|
void FramelessView::setIsFull(bool isFull)
|
|
|
|
|
{
|
2023-03-02 18:21:43 +08:00
|
|
|
|
if(d->m_isFull == isFull)
|
2023-02-26 23:47:07 +08:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
d->m_isFull = isFull;
|
|
|
|
|
emit isFullChanged(d->m_isFull);
|
|
|
|
|
}
|
2023-03-09 11:50:40 +08:00
|
|
|
|
|
2023-03-02 18:21:43 +08:00
|
|
|
|
void FramelessView::setTitleItem(QQuickItem *item)
|
2023-02-26 23:47:07 +08:00
|
|
|
|
{
|
2023-03-02 18:21:43 +08:00
|
|
|
|
d->m_titleItem = item;
|
2023-02-26 23:47:07 +08:00
|
|
|
|
}
|
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
2023-03-02 18:21:43 +08:00
|
|
|
|
bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
|
2023-02-26 23:47:07 +08:00
|
|
|
|
#else
|
2023-03-02 18:21:43 +08:00
|
|
|
|
bool FramelessView::nativeEvent(const QByteArray &eventType, void *message, long *result)
|
2023-02-26 23:47:07 +08:00
|
|
|
|
#endif
|
|
|
|
|
{
|
2023-03-09 13:23:39 +08:00
|
|
|
|
MSG* msg = static_cast<MSG*>(message);
|
2023-03-28 11:53:25 +08:00
|
|
|
|
if (!msg || !msg->hwnd)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (msg->message == WM_NCHITTEST)
|
|
|
|
|
{
|
|
|
|
|
RECT winrect;
|
|
|
|
|
GetWindowRect(HWND(winId()), &winrect);
|
|
|
|
|
long x = GET_X_LPARAM(msg->lParam);
|
|
|
|
|
long y = GET_Y_LPARAM(msg->lParam);
|
|
|
|
|
*result = 0;
|
|
|
|
|
if (!isMaxWin(this) && !isFullWin(this))
|
|
|
|
|
{
|
|
|
|
|
*result = hitTest(winrect, x, y, 4);
|
|
|
|
|
if (0 != *result)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}else if (msg->message == WM_NCCALCSIZE)
|
2023-03-09 13:23:39 +08:00
|
|
|
|
{
|
2023-03-28 11:53:25 +08:00
|
|
|
|
const auto mode = static_cast<BOOL>(msg->wParam);
|
|
|
|
|
const auto clientRect = mode ? &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam)->rgrc[0]) : reinterpret_cast<LPRECT>(msg->lParam);
|
|
|
|
|
if (mode == TRUE)
|
2023-03-09 13:23:39 +08:00
|
|
|
|
{
|
2023-03-28 11:53:25 +08:00
|
|
|
|
*result = WVR_REDRAW;
|
|
|
|
|
if (!isMaxWin(this) && !isFullWin(this))
|
|
|
|
|
{
|
|
|
|
|
if (clientRect->top != 0)
|
|
|
|
|
{
|
|
|
|
|
clientRect->top -= 0.1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (clientRect->top != 0)
|
|
|
|
|
{
|
|
|
|
|
clientRect->top += 0.1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-09 13:23:39 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-26 23:47:07 +08:00
|
|
|
|
return Super::nativeEvent(eventType, message, result);
|
|
|
|
|
}
|
2023-03-02 18:21:43 +08:00
|
|
|
|
|
|
|
|
|
void FramelessView::resizeEvent(QResizeEvent *e)
|
|
|
|
|
{
|
|
|
|
|
Super::resizeEvent(e);
|
|
|
|
|
}
|
2023-03-09 11:50:40 +08:00
|
|
|
|
|
|
|
|
|
bool FramelessView::event(QEvent *ev)
|
|
|
|
|
{
|
|
|
|
|
if (ev->type() == QEvent::Close) {
|
|
|
|
|
if(d->m_deleteLater){
|
|
|
|
|
deleteLater();
|
|
|
|
|
ev->setAccepted(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return QQuickWindow::event(ev);
|
|
|
|
|
}
|
|
|
|
|
|