mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
225 lines
7.2 KiB
C++
225 lines
7.2 KiB
C++
#include "framelessmainwindowwin.h"
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
#include <QTimer>
|
|
#include <QDebug>
|
|
#include <QEvent>
|
|
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <dwmapi.h>
|
|
#pragma comment (lib,"dwmapi.lib")
|
|
#pragma comment (lib, "user32.lib")
|
|
|
|
using namespace vnotex;
|
|
|
|
FramelessMainWindowWin::FramelessMainWindowWin(bool p_frameless, QWidget *p_parent)
|
|
: FramelessMainWindow(p_frameless, p_parent)
|
|
{
|
|
if (m_frameless) {
|
|
m_resizeAreaWidth *= devicePixelRatio();
|
|
|
|
m_redrawTimer = new QTimer(this);
|
|
m_redrawTimer->setSingleShot(true);
|
|
m_redrawTimer->setInterval(500);
|
|
connect(m_redrawTimer, &QTimer::timeout,
|
|
this, &FramelessMainWindowWin::forceRedraw);
|
|
|
|
connect(this, &FramelessMainWindow::windowStateChanged,
|
|
this, &FramelessMainWindowWin::updateMargins);
|
|
|
|
// Enable some window effects on Win, such as snap and maximizing.
|
|
// It will activate the title bar again. Need to remove it in WM_NCCALCSIZE msg.
|
|
HWND hwnd = reinterpret_cast<HWND>(winId());
|
|
DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
|
|
::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION);
|
|
|
|
// Leave 1 pixel width of border so OS will draw a window shadow.
|
|
const MARGINS shadow = {1, 1, 1, 1};
|
|
DwmExtendFrameIntoClientArea(hwnd, &shadow);
|
|
}
|
|
}
|
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
|
|
bool FramelessMainWindowWin::nativeEvent(const QByteArray &p_eventType, void *p_message, qintptr *p_result)
|
|
#else
|
|
bool FramelessMainWindowWin::nativeEvent(const QByteArray &p_eventType, void *p_message, long *p_result)
|
|
#endif
|
|
{
|
|
if (!m_frameless) {
|
|
return FramelessMainWindow::nativeEvent(p_eventType, p_message, p_result);
|
|
}
|
|
|
|
if (p_eventType == QStringLiteral("windows_generic_MSG")) {
|
|
MSG *msg = static_cast<MSG *>(p_message);
|
|
|
|
switch (msg->message) {
|
|
case WM_NCCALCSIZE:
|
|
*p_result = 0;
|
|
return true;
|
|
|
|
case WM_NCHITTEST:
|
|
{
|
|
if (m_windowStates & Qt::WindowFullScreen) {
|
|
*p_result = HTCLIENT;
|
|
return true;
|
|
}
|
|
|
|
RECT windowRect;
|
|
::GetWindowRect(msg->hwnd, &windowRect);
|
|
|
|
// x and y could not be compared with width() and height() in hidpi case.
|
|
const int x = static_cast<int>(GET_X_LPARAM(msg->lParam) - windowRect.left);
|
|
const int y = static_cast<int>(GET_Y_LPARAM(msg->lParam) - windowRect.top);
|
|
|
|
*p_result = 0;
|
|
if (m_resizable) {
|
|
if (x < m_resizeAreaWidth) {
|
|
// Left.
|
|
if (y < m_resizeAreaWidth) {
|
|
// Top.
|
|
*p_result = HTTOPLEFT;
|
|
} else if (y > windowRect.bottom - windowRect.top - m_resizeAreaWidth) {
|
|
// Bottom.
|
|
*p_result = HTBOTTOMLEFT;
|
|
} else {
|
|
*p_result = HTLEFT;
|
|
}
|
|
} else if (x > windowRect.right - windowRect.left - m_resizeAreaWidth) {
|
|
// Right.
|
|
if (y < m_resizeAreaWidth) {
|
|
// Top.
|
|
*p_result = HTTOPRIGHT;
|
|
} else if (y > windowRect.bottom - windowRect.top - m_resizeAreaWidth) {
|
|
// Bottom.
|
|
*p_result = HTBOTTOMRIGHT;
|
|
} else {
|
|
*p_result = HTRIGHT;
|
|
}
|
|
} else if (y < m_resizeAreaWidth) {
|
|
*p_result = HTTOP;
|
|
} else if (y > windowRect.bottom - windowRect.top - m_resizeAreaWidth) {
|
|
*p_result = HTBOTTOM;
|
|
}
|
|
}
|
|
|
|
if (0 != *p_result) {
|
|
return true;
|
|
}
|
|
|
|
if (m_titleBar) {
|
|
if (m_titleBarHeight == 0) {
|
|
m_titleBarHeight = m_titleBar->height() * devicePixelRatio();
|
|
}
|
|
|
|
if (y < m_titleBarHeight) {
|
|
QWidget *child = m_titleBar->childAt(m_titleBar->mapFromGlobal(QCursor::pos()));
|
|
if (!child) {
|
|
*p_result = HTCAPTION;
|
|
if (::GetAsyncKeyState(VK_LBUTTON) < 0 || ::GetAsyncKeyState(VK_RBUTTON) < 0) {
|
|
m_sizeBeforeMove = size();
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WM_POWERBROADCAST:
|
|
{
|
|
if (msg->wParam == PBT_APMSUSPEND) {
|
|
// Minimize when system is going to sleep to avoid bugs.
|
|
showMinimized();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_GETMINMAXINFO:
|
|
{
|
|
// When maximized, OS will expand the content area. To avoid missing the real contents, set extra margins.
|
|
if (::IsZoomed(msg->hwnd)) {
|
|
RECT frame = {0, 0, 0, 0};
|
|
::AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, false, 0);
|
|
const int dpiScale = devicePixelRatio();
|
|
m_maximizedMargins.setLeft(qAbs(frame.left) / dpiScale);
|
|
// Use bottom as top.
|
|
m_maximizedMargins.setTop(qAbs(frame.bottom) / dpiScale);
|
|
m_maximizedMargins.setRight(frame.right / dpiScale);
|
|
m_maximizedMargins.setBottom(frame.bottom / dpiScale);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
if (msg->wParam == PBT_APMRESUMESUSPEND) {
|
|
// Show after resuming from sleep.
|
|
showNormal();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FramelessMainWindow::nativeEvent(p_eventType, p_message, p_result);
|
|
}
|
|
|
|
void FramelessMainWindowWin::moveEvent(QMoveEvent *p_event)
|
|
{
|
|
FramelessMainWindow::moveEvent(p_event);
|
|
|
|
if (m_frameless) {
|
|
if (m_windowStates & Qt::WindowMaximized) {
|
|
m_redrawTimer->stop();
|
|
} else {
|
|
m_redrawTimer->start();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FramelessMainWindowWin::updateMargins()
|
|
{
|
|
if (!m_frameless) {
|
|
return;
|
|
}
|
|
|
|
int topMargin = 0;
|
|
if (isMaximized()) {
|
|
setContentsMargins(m_maximizedMargins);
|
|
topMargin = m_maximizedMargins.top();
|
|
} else {
|
|
setContentsMargins(0, 0, 0, 0);
|
|
}
|
|
|
|
if (m_titleBar) {
|
|
m_titleBarHeight = (m_titleBar->height() + topMargin) * devicePixelRatio();
|
|
}
|
|
}
|
|
|
|
void FramelessMainWindowWin::forceRedraw()
|
|
{
|
|
Q_ASSERT(m_frameless);
|
|
if (m_windowStates & Qt::WindowMaximized) {
|
|
return;
|
|
}
|
|
|
|
const QSize sz = size();
|
|
RECT frame;
|
|
::GetWindowRect((HWND)winId(), &frame);
|
|
const int clientWidth = (frame.right - frame.left) / devicePixelRatio();
|
|
const int clientHeight = (frame.bottom - frame.top) / devicePixelRatio();
|
|
if (clientWidth != sz.width() || clientHeight != sz.height()) {
|
|
// resize() may result to "unable to set geometry" warning.
|
|
// adjustsize() or resize() to another size before could solve this.
|
|
resize(sz.width() + 1, sz.height() + 1);
|
|
if (m_sizeBeforeMove.isEmpty()) {
|
|
resize(clientWidth, clientHeight);
|
|
} else {
|
|
resize(m_sizeBeforeMove);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|