Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-03-13 12:14:08 +08:00
parent d0e19a2b1e
commit 556741cfb1
18 changed files with 355 additions and 284 deletions

View File

@ -2,11 +2,8 @@ cmake_minimum_required(VERSION 3.20)
project(FramelessHelper LANGUAGES CXX)
option(BUILD_EXAMPLES "Build examples." ON)
if(NOT DEFINED BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS ON)
endif()
option(FRAMELESSHELPER_BUILD_STATIC "Build FramelessHelper as a static library." OFF)
option(FRAMELESSHELPER_BUILD_EXAMPLES "Build FramelessHelper demo applications." ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@ -20,8 +17,8 @@ set(CMAKE_AUTORCC ON)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui REQUIRED)
#find_package(QT NAMES Qt6 Qt5 COMPONENTS Quick)
#find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Quick)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Quick)
set(SOURCES
framelesshelper_global.h
@ -34,12 +31,12 @@ set(SOURCES
utilities.cpp
)
#[[if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
list(APPEND SOURCES
framelessquickhelper.h
framelessquickhelper.cpp
)
endif()]]
endif()
if(WIN32)
list(APPEND SOURCES
@ -56,15 +53,18 @@ elseif(UNIX)
list(APPEND SOURCES utilities_linux.cpp)
endif()
if(WIN32 AND BUILD_SHARED_LIBS)
if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC)
enable_language(RC)
list(APPEND SOURCES framelesshelper.rc)
endif()
add_library(${PROJECT_NAME} ${SOURCES})
add_library(wangwenx190::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
if(FRAMELESSHELPER_BUILD_STATIC)
add_library(${PROJECT_NAME} STATIC ${SOURCES})
else()
add_library(${PROJECT_NAME} SHARED ${SOURCES})
endif()
if(NOT BUILD_SHARED_LIBS)
if(FRAMELESSHELPER_BUILD_STATIC)
target_compile_definitions(${PROJECT_NAME} PUBLIC
FRAMELESSHELPER_STATIC
)
@ -85,16 +85,16 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::GuiPrivate
)
#[[if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::Quick
)
endif()]]
endif()
target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
)
if(BUILD_EXAMPLES)
if(FRAMELESSHELPER_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()

View File

@ -7,5 +7,5 @@ if(TARGET Qt${QT_VERSION_MAJOR}::Widgets)
endif()
if(TARGET Qt${QT_VERSION_MAJOR}::Quick)
#add_subdirectory(quick)
add_subdirectory(quick)
endif()

View File

@ -25,7 +25,7 @@ add_executable(MainWindow WIN32 ${SOURCES})
target_link_libraries(MainWindow PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
wangwenx190::FramelessHelper
FramelessHelper
)
target_compile_definitions(MainWindow PRIVATE

View File

@ -23,7 +23,7 @@ add_executable(Quick WIN32 ${SOURCES})
target_link_libraries(Quick PRIVATE
Qt${QT_VERSION_MAJOR}::Quick
Qt${QT_VERSION_MAJOR}::QuickControls2
wangwenx190::FramelessHelper
FramelessHelper
)
target_compile_definitions(Quick PRIVATE

View File

@ -22,77 +22,20 @@
* SOFTWARE.
*/
#include "../../utilities.h"
#include "../../framelessquickhelper.h"
#include <QtGui/qguiapplication.h>
#include <QtQml/qqmlapplicationengine.h>
#include <QtQuickControls2/qquickstyle.h>
#include <framelessquickhelper.h>
FRAMELESSHELPER_USE_NAMESPACE
static constexpr const char qtquicknamespace[] = "wangwenx190.Utils";
class UtilFunctions : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(UtilFunctions)
Q_PROPERTY(bool isWindowsHost READ isWindowsHost CONSTANT)
Q_PROPERTY(bool isWindows10OrGreater READ isWindows10OrGreater CONSTANT)
Q_PROPERTY(bool isWindows11OrGreater READ isWindows11OrGreater CONSTANT)
Q_PROPERTY(QColor activeFrameBorderColor READ activeFrameBorderColor CONSTANT)
Q_PROPERTY(QColor inactiveFrameBorderColor READ inactiveFrameBorderColor CONSTANT)
Q_PROPERTY(qreal frameBorderThickness READ frameBorderThickness CONSTANT)
public:
explicit UtilFunctions(QObject *parent = nullptr) : QObject(parent) {}
~UtilFunctions() override = default;
inline bool isWindowsHost() const {
#ifdef Q_OS_WINDOWS
return true;
#else
return false;
#endif
}
inline bool isWindows10OrGreater() const {
#ifdef Q_OS_WINDOWS
return Utilities::isWin10OrGreater();
#else
return false;
#endif
}
inline bool isWindows11OrGreater() const {
#ifdef Q_OS_WINDOWS
return Utilities::isWin11OrGreater();
#else
return false;
#endif
}
inline QColor activeFrameBorderColor() const {
const ColorizationArea area = Utilities::getColorizationArea();
const bool colorizedBorder = ((area == ColorizationArea::TitleBar_WindowBorder)
|| (area == ColorizationArea::All));
return (colorizedBorder ? Utilities::getColorizationColor() : Qt::black);
}
inline QColor inactiveFrameBorderColor() const {
return Qt::darkGray;
}
inline qreal frameBorderThickness() const {
return 1.0;
}
};
static constexpr const char FRAMELESSHELPER_QUICK_URI[] = "org.wangwenx190.FramelessHelper";
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
@ -100,6 +43,9 @@ int main(int argc, char *argv[])
QGuiApplication application(argc, argv);
QScopedPointer<FramelessQuickHelper> framelessHelper(new FramelessQuickHelper);
QScopedPointer<FramelessQuickUtils> framelessUtils(new FramelessQuickUtils);
QQmlApplicationEngine engine;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
@ -108,14 +54,10 @@ int main(int argc, char *argv[])
QQuickStyle::setStyle(QStringLiteral("Default"));
#endif
qmlRegisterSingletonType<UtilFunctions>(qtquicknamespace, 1, 0, "Utils", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * {
Q_UNUSED(engine);
Q_UNUSED(scriptEngine);
return new UtilFunctions();
});
qmlRegisterType<FramelessQuickHelper>(qtquicknamespace, 1, 0, "FramelessHelper");
qmlRegisterSingletonInstance(FRAMELESSHELPER_QUICK_URI, 1, 0, "FramelessHelper", framelessHelper.data());
qmlRegisterSingletonInstance(FRAMELESSHELPER_QUICK_URI, 1, 0, "FramelessUtils", framelessUtils.data());
const QUrl mainQmlUrl(QStringLiteral("qrc:///qml/main.qml"));
const QUrl mainQmlUrl(QStringLiteral("qrc:///qml/MainWindow.qml"));
const QMetaObject::Connection connection = QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreated,
@ -124,17 +66,16 @@ int main(int argc, char *argv[])
if (url != mainQmlUrl) {
return;
}
if (!object) {
QGuiApplication::exit(-1);
} else {
if (object) {
QObject::disconnect(connection);
} else {
QCoreApplication::exit(-1);
}
},
Qt::QueuedConnection);
engine.load(mainQmlUrl);
return QGuiApplication::exec();
return QCoreApplication::exec();
}
#include "main.moc"

View File

@ -1,6 +1,6 @@
<RCC>
<qresource prefix="/">
<file>qml/main.qml</file>
<file>qml/MainWindow.qml</file>
<file>qml/MinimizeButton.qml</file>
<file>qml/MaximizeButton.qml</file>
<file>qml/CloseButton.qml</file>

View File

@ -24,25 +24,33 @@
import QtQuick 2.0
import QtQuick.Controls 2.0
import org.wangwenx190.FramelessHelper 1.0
Button {
id: button
implicitWidth: 45
implicitHeight: 30
implicitWidth: implicitHeight * 1.5
ToolTip.visible: hovered && !down
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Close")
ToolTip {
visible: button.hovered && !button.down
delay: Qt.styleHints.mousePressAndHoldInterval
text: qsTr("Close")
}
contentItem: Image {
anchors.fill: parent
source: button.down
|| button.hovered ? "qrc:/images/button_close_white.svg" : "qrc:/images/button_close_black.svg"
contentItem: Item {
implicitWidth: 16
implicitHeight: implicitWidth
Image {
anchors.centerIn: parent
source: FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-close.svg" : "qrc:/images/dark/chrome-close.svg"
}
}
background: Rectangle {
visible: button.down || button.hovered
color: button.down ? "#8c0a15" : (button.hovered ? "#e81123" : "transparent")
visible: button.hovered
color: "red"
opacity: 0.5
}
}

View File

@ -22,10 +22,10 @@
* SOFTWARE.
*/
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
import wangwenx190.Utils 1.0
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import org.wangwenx190.FramelessHelper 1.0
Window {
id: window
@ -33,14 +33,7 @@ Window {
width: 800
height: 600
title: qsTr("Hello, World!")
color: "#f0f0f0"
property real _flh_margin: ((window.visibility === Window.Maximized) || (window.visibility === Window.FullScreen)) ? 0 : (Utils.frameBorderThickness / Screen.devicePixelRatio)
property var _win_prev_state: null
FramelessHelper {
id: framelessHelper
}
color: FramelessUtils.darkModeEnabled ? "#202020" : "#f0f0f0"
Timer {
id: timer
@ -52,35 +45,46 @@ Window {
Rectangle {
id: titleBar
height: framelessHelper.titleBarHeight
color: "white"
height: 30
color: window.active ? (FramelessUtils.titleBarColorVisible ? FramelessUtils.systemAccentColor :
(FramelessUtils.darkModeEnabled ? "white" : "black")) :
(FramelessUtils.darkModeEnabled ? "#202020" : "white")
anchors {
top: parent.top
topMargin: window._flh_margin
topMargin: windowTopBorder.height
left: parent.left
leftMargin: window._flh_margin
right: parent.right
rightMargin: window._flh_margin
}
Text {
id: titleBarText
text: window.title
font.pointSize: 13
color: window.active ? "black" : "gray"
color: window.active ? (FramelessUtils.darkModeEnabled ? "white" : "black") : "darkGray"
anchors.left: parent.left
anchors.leftMargin: 15
anchors.verticalCenter: parent.verticalCenter
}
MouseArea {
anchors.fill: parent
anchors.rightMargin: 30 * 1.5 * 3
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onDoubleClicked: maximizeButton.clicked()
onPositionChanged: {
if (containsPress && (window.visibility !== Window.FullScreen)) {
FramelessUtils.startSystemMove2(window);
}
}
}
Row {
anchors.top: parent.top
anchors.right: parent.right
MinimizeButton {
id: minimizeButton
onClicked: framelessHelper.showMinimized()
Component.onCompleted: framelessHelper.setHitTestVisible(minimizeButton, true)
onClicked: FramelessUtils.showMinimized2(window)
}
MaximizeButton {
@ -88,18 +92,15 @@ Window {
maximized: ((window.visibility === Window.Maximized) || (window.visibility === Window.FullScreen))
onClicked: {
if (maximized) {
window.showNormal()
window.showNormal();
} else {
window.showMaximized()
window.showMaximized();
}
}
Component.onCompleted: framelessHelper.setHitTestVisible(maximizeButton, true)
}
CloseButton {
id: closeButton
onClicked: window.close()
Component.onCompleted: framelessHelper.setHitTestVisible(closeButton, true)
}
}
}
@ -111,41 +112,19 @@ Window {
pointSize: 70
bold: true
}
}
Button {
id: fullScreenButton
anchors {
horizontalCenter: parent.horizontalCenter
top: timeLabel.bottom
topMargin: 15
}
property bool _full: window.visibility === Window.FullScreen
text: _full ? qsTr("Exit FullScreen") : qsTr("Enter FullScreen")
onClicked: {
if (_full) {
if (_win_prev_state == Window.Maximized) {
window.showMaximized()
} else if (_win_prev_state == Window.Windowed) {
window.showNormal()
}
} else {
_win_prev_state = window.visibility
window.showFullScreen()
}
}
color: FramelessUtils.darkModeEnabled ? "white" : "black"
}
Rectangle {
id: windowFrame
anchors.fill: parent
color: "transparent"
visible: !Utils.isWindows11OrGreater
border {
color: window.active ? Utils.activeFrameBorderColor : Utils.inactiveFrameBorderColor
width: window._flh_margin
id: windowTopBorder
anchors {
top: parent.top
left: parent.left
right: parent.right
}
height: ((window.visibility === Window.Windowed) && FramelessUtils.frameBorderVisible) ? 1 : 0
color: window.active ? FramelessUtils.frameBorderActiveColor : FramelessUtils.frameBorderInactiveColor
}
Component.onCompleted: framelessHelper.removeWindowFrame()
Component.onCompleted: FramelessHelper.addWindow(window)
}

View File

@ -24,26 +24,37 @@
import QtQuick 2.0
import QtQuick.Controls 2.0
import org.wangwenx190.FramelessHelper 1.0
Button {
id: button
implicitWidth: 45
implicitHeight: 30
implicitWidth: implicitHeight * 1.5
property bool maximized: false
ToolTip.visible: hovered && !down
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: maximized ? qsTr("Restore") : qsTr("Maximize")
ToolTip {
visible: button.hovered && !button.down
delay: Qt.styleHints.mousePressAndHoldInterval
text: button.maximized ? qsTr("Restore") : qsTr("Maximize")
}
contentItem: Image {
anchors.fill: parent
source: maximized ? "qrc:/images/button_restore_black.svg" : "qrc:/images/button_maximize_black.svg"
contentItem: Item {
implicitWidth: 16
implicitHeight: implicitWidth
Image {
anchors.centerIn: parent
source: button.maximized ?
(FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-restore.svg" : "qrc:/images/dark/chrome-restore.svg") :
(FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-maximize.svg" : "qrc:/images/dark/chrome-maximize.svg")
}
}
background: Rectangle {
visible: button.down || button.hovered
color: button.down ? "#808080" : (button.hovered ? "#c7c7c7" : "transparent")
visible: button.hovered
color: "gray"
opacity: 0.5
}
}

View File

@ -24,24 +24,33 @@
import QtQuick 2.0
import QtQuick.Controls 2.0
import org.wangwenx190.FramelessHelper 1.0
Button {
id: button
implicitWidth: 45
implicitHeight: 30
implicitWidth: implicitHeight * 1.5
ToolTip.visible: hovered && !down
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Minimize")
ToolTip {
visible: button.hovered && !button.down
delay: Qt.styleHints.mousePressAndHoldInterval
text: qsTr("Minimize")
}
contentItem: Image {
anchors.fill: parent
source: "qrc:/images/button_minimize_black.svg"
contentItem: Item {
implicitWidth: 16
implicitHeight: implicitWidth
Image {
anchors.centerIn: parent
source: FramelessUtils.darkModeEnabled ? "qrc:/images/light/chrome-minimize.svg" : "qrc:/images/dark/chrome-minimize.svg"
}
}
background: Rectangle {
visible: button.down || button.hovered
color: button.down ? "#808080" : (button.hovered ? "#c7c7c7" : "transparent")
visible: button.hovered
color: "gray"
opacity: 0.5
}
}

View File

@ -23,7 +23,7 @@ add_executable(Widget WIN32 ${SOURCES})
target_link_libraries(Widget PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
wangwenx190::FramelessHelper
FramelessHelper
)
target_compile_definitions(Widget PRIVATE

View File

@ -35,7 +35,7 @@
FRAMELESSHELPER_USE_NAMESPACE
static const QColor systemLightColor = QStringLiteral("#f0f0f0");
static const QColor systemDarkColor = QColor::fromRgb(32, 32, 32);
static const QColor systemDarkColor = QStringLiteral("#202020");
static const QString mainStyleSheet = QStringLiteral(R"(#MainWidget {
background-color: %1;
@ -132,7 +132,7 @@ void Widget::paintEvent(QPaintEvent *event)
painter.save();
QPen pen = {};
pen.setColor(Utilities::getFrameBorderColor(isActiveWindow()));
const int frameBorderThickness = Utilities::getFrameBorderThickness(winId(), false);
const int frameBorderThickness = 1;
pen.setWidth(frameBorderThickness);
painter.setPen(pen);
painter.drawLine(0, frameBorderThickness, width(), frameBorderThickness);
@ -174,7 +174,7 @@ void Widget::initFramelessHelperOnce()
void Widget::setupUi()
{
const int titleBarHeight = /*Utilities::getTitleBarHeight(winId(), false)*/30;
const int titleBarHeight = 30;
const QSize systemButtonSize = {int(qRound(qreal(titleBarHeight) * 1.5)), titleBarHeight};
const QSize systemIconSize = {16, 16};
setObjectName(QStringLiteral("MainWidget"));
@ -327,7 +327,7 @@ void Widget::resetContentsMargins()
{
#ifdef Q_OS_WIN
if (Utilities::isWin10OrGreater()) {
const int frameBorderThickness = Utilities::getFrameBorderThickness(winId(), false);
const int frameBorderThickness = 1;
setContentsMargins(0, frameBorderThickness, 0, 0);
}
#endif

View File

@ -66,8 +66,7 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
}
const QEvent::Type type = event->type();
// We are only interested in mouse events.
if ((type != QEvent::MouseButtonDblClick) && (type != QEvent::MouseButtonPress)
&& (type != QEvent::MouseMove)) {
if ((type != QEvent::MouseButtonPress) && (type != QEvent::MouseMove)) {
return false;
}
const auto window = qobject_cast<QWindow *>(object);

View File

@ -67,7 +67,7 @@ void FramelessHelperWin::addWindow(QWindow *window)
qApp->installNativeEventFilter(g_helper()->instance.data());
}
const WId winId = window->winId();
Utilities::fixupQtInternals(winId);
//Utilities::fixupQtInternals(winId);
Utilities::updateInternalWindowFrameMargins(window, true);
Utilities::updateWindowFrameMargins(winId, false);
const bool dark = Utilities::shouldAppsUseDarkMode();

View File

@ -23,85 +23,180 @@
*/
#include "framelessquickhelper.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1))
# include <QtGui/qpa/qplatformtheme.h>
# include <QtGui/private/qguiapplication_p.h>
#endif
#include "framelesswindowsmanager.h"
#include <QtQuick/qquickwindow.h>
#include "utilities.h"
#ifdef Q_OS_WINDOWS
#include "framelesshelper_windows.h"
# include <QtCore/qt_windows.h>
#endif
FRAMELESSHELPER_BEGIN_NAMESPACE
FramelessQuickHelper::FramelessQuickHelper(QQuickItem *parent) : QQuickItem(parent)
{
}
FramelessQuickHelper::FramelessQuickHelper(QObject *parent) : QObject(parent) {}
qreal FramelessQuickHelper::resizeBorderThickness() const
{
return FramelessWindowsManager::getResizeBorderThickness(window());
}
FramelessQuickHelper::~FramelessQuickHelper() = default;
void FramelessQuickHelper::setResizeBorderThickness(const qreal val)
void FramelessQuickHelper::addWindow(QWindow *window)
{
FramelessWindowsManager::setResizeBorderThickness(window(), qRound(val));
Q_EMIT resizeBorderThicknessChanged(val);
}
qreal FramelessQuickHelper::titleBarHeight() const
{
return FramelessWindowsManager::getTitleBarHeight(window());
}
void FramelessQuickHelper::setTitleBarHeight(const qreal val)
{
FramelessWindowsManager::setTitleBarHeight(window(), qRound(val));
Q_EMIT titleBarHeightChanged(val);
}
bool FramelessQuickHelper::resizable() const
{
return FramelessWindowsManager::getResizable(window());
}
void FramelessQuickHelper::setResizable(const bool val)
{
FramelessWindowsManager::setResizable(window(), val);
Q_EMIT resizableChanged(val);
}
void FramelessQuickHelper::removeWindowFrame()
{
FramelessWindowsManager::addWindow(window());
}
void FramelessQuickHelper::bringBackWindowFrame()
{
FramelessWindowsManager::removeWindow(window());
}
bool FramelessQuickHelper::isWindowFrameless() const
{
return FramelessWindowsManager::isWindowFrameless(window());
}
void FramelessQuickHelper::setHitTestVisible(QQuickItem *item, const bool visible)
{
Q_ASSERT(item);
if (!item) {
Q_ASSERT(window);
if (!window) {
return;
}
FramelessWindowsManager::setHitTestVisible(window(), item, visible);
FramelessWindowsManager::addWindow(window);
}
void FramelessQuickHelper::showMinimized()
void FramelessQuickHelper::removeWindow(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
FramelessWindowsManager::removeWindow(window);
}
FramelessQuickUtils::FramelessQuickUtils(QObject *parent) : QObject(parent)
{
connect(FramelessWindowsManager::instance(), &FramelessWindowsManager::themeChanged, this, [this](){
Q_EMIT frameBorderActiveColorChanged();
Q_EMIT frameBorderInactiveColorChanged();
Q_EMIT darkModeEnabledChanged();
Q_EMIT systemAccentColorChanged();
Q_EMIT titleBarColorVisibleChanged();
});
}
FramelessQuickUtils::~FramelessQuickUtils() = default;
qreal FramelessQuickUtils::titleBarHeight()
{
return 30;
}
bool FramelessQuickUtils::frameBorderVisible()
{
#ifdef Q_OS_WINDOWS
// Work-around a QtQuick bug: https://bugreports.qt.io/browse/QTBUG-69711
// Don't use "SW_SHOWMINIMIZED" because it will activate the current
// window instead of the next window in the Z order, that's not the
// native behavior of Windows applications.
ShowWindow(reinterpret_cast<HWND>(window()->winId()), SW_MINIMIZE);
return (Utilities::isWin10OrGreater() && !Utilities::isWin11OrGreater());
#else
window()->showMinimized();
return false;
#endif
}
qreal FramelessQuickUtils::frameBorderThickness()
{
return 1;
}
QColor FramelessQuickUtils::frameBorderActiveColor()
{
#ifdef Q_OS_WINDOWS
return Utilities::getFrameBorderColor(true);
#else
return {};
#endif
}
QColor FramelessQuickUtils::frameBorderInactiveColor()
{
#ifdef Q_OS_WINDOWS
return Utilities::getFrameBorderColor(false);
#else
return {};
#endif
}
bool FramelessQuickUtils::darkModeEnabled()
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 1))
if (const QPlatformTheme * const theme = QGuiApplicationPrivate::platformTheme()) {
return (theme->appearance() == QPlatformTheme::Appearance::Dark);
}
return false;
#else
# ifdef Q_OS_WINDOWS
return Utilities::shouldAppsUseDarkMode();
# else
return false;
# endif
#endif
}
QColor FramelessQuickUtils::systemAccentColor()
{
#ifdef Q_OS_WINDOWS
return Utilities::getDwmColorizationColor();
#else
return {};
#endif
}
bool FramelessQuickUtils::titleBarColorVisible()
{
#ifdef Q_OS_WINDOWS
if (!Utilities::isWin10OrGreater()) {
return false;
}
const DwmColorizationArea area = Utilities::getDwmColorizationArea();
return ((area == DwmColorizationArea::TitleBar_WindowBorder) || (area == DwmColorizationArea::All));
#else
return false;
#endif
}
void FramelessQuickUtils::showMinimized2(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
#ifdef Q_OS_WINDOWS
// Work-around a QtQuick bug: https://bugreports.qt.io/browse/QTBUG-69711
// Don't use "SW_SHOWMINIMIZED" because it will activate the current window
// instead of the next window in the Z order, which is not the default behavior
// of native Win32 applications.
ShowWindow(reinterpret_cast<HWND>(window->winId()), SW_MINIMIZE);
#else
window->showMinimized();
#endif
}
void FramelessQuickUtils::showSystemMenu(const QPointF &pos)
{
}
void FramelessQuickUtils::startSystemMove2(QWindow *window)
{
Q_ASSERT(window);
if (!window) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
window->startSystemMove();
#else
# ifdef Q_OS_WINDOWS
Utilities::startSystemMove(window);
# endif
#endif
}
void FramelessQuickUtils::startSystemResize2(QWindow *window, const Qt::Edges edges)
{
Q_ASSERT(window);
if (!window) {
return;
}
if (edges == Qt::Edges{}) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
window->startSystemResize(edges);
#else
# ifdef Q_OS_WINDOWS
Utilities::startSystemResize(window, edges);
# endif
#endif
}

View File

@ -25,46 +25,72 @@
#pragma once
#include "framelesshelper_global.h"
#include <QtQuick/qquickitem.h>
#include <QtCore/qobject.h>
#include <QtGui/qwindow.h>
#include <QtQml/qqmlregistration.h>
QT_BEGIN_NAMESPACE
class QWindow;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class FRAMELESSHELPER_API FramelessQuickHelper : public QQuickItem
class FRAMELESSHELPER_API FramelessQuickHelper : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessQuickHelper)
#ifdef QML_NAMED_ELEMENT
QML_NAMED_ELEMENT(FramelessHelper)
#endif
Q_PROPERTY(qreal resizeBorderThickness READ resizeBorderThickness WRITE setResizeBorderThickness NOTIFY resizeBorderThicknessChanged)
Q_PROPERTY(qreal titleBarHeight READ titleBarHeight WRITE setTitleBarHeight NOTIFY titleBarHeightChanged)
Q_PROPERTY(bool resizable READ resizable WRITE setResizable NOTIFY resizableChanged)
public:
explicit FramelessQuickHelper(QQuickItem *parent = nullptr);
explicit FramelessQuickHelper(QObject *parent = nullptr);
~FramelessQuickHelper() override;
Q_NODISCARD qreal resizeBorderThickness() const;
void setResizeBorderThickness(const qreal val);
Q_INVOKABLE static void addWindow(QWindow *window);
Q_INVOKABLE static void removeWindow(QWindow *window);
};
Q_NODISCARD qreal titleBarHeight() const;
void setTitleBarHeight(const qreal val);
class FRAMELESSHELPER_API FramelessQuickUtils : public QObject
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(FramelessQuickUtils)
#ifdef QML_NAMED_ELEMENT
QML_NAMED_ELEMENT(FramelessUtils)
#endif
Q_PROPERTY(qreal titleBarHeight READ titleBarHeight CONSTANT FINAL)
Q_PROPERTY(bool frameBorderVisible READ frameBorderVisible CONSTANT FINAL)
Q_PROPERTY(qreal frameBorderThickness READ frameBorderThickness CONSTANT FINAL)
Q_PROPERTY(QColor frameBorderActiveColor READ frameBorderActiveColor NOTIFY frameBorderActiveColorChanged FINAL)
Q_PROPERTY(QColor frameBorderInactiveColor READ frameBorderInactiveColor NOTIFY frameBorderInactiveColorChanged FINAL)
Q_PROPERTY(bool darkModeEnabled READ darkModeEnabled NOTIFY darkModeEnabledChanged FINAL)
Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemAccentColorChanged FINAL)
Q_PROPERTY(bool titleBarColorVisible READ titleBarColorVisible NOTIFY titleBarColorVisibleChanged FINAL)
Q_NODISCARD bool resizable() const;
void setResizable(const bool val);
public:
explicit FramelessQuickUtils(QObject *parent = nullptr);
~FramelessQuickUtils() override;
Q_NODISCARD Q_INVOKABLE bool isWindowFrameless() const;
Q_NODISCARD static qreal titleBarHeight();
Q_NODISCARD static bool frameBorderVisible();
Q_NODISCARD static qreal frameBorderThickness();
Q_NODISCARD static QColor frameBorderActiveColor();
Q_NODISCARD static QColor frameBorderInactiveColor();
Q_NODISCARD static bool darkModeEnabled();
Q_NODISCARD static QColor systemAccentColor();
Q_NODISCARD static bool titleBarColorVisible();
public Q_SLOTS:
void removeWindowFrame();
void bringBackWindowFrame();
void setHitTestVisible(QQuickItem *item, const bool visible);
void showMinimized();
Q_INVOKABLE static void showMinimized2(QWindow *window);
Q_INVOKABLE static void showSystemMenu(const QPointF &pos);
Q_INVOKABLE static void startSystemMove2(QWindow *window);
Q_INVOKABLE static void startSystemResize2(QWindow *window, const Qt::Edges edges);
Q_SIGNALS:
void resizeBorderThicknessChanged(qreal);
void titleBarHeightChanged(qreal);
void resizableChanged(bool);
void frameBorderActiveColorChanged();
void frameBorderInactiveColorChanged();
void darkModeEnabledChanged();
void systemAccentColorChanged();
void titleBarColorVisibleChanged();
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -158,20 +158,22 @@ void FramelessWindowsManager::addWindow(QWindow *window)
g_managerPrivate()->qtFramelessHelpers.insert(uuid, qtFramelessHelper);
}
#ifdef Q_OS_WINDOWS
// Work-around Win32 multi-monitor artifacts.
const QMetaObject::Connection workaroundConnection =
if (!g_usePureQtImplementation) {
// Work-around Win32 multi-monitor artifacts.
const QMetaObject::Connection workaroundConnection =
connect(window, &QWindow::screenChanged, window, [window](QScreen *screen){
Q_UNUSED(screen);
// Force a WM_NCCALCSIZE event to inform Windows about our custom window frame,
// this is only necessary when the window is being moved cross monitors.
Utilities::triggerFrameChange(window->winId());
// For some reason the window is not repainted correctly when moving cross monitors,
// we workaround this issue by force a re-paint and re-layout of the window by triggering
// a resize event manually. Although the actual size does not change, the issue we
// observed disappeared indeed, amazingly.
window->resize(window->size());
});
g_managerPrivate()->win32WorkaroundConnections.insert(uuid, workaroundConnection);
Q_UNUSED(screen);
// Force a WM_NCCALCSIZE event to inform Windows about our custom window frame,
// this is only necessary when the window is being moved cross monitors.
Utilities::triggerFrameChange(window->winId());
// For some reason the window is not repainted correctly when moving cross monitors,
// we workaround this issue by force a re-paint and re-layout of the window by triggering
// a resize event manually. Although the actual size does not change, the issue we
// observed disappeared indeed, amazingly.
window->resize(window->size());
});
g_managerPrivate()->win32WorkaroundConnections.insert(uuid, workaroundConnection);
}
#endif
g_managerPrivate()->mutex.unlock();
if (g_usePureQtImplementation) {

View File

@ -49,6 +49,7 @@ public:
Q_SIGNALS:
void themeChanged();
void systemMenuRequested(const QPointF &);
};
FRAMELESSHELPER_END_NAMESPACE