I have found out that this problem is mainly related to WinAPI + framelessness, not to Qt. I didn't manage to find any working WinAPI solution, for example, I've tried this one: melak47's solution. So I've chosen Qt way. This approach is not as concise as I expected from the WinAPI approach, but it works.
Here is a code snippet describing only the necessary parts.
.hpp
class FramelessWindow : public QQuickWindow
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(bool isMaximized READ isMaximized NOTIFY isMaximizedChanged)
signals:
void isMaximizedChanged();
public:
FramelessWindow() noexcept;
Q_INVOKABLE void showNormal() noexcept;
Q_INVOKABLE void showMaximized() noexcept;
bool isMaximized() const noexcept;
private:
bool eventFilter(QObject* watched, QEvent* event) override;
bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override;
QRect restoredGeometry_;
bool isMaximized_;
};
.cpp
FramelessWindow::FramelessWindow() noexcept :
isMaximized_ { false }
{
setFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMaximizeButtonHint);
installEventFilter(this);
SetWindowLongPtr((HWND)winId(), GWL_STYLE, WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
}
void FramelessWindow::showNormal() noexcept
{
setGeometry(restoredGeometry_);
isMaximized_ = false;
emit isMaximizedChanged();
}
void FramelessWindow::showMaximized() noexcept
{
restoredGeometry_ = geometry();
setGeometry(screen()->availableGeometry());
isMaximized_ = true;
emit isMaximizedChanged();
}
bool FramelessWindow::isMaximized() const noexcept
{
return isMaximized_;
}
bool FramelessWindow::eventFilter(QObject* watched, QEvent* event)
{
QPoint cursorPos = QCursor::pos();
qreal dpr = devicePixelRatio();
QRect draggingArea = geometry();
draggingArea.setHeight(32 * dpr);
draggingArea.setY(draggingArea.y() + dpr * ResizeBorderWidth);
if (draggingArea.contains(cursorPos))
{
if (event->type() == QEvent::MouseButtonPress)
{
if (isMaximized_)
{
restoredGeometry_.moveTo({ QCursor::pos().x() - restoredGeometry_.width() / 2, QCursor::pos().y() - 10 });
showNormal();
}
startSystemMove();
return true;
}
else if (isResizable_ && event->type() == QEvent::MouseButtonDblClick)
{
if (draggingArea.contains(cursorPos))
{
if (isMaximized_)
{
showNormal();
}
else
{
showMaximized();
}
return true;
}
}
else if (event->type() == QEvent::WindowStateChange && QWindow::visibility() == QWindow::Maximized)
{
setGeometry(screen()->availableGeometry());
isMaximized_ = true;
emit isMaximizedChanged();
return true;
}
}
return QQuickWindow::eventFilter(watched, event);
}
bool FramelessWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
{
if (auto* msg = static_cast<MSG*>(message); msg->message == WM_NCCALCSIZE)
{
NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam);
if (params.rgrc[0].top != 0)
{
--params.rgrc[0].top;
}
*result = 0;
return true;
}
return QQuickWindow::nativeEvent(eventType, message, result);
}