When you customized the title bar on Windows 10, did anyone succeed in working with Aero Snap?
The basic title bar is too ugly.
I haven't solved this problem for a week. Please let me know if there is a solution, whether it is Widget or Qt Quick.
I solved the problem. I apologize if my question was rude. I am not good at English.
At first, I was going to implement Frameless Windows with QWidget and embed QQuickWidget on top of it, but I found a better way.
framelesshelper.h
#ifndef FRAMELESSHELPER_H
#define FRAMELESSHELPER_H
#include <QObject>
#include <QQmlParserStatus>
#include <QQuickItem>
#include <QAbstractNativeEventFilter>
// QML : import FramelessHelper 1.0
class QQuickWindow;
class FramelessHelper : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QQuickItem* titlebar READ titlebar WRITE setTitlebar NOTIFY titlebarChanged)
Q_PROPERTY(qreal borderWidth READ borderWidth WRITE setBorderWidth NOTIFY borderWidthChanged)
public:
explicit FramelessHelper(QObject *parent = nullptr);
~FramelessHelper();
static void QmlInitialize();
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);
virtual void classBegin();
virtual void componentComplete();
QQuickItem *titlebar() const;
void setTitlebar(QQuickItem *titlebar);
qreal borderWidth() const;
void setBorderWidth(qreal borderWidth);
signals:
void borderWidthChanged();
void titlebarChanged();
private:
bool containAcceptMouseButtons(QQuickItem *, qreal x, qreal y);
QQuickItem *mTitlebar = nullptr;
QQuickWindow *mQuickWindow = nullptr;
qreal mBorderWidth = 7;
class NativeEventFilter : public QAbstractNativeEventFilter
{
public:
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);
static void deliver(QQuickWindow* window, FramelessHelper *helper);
static NativeEventFilter *instance;
static QHash<WId, FramelessHelper*> helpers;
};
};
#endif // FRAMELESSHELPER_H
framelesshelper.cpp
#include "framelesshelper.h"
#include <QQuickWindow>
using namespace std;
FramelessHelper::FramelessHelper(QObject *parent) : QObject(parent)
{
}
FramelessHelper::~FramelessHelper()
{
NativeEventFilter::helpers.remove(mQuickWindow->winId());
}
void FramelessHelper::QmlInitialize()
{
qmlRegisterType<FramelessHelper>("FramelessHelper", 1,0, "FramelessHelper");
}
QQuickItem *FramelessHelper::titlebar() const
{
return mTitlebar;
}
void FramelessHelper::setTitlebar(QQuickItem *titlebar)
{
if (mTitlebar != titlebar) {
mTitlebar = titlebar;
emit titlebarChanged();
}
}
qreal FramelessHelper::borderWidth() const
{
return mBorderWidth;
}
void FramelessHelper::setBorderWidth(qreal borderWidth)
{
if (mBorderWidth != borderWidth) {
mBorderWidth = borderWidth;
emit borderWidthChanged();
}
}
bool FramelessHelper::containAcceptMouseButtons(QQuickItem *item, qreal x, qreal y)
{
if (item->acceptedMouseButtons() != Qt::NoButton)
return true;
QPointF pos = item->mapToGlobal(QPoint(0, 0));
int rx = x - pos.x();
int ry = y - pos.y();
QQuickItem *child = item->childAt(rx, ry);
return child ? containAcceptMouseButtons(child, x, y) : false;
}
#ifdef Q_OS_WIN
#include <QPoint>
#include <QScreen>
#include <QSize>
#include <windowsx.h>
#include <QCoreApplication>
//#include <dwmapi.h>
//#include <objidl.h>
//#include <gdiplus.h>
//#include <GdiPlusColor.h>
//#include <windows.h>
//#include <WinUser.h>
//#pragma comment (lib,"Dwmapi.lib")
//#pragma comment (lib,"user32.lib")
bool FramelessHelper::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
Q_UNUSED(eventType)
#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
MSG* msg = *reinterpret_cast<MSG**>(message);
#else
MSG* msg = reinterpret_cast<MSG*>(message);
#endif
switch (msg->message) {
case WM_NCHITTEST: {
*result = 0;
const LONG border_width = mBorderWidth;
RECT winrect;
GetWindowRect(HWND(mQuickWindow->winId()), &winrect);
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);
if (mQuickWindow->visibility() == QWindow::Windowed) {
bool resizeWidth = mQuickWindow->minimumWidth() != mQuickWindow->maximumWidth();
bool resizeHeight = mQuickWindow->minimumHeight() != mQuickWindow->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;
}
if (!mTitlebar) return false;
QPointF tbPos = mTitlebar->mapToGlobal(QPoint(0, 0));
if (mQuickWindow->visibility() != QWindow::FullScreen &&
tbPos.y() <= y && tbPos.y() + mTitlebar->height() >= y &&
tbPos.x() <= x && tbPos.x() + mTitlebar->width() >= x &&
!containAcceptMouseButtons(mTitlebar, x, y))
{
*result = HTCAPTION;
return true;
}
break;
}
case WM_NCCALCSIZE:
{
NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam);
if (params.rgrc[0].top != 0)
params.rgrc[0].top -= 1;
*result = WVR_REDRAW;
return true;
}}
return false;
}
void FramelessHelper::classBegin()
{
}
void FramelessHelper::componentComplete()
{
auto obj = parent();
while (obj != nullptr) {
if (obj->inherits("QQuickRootItem")) {
auto rootItem = qobject_cast<QQuickItem *>(obj);
mQuickWindow = qobject_cast<QQuickWindow *>(rootItem->window());
if (mQuickWindow) {
NativeEventFilter::deliver(mQuickWindow, this);
// 이동한 모니터의 DPI가 다를경우 화면을 다시 그린다.
connect(mQuickWindow, &QWindow::screenChanged, this, [this](QScreen *){
SetWindowPos((HWND) mQuickWindow->winId(), NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
});
break;
}
}
obj = obj->parent();
}
}
void FramelessHelper::NativeEventFilter::deliver(QQuickWindow *window, FramelessHelper *helper)
{
if (instance == nullptr) {
instance = new NativeEventFilter;
if (instance) qApp->installNativeEventFilter(instance);
}
if (window && helper) {
auto wid = window->winId();
if (!helpers.contains(wid)) {
auto hwnd = reinterpret_cast<HWND>(wid);
// set new window style
DWORD oldStyle = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, oldStyle | WS_CAPTION
| WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME);
helpers.insert(wid, helper);
}
}
if (window && helper == nullptr) {
helpers.remove(window->winId());
}
}
bool FramelessHelper::NativeEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
Q_UNUSED(eventType)
auto lpMsg = (LPMSG)message;
auto wid = reinterpret_cast<WId>(lpMsg->hwnd);
if (auto helper = helpers.value(wid)) {
return helper->nativeEventFilter(eventType, message, result);
}
return false;
}
FramelessHelper::NativeEventFilter *FramelessHelper::NativeEventFilter::instance = nullptr;
QHash<WId, FramelessHelper*> FramelessHelper::NativeEventFilter::helpers;
#else
void FramelessHelper::classBegin()
{
}
void FramelessHelper::componentComplete()
{
}
void FramelessHelper::NativeEventFilter::deliver(QQuickWindow *window, FramelessHelper *helper)
{
Q_UNUSED(window)
Q_UNUSED(helper)
}
bool FramelessHelper::NativeEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
Q_UNUSED(eventType)
Q_UNUSED(message)
Q_UNUSED(result)
return false;
}
bool FramelessHelper::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
Q_UNUSED(eventType)
Q_UNUSED(message)
Q_UNUSED(result)
return false;
}
#endif
import FramelessHelper 1.0 import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15
ApplicationWindow {
id: window
readonly property real borderWidth: 7
visible: true
width: 1024
height: 800
minimumWidth: 800
minimumHeight: 600
flags: Qt.Window | Qt.FramelessWindowHint
FramelessHelper {
titlebar: menuBar
borderWidth: window.borderWidth
}
menuBar: Titlebar { }
}
Use QWindow::startSystemMove()
and Window::startSystemResize(Qt::Edges edges)
instead of manually changing the position and dimension of your window.
In your title bar call this when the mouse is pressed:
onPressed: Window.window.startSystemMove() // or windowId.startSystemMove()