Add a Qt Quick helper class.

It's much easier to use it in Qt Quick now.

Fixes: #11

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2020-05-12 20:53:53 +08:00
parent 9a70ebaaa1
commit 2ca6a90ae2
8 changed files with 567 additions and 26 deletions

View File

@ -332,8 +332,9 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) {
#ifdef QT_QUICK_LIB #ifdef QT_QUICK_LIB
const auto quickItem = qobject_cast<QQuickItem *>(obj); const auto quickItem = qobject_cast<QQuickItem *>(obj);
if (quickItem) { if (quickItem) {
if (QRect(quickItem->x(), quickItem->y(), const auto pos = quickItem->mapToScene({0, 0});
quickItem->width(), quickItem->height()) if (QRect(pos.x(), pos.y(), quickItem->width(),
quickItem->height())
.contains(x, y)) { .contains(x, y)) {
return true; return true;
} }

View File

@ -3,7 +3,11 @@ debug: TARGET = $$join(TARGET,,,_debug)
TEMPLATE = app TEMPLATE = app
QT += gui-private QT += gui-private
qtHaveModule(widgets): QT += widgets qtHaveModule(widgets): QT += widgets
qtHaveModule(quick): QT += quick qtHaveModule(quick) {
QT += quick
HEADERS += framelessquickhelper.h
SOURCES += framelessquickhelper.cpp
}
CONFIG += c++17 strict_c++ warn_on utf8_source CONFIG += c++17 strict_c++ warn_on utf8_source
DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
VERSION = 1.0.0 VERSION = 1.0.0

View File

@ -3,7 +3,11 @@ debug: TARGET = $$join(TARGET,,,d)
TEMPLATE = app TEMPLATE = app
QT += gui-private QT += gui-private
qtHaveModule(widgets): QT += widgets qtHaveModule(widgets): QT += widgets
qtHaveModule(quick): QT += quick qtHaveModule(quick) {
QT += quick
HEADERS += framelessquickhelper.h
SOURCES += framelessquickhelper.cpp
}
CONFIG += c++17 strict_c++ utf8_source warn_on windeployqt CONFIG += c++17 strict_c++ utf8_source warn_on windeployqt
DEFINES += WIN32_LEAN_AND_MEAN QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII DEFINES += WIN32_LEAN_AND_MEAN QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
CONFIG -= embed_manifest_exe CONFIG -= embed_manifest_exe

442
framelessquickhelper.cpp Normal file
View File

@ -0,0 +1,442 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "framelessquickhelper.h"
#ifdef Q_OS_WINDOWS
#include "winnativeeventfilter.h"
#else
#include "framelesshelper.h"
#endif
#include <QQuickWindow>
#ifdef Q_OS_WINDOWS
namespace {
const int m_defaultBorderWidth = 8, m_defaultBorderHeight = 8,
m_defaultTitleBarHeight = 30;
}
#endif
FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent)
: QQuickItem(parent) {}
int FramelessQuickHelper::borderWidth() const {
#ifdef Q_OS_WINDOWS
const auto win = window();
if (win) {
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
return WinNativeEventFilter::getSystemMetric(
hWnd, WinNativeEventFilter::SystemMetric::BorderWidth, false);
}
}
return m_defaultBorderWidth;
#else
return m_framelessHelper.getBorderWidth();
#endif
}
void FramelessQuickHelper::setBorderWidth(const int val) {
#ifdef Q_OS_WINDOWS
const auto win = window();
if (win) {
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->borderWidth = val;
Q_EMIT borderWidthChanged(val);
}
}
}
#else
m_framelessHelper.setBorderWidth(val);
Q_EMIT borderWidthChanged(val);
#endif
}
int FramelessQuickHelper::borderHeight() const {
#ifdef Q_OS_WINDOWS
const auto win = window();
if (win) {
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
return WinNativeEventFilter::getSystemMetric(
hWnd, WinNativeEventFilter::SystemMetric::BorderHeight, false);
}
}
return m_defaultBorderHeight;
#else
return m_framelessHelper.getBorderHeight();
#endif
}
void FramelessQuickHelper::setBorderHeight(const int val) {
#ifdef Q_OS_WINDOWS
const auto win = window();
if (win) {
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->borderHeight = val;
Q_EMIT borderHeightChanged(val);
}
}
}
#else
m_framelessHelper.setBorderHeight(val);
Q_EMIT borderHeightChanged(val);
#endif
}
int FramelessQuickHelper::titleBarHeight() const {
#ifdef Q_OS_WINDOWS
const auto win = window();
if (win) {
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
return WinNativeEventFilter::getSystemMetric(
hWnd, WinNativeEventFilter::SystemMetric::TitleBarHeight,
false);
}
}
return m_defaultTitleBarHeight;
#else
return m_framelessHelper.getTitleBarHeight();
#endif
}
void FramelessQuickHelper::setTitleBarHeight(const int val) {
#ifdef Q_OS_WINDOWS
const auto win = window();
if (win) {
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->titleBarHeight = val;
Q_EMIT titleBarHeightChanged(val);
}
}
}
#else
m_framelessHelper.setTitleBarHeight(val);
Q_EMIT titleBarHeightChanged(val);
#endif
}
bool FramelessQuickHelper::resizable() const {
#ifdef Q_OS_WINDOWS
const auto win = window();
if (win) {
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
return !data->fixedSize;
}
}
}
return true;
#else
// ### TODO: impl
return true;
#endif
}
void FramelessQuickHelper::setResizable(const bool val) {
#ifdef Q_OS_WINDOWS
const auto win = window();
if (win) {
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->fixedSize = !val;
Q_EMIT resizableChanged(val);
}
}
}
#else
// ### TODO: impl
Q_UNUSED(val)
#endif
}
void FramelessQuickHelper::removeWindowFrame(const bool center) {
const auto win = window();
if (win) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
WinNativeEventFilter::addFramelessWindow(hWnd, nullptr, center);
}
#else
m_framelessHelper.removeWindowFrame(win);
if (center) {
FramelessHelper::moveWindowToDesktopCenter(win);
}
#endif
}
}
void FramelessQuickHelper::setIgnoreAreas(const QVector<QRect> &val) {
const auto win = window();
if (win) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->ignoreAreas = val;
}
}
#else
m_framelessHelper.setIgnoreAreas(win, val);
#endif
}
}
void FramelessQuickHelper::clearIgnoreAreas() {
const auto win = window();
if (win) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->ignoreAreas.clear();
}
}
#else
m_framelessHelper.setIgnoreAreas(win, {});
#endif
}
}
void FramelessQuickHelper::addIgnoreArea(const QRect &val) {
const auto win = window();
if (win) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->ignoreAreas.append(val);
}
}
#else
QVector<QRect> areas = m_framelessHelper.getIgnoreAreas(win);
areas.append(val);
m_framelessHelper.setIgnoreAreas(win, areas);
#endif
}
}
void FramelessQuickHelper::setDraggableAreas(const QVector<QRect> &val) {
const auto win = window();
if (win) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->draggableAreas = val;
}
}
#else
m_framelessHelper.setDraggableAreas(win, val);
#endif
}
}
void FramelessQuickHelper::clearDraggableAreas() {
const auto win = window();
if (win) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->draggableAreas.clear();
}
}
#else
m_framelessHelper.setDraggableAreas(win, {});
#endif
}
}
void FramelessQuickHelper::addDraggableArea(const QRect &val) {
const auto win = window();
if (win) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->draggableAreas.append(val);
}
}
#else
QVector<QRect> areas = m_framelessHelper.getDraggableAreas(win);
areas.append(val);
m_framelessHelper.setDraggableAreas(win, areas);
#endif
}
}
void FramelessQuickHelper::setIgnoreObjects(const QVector<QQuickItem *> &val) {
const auto win = window();
if (win) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->ignoreObjects.clear();
if (!val.isEmpty()) {
for (auto &&obj : qAsConst(val)) {
data->ignoreObjects.append(obj);
}
}
}
}
#else
QVector<QPointer<QObject>> objs;
if (!val.isEmpty()) {
for (auto &&obj : qAsConst(val)) {
objs.append(obj);
}
m_framelessHelper.setIgnoreObjects(win, objs);
}
#endif
}
}
void FramelessQuickHelper::clearIgnoreObjects() {
const auto win = window();
if (win) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->ignoreObjects.clear();
}
}
#else
m_framelessHelper.setIgnoreObjects(win, {});
#endif
}
}
void FramelessQuickHelper::addIgnoreObject(QQuickItem *val) {
const auto win = window();
if (win && val) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->ignoreObjects.append(val);
}
}
#else
QVector<QPointer<QObject>> objs =
m_framelessHelper.getIgnoreObjects(win);
objs.append(val);
m_framelessHelper.setIgnoreObjects(win, objs);
#endif
}
}
void FramelessQuickHelper::setDraggableObjects(
const QVector<QQuickItem *> &val) {
const auto win = window();
if (win) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->draggableObjects.clear();
if (!val.isEmpty()) {
for (auto &&obj : qAsConst(val)) {
data->draggableObjects.append(obj);
}
}
}
}
#else
QVector<QPointer<QObject>> objs;
if (!val.isEmpty()) {
for (auto &&obj : qAsConst(val)) {
objs.append(obj);
}
m_framelessHelper.setDraggableObjects(win, objs);
}
#endif
}
}
void FramelessQuickHelper::clearDraggableObjects() {
const auto win = window();
if (win) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->draggableObjects.clear();
}
}
#else
m_framelessHelper.setDraggableObjects(win, {});
#endif
}
}
void FramelessQuickHelper::addDraggableObject(QQuickItem *val) {
const auto win = window();
if (win && val) {
#ifdef Q_OS_WINDOWS
const auto hWnd = reinterpret_cast<HWND>(win->winId());
if (hWnd) {
const auto data = WinNativeEventFilter::windowData(hWnd);
if (data) {
data->draggableObjects.append(val);
}
}
#else
QVector<QPointer<QObject>> objs =
m_framelessHelper.getDraggableObjects(win);
objs.append(val);
m_framelessHelper.setDraggableObjects(win, objs);
#endif
}
}

90
framelessquickhelper.h Normal file
View File

@ -0,0 +1,90 @@
/*
* MIT License
*
* Copyright (C) 2020 by wangwenx190 (Yuhang Zhao)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <QQuickItem>
#ifndef Q_OS_WINDOWS
#include "framelesshelper.h"
#endif
class FramelessQuickHelper : public QQuickItem {
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessQuickHelper)
Q_PROPERTY(int borderWidth READ borderWidth WRITE setBorderWidth NOTIFY
borderWidthChanged)
Q_PROPERTY(int borderHeight READ borderHeight WRITE setBorderHeight NOTIFY
borderHeightChanged)
Q_PROPERTY(int titleBarHeight READ titleBarHeight WRITE setTitleBarHeight
NOTIFY titleBarHeightChanged)
Q_PROPERTY(bool resizable READ resizable WRITE setResizable NOTIFY
resizableChanged)
public:
explicit FramelessQuickHelper(QQuickItem *parent = nullptr);
~FramelessQuickHelper() override = default;
int borderWidth() const;
void setBorderWidth(const int val);
int borderHeight() const;
void setBorderHeight(const int val);
int titleBarHeight() const;
void setTitleBarHeight(const int val);
bool resizable() const;
void setResizable(const bool val);
public Q_SLOTS:
void removeWindowFrame(const bool center = true);
void setIgnoreAreas(const QVector<QRect> &val);
void clearIgnoreAreas();
void addIgnoreArea(const QRect &val);
void setDraggableAreas(const QVector<QRect> &val);
void clearDraggableAreas();
void addDraggableArea(const QRect &val);
void setIgnoreObjects(const QVector<QQuickItem *> &val);
void clearIgnoreObjects();
void addIgnoreObject(QQuickItem *val);
void setDraggableObjects(const QVector<QQuickItem *> &val);
void clearDraggableObjects();
void addDraggableObject(QQuickItem *val);
Q_SIGNALS:
void borderWidthChanged(int);
void borderHeightChanged(int);
void titleBarHeightChanged(int);
void resizableChanged(bool);
private:
#ifndef Q_OS_WINDOWS
FramelessHelper m_framelessHelper;
#endif
};

View File

@ -4,8 +4,8 @@
#include <QLabel> #include <QLabel>
#include <QPushButton> #include <QPushButton>
#ifdef QT_QUICK_LIB #ifdef QT_QUICK_LIB
#include "framelessquickhelper.h"
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QWindow>
#endif #endif
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QWidget> #include <QWidget>
@ -97,6 +97,8 @@ int main(int argc, char *argv[]) {
#ifdef QT_QUICK_LIB #ifdef QT_QUICK_LIB
// Qt Quick example: // Qt Quick example:
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
qmlRegisterType<FramelessQuickHelper>("wangwenx190.Utils", 1, 0,
"FramelessHelper");
const QUrl url(QString::fromUtf8("qrc:///qml/main.qml")); const QUrl url(QString::fromUtf8("qrc:///qml/main.qml"));
QObject::connect( QObject::connect(
&engine, &QQmlApplicationEngine::objectCreated, &application, &engine, &QQmlApplicationEngine::objectCreated, &application,
@ -107,25 +109,6 @@ int main(int argc, char *argv[]) {
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
engine.load(url); engine.load(url);
const auto window = qobject_cast<QWindow *>(engine.rootObjects().at(0));
Q_ASSERT(window);
const auto hWnd_qml = reinterpret_cast<HWND>(window->winId());
WinNativeEventFilter::addFramelessWindow(hWnd_qml, nullptr, true);
QObject::connect(
window, &QWindow::widthChanged, [window, hWnd_qml](const int arg) {
if (arg >= window->minimumWidth()) {
const auto data = WinNativeEventFilter::windowData(hWnd_qml);
if (data) {
const int tbh_qml = WinNativeEventFilter::getSystemMetric(
hWnd_qml,
WinNativeEventFilter::SystemMetric::TitleBarHeight,
false);
data->draggableAreas = {
{0, 0, (window->width() - (m_defaultButtonWidth * 3)),
tbh_qml}};
}
}
});
#endif #endif
return QApplication::exec(); return QApplication::exec();

View File

@ -1,5 +1,6 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Window 2.15 import QtQuick.Window 2.15
import wangwenx190.Utils 1.0
Window { Window {
id: root id: root
@ -8,6 +9,11 @@ Window {
height: 600 height: 600
title: qsTr("Hello, World!") title: qsTr("Hello, World!")
FramelessHelper {
id: framelessHelper
Component.onCompleted: framelessHelper.removeWindowFrame()
}
Rectangle { Rectangle {
id: titleBar id: titleBar
height: 30 height: 30
@ -32,10 +38,15 @@ Window {
anchors.right: parent.right anchors.right: parent.right
MinimizeButton { MinimizeButton {
id: minimizeButton
onClicked: root.showMinimized() onClicked: root.showMinimized()
Component.onCompleted: framelessHelper.addIgnoreObject(
minimizeButton)
} }
MaximizeButton { MaximizeButton {
id: maximizeButton
// QWindow::Visibility::Maximized
maximized: root.visibility === 4 maximized: root.visibility === 4
onClicked: { onClicked: {
if (maximized) { if (maximized) {
@ -44,10 +55,15 @@ Window {
root.showMaximized() root.showMaximized()
} }
} }
Component.onCompleted: framelessHelper.addIgnoreObject(
maximizeButton)
} }
CloseButton { CloseButton {
id: closeButton
onClicked: root.close() onClicked: root.close()
Component.onCompleted: framelessHelper.addIgnoreObject(
closeButton)
} }
} }
} }

View File

@ -1324,8 +1324,9 @@ bool WinNativeEventFilter::nativeEventFilter(const QByteArray &eventType,
const auto quickItem = const auto quickItem =
qobject_cast<QQuickItem *>(object); qobject_cast<QQuickItem *>(object);
if (quickItem) { if (quickItem) {
if (QRect(qRound(quickItem->x() * dpr), const auto pos = quickItem->mapToScene({0, 0});
qRound(quickItem->y() * dpr), if (QRect(qRound(pos.x() * dpr),
qRound(pos.y() * dpr),
qRound(quickItem->width() * dpr), qRound(quickItem->width() * dpr),
qRound(quickItem->height() * dpr)) qRound(quickItem->height() * dpr))
.contains(x, y)) { .contains(x, y)) {