diff --git a/CMakeLists.txt b/CMakeLists.txt index 771586f..8be2394 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ cmake_minimum_required(VERSION 3.20) project(FramelessHelper - VERSION 2.1.8.0 + VERSION 2.2.0.0 DESCRIPTION "Cross-platform window customization framework for Qt Widgets and Qt Quick." HOMEPAGE_URL "https://github.com/wangwenx190/framelesshelper/" LANGUAGES CXX diff --git a/doc/mac_dark.png b/doc/mac_dark.png index 8753e23..54b6d2e 100644 Binary files a/doc/mac_dark.png and b/doc/mac_dark.png differ diff --git a/doc/mac_light.png b/doc/mac_light.png index 01274d5..34e9cff 100644 Binary files a/doc/mac_light.png and b/doc/mac_light.png differ diff --git a/doc/win_dark.png b/doc/win_dark.png index cc1e8a0..de8551a 100644 Binary files a/doc/win_dark.png and b/doc/win_dark.png differ diff --git a/doc/win_light.png b/doc/win_light.png index 5b09941..6e7f013 100644 Binary files a/doc/win_light.png and b/doc/win_light.png differ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 74fcaca..6e7f2d3 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -26,10 +26,8 @@ if(FRAMELESSHELPER_BUILD_WIDGETS AND TARGET Qt${QT_VERSION_MAJOR}::Widgets) add_subdirectory(widget) add_subdirectory(mainwindow) add_subdirectory(openglwidget) - add_subdirectory(widget-blur) endif() if(FRAMELESSHELPER_BUILD_QUICK AND TARGET Qt${QT_VERSION_MAJOR}::Quick AND ${QT_VERSION_MAJOR} GREATER_EQUAL 6) add_subdirectory(quick) - add_subdirectory(quick-blur) endif() diff --git a/examples/mainwindow/CMakeLists.txt b/examples/mainwindow/CMakeLists.txt index 9e37d07..0dbd977 100644 --- a/examples/mainwindow/CMakeLists.txt +++ b/examples/mainwindow/CMakeLists.txt @@ -36,27 +36,16 @@ endif() add_executable(MainWindow ${SOURCES}) -set_target_properties(MainWindow PROPERTIES - WIN32_EXECUTABLE TRUE - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_GUI_IDENTIFIER org.wangwenx190.framelesshelper.demo - MACOSX_BUNDLE_BUNDLE_VERSION 1.0.0.0 - MACOSX_BUNDLE_SHORT_VERSION_STRING 1.0 -) - target_link_libraries(MainWindow PRIVATE Qt${QT_VERSION_MAJOR}::Widgets FramelessHelper::Widgets ) target_compile_definitions(MainWindow PRIVATE - QT_NO_CAST_FROM_ASCII - QT_NO_CAST_TO_ASCII QT_NO_KEYWORDS - QT_USE_QSTRINGBUILDER - QT_DEPRECATED_WARNINGS - QT_DISABLE_DEPRECATED_BEFORE=0x060500 ) -include(../deployqt.cmake) -deploy_qt_libraries(MainWindow) +include(../../src/core/cmakehelper.cmake) +setup_gui_app(MainWindow) +setup_compile_params(MainWindow) +deploy_qt_runtime(MainWindow) diff --git a/examples/mainwindow/main.cpp b/examples/mainwindow/main.cpp index 136ae47..be3e550 100644 --- a/examples/mainwindow/main.cpp +++ b/examples/mainwindow/main.cpp @@ -37,6 +37,7 @@ int main(int argc, char *argv[]) QApplication application(argc, argv); FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners); + FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); MainWindow mainWindow; mainWindow.show(); diff --git a/examples/openglwidget/CMakeLists.txt b/examples/openglwidget/CMakeLists.txt index 4ca97d4..7acc710 100644 --- a/examples/openglwidget/CMakeLists.txt +++ b/examples/openglwidget/CMakeLists.txt @@ -48,14 +48,6 @@ endif() add_executable(OpenGLWidget ${SOURCES}) -set_target_properties(OpenGLWidget PROPERTIES - WIN32_EXECUTABLE TRUE - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_GUI_IDENTIFIER org.wangwenx190.framelesshelper.demo - MACOSX_BUNDLE_BUNDLE_VERSION 1.0.0.0 - MACOSX_BUNDLE_SHORT_VERSION_STRING 1.0 -) - target_link_libraries(OpenGLWidget PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::OpenGL @@ -69,13 +61,10 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) endif() target_compile_definitions(OpenGLWidget PRIVATE - QT_NO_CAST_FROM_ASCII - QT_NO_CAST_TO_ASCII QT_NO_KEYWORDS - QT_USE_QSTRINGBUILDER - QT_DEPRECATED_WARNINGS - QT_DISABLE_DEPRECATED_BEFORE=0x060500 ) -include(../deployqt.cmake) -deploy_qt_libraries(OpenGLWidget) +include(../../src/core/cmakehelper.cmake) +setup_gui_app(OpenGLWidget) +setup_compile_params(OpenGLWidget) +deploy_qt_runtime(OpenGLWidget) diff --git a/examples/openglwidget/main.cpp b/examples/openglwidget/main.cpp index 007291a..b5ce5ff 100644 --- a/examples/openglwidget/main.cpp +++ b/examples/openglwidget/main.cpp @@ -74,6 +74,7 @@ int main(int argc, char *argv[]) QApplication application(argc, argv); FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners); + FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); QSurfaceFormat fmt = {}; fmt.setDepthBufferSize(24); diff --git a/examples/quick-blur/CMakeLists.txt b/examples/quick-blur/CMakeLists.txt deleted file mode 100644 index 68b4c16..0000000 --- a/examples/quick-blur/CMakeLists.txt +++ /dev/null @@ -1,87 +0,0 @@ -#[[ - MIT License - - Copyright (C) 2022 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. -]] - -find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Qml Quick QuickControls2) -find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Qml Quick QuickControls2) - -set(SOURCES - main.cpp - settings.h - settings.cpp -) - -if(${QT_VERSION} VERSION_LESS 6.2) - list(APPEND SOURCES qml.qrc) -endif() - -if(WIN32) - enable_language(RC) - list(APPEND SOURCES ../example.rc ../example.manifest) -endif() - -add_executable(QuickBlur ${SOURCES}) - -if(${QT_VERSION} VERSION_GREATER_EQUAL 6.2) - qt_add_qml_module(QuickBlur - URI Demo - VERSION 1.0 - IMPORT_PATH ${PROJECT_BINARY_DIR}/imports - IMPORTS - QtQml - QtQuick - QtQuick.Controls.Basic - org.wangwenx190.FramelessHelper - QML_FILES MainWindow.qml - #ENABLE_TYPE_COMPILER # We can't use it for now due to it still can't compile singletons. - # There's some hope to get it supported in Qt 6.5. - ) -endif() - -set_target_properties(QuickBlur PROPERTIES - WIN32_EXECUTABLE TRUE - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_GUI_IDENTIFIER org.wangwenx190.framelesshelper.demo - MACOSX_BUNDLE_BUNDLE_VERSION 1.0.0.0 - MACOSX_BUNDLE_SHORT_VERSION_STRING 1.0 -) - -target_link_libraries(QuickBlur PRIVATE - Qt${QT_VERSION_MAJOR}::QmlPrivate - Qt${QT_VERSION_MAJOR}::QuickPrivate - Qt${QT_VERSION_MAJOR}::QuickControls2Private - FramelessHelper::Quick -) - -target_compile_definitions(QuickBlur PRIVATE - QT_NO_CAST_FROM_ASCII - QT_NO_CAST_TO_ASCII - QT_NO_KEYWORDS - QT_USE_QSTRINGBUILDER - QT_DEPRECATED_WARNINGS - QT_DISABLE_DEPRECATED_BEFORE=0x060500 - $<$,$>:QT_QML_DEBUG> -) - -include(../deployqt.cmake) -deploy_qt_libraries(QuickBlur) diff --git a/examples/quick-blur/MainWindow.qml b/examples/quick-blur/MainWindow.qml deleted file mode 100644 index 23002b1..0000000 --- a/examples/quick-blur/MainWindow.qml +++ /dev/null @@ -1,86 +0,0 @@ -/* - * MIT License - * - * Copyright (C) 2022 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. - */ - -import QtQuick -import QtQuick.Controls.Basic -import org.wangwenx190.FramelessHelper -import Demo - -FramelessWindow { - id: window - visible: false // Hide the window before we sets up it's correct size and position. - width: 800 - height: 600 - title: qsTr("FramelessHelper demo application - Qt Quick (Blur behind window)") - onClosing: Settings.saveGeometry(window) - - FramelessHelper.onReady: { - // Let FramelessHelper know what's our homemade title bar, otherwise - // our window won't be draggable. - FramelessHelper.titleBarItem = titleBar; - // Make our own items visible to the hit test and on Windows, enable - // the snap layout feature (available since Windows 11). - FramelessHelper.setSystemButton(titleBar.minimizeButton, FramelessHelperConstants.Minimize); - FramelessHelper.setSystemButton(titleBar.maximizeButton, FramelessHelperConstants.Maximize); - FramelessHelper.setSystemButton(titleBar.closeButton, FramelessHelperConstants.Close); - if (!Settings.restoreGeometry(window)) { - FramelessHelper.moveWindowToDesktopCenter(); - } - FramelessHelper.blurBehindWindowEnabled = true; - // Finally, show the window after everything is setted. - window.visible = true; - } - - Timer { - interval: 500 - running: true - repeat: true - onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss") - } - - Label { - id: timeLabel - anchors.centerIn: parent - font { - pointSize: 70 - bold: true - } - color: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) ? "white" : "black" - } - - StandardTitleBar { - id: titleBar - anchors { - top: window.topBorderBottom // VERY IMPORTANT! - left: parent.left - right: parent.right - } - titleLabelAlignment: Qt.AlignCenter - chromePalette { - titleBarActiveBackgroundColor: "transparent" - titleBarInactiveBackgroundColor: "transparent" - titleBarActiveForegroundColor: "black" - } - } -} diff --git a/examples/quick-blur/main.cpp b/examples/quick-blur/main.cpp deleted file mode 100644 index 0d46e98..0000000 --- a/examples/quick-blur/main.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * MIT License - * - * Copyright (C) 2022 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. - */ - -#ifndef QMLTC_ENABLED -# define QMLTC_ENABLED 0 // We disable it for now, because currently (6.4) it can't process singletons yet. - // There's some hope to get it supported in Qt 6.5 -#endif - -#include -#include -#include -#include -#include -#include -#include "settings.h" -#if QMLTC_ENABLED -# include -#endif - -FRAMELESSHELPER_USE_NAMESPACE - -int main(int argc, char *argv[]) -{ - // Not necessary, but better call this function, before the construction - // of any Q(Core|Gui)Application instances. - FramelessHelper::Quick::initialize(); - - QGuiApplication application(argc, argv); - - FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners); - - // Enable QtRHI debug output if not explicitly requested by the user. - if (!qEnvironmentVariableIsSet("QSG_INFO")) { - qputenv("QSG_INFO", FRAMELESSHELPER_BYTEARRAY_LITERAL("1")); - } - - // Allow testing other RHI backends through environment variable. - if (!qEnvironmentVariableIsSet("QSG_RHI_BACKEND")) { - // This line is not relevant to FramelessHelper, we change - // the default RHI backend to "software" just because it's - // stable enough and have exactly the same behavior on all - // supported platforms. Other backends may behave differently - // on different platforms and graphics cards, so I think they - // are not suitable for our demonstration. -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - QQuickWindow::setGraphicsApi(QSGRendererInterface::Software); -#elif (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software); -#endif - } - - QQmlApplicationEngine engine; -#if !QMLTC_ENABLED - engine.addImportPath(FRAMELESSHELPER_STRING_LITERAL("../imports")); -#endif - -#if (QT_VERSION < QT_VERSION_CHECK(6, 2, 0)) && !QMLTC_ENABLED - // Don't forget to register our own custom QML types! - FramelessHelper::Quick::registerTypes(&engine); - - qmlRegisterSingletonType("Demo", 1, 0, "Settings", - [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { - Q_UNUSED(engine); - Q_UNUSED(scriptEngine); - return new Settings; - }); -#endif - -#if !QMLTC_ENABLED - // This line is not relevant to FramelessHelper, we change the default - // Qt Quick Controls theme to "Basic" (Qt6) or "Default" (Qt5) just - // because other themes will make our homemade system buttons look - // not good. This line has nothing to do with FramelessHelper itself. -# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - QQuickStyle::setStyle(FRAMELESSHELPER_STRING_LITERAL("Basic")); -# else - QQuickStyle::setStyle(FRAMELESSHELPER_STRING_LITERAL("Default")); -# endif -#endif - -#if !QMLTC_ENABLED - const QUrl mainUrl(FRAMELESSHELPER_STRING_LITERAL("qrc:///Demo/MainWindow.qml")); -#endif - -#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) - QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &application, - [](const QUrl &url){ - qCritical() << "The QML engine failed to create component:" << url; - QCoreApplication::exit(-1); - }, Qt::QueuedConnection); -#elif !QMLTC_ENABLED - const QMetaObject::Connection connection = QObject::connect( - &engine, &QQmlApplicationEngine::objectCreated, &application, - [&mainUrl, &connection](QObject *object, const QUrl &url) { - if (url != mainUrl) { - return; - } - if (object) { - QObject::disconnect(connection); - } else { - QCoreApplication::exit(-1); - } - }, Qt::QueuedConnection); -#endif - -#if !QMLTC_ENABLED - engine.load(mainUrl); -#endif - -#if QMLTC_ENABLED - QScopedPointer mainWindow(new MainWindow(&engine)); - mainWindow->show(); -#endif - - return QCoreApplication::exec(); -} diff --git a/examples/quick-blur/qml.qrc b/examples/quick-blur/qml.qrc deleted file mode 100644 index e9b1d1b..0000000 --- a/examples/quick-blur/qml.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - MainWindow.qml - - diff --git a/examples/quick-blur/scripts/d3d11.bat b/examples/quick-blur/scripts/d3d11.bat deleted file mode 100644 index af27dc2..0000000 --- a/examples/quick-blur/scripts/d3d11.bat +++ /dev/null @@ -1,28 +0,0 @@ -:: MIT License -:: -:: Copyright (C) 2022 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. - -@echo off -setlocal -set QSG_RHI_BACKEND=d3d11 -"%~dp0QuickBlur.exe" -endlocal -exit /b 0 diff --git a/examples/quick-blur/scripts/opengl.bat b/examples/quick-blur/scripts/opengl.bat deleted file mode 100644 index b66fbab..0000000 --- a/examples/quick-blur/scripts/opengl.bat +++ /dev/null @@ -1,28 +0,0 @@ -:: MIT License -:: -:: Copyright (C) 2022 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. - -@echo off -setlocal -set QSG_RHI_BACKEND=opengl -"%~dp0QuickBlur.exe" -endlocal -exit /b 0 diff --git a/examples/quick-blur/scripts/vulkan.bat b/examples/quick-blur/scripts/vulkan.bat deleted file mode 100644 index 48d8563..0000000 --- a/examples/quick-blur/scripts/vulkan.bat +++ /dev/null @@ -1,28 +0,0 @@ -:: MIT License -:: -:: Copyright (C) 2022 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. - -@echo off -setlocal -set QSG_RHI_BACKEND=vulkan -"%~dp0QuickBlur.exe" -endlocal -exit /b 0 diff --git a/examples/quick-blur/settings.cpp b/examples/quick-blur/settings.cpp deleted file mode 100644 index 0df0da8..0000000 --- a/examples/quick-blur/settings.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * MIT License - * - * Copyright (C) 2022 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 "settings.h" -#include -#include -#include -#include -#include - -FRAMELESSHELPER_STRING_CONSTANT2(IniKeyPath, "Window/Geometry") - -Settings::Settings(QObject *parent) : QObject(parent) -{ - const QFileInfo fileInfo(QCoreApplication::applicationFilePath()); - const QString iniFileName = fileInfo.completeBaseName() + FRAMELESSHELPER_STRING_LITERAL(".ini"); - const QString iniFilePath = fileInfo.canonicalPath() + QDir::separator() + iniFileName; - m_settings.reset(new QSettings(iniFilePath, QSettings::IniFormat)); -} - -Settings::~Settings() = default; - -void Settings::saveGeometry(QWindow *window) -{ - Q_ASSERT(window); - if (!window) { - return; - } - QByteArray data = {}; - QDataStream stream(&data, QDataStream::WriteOnly); - stream.setVersion(QDataStream::Qt_5_6); - stream << window->geometry(); - m_settings->setValue(kIniKeyPath, data); -} - -bool Settings::restoreGeometry(QWindow *window) -{ - Q_ASSERT(window); - if (!window) { - return false; - } - const QByteArray data = m_settings->value(kIniKeyPath).toByteArray(); - if (data.isEmpty()) { - return false; - } - QRect geometry = {}; - QDataStream stream(data); - stream.setVersion(QDataStream::Qt_5_6); - stream >> geometry; - if (!geometry.isValid()) { - return false; - } - window->setGeometry(geometry); - return true; -} diff --git a/examples/quick/CMakeLists.txt b/examples/quick/CMakeLists.txt index e0c8334..ab0efea 100644 --- a/examples/quick/CMakeLists.txt +++ b/examples/quick/CMakeLists.txt @@ -58,14 +58,6 @@ if(${QT_VERSION} VERSION_GREATER_EQUAL 6.2) ) endif() -set_target_properties(Quick PROPERTIES - WIN32_EXECUTABLE TRUE - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_GUI_IDENTIFIER org.wangwenx190.framelesshelper.demo - MACOSX_BUNDLE_BUNDLE_VERSION 1.0.0.0 - MACOSX_BUNDLE_SHORT_VERSION_STRING 1.0 -) - target_link_libraries(Quick PRIVATE Qt${QT_VERSION_MAJOR}::QmlPrivate Qt${QT_VERSION_MAJOR}::QuickPrivate @@ -74,14 +66,11 @@ target_link_libraries(Quick PRIVATE ) target_compile_definitions(Quick PRIVATE - QT_NO_CAST_FROM_ASCII - QT_NO_CAST_TO_ASCII QT_NO_KEYWORDS - QT_USE_QSTRINGBUILDER - QT_DEPRECATED_WARNINGS - QT_DISABLE_DEPRECATED_BEFORE=0x060500 $<$,$>:QT_QML_DEBUG> ) -include(../deployqt.cmake) -deploy_qt_libraries(Quick) +include(../../src/core/cmakehelper.cmake) +setup_gui_app(Quick) +setup_compile_params(Quick) +deploy_qt_runtime(Quick) diff --git a/examples/quick/MainWindow.qml b/examples/quick/MainWindow.qml index 02bb7de..95ffbbe 100644 --- a/examples/quick/MainWindow.qml +++ b/examples/quick/MainWindow.qml @@ -33,8 +33,15 @@ FramelessWindow { width: 800 height: 600 title: qsTr("FramelessHelper demo application - Qt Quick") - color: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) - ? FramelessUtils.defaultSystemDarkColor : FramelessUtils.defaultSystemLightColor + color: { + if (FramelessHelper.blurBehindWindowEnabled) { + return Qt.color("transparent"); + } + if (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) { + return FramelessUtils.defaultSystemDarkColor; + } + return FramelessUtils.defaultSystemLightColor; + } onClosing: Settings.saveGeometry(window) FramelessHelper.onReady: { @@ -67,7 +74,7 @@ FramelessWindow { pointSize: 70 bold: true } - color: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) ? "white" : "black" + color: (FramelessUtils.systemTheme === FramelessHelperConstants.Dark) ? Qt.color("white") : Qt.color("black") } StandardTitleBar { diff --git a/examples/quick/main.cpp b/examples/quick/main.cpp index 0d46e98..c1ecab3 100644 --- a/examples/quick/main.cpp +++ b/examples/quick/main.cpp @@ -49,6 +49,7 @@ int main(int argc, char *argv[]) QGuiApplication application(argc, argv); FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners); + FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); // Enable QtRHI debug output if not explicitly requested by the user. if (!qEnvironmentVariableIsSet("QSG_INFO")) { diff --git a/examples/widget-blur/CMakeLists.txt b/examples/widget-blur/CMakeLists.txt deleted file mode 100644 index 768ad5b..0000000 --- a/examples/widget-blur/CMakeLists.txt +++ /dev/null @@ -1,61 +0,0 @@ -#[[ - MIT License - - Copyright (C) 2022 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. -]] - -set(SOURCES - widget.h - widget.cpp - main.cpp -) - -if(WIN32) - enable_language(RC) - list(APPEND SOURCES ../example.rc ../example.manifest) -endif() - -add_executable(WidgetBlur ${SOURCES}) - -set_target_properties(WidgetBlur PROPERTIES - WIN32_EXECUTABLE TRUE - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_GUI_IDENTIFIER org.wangwenx190.framelesshelper.demo - MACOSX_BUNDLE_BUNDLE_VERSION 1.0.0.0 - MACOSX_BUNDLE_SHORT_VERSION_STRING 1.0 -) - -target_link_libraries(WidgetBlur PRIVATE - Qt${QT_VERSION_MAJOR}::Widgets - FramelessHelper::Widgets -) - -target_compile_definitions(WidgetBlur PRIVATE - QT_NO_CAST_FROM_ASCII - QT_NO_CAST_TO_ASCII - QT_NO_KEYWORDS - QT_USE_QSTRINGBUILDER - QT_DEPRECATED_WARNINGS - QT_DISABLE_DEPRECATED_BEFORE=0x060500 -) - -include(../deployqt.cmake) -deploy_qt_libraries(WidgetBlur) diff --git a/examples/widget-blur/main.cpp b/examples/widget-blur/main.cpp deleted file mode 100644 index 5079187..0000000 --- a/examples/widget-blur/main.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * MIT License - * - * Copyright (C) 2022 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 -#include -#include "widget.h" - -FRAMELESSHELPER_USE_NAMESPACE - -int main(int argc, char *argv[]) -{ - // Not necessary, but better call this function, before the construction - // of any Q(Core|Gui)Application instances. - FramelessHelper::Widgets::initialize(); - - QApplication application(argc, argv); - - FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners); - - Widget widget; - widget.show(); - - return QCoreApplication::exec(); -} diff --git a/examples/widget-blur/widget.cpp b/examples/widget-blur/widget.cpp deleted file mode 100644 index c214075..0000000 --- a/examples/widget-blur/widget.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * MIT License - * - * Copyright (C) 2022 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 "widget.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -FRAMELESSHELPER_USE_NAMESPACE - -using namespace Global; - -FRAMELESSHELPER_STRING_CONSTANT2(IniKeyPath, "Window/Geometry") - -[[nodiscard]] static inline QSettings *appConfigFile() -{ - const QFileInfo fileInfo(QCoreApplication::applicationFilePath()); - const QString iniFileName = fileInfo.completeBaseName() + FRAMELESSHELPER_STRING_LITERAL(".ini"); - const QString iniFilePath = fileInfo.canonicalPath() + QDir::separator() + iniFileName; - const auto settings = new QSettings(iniFilePath, QSettings::IniFormat); - return settings; -} - -Widget::Widget(QWidget *parent) : FramelessWidget(parent) -{ - initialize(); - startTimer(500); - connect(FramelessManager::instance(), &FramelessManager::systemThemeChanged, this, &Widget::updateStyleSheet); -} - -Widget::~Widget() = default; - -void Widget::timerEvent(QTimerEvent *event) -{ - FramelessWidget::timerEvent(event); - if (m_clockLabel) { - m_clockLabel->setText(QTime::currentTime().toString(FRAMELESSHELPER_STRING_LITERAL("hh:mm:ss"))); - } -} - -void Widget::closeEvent(QCloseEvent *event) -{ - const QScopedPointer settings(appConfigFile()); - settings->setValue(kIniKeyPath, saveGeometry()); - FramelessWidget::closeEvent(event); -} - -void Widget::initialize() -{ - setWindowTitle(tr("FramelessHelper demo application - Qt Widgets (Blur behind window)")); - resize(800, 600); - m_titleBar.reset(new StandardTitleBar(this)); - m_titleBar->setTitleLabelAlignment(Qt::AlignCenter); - ChromePalette *pal = m_titleBar->chromePalette(); - pal->setTitleBarActiveBackgroundColor(kDefaultTransparentColor); - pal->setTitleBarInactiveBackgroundColor(kDefaultTransparentColor); - pal->setTitleBarActiveForegroundColor(kDefaultBlackColor); - m_clockLabel.reset(new QLabel(this)); - m_clockLabel->setFrameShape(QFrame::NoFrame); - QFont clockFont = font(); - clockFont.setBold(true); - clockFont.setPointSize(70); - m_clockLabel->setFont(clockFont); - const auto contentLayout = new QHBoxLayout; - contentLayout->setContentsMargins(0, 0, 0, 0); - contentLayout->setSpacing(0); - contentLayout->addStretch(); - contentLayout->addWidget(m_clockLabel.data()); - contentLayout->addStretch(); - const auto mainLayout = new QVBoxLayout(this); - mainLayout->setSpacing(0); - mainLayout->setContentsMargins(0, 0, 0, 0); - mainLayout->addWidget(m_titleBar.data()); - mainLayout->addLayout(contentLayout); - setLayout(mainLayout); - updateStyleSheet(); - - FramelessWidgetsHelper *helper = FramelessWidgetsHelper::get(this); - helper->setTitleBarWidget(m_titleBar.data()); - helper->setSystemButton(m_titleBar->minimizeButton(), SystemButtonType::Minimize); - helper->setSystemButton(m_titleBar->maximizeButton(), SystemButtonType::Maximize); - helper->setSystemButton(m_titleBar->closeButton(), SystemButtonType::Close); - connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper](){ - const QScopedPointer settings(appConfigFile()); - const QByteArray data = settings->value(kIniKeyPath).toByteArray(); - if (data.isEmpty()) { - helper->moveWindowToDesktopCenter(); - } else { - restoreGeometry(data); - } - helper->setBlurBehindWindowEnabled(true); - }); -} - -void Widget::updateStyleSheet() -{ - const bool dark = Utils::shouldAppsUseDarkMode(); - const QColor clockLabelTextColor = (dark ? kDefaultWhiteColor : kDefaultBlackColor); - m_clockLabel->setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("color: %1;").arg(clockLabelTextColor.name())); - update(); -} diff --git a/examples/widget/CMakeLists.txt b/examples/widget/CMakeLists.txt index 504f697..890f17a 100644 --- a/examples/widget/CMakeLists.txt +++ b/examples/widget/CMakeLists.txt @@ -35,27 +35,16 @@ endif() add_executable(Widget ${SOURCES}) -set_target_properties(Widget PROPERTIES - WIN32_EXECUTABLE TRUE - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_GUI_IDENTIFIER org.wangwenx190.framelesshelper.demo - MACOSX_BUNDLE_BUNDLE_VERSION 1.0.0.0 - MACOSX_BUNDLE_SHORT_VERSION_STRING 1.0 -) - target_link_libraries(Widget PRIVATE Qt${QT_VERSION_MAJOR}::Widgets FramelessHelper::Widgets ) target_compile_definitions(Widget PRIVATE - QT_NO_CAST_FROM_ASCII - QT_NO_CAST_TO_ASCII QT_NO_KEYWORDS - QT_USE_QSTRINGBUILDER - QT_DEPRECATED_WARNINGS - QT_DISABLE_DEPRECATED_BEFORE=0x060500 ) -include(../deployqt.cmake) -deploy_qt_libraries(Widget) +include(../../src/core/cmakehelper.cmake) +setup_gui_app(Widget) +setup_compile_params(Widget) +deploy_qt_runtime(Widget) diff --git a/examples/widget/main.cpp b/examples/widget/main.cpp index 5079187..f17eff2 100644 --- a/examples/widget/main.cpp +++ b/examples/widget/main.cpp @@ -37,6 +37,7 @@ int main(int argc, char *argv[]) QApplication application(argc, argv); FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners); + FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); Widget widget; widget.show(); diff --git a/examples/widget/widget.cpp b/examples/widget/widget.cpp index 0f0d610..7dcd019 100644 --- a/examples/widget/widget.cpp +++ b/examples/widget/widget.cpp @@ -120,8 +120,12 @@ void Widget::updateStyleSheet() { const bool dark = Utils::shouldAppsUseDarkMode(); const QColor clockLabelTextColor = (dark ? kDefaultWhiteColor : kDefaultBlackColor); - const QColor widgetBackgroundColor = (dark ? kDefaultSystemDarkColor : kDefaultSystemLightColor); m_clockLabel->setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("color: %1;").arg(clockLabelTextColor.name())); - setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("background-color: %1;").arg(widgetBackgroundColor.name())); + if (FramelessWidgetsHelper::get(this)->isBlurBehindWindowEnabled()) { + setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("background-color: transparent")); + } else { + const QColor windowBackgroundColor = (dark ? kDefaultSystemDarkColor : kDefaultSystemLightColor); + setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("background-color: %1").arg(windowBackgroundColor.name())); + } update(); } diff --git a/include/FramelessHelper/Core/MicaMaterial b/include/FramelessHelper/Core/MicaMaterial new file mode 100644 index 0000000..296cdea --- /dev/null +++ b/include/FramelessHelper/Core/MicaMaterial @@ -0,0 +1 @@ +#include diff --git a/include/FramelessHelper/Core/chromepalette.h b/include/FramelessHelper/Core/chromepalette.h index 569ce6a..1ebc57d 100644 --- a/include/FramelessHelper/Core/chromepalette.h +++ b/include/FramelessHelper/Core/chromepalette.h @@ -26,9 +26,12 @@ #include "framelesshelpercore_global.h" #include +#include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcChromePalette) + class ChromePalettePrivate; class FRAMELESSHELPER_CORE_API ChromePalette : public QObject diff --git a/include/FramelessHelper/Core/framelesshelper_qt.h b/include/FramelessHelper/Core/framelesshelper_qt.h index 3028599..317aa8d 100644 --- a/include/FramelessHelper/Core/framelesshelper_qt.h +++ b/include/FramelessHelper/Core/framelesshelper_qt.h @@ -26,9 +26,12 @@ #include "framelesshelpercore_global.h" #include +#include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcFramelessHelperQt) + class FRAMELESSHELPER_CORE_API FramelessHelperQt : public QObject { Q_OBJECT diff --git a/include/FramelessHelper/Core/framelesshelper_win.h b/include/FramelessHelper/Core/framelesshelper_win.h index d56c9e7..dced04d 100644 --- a/include/FramelessHelper/Core/framelesshelper_win.h +++ b/include/FramelessHelper/Core/framelesshelper_win.h @@ -26,9 +26,12 @@ #include "framelesshelpercore_global.h" #include +#include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcFramelessHelperWin) + class FRAMELESSHELPER_CORE_API FramelessHelperWin : public QAbstractNativeEventFilter { Q_DISABLE_COPY_MOVE(FramelessHelperWin) diff --git a/include/FramelessHelper/Core/framelesshelper_windows.h b/include/FramelessHelper/Core/framelesshelper_windows.h index 369b94c..efd1337 100644 --- a/include/FramelessHelper/Core/framelesshelper_windows.h +++ b/include/FramelessHelper/Core/framelesshelper_windows.h @@ -385,3 +385,4 @@ EXTERN_C_END [[maybe_unused]] static constexpr const wchar_t kDwmColorKeyName[] = L"ColorPrevalence"; [[maybe_unused]] static constexpr const wchar_t kSystemDarkThemeResourceName[] = L"DarkMode_Explorer"; [[maybe_unused]] static constexpr const wchar_t kSystemLightThemeResourceName[] = L"Explorer"; +[[maybe_unused]] static constexpr const wchar_t kDesktopRegistryKey[] = LR"(Control Panel\Desktop)"; diff --git a/include/FramelessHelper/Core/framelesshelpercore_global.h b/include/FramelessHelper/Core/framelesshelpercore_global.h index 4c932d1..eb9060c 100644 --- a/include/FramelessHelper/Core/framelesshelpercore_global.h +++ b/include/FramelessHelper/Core/framelesshelpercore_global.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -185,6 +186,8 @@ QT_END_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcCoreGlobal) + #include [[maybe_unused]] static constexpr const int FRAMELESSHELPER_VERSION = @@ -228,7 +231,8 @@ enum class Option DisableWindowsSnapLayout = 3, WindowUseRoundCorners = 4, CenterWindowBeforeShow = 5, - EnableBlurBehindWindow = 6 + EnableBlurBehindWindow = 6, + ForceNonNativeBackgroundBlur = 7 }; Q_ENUM_NS(Option) @@ -333,6 +337,17 @@ enum class BlurMode }; Q_ENUM_NS(BlurMode) +enum class WallpaperAspectStyle +{ + Fill = 0, // Keep aspect ratio to fill, expand/crop if necessary. + Fit = 1, // Keep aspect ratio to fill, but don't expand/crop. + Stretch = 2, // Ignore aspect ratio to fill. + Tile = 3, + Center = 4, + Span = 5 // ??? +}; +Q_ENUM_NS(WallpaperAspectStyle) + struct VersionNumber { int major = 0; diff --git a/include/FramelessHelper/Core/framelessmanager.h b/include/FramelessHelper/Core/framelessmanager.h index 2ae248b..3c1a397 100644 --- a/include/FramelessHelper/Core/framelessmanager.h +++ b/include/FramelessHelper/Core/framelessmanager.h @@ -26,9 +26,12 @@ #include "framelesshelpercore_global.h" #include +#include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcFramelessManager) + class FramelessManagerPrivate; class FRAMELESSHELPER_CORE_API FramelessManager : public QObject @@ -38,6 +41,8 @@ class FRAMELESSHELPER_CORE_API FramelessManager : public QObject Q_DISABLE_COPY_MOVE(FramelessManager) Q_PROPERTY(Global::SystemTheme systemTheme READ systemTheme NOTIFY systemThemeChanged FINAL) Q_PROPERTY(QColor systemAccentColor READ systemAccentColor NOTIFY systemThemeChanged FINAL) + Q_PROPERTY(QString wallpaper READ wallpaper NOTIFY wallpaperChanged FINAL) + Q_PROPERTY(Global::WallpaperAspectStyle wallpaperAspectStyle READ wallpaperAspectStyle NOTIFY wallpaperChanged FINAL) public: explicit FramelessManager(QObject *parent = nullptr); @@ -47,12 +52,15 @@ public: Q_NODISCARD Global::SystemTheme systemTheme() const; Q_NODISCARD QColor systemAccentColor() const; + Q_NODISCARD QString wallpaper() const; + Q_NODISCARD Global::WallpaperAspectStyle wallpaperAspectStyle() const; public Q_SLOTS: void addWindow(const Global::SystemParameters ¶ms); Q_SIGNALS: void systemThemeChanged(); + void wallpaperChanged(); private: QScopedPointer d_ptr; diff --git a/include/FramelessHelper/Core/micamaterial.h b/include/FramelessHelper/Core/micamaterial.h new file mode 100644 index 0000000..fee757c --- /dev/null +++ b/include/FramelessHelper/Core/micamaterial.h @@ -0,0 +1,76 @@ +/* + * MIT License + * + * Copyright (C) 2022 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 "framelesshelpercore_global.h" +#include +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcMicaMaterial) + +class MicaMaterialPrivate; + +class FRAMELESSHELPER_CORE_API MicaMaterial : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(MicaMaterial) + Q_DECLARE_PRIVATE(MicaMaterial) + + Q_PROPERTY(QColor tintColor READ tintColor WRITE setTintColor NOTIFY tintColorChanged FINAL) + Q_PROPERTY(qreal tintOpacity READ tintOpacity WRITE setTintOpacity NOTIFY tintOpacityChanged FINAL) + Q_PROPERTY(qreal noiseOpacity READ noiseOpacity WRITE setNoiseOpacity NOTIFY noiseOpacityChanged FINAL) + +public: + explicit MicaMaterial(QObject *parent = nullptr); + ~MicaMaterial() override; + + Q_NODISCARD QColor tintColor() const; + void setTintColor(const QColor &value); + + Q_NODISCARD qreal tintOpacity() const; + void setTintOpacity(const qreal value); + + Q_NODISCARD qreal noiseOpacity() const; + void setNoiseOpacity(const qreal value); + +public Q_SLOTS: + void paint(QPainter *painter, const QSize &size, const QPoint &pos) const; + Q_NODISCARD static MicaMaterial *attach(QObject *target); + +Q_SIGNALS: + void tintColorChanged(); + void tintOpacityChanged(); + void noiseOpacityChanged(); + void shouldRedraw(); + +private: + QScopedPointer d_ptr; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(MicaMaterial)) diff --git a/include/FramelessHelper/Core/private/framelessconfig_p.h b/include/FramelessHelper/Core/private/framelessconfig_p.h index 113406d..4e42658 100644 --- a/include/FramelessHelper/Core/private/framelessconfig_p.h +++ b/include/FramelessHelper/Core/private/framelessconfig_p.h @@ -27,6 +27,7 @@ #include "framelesshelpercore_global.h" #include #include +#include FRAMELESSHELPER_BEGIN_NAMESPACE @@ -49,12 +50,16 @@ public: static void setLoadFromEnvironmentVariablesDisabled(const bool on = true); static void setLoadFromConfigurationFileDisabled(const bool on = true); - Q_NODISCARD QVariant setInternal(const QString &key, const QVariant &value); - Q_NODISCARD QVariant getInternal(const QString &key) const; + Q_NODISCARD std::optional setInternal(const QString &key, const QVariant &value); + Q_NODISCARD std::optional getInternal(const QString &key) const; template - Q_NODISCARD T getInternal(const QString &key) const + Q_NODISCARD std::optional getInternal(const QString &key) const { - return qvariant_cast(getInternal(key)); + const std::optional var = getInternal(key); + if (var.has_value()) { + return qvariant_cast(var.value()); + } + return std::nullopt; } }; diff --git a/include/FramelessHelper/Core/private/framelessmanager_p.h b/include/FramelessHelper/Core/private/framelessmanager_p.h index 088be2c..ec53d3b 100644 --- a/include/FramelessHelper/Core/private/framelessmanager_p.h +++ b/include/FramelessHelper/Core/private/framelessmanager_p.h @@ -25,12 +25,12 @@ #pragma once #include "framelesshelpercore_global.h" +#include "framelessmanager.h" #include +#include FRAMELESSHELPER_BEGIN_NAMESPACE -class FramelessManager; - class FRAMELESSHELPER_CORE_API FramelessManagerPrivate : public QObject { Q_OBJECT @@ -49,20 +49,26 @@ public: Q_NODISCARD Global::SystemTheme systemTheme() const; Q_NODISCARD QColor systemAccentColor() const; + Q_NODISCARD QString wallpaper() const; + Q_NODISCARD Global::WallpaperAspectStyle wallpaperAspectStyle() const; static void addWindow(const Global::SystemParameters ¶ms); + Q_INVOKABLE void notifySystemThemeHasChangedOrNot(); + Q_INVOKABLE void notifyWallpaperHasChangedOrNot(); private: void initialize(); private: - FramelessManager *q_ptr = nullptr; + QPointer q_ptr = nullptr; Global::SystemTheme m_systemTheme = Global::SystemTheme::Unknown; QColor m_accentColor = {}; #ifdef Q_OS_WINDOWS Global::DwmColorizationArea m_colorizationArea = Global::DwmColorizationArea::None_; #endif + QString m_wallpaper = {}; + Global::WallpaperAspectStyle m_wallpaperAspectStyle = Global::WallpaperAspectStyle::Fill; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/examples/quick-blur/settings.h b/include/FramelessHelper/Core/private/micamaterial_p.h similarity index 53% rename from examples/quick-blur/settings.h rename to include/FramelessHelper/Core/private/micamaterial_p.h index f07d135..fb1a606 100644 --- a/examples/quick-blur/settings.h +++ b/include/FramelessHelper/Core/private/micamaterial_p.h @@ -24,38 +24,45 @@ #pragma once +#include "framelesshelpercore_global.h" #include -#include -#include -#if __has_include() -# include -#else -# include -#endif +#include +#include -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +FRAMELESSHELPER_BEGIN_NAMESPACE -class Settings : public QObject +class MicaMaterial; + +class FRAMELESSHELPER_CORE_API MicaMaterialPrivate : public QObject { Q_OBJECT -#ifdef QML_ELEMENT - QML_ELEMENT -#endif -#ifdef QML_SINGLETON - QML_SINGLETON -#endif - Q_DISABLE_COPY_MOVE(Settings) + Q_DISABLE_COPY_MOVE(MicaMaterialPrivate) + Q_DECLARE_PUBLIC(MicaMaterial) public: - explicit Settings(QObject *parent = nullptr); - ~Settings() override; + explicit MicaMaterialPrivate(MicaMaterial *q); + ~MicaMaterialPrivate() override; + + Q_NODISCARD static MicaMaterialPrivate *get(MicaMaterial *q); + Q_NODISCARD static const MicaMaterialPrivate *get(const MicaMaterial *q); public Q_SLOTS: - void saveGeometry(QWindow *window); - Q_NODISCARD bool restoreGeometry(QWindow *window); + void maybeGenerateBlurredWallpaper(const bool force = false); + void updateMaterialBrush(); + void paint(QPainter *painter, const QSize &size, const QPoint &pos) const; + Q_NODISCARD static MicaMaterial *attach(QObject *target); private: - QScopedPointer m_settings; + void initialize(); + +private: + QPointer q_ptr = nullptr; + QColor tintColor = {}; + qreal tintOpacity = 0.0; + qreal noiseOpacity = 0.0; + QBrush micaBrush = {}; }; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(MicaMaterialPrivate)) diff --git a/include/FramelessHelper/Core/utils.h b/include/FramelessHelper/Core/utils.h index 51f8a23..aaa7a35 100644 --- a/include/FramelessHelper/Core/utils.h +++ b/include/FramelessHelper/Core/utils.h @@ -26,9 +26,19 @@ #include "framelesshelpercore_global.h" #include +#include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcUtilsCommon) +#ifdef Q_OS_WINDOWS + Q_DECLARE_LOGGING_CATEGORY(lcUtilsWin) +#elif defined(Q_OS_LINUX) + Q_DECLARE_LOGGING_CATEGORY(lcUtilsLinux) +#elif defined(Q_OS_MACOS) + Q_DECLARE_LOGGING_CATEGORY(lcUtilsMac) +#endif + namespace Utils { @@ -55,6 +65,9 @@ FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter( [[nodiscard]] FRAMELESSHELPER_CORE_API bool isTitleBarColorized(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool setBlurBehindWindowEnabled(const WId windowId, const Global::BlurMode mode, const QColor &color); +[[nodiscard]] FRAMELESSHELPER_CORE_API QString getWallpaperFilePath(); +[[nodiscard]] FRAMELESSHELPER_CORE_API Global::WallpaperAspectStyle getWallpaperAspectStyle(); +[[nodiscard]] FRAMELESSHELPER_CORE_API bool isBlurBehindWindowSupported(); #ifdef Q_OS_WINDOWS [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowsVersionOrGreater(const Global::WindowsVersion version); diff --git a/include/FramelessHelper/Quick/QuickMicaMaterial b/include/FramelessHelper/Quick/QuickMicaMaterial new file mode 100644 index 0000000..24aa4be --- /dev/null +++ b/include/FramelessHelper/Quick/QuickMicaMaterial @@ -0,0 +1 @@ +#include diff --git a/include/FramelessHelper/Quick/framelesshelperquick_global.h b/include/FramelessHelper/Quick/framelesshelperquick_global.h index e074e07..8a0a398 100644 --- a/include/FramelessHelper/Quick/framelesshelperquick_global.h +++ b/include/FramelessHelper/Quick/framelesshelperquick_global.h @@ -25,6 +25,7 @@ #pragma once #include +#include #if __has_include() # include #else @@ -74,6 +75,8 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcQuickGlobal) + [[maybe_unused]] static constexpr const char FRAMELESSHELPER_QUICK_URI[] = "org.wangwenx190.FramelessHelper"; struct FRAMELESSHELPER_QUICK_API QuickGlobal diff --git a/include/FramelessHelper/Quick/framelessquickhelper.h b/include/FramelessHelper/Quick/framelessquickhelper.h index 0cb552a..87ad9a5 100644 --- a/include/FramelessHelper/Quick/framelessquickhelper.h +++ b/include/FramelessHelper/Quick/framelessquickhelper.h @@ -26,9 +26,12 @@ #include "framelesshelperquick_global.h" #include +#include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcFramelessQuickHelper) + class FramelessQuickHelperPrivate; class FRAMELESSHELPER_QUICK_API FramelessQuickHelper : public QQuickItem diff --git a/include/FramelessHelper/Quick/framelessquickmodule.h b/include/FramelessHelper/Quick/framelessquickmodule.h index c1e98c0..3bf22f6 100644 --- a/include/FramelessHelper/Quick/framelessquickmodule.h +++ b/include/FramelessHelper/Quick/framelessquickmodule.h @@ -25,6 +25,7 @@ #pragma once #include "framelesshelperquick_global.h" +#include QT_BEGIN_NAMESPACE class QQmlEngine; @@ -32,6 +33,8 @@ QT_END_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcQuickModule) + namespace FramelessHelper::Quick { FRAMELESSHELPER_QUICK_API void registerTypes(QQmlEngine *engine); diff --git a/include/FramelessHelper/Quick/framelessquickutils.h b/include/FramelessHelper/Quick/framelessquickutils.h index 549c6a9..bdab561 100644 --- a/include/FramelessHelper/Quick/framelessquickutils.h +++ b/include/FramelessHelper/Quick/framelessquickutils.h @@ -26,6 +26,7 @@ #include "framelesshelperquick_global.h" #include +#include #include #include @@ -35,6 +36,8 @@ QT_END_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcFramelessQuickUtils) + class FRAMELESSHELPER_QUICK_API FramelessQuickUtils : public QObject { Q_OBJECT diff --git a/include/FramelessHelper/Quick/private/framelessquickwindow_p_p.h b/include/FramelessHelper/Quick/private/framelessquickwindow_p_p.h index a715102..8976a2b 100644 --- a/include/FramelessHelper/Quick/private/framelessquickwindow_p_p.h +++ b/include/FramelessHelper/Quick/private/framelessquickwindow_p_p.h @@ -75,7 +75,7 @@ private Q_SLOTS: void updateTopBorderHeight(); private: - FramelessQuickWindow *q_ptr = nullptr; + QPointer q_ptr = nullptr; QScopedPointer m_topBorderRectangle; QScopedPointer m_topBorderAnchors; QQuickWindow::Visibility m_savedVisibility = QQuickWindow::Windowed; diff --git a/include/FramelessHelper/Quick/private/quickmicamaterial_p.h b/include/FramelessHelper/Quick/private/quickmicamaterial_p.h new file mode 100644 index 0000000..65d6ac7 --- /dev/null +++ b/include/FramelessHelper/Quick/private/quickmicamaterial_p.h @@ -0,0 +1,66 @@ +/* + * MIT License + * + * Copyright (C) 2022 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 "framelesshelperquick_global.h" +#include +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +class QuickMicaMaterial; +class WallpaperImageNode; + +class FRAMELESSHELPER_QUICK_API QuickMicaMaterialPrivate : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(QuickMicaMaterialPrivate) + Q_DECLARE_PUBLIC(QuickMicaMaterial) + +public: + explicit QuickMicaMaterialPrivate(QuickMicaMaterial *q); + ~QuickMicaMaterialPrivate() override; + + Q_NODISCARD static QuickMicaMaterialPrivate *get(QuickMicaMaterial *q); + Q_NODISCARD static const QuickMicaMaterialPrivate *get(const QuickMicaMaterial *q); + +public Q_SLOTS: + void rebindWindow(); + void forceRegenerateWallpaperImageCache(); + void appendNode(WallpaperImageNode *node); + +private: + void initialize(); + +private: + QPointer q_ptr = nullptr; + QMetaObject::Connection m_rootWindowXChangedConnection = {}; + QMetaObject::Connection m_rootWindowYChangedConnection = {}; + QList> m_nodes = {}; +}; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickMicaMaterialPrivate)) diff --git a/include/FramelessHelper/Quick/quickchromepalette.h b/include/FramelessHelper/Quick/quickchromepalette.h index 3513864..31792ba 100644 --- a/include/FramelessHelper/Quick/quickchromepalette.h +++ b/include/FramelessHelper/Quick/quickchromepalette.h @@ -25,11 +25,14 @@ #pragma once #include "framelesshelperquick_global.h" +#include #include #include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcQuickChromePalette) + class FRAMELESSHELPER_QUICK_API QuickChromePalette : public ChromePalette { Q_OBJECT diff --git a/examples/widget-blur/widget.h b/include/FramelessHelper/Quick/quickmicamaterial.h similarity index 59% rename from examples/widget-blur/widget.h rename to include/FramelessHelper/Quick/quickmicamaterial.h index 550771c..feb9795 100644 --- a/examples/widget-blur/widget.h +++ b/include/FramelessHelper/Quick/quickmicamaterial.h @@ -24,36 +24,38 @@ #pragma once -#include - -QT_BEGIN_NAMESPACE -class QLabel; -QT_END_NAMESPACE +#include "framelesshelperquick_global.h" +#include +#include FRAMELESSHELPER_BEGIN_NAMESPACE -class StandardTitleBar; -FRAMELESSHELPER_END_NAMESPACE -class Widget : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget) +Q_DECLARE_LOGGING_CATEGORY(lcQuickMicaMaterial) + +class QuickMicaMaterialPrivate; + +class FRAMELESSHELPER_QUICK_API QuickMicaMaterial : public QQuickItem { Q_OBJECT - Q_DISABLE_COPY_MOVE(Widget) +#ifdef QML_NAMED_ELEMENT + QML_NAMED_ELEMENT(MicaMaterial) +#endif + Q_DISABLE_COPY_MOVE(QuickMicaMaterial) + Q_DECLARE_PRIVATE(QuickMicaMaterial) public: - explicit Widget(QWidget *parent = nullptr); - ~Widget() override; + explicit QuickMicaMaterial(QQuickItem *parent = nullptr); + ~QuickMicaMaterial() override; protected: - void timerEvent(QTimerEvent *event) override; - void closeEvent(QCloseEvent *event) override; + void itemChange(const ItemChange change, const ItemChangeData &value) override; + [[nodiscard]] QSGNode *updatePaintNode(QSGNode *old, UpdatePaintNodeData *data) override; private: - void initialize(); - -private Q_SLOTS: - void updateStyleSheet(); - -private: - QScopedPointer m_clockLabel; - QScopedPointer m_titleBar; + QScopedPointer d_ptr; }; + +FRAMELESSHELPER_END_NAMESPACE + +Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickMicaMaterial)) +QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickMicaMaterial)) diff --git a/include/FramelessHelper/Widgets/framelesshelperwidgets_global.h b/include/FramelessHelper/Widgets/framelesshelperwidgets_global.h index 8492d87..df89c6a 100644 --- a/include/FramelessHelper/Widgets/framelesshelperwidgets_global.h +++ b/include/FramelessHelper/Widgets/framelesshelperwidgets_global.h @@ -25,6 +25,7 @@ #pragma once #include +#include #ifndef FRAMELESSHELPER_WIDGETS_API # ifdef FRAMELESSHELPER_WIDGETS_STATIC @@ -40,6 +41,8 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcWidgetsGlobal) + namespace FramelessHelper::Widgets { FRAMELESSHELPER_WIDGETS_API void initialize(); diff --git a/include/FramelessHelper/Widgets/framelessmainwindow.h b/include/FramelessHelper/Widgets/framelessmainwindow.h index 36585a5..f13b353 100644 --- a/include/FramelessHelper/Widgets/framelessmainwindow.h +++ b/include/FramelessHelper/Widgets/framelessmainwindow.h @@ -25,10 +25,13 @@ #pragma once #include "framelesshelperwidgets_global.h" +#include #include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcFramelessMainWindow) + class FramelessMainWindowPrivate; class FRAMELESSHELPER_WIDGETS_API FramelessMainWindow : public QMainWindow diff --git a/include/FramelessHelper/Widgets/framelesswidget.h b/include/FramelessHelper/Widgets/framelesswidget.h index 153bed9..de10af1 100644 --- a/include/FramelessHelper/Widgets/framelesswidget.h +++ b/include/FramelessHelper/Widgets/framelesswidget.h @@ -25,10 +25,13 @@ #pragma once #include "framelesshelperwidgets_global.h" +#include #include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcFramelessWidget) + class FramelessWidgetPrivate; class FRAMELESSHELPER_WIDGETS_API FramelessWidget : public QWidget diff --git a/include/FramelessHelper/Widgets/framelesswidgetshelper.h b/include/FramelessHelper/Widgets/framelesswidgetshelper.h index 6ee270c..10e6dfd 100644 --- a/include/FramelessHelper/Widgets/framelesswidgetshelper.h +++ b/include/FramelessHelper/Widgets/framelesswidgetshelper.h @@ -26,9 +26,12 @@ #include "framelesshelperwidgets_global.h" #include +#include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcFramelessWidgetsHelper) + class FramelessWidgetsHelperPrivate; class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelper : public QObject diff --git a/include/FramelessHelper/Widgets/private/framelessmainwindow_p.h b/include/FramelessHelper/Widgets/private/framelessmainwindow_p.h index 5591387..ecb25d0 100644 --- a/include/FramelessHelper/Widgets/private/framelessmainwindow_p.h +++ b/include/FramelessHelper/Widgets/private/framelessmainwindow_p.h @@ -26,6 +26,7 @@ #include "framelesshelperwidgets_global.h" #include +#include FRAMELESSHELPER_BEGIN_NAMESPACE @@ -51,11 +52,13 @@ public: void toggleMaximized(); void toggleFullScreen(); + Q_NODISCARD WidgetsSharedHelper *widgetsSharedHelper() const; + private: void initialize(); private: - FramelessMainWindow *q_ptr = nullptr; + QPointer q_ptr = nullptr; Qt::WindowState m_savedWindowState = Qt::WindowNoState; QScopedPointer m_helper; }; diff --git a/include/FramelessHelper/Widgets/private/framelesswidget_p.h b/include/FramelessHelper/Widgets/private/framelesswidget_p.h index dc1a011..ea6b0ab 100644 --- a/include/FramelessHelper/Widgets/private/framelesswidget_p.h +++ b/include/FramelessHelper/Widgets/private/framelesswidget_p.h @@ -26,6 +26,7 @@ #include "framelesshelperwidgets_global.h" #include +#include FRAMELESSHELPER_BEGIN_NAMESPACE @@ -51,11 +52,13 @@ public: void toggleMaximized(); void toggleFullScreen(); + Q_NODISCARD WidgetsSharedHelper *widgetsSharedHelper() const; + private: void initialize(); private: - FramelessWidget *q_ptr = nullptr; + QPointer q_ptr = nullptr; Qt::WindowState m_savedWindowState = Qt::WindowNoState; QScopedPointer m_helper; }; diff --git a/include/FramelessHelper/Widgets/private/widgetssharedhelper_p.h b/include/FramelessHelper/Widgets/private/widgetssharedhelper_p.h index 89d5cfe..1565809 100644 --- a/include/FramelessHelper/Widgets/private/widgetssharedhelper_p.h +++ b/include/FramelessHelper/Widgets/private/widgetssharedhelper_p.h @@ -27,6 +27,7 @@ #include "framelesshelperwidgets_global.h" #include #include +#include QT_BEGIN_NAMESPACE class QPaintEvent; @@ -34,10 +35,13 @@ QT_END_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE +class MicaMaterial; + class FRAMELESSHELPER_WIDGETS_API WidgetsSharedHelper : public QObject { Q_OBJECT Q_DISABLE_COPY_MOVE(WidgetsSharedHelper) + Q_PROPERTY(bool micaEnabled READ isMicaEnabled WRITE setMicaEnabled NOTIFY micaEnabledChanged FINAL) public: explicit WidgetsSharedHelper(QObject *parent = nullptr); @@ -45,19 +49,32 @@ public: void setup(QWidget *widget); + Q_NODISCARD bool isMicaEnabled() const; + void setMicaEnabled(const bool value); + protected: Q_NODISCARD bool eventFilter(QObject *object, QEvent *event) override; private Q_SLOTS: void updateContentsMargins(); + void handleScreenChanged(QScreen *screen); private: void changeEventHandler(QEvent *event); void paintEventHandler(QPaintEvent *event); Q_NODISCARD bool shouldDrawFrameBorder() const; +Q_SIGNALS: + void micaEnabledChanged(); + private: QPointer m_targetWidget = nullptr; + bool m_micaEnabled = false; + QPointer m_micaMaterial; + QMetaObject::Connection m_micaRedrawConnection = {}; + QPointer m_screen = nullptr; + qreal m_screenDpr = 0.0; + QMetaObject::Connection m_screenDpiChangeConnection = {}; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/include/FramelessHelper/Widgets/standardsystembutton.h b/include/FramelessHelper/Widgets/standardsystembutton.h index e21c146..76c37b6 100644 --- a/include/FramelessHelper/Widgets/standardsystembutton.h +++ b/include/FramelessHelper/Widgets/standardsystembutton.h @@ -25,10 +25,13 @@ #pragma once #include "framelesshelperwidgets_global.h" +#include #include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcStandardSystemButton) + class StandardSystemButtonPrivate; class FRAMELESSHELPER_WIDGETS_API StandardSystemButton : public QAbstractButton diff --git a/include/FramelessHelper/Widgets/standardtitlebar.h b/include/FramelessHelper/Widgets/standardtitlebar.h index d781d8d..dc7696f 100644 --- a/include/FramelessHelper/Widgets/standardtitlebar.h +++ b/include/FramelessHelper/Widgets/standardtitlebar.h @@ -25,11 +25,14 @@ #pragma once #include "framelesshelperwidgets_global.h" +#include #include #include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcStandardTitleBar) + class StandardTitleBarPrivate; class StandardSystemButton; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index d04c985..7c50a23 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -44,6 +44,7 @@ set(PUBLIC_HEADERS ${INCLUDE_PREFIX}/framelessmanager.h ${INCLUDE_PREFIX}/utils.h ${INCLUDE_PREFIX}/chromepalette.h + ${INCLUDE_PREFIX}/micamaterial.h ) set(PUBLIC_HEADERS_ALIAS @@ -52,6 +53,7 @@ set(PUBLIC_HEADERS_ALIAS ${INCLUDE_PREFIX}/FramelessManager ${INCLUDE_PREFIX}/Utils ${INCLUDE_PREFIX}/ChromePalette + ${INCLUDE_PREFIX}/MicaMaterial ) set(PRIVATE_HEADERS @@ -59,6 +61,7 @@ set(PRIVATE_HEADERS ${INCLUDE_PREFIX}/private/framelessconfig_p.h ${INCLUDE_PREFIX}/private/sysapiloader_p.h ${INCLUDE_PREFIX}/private/chromepalette_p.h + ${INCLUDE_PREFIX}/private/micamaterial_p.h ) set(SOURCES @@ -70,6 +73,7 @@ set(SOURCES sysapiloader.cpp chromepalette.cpp global.cpp + micamaterial.cpp ) if(WIN32) @@ -118,38 +122,10 @@ if(FRAMELESSHELPER_BUILD_STATIC) endif() target_compile_definitions(${SUB_PROJ_NAME} PRIVATE - QT_NO_CAST_FROM_ASCII - QT_NO_CAST_TO_ASCII - QT_NO_URL_CAST_FROM_STRING - QT_NO_CAST_FROM_BYTEARRAY QT_NO_KEYWORDS - QT_NO_NARROWING_CONVERSIONS_IN_CONNECT - QT_NO_FOREACH - QT_USE_QSTRINGBUILDER - QT_DEPRECATED_WARNINGS - QT_DISABLE_DEPRECATED_BEFORE=0x060500 FRAMELESSHELPER_CORE_LIBRARY ) -if(MSVC) - set(_WIN32_WINNT_WIN10 0x0A00) - set(NTDDI_WIN10_CO 0x0A00000B) - target_compile_definitions(${SUB_PROJ_NAME} PRIVATE - _CRT_NON_CONFORMING_SWPRINTFS _CRT_SECURE_NO_WARNINGS - _ENABLE_EXTENDED_ALIGNED_STORAGE NOMINMAX UNICODE - _UNICODE WIN32_LEAN_AND_MEAN WINRT_LEAN_AND_MEAN - WINVER=${_WIN32_WINNT_WIN10} _WIN32_WINNT=${_WIN32_WINNT_WIN10} - _WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_CO} - ) - target_compile_options(${SUB_PROJ_NAME} PRIVATE - /utf-8 /W3 /WX # Cannot use /W4 here, Qt's own headers are not warning-clean. - ) -else() - target_compile_options(${SUB_PROJ_NAME} PRIVATE - -Wall -Wextra -Werror - ) -endif() - if(APPLE) target_link_libraries(${SUB_PROJ_NAME} PRIVATE "-framework Foundation" @@ -182,25 +158,6 @@ target_include_directories(${SUB_PROJ_NAME} PUBLIC "$" ) -install(TARGETS ${SUB_PROJ_NAME} - EXPORT ${SUB_PROJ_NAME}Targets - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH} -) - -export(EXPORT ${SUB_PROJ_NAME}Targets - FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${SUB_PROJ_NAME}Targets.cmake" - NAMESPACE ${PROJECT_NAME}:: -) - -install(FILES ${PUBLIC_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}) -install(FILES ${PUBLIC_HEADERS_ALIAS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}) -install(FILES ${PRIVATE_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}/private) - -install(EXPORT ${SUB_PROJ_NAME}Targets - FILE ${SUB_PROJ_NAME}Targets.cmake - NAMESPACE ${PROJECT_NAME}:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} -) +include(cmakehelper.cmake) +setup_compile_params(${SUB_PROJ_NAME}) +setup_package_export(${SUB_PROJ_NAME} ${SUB_PROJ_PATH} "${PUBLIC_HEADERS}" "${PUBLIC_HEADERS_ALIAS}" "${PRIVATE_HEADERS}") diff --git a/src/core/chromepalette.cpp b/src/core/chromepalette.cpp index d192fde..a2d1ebd 100644 --- a/src/core/chromepalette.cpp +++ b/src/core/chromepalette.cpp @@ -29,6 +29,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcChromePalette, "wangwenx190.framelesshelper.core.chromepalette") +#define INFO qCInfo(lcChromePalette) +#define DEBUG qCDebug(lcChromePalette) +#define WARNING qCWarning(lcChromePalette) +#define CRITICAL qCCritical(lcChromePalette) + using namespace Global; ChromePalettePrivate::ChromePalettePrivate(ChromePalette *q) : QObject(q) diff --git a/examples/deployqt.cmake b/src/core/cmakehelper.cmake similarity index 52% rename from examples/deployqt.cmake rename to src/core/cmakehelper.cmake index 14f6625..5ecee89 100644 --- a/examples/deployqt.cmake +++ b/src/core/cmakehelper.cmake @@ -22,7 +22,74 @@ SOFTWARE. ]] -function(deploy_qt_libraries arg_target) +function(setup_compile_params arg_target) + target_compile_definitions(${arg_target} PRIVATE + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_TO_ASCII + QT_NO_URL_CAST_FROM_STRING + QT_NO_CAST_FROM_BYTEARRAY + #QT_NO_KEYWORDS # QtQuick headers still use traditional Qt keywords. + QT_NO_NARROWING_CONVERSIONS_IN_CONNECT + QT_NO_FOREACH + QT_USE_QSTRINGBUILDER + QT_DEPRECATED_WARNINGS + QT_DISABLE_DEPRECATED_BEFORE=0x060500 + ) + if(MSVC) + set(_WIN32_WINNT_WIN10 0x0A00) + set(NTDDI_WIN10_CO 0x0A00000B) + target_compile_definitions(${arg_target} PRIVATE + _CRT_NON_CONFORMING_SWPRINTFS _CRT_SECURE_NO_WARNINGS + _ENABLE_EXTENDED_ALIGNED_STORAGE NOMINMAX UNICODE + _UNICODE WIN32_LEAN_AND_MEAN WINRT_LEAN_AND_MEAN + WINVER=${_WIN32_WINNT_WIN10} _WIN32_WINNT=${_WIN32_WINNT_WIN10} + _WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_CO} + ) + target_compile_options(${arg_target} PRIVATE + /utf-8 /W3 /WX # Cannot use /W4 here, Qt's own headers are not warning-clean. + ) + target_link_options(${arg_target} PRIVATE /WX) + else() + target_compile_options(${arg_target} PRIVATE + -Wall -Wextra -Werror + ) + endif() +endfunction() + +function(setup_gui_app arg_target) + set_target_properties(${arg_target} PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER org.wangwenx190.${PROJECT_NAME}.app + MACOSX_BUNDLE_BUNDLE_VERSION 1.0.0.0 + MACOSX_BUNDLE_SHORT_VERSION_STRING 1.0 + ) +endfunction() + +function(setup_package_export arg_target arg_path arg_public arg_alias arg_private) + include(GNUInstallDirs) + install(TARGETS ${arg_target} + EXPORT ${arg_target}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${arg_path} + ) + export(EXPORT ${arg_target}Targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${arg_target}Targets.cmake" + NAMESPACE ${PROJECT_NAME}:: + ) + install(FILES ${arg_public} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${arg_path}) + install(FILES ${arg_alias} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${arg_path}) + install(FILES ${arg_private} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${arg_path}/private) + install(EXPORT ${arg_target}Targets + FILE ${arg_target}Targets.cmake + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + ) +endfunction() + +function(deploy_qt_runtime arg_target) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) if(NOT DEFINED QT_QMAKE_EXECUTABLE) diff --git a/src/core/framelessconfig.cpp b/src/core/framelessconfig.cpp index 2eacae8..87cdc81 100644 --- a/src/core/framelessconfig.cpp +++ b/src/core/framelessconfig.cpp @@ -30,6 +30,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcFramelessConfig, "wangwenx190.framelesshelper.core.framelessconfig") +#define INFO qCInfo(lcFramelessConfig) +#define DEBUG qCDebug(lcFramelessConfig) +#define WARNING qCWarning(lcFramelessConfig) +#define CRITICAL qCCritical(lcFramelessConfig) + using namespace Global; FRAMELESSHELPER_STRING_CONSTANT2(ConfigFileName, ".framelesshelper.ini") @@ -52,7 +58,9 @@ static const struct {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_CENTER_WINDOW_BEFORE_SHOW"), FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/CenterWindowBeforeShow")}, {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_ENABLE_BLUR_BEHIND_WINDOW"), - FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/EnableBlurBehindWindow")} + FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/EnableBlurBehindWindow")}, + {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_FORCE_NON_NATIVE_BACKGROUND_BLUR"), + FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/ForceNonNativeBackgroundBlur")} }; static constexpr const auto OptionCount = std::size(OptionsTable); @@ -131,14 +139,14 @@ void FramelessConfig::setLoadFromConfigurationFileDisabled(const bool on) g_data()->disableCfgFile = on; } -QVariant FramelessConfig::setInternal(const QString &key, const QVariant &value) +std::optional FramelessConfig::setInternal(const QString &key, const QVariant &value) { Q_ASSERT(!key.isEmpty()); Q_ASSERT(value.isValid()); if (key.isEmpty() || !value.isValid()) { - return {}; + return std::nullopt; } - QVariant previous = {}; + std::optional previous = std::nullopt; QMutexLocker locker(&g_data()->mutex); if (g_data()->internals.contains(key)) { previous = g_data()->internals.value(key); @@ -148,14 +156,17 @@ QVariant FramelessConfig::setInternal(const QString &key, const QVariant &value) return previous; } -QVariant FramelessConfig::getInternal(const QString &key) const +std::optional FramelessConfig::getInternal(const QString &key) const { Q_ASSERT(!key.isEmpty()); if (key.isEmpty()) { - return {}; + return std::nullopt; } QMutexLocker locker(&g_data()->mutex); - return (g_data()->internals.contains(key) ? g_data()->internals.value(key) : QVariant{}); + if (g_data()->internals.contains(key)) { + return g_data()->internals.value(key); + } + return std::nullopt; } FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/framelesshelper_qt.cpp b/src/core/framelesshelper_qt.cpp index 6c24057..385f1b7 100644 --- a/src/core/framelesshelper_qt.cpp +++ b/src/core/framelesshelper_qt.cpp @@ -33,6 +33,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcFramelessHelperQt, "wangwenx190.framelesshelper.core.impl.qt") +#define INFO qCInfo(lcFramelessHelperQt) +#define DEBUG qCDebug(lcFramelessHelperQt) +#define WARNING qCWarning(lcFramelessHelperQt) +#define CRITICAL qCCritical(lcFramelessHelperQt) + using namespace Global; struct QtHelperData @@ -104,9 +110,12 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) // First detect whether we got a theme change event or not, if so, // inform the user the system theme has changed. if (Utils::isThemeChangeEvent(event)) { - FramelessManager *manager = FramelessManager::instance(); - FramelessManagerPrivate *managerPriv = FramelessManagerPrivate::get(manager); - managerPriv->notifySystemThemeHasChangedOrNot(); + // Sometimes the FramelessManager instance may be destroyed already. + if (FramelessManager * const manager = FramelessManager::instance()) { + if (FramelessManagerPrivate * const managerPriv = FramelessManagerPrivate::get(manager)) { + managerPriv->notifySystemThemeHasChangedOrNot(); + } + } return false; } // We are only interested in events that are dispatched to top level windows. diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index e1ffb8a..2e254d2 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -38,27 +38,15 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcFramelessHelperWin, "wangwenx190.framelesshelper.core.impl.win") +#define INFO qCInfo(lcFramelessHelperWin) +#define DEBUG qCDebug(lcFramelessHelperWin) +#define WARNING qCWarning(lcFramelessHelperWin) +#define CRITICAL qCCritical(lcFramelessHelperWin) + using namespace Global; -struct Win32HelperData -{ - SystemParameters params = {}; - bool trackingMouse = false; - WId fallbackTitleBarWindowId = 0; -}; - -struct Win32Helper -{ - QMutex mutex; - QScopedPointer nativeEventFilter; - QHash data = {}; - QHash fallbackTitleBarToParentWindowMapping = {}; -}; - -Q_GLOBAL_STATIC(Win32Helper, g_win32Helper) - -static constexpr const wchar_t FALLBACK_TITLEBAR_CLASS_NAME[] = L"FALLBACK_TITLEBAR_WINDOW_CLASS\0"; - +static constexpr const wchar_t kFallbackTitleBarWindowClass[] = L"FALLBACK_TITLEBAR_WINDOW_CLASS\0"; FRAMELESSHELPER_BYTEARRAY_CONSTANT2(Win32MessageTypeName, "windows_generic_MSG") FRAMELESSHELPER_STRING_CONSTANT(MonitorFromWindow) FRAMELESSHELPER_STRING_CONSTANT(GetMonitorInfoW) @@ -84,6 +72,23 @@ FRAMELESSHELPER_STRING_CONSTANT(SetWindowPos) FRAMELESSHELPER_STRING_CONSTANT(TrackMouseEvent) FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) +struct Win32HelperData +{ + SystemParameters params = {}; + bool trackingMouse = false; + WId fallbackTitleBarWindowId = 0; +}; + +struct Win32Helper +{ + QMutex mutex; + QScopedPointer nativeEventFilter; + QHash data = {}; + QHash fallbackTitleBarToParentWindowMapping = {}; +}; + +Q_GLOBAL_STATIC(Win32Helper, g_win32Helper) + [[nodiscard]] static inline LRESULT CALLBACK FallbackTitleBarWindowProc (const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) { @@ -109,27 +114,6 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) // Hit-testing event should not be considered as a mouse event. const bool isMouseEvent = (((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST)) || ((uMsg >= WM_NCMOUSEMOVE) && (uMsg <= WM_NCXBUTTONDBLCLK))); -#if 0 // Need extra safe guard, otherwise will crash, but since it's not used, just comment them out. - // We only use this fallback title bar window to activate the snap layout feature, if the parent - // window is not resizable, the snap layout feature should also be disabled at the same time, - // hence forward everything to the parent window, we don't need to handle anything here. - if (data.params.isWindowFixedSize()) { - // Let the mouse event pass through our fallback title bar window to the root window - // under it, to ensure our homemade title bar keep functional. - if (uMsg == WM_NCHITTEST) { - return HTTRANSPARENT; - } - // Forward all mouse events to the parent window to let the controls inside - // our homemade title bar still continue to work normally. But ignore these - // events in this fallback title bar window due to there are no controls in it. - if (isMouseEvent) { - SendMessageW(parentWindowHandle, uMsg, wParam, lParam); - return 0; - } - // For all other events just use the default handling. - return DefWindowProcW(hWnd, uMsg, wParam, lParam); - } -#endif const auto releaseButtons = [&data](const std::optional exclude) -> void { static constexpr const auto defaultButtonState = ButtonState::Unspecified; if (!exclude.has_value() || (exclude.value() != SystemButtonType::WindowIcon)) { @@ -170,7 +154,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) const POINT nativeGlobalPos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; POINT nativeLocalPos = nativeGlobalPos; if (ScreenToClient(hWnd, &nativeLocalPos) == FALSE) { - qWarning() << Utils::getSystemErrorMessage(kScreenToClient); + WARNING << Utils::getSystemErrorMessage(kScreenToClient); break; } const qreal devicePixelRatio = data.params.getWindowDevicePixelRatio(); @@ -253,7 +237,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) tme.hwndTrack = hWnd; tme.dwHoverTime = HOVER_DEFAULT; // We don't _really_ care about this. if (TrackMouseEvent(&tme) == FALSE) { - qWarning() << Utils::getSystemErrorMessage(kTrackMouseEvent); + WARNING << Utils::getSystemErrorMessage(kTrackMouseEvent); break; } QMutexLocker locker(&g_win32Helper()->mutex); @@ -368,7 +352,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) const auto parentWindowHandle = reinterpret_cast(parentWindowId); RECT parentWindowClientRect = {}; if (GetClientRect(parentWindowHandle, &parentWindowClientRect) == FALSE) { - qWarning() << Utils::getSystemErrorMessage(kGetClientRect); + WARNING << Utils::getSystemErrorMessage(kGetClientRect); return false; } const int titleBarHeight = Utils::getTitleBarHeight(parentWindowId, true); @@ -385,7 +369,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) // into all the magic behind it. if (SetWindowPos(fallbackTitleBarWindowHandle, HWND_TOP, 0, 0, parentWindowClientRect.right, titleBarHeight, flags) == FALSE) { - qWarning() << Utils::getSystemErrorMessage(kSetWindowPos); + WARNING << Utils::getSystemErrorMessage(kSetWindowPos); return false; } return true; @@ -399,14 +383,14 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) } static const bool isWin10OrGreater = Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1507); if (!isWin10OrGreater) { - qWarning() << "The fallback title bar window is only supported on Windows 10 and onwards."; + WARNING << "The fallback title bar window is only supported on Windows 10 and onwards."; return false; } const auto parentWindowHandle = reinterpret_cast(parentWindowId); const auto instance = static_cast(GetModuleHandleW(nullptr)); Q_ASSERT(instance); if (!instance) { - qWarning() << Utils::getSystemErrorMessage(kGetModuleHandleW); + WARNING << Utils::getSystemErrorMessage(kGetModuleHandleW); return false; } static const ATOM fallbackTitleBarWindowClass = [instance]() -> ATOM { @@ -414,7 +398,7 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) SecureZeroMemory(&wcex, sizeof(wcex)); wcex.cbSize = sizeof(wcex); wcex.style = (CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS); - wcex.lpszClassName = FALLBACK_TITLEBAR_CLASS_NAME; + wcex.lpszClassName = kFallbackTitleBarWindowClass; wcex.hbrBackground = static_cast(GetStockObject(BLACK_BRUSH)); wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW); wcex.lpfnWndProc = FallbackTitleBarWindowProc; @@ -423,24 +407,24 @@ FRAMELESSHELPER_STRING_CONSTANT(FindWindowW) }(); Q_ASSERT(fallbackTitleBarWindowClass); if (!fallbackTitleBarWindowClass) { - qWarning() << Utils::getSystemErrorMessage(kRegisterClassExW); + WARNING << Utils::getSystemErrorMessage(kRegisterClassExW); return false; } const HWND fallbackTitleBarWindowHandle = CreateWindowExW((WS_EX_LAYERED | WS_EX_NOREDIRECTIONBITMAP), - FALLBACK_TITLEBAR_CLASS_NAME, nullptr, WS_CHILD, 0, 0, 0, 0, + kFallbackTitleBarWindowClass, nullptr, WS_CHILD, 0, 0, 0, 0, parentWindowHandle, nullptr, instance, nullptr); Q_ASSERT(fallbackTitleBarWindowHandle); if (!fallbackTitleBarWindowHandle) { - qWarning() << Utils::getSystemErrorMessage(kCreateWindowExW); + WARNING << Utils::getSystemErrorMessage(kCreateWindowExW); return false; } if (SetLayeredWindowAttributes(fallbackTitleBarWindowHandle, 0, 255, LWA_ALPHA) == FALSE) { - qWarning() << Utils::getSystemErrorMessage(kSetLayeredWindowAttributes); + WARNING << Utils::getSystemErrorMessage(kSetLayeredWindowAttributes); return false; } const auto fallbackTitleBarWindowId = reinterpret_cast(fallbackTitleBarWindowHandle); if (!resizeFallbackTitleBarWindow(parentWindowId, fallbackTitleBarWindowId, hide)) { - qWarning() << "Failed to re-position the fallback title bar window."; + WARNING << "Failed to re-position the fallback title bar window."; return false; } QMutexLocker locker(&g_win32Helper()->mutex); @@ -479,9 +463,7 @@ void FramelessHelperWin::addWindow(const SystemParameters ¶ms) static const bool isWin10RS1OrGreater = Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1607); if (isWin10RS1OrGreater) { const bool dark = Utils::shouldAppsUseDarkMode(); -#if (QT_VERSION < QT_VERSION_CHECK(6, 4, 0)) Utils::updateWindowFrameBorderColor(windowId, dark); -#endif static const bool isWin10RS5OrGreater = Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1809); if (isWin10RS5OrGreater) { static const bool isQtQuickApplication = (params.getCurrentApplicationType() == ApplicationType::Quick); @@ -497,7 +479,7 @@ void FramelessHelperWin::addWindow(const SystemParameters ¶ms) // introduced in Windows 11, so it's not necessary to create it on systems below Win11. if (!config->isSet(Option::DisableWindowsSnapLayout)) { if (!createFallbackTitleBarWindow(windowId, data.params.isWindowFixedSize())) { - qWarning() << "Failed to create the fallback title bar window."; + WARNING << "Failed to create the fallback title bar window."; } } } @@ -684,11 +666,11 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me monitorInfo.cbSize = sizeof(monitorInfo); const HMONITOR monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); if (!monitor) { - qWarning() << Utils::getSystemErrorMessage(kMonitorFromWindow); + WARNING << Utils::getSystemErrorMessage(kMonitorFromWindow); break; } if (GetMonitorInfoW(monitor, &monitorInfo) == FALSE) { - qWarning() << Utils::getSystemErrorMessage(kGetMonitorInfoW); + WARNING << Utils::getSystemErrorMessage(kGetMonitorInfoW); break; } // This helper can be used to determine if there's a @@ -716,12 +698,12 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me if (_abd.hWnd) { const HMONITOR windowMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); if (!windowMonitor) { - qWarning() << Utils::getSystemErrorMessage(kMonitorFromWindow); + WARNING << Utils::getSystemErrorMessage(kMonitorFromWindow); break; } const HMONITOR taskbarMonitor = MonitorFromWindow(_abd.hWnd, MONITOR_DEFAULTTOPRIMARY); if (!taskbarMonitor) { - qWarning() << Utils::getSystemErrorMessage(kMonitorFromWindow); + WARNING << Utils::getSystemErrorMessage(kMonitorFromWindow); break; } if (taskbarMonitor == windowMonitor) { @@ -729,7 +711,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me edge = _abd.uEdge; } } else { - qWarning() << Utils::getSystemErrorMessage(kFindWindowW); + WARNING << Utils::getSystemErrorMessage(kFindWindowW); break; } top = (edge == ABE_TOP); @@ -857,7 +839,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me const POINT nativeGlobalPos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; POINT nativeLocalPos = nativeGlobalPos; if (ScreenToClient(hWnd, &nativeLocalPos) == FALSE) { - qWarning() << Utils::getSystemErrorMessage(kScreenToClient); + WARNING << Utils::getSystemErrorMessage(kScreenToClient); break; } const qreal dpr = data.params.getWindowDevicePixelRatio(); @@ -914,7 +896,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me if (!isFixedSize) { RECT clientRect = {0, 0, 0, 0}; if (GetClientRect(hWnd, &clientRect) == FALSE) { - qWarning() << Utils::getSystemErrorMessage(kGetClientRect); + WARNING << Utils::getSystemErrorMessage(kGetClientRect); break; } const LONG width = clientRect.right; @@ -1033,7 +1015,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me SetLastError(ERROR_SUCCESS); const auto oldStyle = static_cast(GetWindowLongPtrW(hWnd, GWL_STYLE)); if (oldStyle == 0) { - qWarning() << Utils::getSystemErrorMessage(kGetWindowLongPtrW); + WARNING << Utils::getSystemErrorMessage(kGetWindowLongPtrW); break; } // Prevent Windows from drawing the default title bar by temporarily @@ -1041,14 +1023,14 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me const DWORD newStyle = (oldStyle & ~WS_VISIBLE); SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(hWnd, GWL_STYLE, static_cast(newStyle)) == 0) { - qWarning() << Utils::getSystemErrorMessage(kSetWindowLongPtrW); + WARNING << Utils::getSystemErrorMessage(kSetWindowLongPtrW); break; } Utils::triggerFrameChange(windowId); const LRESULT ret = DefWindowProcW(hWnd, uMsg, wParam, lParam); SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(hWnd, GWL_STYLE, static_cast(oldStyle)) == 0) { - qWarning() << Utils::getSystemErrorMessage(kSetWindowLongPtrW); + WARNING << Utils::getSystemErrorMessage(kSetWindowLongPtrW); break; } Utils::triggerFrameChange(windowId); @@ -1059,21 +1041,22 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me break; } } - static const bool isWin10OrGreater = Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1507); - if (isWin10OrGreater && data.fallbackTitleBarWindowId) { + static const bool isWin11OrGreater = Utils::isWindowsVersionOrGreater(WindowsVersion::_11_21H2); + if (isWin11OrGreater && data.fallbackTitleBarWindowId) { switch (uMsg) { case WM_SIZE: // Sent to a window after its size has changed. case WM_DISPLAYCHANGE: // Sent to a window when the display resolution has changed. { const bool isFixedSize = data.params.isWindowFixedSize(); if (!resizeFallbackTitleBarWindow(windowId, data.fallbackTitleBarWindowId, isFixedSize)) { - qWarning() << "Failed to re-position the fallback title bar window."; + WARNING << "Failed to re-position the fallback title bar window."; } } break; default: break; } } + const bool wallpaperChanged = ((uMsg == WM_SETTINGCHANGE) && (wParam == SPI_SETDESKWALLPAPER)); bool systemThemeChanged = ((uMsg == WM_THEMECHANGED) || (uMsg == WM_SYSCOLORCHANGE) || (uMsg == WM_DWMCOLORIZATIONCOLORCHANGED)); static const bool isWin10RS1OrGreater = Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1607); @@ -1083,9 +1066,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me && (std::wcscmp(reinterpret_cast(lParam), kThemeSettingChangeEventName) == 0)) { systemThemeChanged = true; const bool dark = Utils::shouldAppsUseDarkMode(); -#if (QT_VERSION < QT_VERSION_CHECK(6, 4, 0)) Utils::updateWindowFrameBorderColor(windowId, dark); -#endif static const bool isWin10RS5OrGreater = Utils::isWindowsVersionOrGreater(WindowsVersion::_10_1809); if (isWin10RS5OrGreater) { static const bool isQtQuickApplication = (data.params.getCurrentApplicationType() == ApplicationType::Quick); @@ -1097,12 +1078,17 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me } } } - if (systemThemeChanged) { - // In some rare cases the FramelessManager instance may be destroyed already. - FramelessManager *manager = FramelessManager::instance(); - if (manager) { - FramelessManagerPrivate *managerPriv = FramelessManagerPrivate::get(manager); - managerPriv->notifySystemThemeHasChangedOrNot(); + if (systemThemeChanged || wallpaperChanged) { + // Sometimes the FramelessManager instance may be destroyed already. + if (FramelessManager * const manager = FramelessManager::instance()) { + if (FramelessManagerPrivate * const managerPriv = FramelessManagerPrivate::get(manager)) { + if (systemThemeChanged) { + managerPriv->notifySystemThemeHasChangedOrNot(); + } + if (wallpaperChanged) { + managerPriv->notifyWallpaperHasChangedOrNot(); + } + } } } return false; diff --git a/src/core/framelesshelpercore.qrc b/src/core/framelesshelpercore.qrc index af9ba55..12aefaf 100644 --- a/src/core/framelesshelpercore.qrc +++ b/src/core/framelesshelpercore.qrc @@ -1,5 +1,6 @@ resources/fonts/Micon.ttf + resources/images/noise.png diff --git a/src/core/framelessmanager.cpp b/src/core/framelessmanager.cpp index d0731de..21b01c4 100644 --- a/src/core/framelessmanager.cpp +++ b/src/core/framelessmanager.cpp @@ -22,7 +22,6 @@ * SOFTWARE. */ -#include "framelessmanager.h" #include "framelessmanager_p.h" #include #include @@ -47,6 +46,12 @@ static inline void initResource() FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcFramelessManager, "wangwenx190.framelesshelper.core.framelessmanager") +#define INFO qCInfo(lcFramelessManager) +#define DEBUG qCDebug(lcFramelessManager) +#define WARNING qCWarning(lcFramelessManager) +#define CRITICAL qCCritical(lcFramelessManager) + using namespace Global; struct FramelessManagerHelper @@ -63,7 +68,11 @@ FRAMELESSHELPER_STRING_CONSTANT2(IconFontFilePath, ":/org.wangwenx190.FramelessH FRAMELESSHELPER_STRING_CONSTANT2(IconFontFamilyName_win11, "Segoe Fluent Icons") FRAMELESSHELPER_STRING_CONSTANT2(IconFontFamilyName_win10, "Segoe MDL2 Assets") FRAMELESSHELPER_STRING_CONSTANT2(IconFontFamilyName_common, "micon_nb") -static constexpr const int kIconFontPointSize = 8; +#ifdef Q_OS_MACOS + static constexpr const int kIconFontPointSize = 10; +#else + static constexpr const int kIconFontPointSize = 8; +#endif [[nodiscard]] static inline QString iconFontFamilyName() { @@ -121,11 +130,12 @@ void FramelessManagerPrivate::initializeIconFont() } inited = true; initResource(); + // We always register this font because it's our only fallback. const int id = QFontDatabase::addApplicationFont(kIconFontFilePath); if (id < 0) { - qWarning() << "Failed to load icon font:" << kIconFontFilePath; + WARNING << "Failed to load icon font:" << kIconFontFilePath; } else { - qDebug() << "Successfully registered icon font:" << QFontDatabase::applicationFontFamilies(id); + DEBUG << "Successfully registered icon font:" << QFontDatabase::applicationFontFamilies(id); } } @@ -142,14 +152,28 @@ QFont FramelessManagerPrivate::getIconFont() SystemTheme FramelessManagerPrivate::systemTheme() const { + QMutexLocker locker(&g_helper()->mutex); return m_systemTheme; } QColor FramelessManagerPrivate::systemAccentColor() const { + QMutexLocker locker(&g_helper()->mutex); return m_accentColor; } +QString FramelessManagerPrivate::wallpaper() const +{ + QMutexLocker locker(&g_helper()->mutex); + return m_wallpaper; +} + +WallpaperAspectStyle FramelessManagerPrivate::wallpaperAspectStyle() const +{ + QMutexLocker locker(&g_helper()->mutex); + return m_wallpaperAspectStyle; +} + void FramelessManagerPrivate::addWindow(const SystemParameters ¶ms) { Q_ASSERT(params.isValid()); @@ -203,11 +227,11 @@ void FramelessManagerPrivate::addWindow(const SystemParameters ¶ms) void FramelessManagerPrivate::notifySystemThemeHasChangedOrNot() { - Q_Q(FramelessManager); + QMutexLocker locker(&g_helper()->mutex); const SystemTheme currentSystemTheme = Utils::getSystemTheme(); #ifdef Q_OS_WINDOWS const DwmColorizationArea currentColorizationArea = Utils::getDwmColorizationArea(); - const QColor currentAccentColor = Utils::getDwmColorizationColor(); + const QColor currentAccentColor = Utils::getDwmAccentColor(); #endif #ifdef Q_OS_LINUX const QColor currentAccentColor = Utils::getWmThemeColor(); @@ -231,16 +255,40 @@ void FramelessManagerPrivate::notifySystemThemeHasChangedOrNot() } #endif if (notify) { + Q_Q(FramelessManager); Q_EMIT q->systemThemeChanged(); + DEBUG << "Detected system theme changed."; + } +} + +void FramelessManagerPrivate::notifyWallpaperHasChangedOrNot() +{ + QMutexLocker locker(&g_helper()->mutex); + const QString currentWallpaper = Utils::getWallpaperFilePath(); + const WallpaperAspectStyle currentWallpaperAspectStyle = Utils::getWallpaperAspectStyle(); + bool notify = false; + if (m_wallpaper != currentWallpaper) { + m_wallpaper = currentWallpaper; + notify = true; + } + if (m_wallpaperAspectStyle != currentWallpaperAspectStyle) { + m_wallpaperAspectStyle = currentWallpaperAspectStyle; + notify = true; + } + if (notify) { + Q_Q(FramelessManager); + Q_EMIT q->wallpaperChanged(); + DEBUG << "Detected wallpaper changed."; } } void FramelessManagerPrivate::initialize() { + QMutexLocker locker(&g_helper()->mutex); m_systemTheme = Utils::getSystemTheme(); #ifdef Q_OS_WINDOWS m_colorizationArea = Utils::getDwmColorizationArea(); - m_accentColor = Utils::getDwmColorizationColor(); + m_accentColor = Utils::getDwmAccentColor(); #endif #ifdef Q_OS_LINUX m_accentColor = Utils::getWmThemeColor(); @@ -248,6 +296,8 @@ void FramelessManagerPrivate::initialize() #ifdef Q_OS_MACOS m_accentColor = Utils::getControlsAccentColor(); #endif + m_wallpaper = Utils::getWallpaperFilePath(); + m_wallpaperAspectStyle = Utils::getWallpaperAspectStyle(); } FramelessManager::FramelessManager(QObject *parent) : @@ -274,6 +324,18 @@ QColor FramelessManager::systemAccentColor() const return d->systemAccentColor(); } +QString FramelessManager::wallpaper() const +{ + Q_D(const FramelessManager); + return d->wallpaper(); +} + +WallpaperAspectStyle FramelessManager::wallpaperAspectStyle() const +{ + Q_D(const FramelessManager); + return d->wallpaperAspectStyle(); +} + void FramelessManager::addWindow(const SystemParameters ¶ms) { Q_D(FramelessManager); diff --git a/src/core/global.cpp b/src/core/global.cpp index bf7c5fe..1186975 100644 --- a/src/core/global.cpp +++ b/src/core/global.cpp @@ -30,10 +30,12 @@ #endif #include "framelesshelper_qt.h" #include "chromepalette.h" +#include "micamaterial.h" #include "sysapiloader_p.h" #include "framelessmanager_p.h" #include "framelessconfig_p.h" #include "chromepalette_p.h" +#include "micamaterial_p.h" #include #ifndef COMPILER_STRING @@ -72,6 +74,12 @@ FRAMELESSHELPER_BYTEARRAY_CONSTANT2(ValueOne, "1") FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcCoreGlobal, "wangwenx190.framelesshelper.core.global") +#define INFO qCInfo(lcCoreGlobal) +#define DEBUG qCDebug(lcCoreGlobal) +#define WARNING qCWarning(lcCoreGlobal) +#define CRITICAL qCCritical(lcCoreGlobal) + using namespace Global; namespace FramelessHelper::Core @@ -141,6 +149,7 @@ void initialize() qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); @@ -155,6 +164,8 @@ void initialize() qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); #endif } diff --git a/src/core/micamaterial.cpp b/src/core/micamaterial.cpp new file mode 100644 index 0000000..02eb42a --- /dev/null +++ b/src/core/micamaterial.cpp @@ -0,0 +1,684 @@ +/* + * MIT License + * + * Copyright (C) 2022 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 "micamaterial.h" +#include "micamaterial_p.h" +#include "framelessmanager.h" +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// The "Q_INIT_RESOURCE()" macro can't be used within a namespace, +// so we wrap it into a separate function outside of the namespace and +// then call it instead inside the namespace, that's also the recommended +// workaround provided by Qt's official documentation. +static inline void initResource() +{ + Q_INIT_RESOURCE(framelesshelpercore); +} + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcMicaMaterial, "wangwenx190.framelesshelper.core.micamaterial") +#define INFO qCInfo(lcMicaMaterial) +#define DEBUG qCDebug(lcMicaMaterial) +#define WARNING qCWarning(lcMicaMaterial) +#define CRITICAL qCCritical(lcMicaMaterial) + +using namespace Global; + +static constexpr const qreal kDefaultTintOpacity = 0.7; +static constexpr const qreal kDefaultNoiseOpacity = 0.04; +static constexpr const qreal kDefaultBlurRadius = 128.0; + +FRAMELESSHELPER_STRING_CONSTANT2(NoiseImageFilePath, ":/org.wangwenx190.FramelessHelper/resources/images/noise.png") + +struct MicaMaterialData +{ + QMutex mutex; + QPixmap blurredWallpaper = {}; +}; + +Q_GLOBAL_STATIC(MicaMaterialData, g_micaMaterialData) + +template +[[nodiscard]] static inline int qt_static_shift(const int value) +{ + if (shift == 0) { + return value; + } + if (shift > 0) { + return (value << (quint32(shift) & 0x1f)); + } + return (value >> (quint32(-shift) & 0x1f)); +} + +template +static inline void qt_blurinner(uchar *bptr, int &zR, int &zG, int &zB, int &zA, const int alpha) +{ + const auto pixel = reinterpret_cast(bptr); + +#define Z_MASK (0xff << zprec) + const int A_zprec = (qt_static_shift(*pixel) & Z_MASK); + const int R_zprec = (qt_static_shift(*pixel) & Z_MASK); + const int G_zprec = (qt_static_shift(*pixel) & Z_MASK); + const int B_zprec = (qt_static_shift(*pixel) & Z_MASK); +#undef Z_MASK + + const int zR_zprec = (zR >> aprec); + const int zG_zprec = (zG >> aprec); + const int zB_zprec = (zB >> aprec); + const int zA_zprec = (zA >> aprec); + + zR += (alpha * (R_zprec - zR_zprec)); + zG += (alpha * (G_zprec - zG_zprec)); + zB += (alpha * (B_zprec - zB_zprec)); + zA += (alpha * (A_zprec - zA_zprec)); + +#define ZA_MASK (0xff << (zprec + aprec)) + *pixel = (qt_static_shift<24 - zprec - aprec>(zA & ZA_MASK) + | qt_static_shift<16 - zprec - aprec>(zR & ZA_MASK) + | qt_static_shift<8 - zprec - aprec>(zG & ZA_MASK) + | qt_static_shift<-zprec - aprec>(zB & ZA_MASK)); +#undef ZA_MASK +} + +static constexpr const int alphaIndex = ((QSysInfo::ByteOrder == QSysInfo::BigEndian) ? 0 : 3); + +template +static inline void qt_blurinner_alphaOnly(uchar *bptr, int &z, const int alpha) +{ + const int A_zprec = (int(*(bptr)) << zprec); + const int z_zprec = (z >> aprec); + z += (alpha * (A_zprec - z_zprec)); + *(bptr) = (z >> (zprec + aprec)); +} + +template +static inline void qt_blurrow(QImage &im, const int line, const int alpha) +{ + uchar *bptr = im.scanLine(line); + + int zR = 0, zG = 0, zB = 0, zA = 0; + + if (alphaOnly && (im.format() != QImage::Format_Indexed8)) { + bptr += alphaIndex; + } + + const int stride = (im.depth() >> 3); + const int im_width = im.width(); + for (int index = 0; index != im_width; ++index) { + if (alphaOnly) { + qt_blurinner_alphaOnly(bptr, zA, alpha); + } else { + qt_blurinner(bptr, zR, zG, zB, zA, alpha); + } + bptr += stride; + } + + bptr -= stride; + + for (int index = (im_width - 2); index >= 0; --index) { + bptr -= stride; + if (alphaOnly) { + qt_blurinner_alphaOnly(bptr, zA, alpha); + } else { + qt_blurinner(bptr, zR, zG, zB, zA, alpha); + } + } +} + +/* +* expblur(QImage &img, int radius) +* +* Based on exponential blur algorithm by Jani Huhtanen +* +* In-place blur of image 'img' with kernel +* of approximate radius 'radius'. +* +* Blurs with two sided exponential impulse +* response. +* +* aprec = precision of alpha parameter +* in fixed-point format 0.aprec +* +* zprec = precision of state parameters +* zR,zG,zB and zA in fp format 8.zprec +*/ +template +static inline void expblur(QImage &img, qreal radius, const bool improvedQuality = false, const int transposed = 0) +{ + Q_ASSERT((img.format() == QImage::Format_ARGB32_Premultiplied) + || (img.format() == QImage::Format_RGB32) + || (img.format() == QImage::Format_Indexed8) + || (img.format() == QImage::Format_Grayscale8)); + if ((img.format() != QImage::Format_ARGB32_Premultiplied) + && (img.format() != QImage::Format_RGB32) + && (img.format() != QImage::Format_Indexed8) + && (img.format() != QImage::Format_Grayscale8)) { + return; + } + + // halve the radius if we're using two passes + if (improvedQuality) { + radius *= 0.5; + } + + // choose the alpha such that pixels at radius distance from a fully + // saturated pixel will have an alpha component of no greater than + // the cutOffIntensity + static constexpr const qreal cutOffIntensity = 2.0; + const int alpha = ((radius <= qreal(1e-5)) ? ((1 << aprec) - 1) : + qRound((1 << aprec) * (1 - qPow(cutOffIntensity / qreal(255), qreal(1) / radius)))); + + int img_height = img.height(); + for (int row = 0; row != img_height; ++row) { + for (int i = 0; i <= int(improvedQuality); ++i) { + qt_blurrow(img, row, alpha); + } + } + + QImage temp(img.height(), img.width(), img.format()); + temp.setDevicePixelRatio(img.devicePixelRatio()); + + if (transposed >= 0) { + if (img.depth() == 8) { + qt_memrotate270(reinterpret_cast(img.bits()), + img.width(), img.height(), img.bytesPerLine(), + reinterpret_cast(temp.bits()), + temp.bytesPerLine()); + } else { + qt_memrotate270(reinterpret_cast(img.bits()), + img.width(), img.height(), img.bytesPerLine(), + reinterpret_cast(temp.bits()), + temp.bytesPerLine()); + } + } else { + if (img.depth() == 8) { + qt_memrotate90(reinterpret_cast(img.bits()), + img.width(), img.height(), img.bytesPerLine(), + reinterpret_cast(temp.bits()), + temp.bytesPerLine()); + } else { + qt_memrotate90(reinterpret_cast(img.bits()), + img.width(), img.height(), img.bytesPerLine(), + reinterpret_cast(temp.bits()), + temp.bytesPerLine()); + } + } + + img_height = temp.height(); + for (int row = 0; row != img_height; ++row) { + for (int i = 0; i <= int(improvedQuality); ++i) { + qt_blurrow(temp, row, alpha); + } + } + + if (transposed == 0) { + if (img.depth() == 8) { + qt_memrotate90(reinterpret_cast(temp.bits()), + temp.width(), temp.height(), temp.bytesPerLine(), + reinterpret_cast(img.bits()), + img.bytesPerLine()); + } else { + qt_memrotate90(reinterpret_cast(temp.bits()), + temp.width(), temp.height(), temp.bytesPerLine(), + reinterpret_cast(img.bits()), + img.bytesPerLine()); + } + } else { + img = temp; + } +} + +#define AVG(a,b) ( ((((a)^(b)) & 0xfefefefeUL) >> 1) + ((a)&(b)) ) +#define AVG16(a,b) ( ((((a)^(b)) & 0xf7deUL) >> 1) + ((a)&(b)) ) + +[[nodiscard]] static inline QImage qt_halfScaled(const QImage &source) +{ + if ((source.width() < 2) || (source.height() < 2)) { + return {}; + } + + QImage srcImage = source; + + if ((source.format() == QImage::Format_Indexed8) + || (source.format() == QImage::Format_Grayscale8)) { + // assumes grayscale + QImage dest(source.width() / 2, source.height() / 2, srcImage.format()); + dest.setDevicePixelRatio(source.devicePixelRatio()); + + auto src = reinterpret_cast(const_cast(srcImage).bits()); + const qsizetype sx = srcImage.bytesPerLine(); + const qsizetype sx2 = (sx << 1); + + auto dst = reinterpret_cast(dest.bits()); + const qsizetype dx = dest.bytesPerLine(); + const int ww = dest.width(); + const int hh = dest.height(); + + for (int y = hh; y; --y, dst += dx, src += sx2) { + const uchar *p1 = src; + const uchar *p2 = (src + sx); + uchar *q = dst; + for (int x = ww; x; --x, ++q, p1 += 2, p2 += 2) { + *q = (((int(p1[0]) + int(p1[1]) + int(p2[0]) + int(p2[1])) + 2) >> 2); + } + } + + return dest; + } + if (source.format() == QImage::Format_ARGB8565_Premultiplied) { + QImage dest(source.width() / 2, source.height() / 2, srcImage.format()); + dest.setDevicePixelRatio(source.devicePixelRatio()); + + auto src = reinterpret_cast(const_cast(srcImage).bits()); + const qsizetype sx = srcImage.bytesPerLine(); + const qsizetype sx2 = (sx << 1); + + auto dst = reinterpret_cast(dest.bits()); + const qsizetype dx = dest.bytesPerLine(); + const int ww = dest.width(); + const int hh = dest.height(); + + for (int y = hh; y; --y, dst += dx, src += sx2) { + const uchar *p1 = src; + const uchar *p2 = (src + sx); + uchar *q = dst; + for (int x = ww; x; --x, q += 3, p1 += 6, p2 += 6) { + // alpha + q[0] = AVG(AVG(p1[0], p1[3]), AVG(p2[0], p2[3])); + // rgb + const quint16 p16_1 = ((p1[2] << 8) | p1[1]); + const quint16 p16_2 = ((p1[5] << 8) | p1[4]); + const quint16 p16_3 = ((p2[2] << 8) | p2[1]); + const quint16 p16_4 = ((p2[5] << 8) | p2[4]); + const quint16 result = AVG16(AVG16(p16_1, p16_2), AVG16(p16_3, p16_4)); + q[1] = (result & 0xff); + q[2] = (result >> 8); + } + } + + return dest; + } + if ((source.format() != QImage::Format_ARGB32_Premultiplied) + && (source.format() != QImage::Format_RGB32)) { + srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied); + } + + QImage dest(source.width() / 2, source.height() / 2, srcImage.format()); + dest.setDevicePixelRatio(source.devicePixelRatio()); + + auto src = reinterpret_cast(const_cast(srcImage).bits()); + const qsizetype sx = (srcImage.bytesPerLine() >> 2); + const qsizetype sx2 = (sx << 1); + + auto dst = reinterpret_cast(dest.bits()); + const qsizetype dx = (dest.bytesPerLine() >> 2); + const int ww = dest.width(); + const int hh = dest.height(); + + for (int y = hh; y; --y, dst += dx, src += sx2) { + const quint32 *p1 = src; + const quint32 *p2 = (src + sx); + quint32 *q = dst; + for (int x = ww; x; --x, q++, p1 += 2, p2 += 2) { + *q = AVG(AVG(p1[0], p1[1]), AVG(p2[0], p2[1])); + } + } + + return dest; +} + +[[maybe_unused]] static inline void qt_blurImage(QPainter *p, QImage &blurImage, + qreal radius, const bool quality, const bool alphaOnly, const int transposed = 0) +{ + if ((blurImage.format() != QImage::Format_ARGB32_Premultiplied) + && (blurImage.format() != QImage::Format_RGB32)) { + blurImage = blurImage.convertToFormat(QImage::Format_ARGB32_Premultiplied); + } + + qreal scale = 1.0; + if ((radius >= 4) && (blurImage.width() >= 2) && (blurImage.height() >= 2)) { + blurImage = qt_halfScaled(blurImage); + scale = 2.0; + radius *= 0.5; + } + + if (alphaOnly) { + expblur<12, 10, true>(blurImage, radius, quality, transposed); + } else { + expblur<12, 10, false>(blurImage, radius, quality, transposed); + } + + if (p) { + p->scale(scale, scale); + p->setRenderHints(QPainter::Antialiasing | + QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)) + const QSize imageSize = blurImage.deviceIndependentSize().toSize(); +#else + const QSize imageSize = QSizeF(QSizeF(blurImage.size()) / blurImage.devicePixelRatio()).toSize(); +#endif + p->drawImage(QRect(QPoint(0, 0), imageSize), blurImage); + } +} + +[[maybe_unused]] static inline void qt_blurImage(QImage &blurImage, + const qreal radius, const bool quality, const int transposed = 0) +{ + if ((blurImage.format() == QImage::Format_Indexed8) + || (blurImage.format() == QImage::Format_Grayscale8)) { + expblur<12, 10, true>(blurImage, radius, quality, transposed); + } else { + expblur<12, 10, false>(blurImage, radius, quality, transposed); + } +} + +/*! + Transforms an \a alignment of Qt::AlignLeft or Qt::AlignRight + without Qt::AlignAbsolute into Qt::AlignLeft or Qt::AlignRight with + Qt::AlignAbsolute according to the layout \a direction. The other + alignment flags are left untouched. + + If no horizontal alignment was specified, the function returns the + default alignment for the given layout \a direction. + + \sa QWidget::layoutDirection +*/ +[[nodiscard]] static inline Qt::Alignment visualAlignment + (const Qt::LayoutDirection direction, const Qt::Alignment alignment) +{ + return QGuiApplicationPrivate::visualAlignment(direction, alignment); +} + +/*! + Returns a new rectangle of the specified \a size that is aligned to the given + \a rectangle according to the specified \a alignment and \a direction. + */ +[[nodiscard]] static inline QRect alignedRect(const Qt::LayoutDirection direction, + Qt::Alignment alignment, const QSize &size, const QRect &rectangle) +{ + alignment = visualAlignment(direction, alignment); + int x = rectangle.x(); + int y = rectangle.y(); + int w = size.width(); + int h = size.height(); + if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter) { + y += ((rectangle.size().height() / 2) - (h / 2)); + } else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom) { + y += (rectangle.size().height() - h); + } + if ((alignment & Qt::AlignRight) == Qt::AlignRight) { + x += (rectangle.size().width() - w); + } else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter) { + x += ((rectangle.size().width() / 2) - (w / 2)); + } + return {x, y, w, h}; +} + +MicaMaterialPrivate::MicaMaterialPrivate(MicaMaterial *q) : QObject(q) +{ + Q_ASSERT(q); + if (!q) { + return; + } + q_ptr = q; + initialize(); +} + +MicaMaterialPrivate::~MicaMaterialPrivate() = default; + +MicaMaterialPrivate *MicaMaterialPrivate::get(MicaMaterial *q) +{ + Q_ASSERT(q); + if (!q) { + return nullptr; + } + return q->d_func(); +} + +const MicaMaterialPrivate *MicaMaterialPrivate::get(const MicaMaterial *q) +{ + Q_ASSERT(q); + if (!q) { + return nullptr; + } + return q->d_func(); +} + +void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force) +{ + g_micaMaterialData()->mutex.lock(); + if (!g_micaMaterialData()->blurredWallpaper.isNull() && !force) { + g_micaMaterialData()->mutex.unlock(); + return; + } + g_micaMaterialData()->mutex.unlock(); + const QSize size = QGuiApplication::primaryScreen()->virtualSize(); + g_micaMaterialData()->mutex.lock(); + g_micaMaterialData()->blurredWallpaper = QPixmap(size); + g_micaMaterialData()->blurredWallpaper.fill(kDefaultTransparentColor); + g_micaMaterialData()->mutex.unlock(); + const QString wallpaperFilePath = Utils::getWallpaperFilePath(); + if (wallpaperFilePath.isEmpty()) { + WARNING << "Failed to retrieve the wallpaper file path."; + return; + } + QImage image(wallpaperFilePath); + if (image.isNull()) { + WARNING << "QImage doesn't support this kind of file:" << wallpaperFilePath; + return; + } + WallpaperAspectStyle aspectStyle = Utils::getWallpaperAspectStyle(); + QImage buffer(size, QImage::Format_ARGB32_Premultiplied); +#ifdef Q_OS_WINDOWS + if (aspectStyle == WallpaperAspectStyle::Center) { + buffer.fill(kDefaultBlackColor); + } +#endif + if ((aspectStyle == WallpaperAspectStyle::Stretch) + || (aspectStyle == WallpaperAspectStyle::Fit) + || (aspectStyle == WallpaperAspectStyle::Fill)) { + Qt::AspectRatioMode mode = Qt::KeepAspectRatioByExpanding; + if (aspectStyle == WallpaperAspectStyle::Stretch) { + mode = Qt::IgnoreAspectRatio; + } else if (aspectStyle == WallpaperAspectStyle::Fit) { + mode = Qt::KeepAspectRatio; + } + QSize newSize = image.size(); + newSize.scale(size, mode); + image = image.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + static constexpr const QPoint desktopOriginPoint = {0, 0}; + const QRect desktopRect = {desktopOriginPoint, size}; + if (aspectStyle == WallpaperAspectStyle::Tile) { + QPainter bufferPainter(&buffer); + const QBrush brush(image); + bufferPainter.fillRect(desktopRect, brush); + } else { + QPainter bufferPainter(&buffer); + const QRect rect = alignedRect(Qt::LeftToRight, Qt::AlignCenter, image.size(), desktopRect); + bufferPainter.drawImage(rect.topLeft(), image); + } + g_micaMaterialData()->mutex.lock(); + QPainter painter(&g_micaMaterialData()->blurredWallpaper); +#if 1 + qt_blurImage(&painter, buffer, kDefaultBlurRadius, true, false); +#else + painter.drawImage(desktopOriginPoint, buffer); +#endif + g_micaMaterialData()->mutex.unlock(); + Q_Q(MicaMaterial); + Q_EMIT q->shouldRedraw(); +} + +void MicaMaterialPrivate::updateMaterialBrush() +{ + initResource(); + static const QImage noiseTexture = QImage(kNoiseImageFilePath); + QImage micaTexture = QImage(QSize(64, 64), QImage::Format_ARGB32_Premultiplied); + QColor fillColor = (Utils::shouldAppsUseDarkMode() ? kDefaultSystemDarkColor : kDefaultSystemLightColor); + fillColor.setAlphaF(0.9f); + micaTexture.fill(fillColor); + QPainter painter(&micaTexture); + painter.setOpacity(tintOpacity); + const QRect rect = {QPoint(0, 0), micaTexture.size()}; + painter.fillRect(rect, tintColor); + painter.setOpacity(noiseOpacity); + painter.fillRect(rect, QBrush(noiseTexture)); + micaBrush = QBrush(micaTexture); + Q_Q(MicaMaterial); + Q_EMIT q->shouldRedraw(); +} + +void MicaMaterialPrivate::paint(QPainter *painter, const QSize &size, const QPoint &pos) const +{ + Q_ASSERT(painter); + if (!painter) { + return; + } + static constexpr const QPoint originPoint = {0, 0}; + painter->save(); + g_micaMaterialData()->mutex.lock(); + painter->drawPixmap(originPoint, g_micaMaterialData()->blurredWallpaper, QRect(pos, size)); + g_micaMaterialData()->mutex.unlock(); + painter->setCompositionMode(QPainter::CompositionMode_SourceOver); + painter->setOpacity(1.0); + painter->fillRect(QRect(originPoint, size), micaBrush); + painter->restore(); +} + +MicaMaterial *MicaMaterialPrivate::attach(QObject *target) +{ + Q_ASSERT(target); + if (!target) { + return nullptr; + } + if (const auto instance = target->findChild()) { + return instance; + } + const auto instance = new MicaMaterial(target); + return instance; +} + +void MicaMaterialPrivate::initialize() +{ + tintColor = kDefaultTransparentColor; + tintOpacity = kDefaultTintOpacity; + noiseOpacity = kDefaultNoiseOpacity; + + connect(FramelessManager::instance(), &FramelessManager::systemThemeChanged, + this, &MicaMaterialPrivate::updateMaterialBrush); + connect(FramelessManager::instance(), &FramelessManager::wallpaperChanged, + this, [this](){ + maybeGenerateBlurredWallpaper(true); + }); + + maybeGenerateBlurredWallpaper(); + updateMaterialBrush(); +} + +MicaMaterial::MicaMaterial(QObject *parent) + : QObject(parent), d_ptr(new MicaMaterialPrivate(this)) +{ +} + +MicaMaterial::~MicaMaterial() = default; + +QColor MicaMaterial::tintColor() const +{ + Q_D(const MicaMaterial); + return d->tintColor; +} + +void MicaMaterial::setTintColor(const QColor &value) +{ + Q_ASSERT(value.isValid()); + if (!value.isValid()) { + return; + } + Q_D(MicaMaterial); + if (d->tintColor == value) { + return; + } + d->tintColor = value; + d->updateMaterialBrush(); + Q_EMIT tintColorChanged(); +} + +qreal MicaMaterial::tintOpacity() const +{ + Q_D(const MicaMaterial); + return d->tintOpacity; +} + +void MicaMaterial::setTintOpacity(const qreal value) +{ + Q_D(MicaMaterial); + if (qFuzzyCompare(d->tintOpacity, value)) { + return; + } + d->tintOpacity = value; + d->updateMaterialBrush(); + Q_EMIT tintOpacityChanged(); +} + +qreal MicaMaterial::noiseOpacity() const +{ + Q_D(const MicaMaterial); + return d->noiseOpacity; +} + +void MicaMaterial::setNoiseOpacity(const qreal value) +{ + Q_D(MicaMaterial); + if (qFuzzyCompare(d->noiseOpacity, value)) { + return; + } + d->noiseOpacity = value; + d->updateMaterialBrush(); + Q_EMIT noiseOpacityChanged(); +} + +void MicaMaterial::paint(QPainter *painter, const QSize &size, const QPoint &pos) const +{ + Q_D(const MicaMaterial); + d->paint(painter, size, pos); +} + +MicaMaterial *MicaMaterial::attach(QObject *target) +{ + return MicaMaterialPrivate::attach(target); +} + +FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/micamaterial.h b/src/core/micamaterial.h new file mode 100644 index 0000000..1f0b6dc --- /dev/null +++ b/src/core/micamaterial.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Core/micamaterial.h" diff --git a/src/core/micamaterial_p.h b/src/core/micamaterial_p.h new file mode 100644 index 0000000..08da739 --- /dev/null +++ b/src/core/micamaterial_p.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Core/private/micamaterial_p.h" diff --git a/src/core/resources/images/noise.png b/src/core/resources/images/noise.png new file mode 100644 index 0000000..a78e829 Binary files /dev/null and b/src/core/resources/images/noise.png differ diff --git a/src/core/sysapiloader.cpp b/src/core/sysapiloader.cpp index 70e7398..9e626a5 100644 --- a/src/core/sysapiloader.cpp +++ b/src/core/sysapiloader.cpp @@ -32,6 +32,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcSysApiLoader, "wangwenx190.framelesshelper.core.sysapiloader") +#define INFO qCInfo(lcSysApiLoader) +#define DEBUG qCDebug(lcSysApiLoader) +#define WARNING qCWarning(lcSysApiLoader) +#define CRITICAL qCCritical(lcSysApiLoader) + Q_GLOBAL_STATIC(SysApiLoader, g_sysApiLoader) SysApiLoader::SysApiLoader(QObject *parent) : QObject(parent) @@ -83,11 +89,11 @@ bool SysApiLoader::isAvailable(const QString &library, const QString &function) const QFunctionPointer symbol = SysApiLoader::resolve(library, function); if (symbol) { m_functionCache.insert(function, symbol); - qDebug() << "Successfully loaded" << function << "from" << library; + DEBUG << "Successfully loaded" << function << "from" << library; return true; } m_functionCache.insert(function, std::nullopt); - qWarning() << "Failed to load" << function << "from" << library; + WARNING << "Failed to load" << function << "from" << library; return false; } diff --git a/src/core/utils.cpp b/src/core/utils.cpp index 782c125..7c93cf0 100644 --- a/src/core/utils.cpp +++ b/src/core/utils.cpp @@ -34,6 +34,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcUtilsCommon, "wangwenx190.framelesshelper.core.utils.common") +#define INFO qCInfo(lcUtilsCommon) +#define DEBUG qCDebug(lcUtilsCommon) +#define WARNING qCWarning(lcUtilsCommon) +#define CRITICAL qCCritical(lcUtilsCommon) + using namespace Global; struct FONT_ICON @@ -125,7 +131,7 @@ QString Utils::getSystemButtonIconCode(const SystemButtonType button) { const auto index = static_cast(button); if (!g_fontIconsTable.contains(index)) { - qWarning() << "FIXME: Add FONT_ICON value for button" << button; + WARNING << "FIXME: Add FONT_ICON value for button" << button; return {}; } const FONT_ICON icon = g_fontIconsTable.value(index); @@ -211,6 +217,10 @@ bool Utils::isThemeChangeEvent(const QEvent * const event) if (!event) { return false; } + // QGuiApplication will only deliver theme change events to top level QWindow(QQuickWindow)s, + // QWidgets won't get such notifications, no matter whether it's top level widget or not. + // QEvent::ThemeChange: Send by the Windows QPA. + // QEvent::ApplicationPaletteChange: All other platforms (Linux & macOS). const QEvent::Type type = event->type(); return ((type == QEvent::ThemeChange) || (type == QEvent::ApplicationPaletteChange)); } @@ -229,7 +239,7 @@ QColor Utils::calculateSystemButtonBackgroundColor(const SystemButtonType button } if (isTitleColor) { #ifdef Q_OS_WINDOWS - return getDwmColorizationColor(); + return getDwmAccentColor(); #endif #ifdef Q_OS_LINUX return getWmThemeColor(); diff --git a/src/core/utils_linux.cpp b/src/core/utils_linux.cpp index 2220f44..707bb9e 100644 --- a/src/core/utils_linux.cpp +++ b/src/core/utils_linux.cpp @@ -23,6 +23,7 @@ */ #include "utils.h" +#include "framelessconfig_p.h" #include #include #include @@ -41,6 +42,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcUtilsLinux, "wangwenx190.framelesshelper.core.utils.linux") +#define INFO qCInfo(lcUtilsLinux) +#define DEBUG qCDebug(lcUtilsLinux) +#define WARNING qCWarning(lcUtilsLinux) +#define CRITICAL qCCritical(lcUtilsLinux) + using namespace Global; using Display = struct _XDisplay; @@ -478,4 +485,36 @@ bool Utils::shouldAppsUseDarkMode_linux() return false; } +bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, const QColor &color) +{ + Q_UNUSED(windowId); + Q_UNUSED(mode); + Q_UNUSED(color); + return false; +} + +QString Utils::getWallpaperFilePath() +{ + // ### TODO + return {}; +} + +WallpaperAspectStyle Utils::getWallpaperAspectStyle() +{ + // ### TODO + return WallpaperAspectStyle::Fill; +} + +bool Utils::isBlurBehindWindowSupported() +{ + static const bool result = []() -> bool { + if (FramelessConfig::instance()->isSet(Option::ForceNonNativeBackgroundBlur)) { + return false; + } + // Currently not supported due to the desktop environments vary too much. + return false; + }(); + return result; +} + FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/utils_mac.mm b/src/core/utils_mac.mm index 81b12b5..8db135b 100644 --- a/src/core/utils_mac.mm +++ b/src/core/utils_mac.mm @@ -24,10 +24,16 @@ #include "utils.h" #include "framelessmanager.h" +#include "framelessconfig_p.h" #include #include #include #include +#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) +# include +#else +# include +#endif #include #include #include @@ -38,6 +44,12 @@ QT_END_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcUtilsMac, "wangwenx190.framelesshelper.core.utils.mac") +#define INFO qCInfo(lcUtilsMac) +#define DEBUG qCDebug(lcUtilsMac) +#define WARNING qCWarning(lcUtilsMac) +#define CRITICAL qCCritical(lcUtilsMac) + using namespace Global; class NSWindowProxy : public QObject @@ -508,7 +520,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, if ((mode == BlurMode::Disable) || (mode == BlurMode::Default)) { return mode; } - qWarning() << "The BlurMode::Windows_* enum values are not supported on macOS."; + WARNING << "The BlurMode::Windows_* enum values are not supported on macOS."; return BlurMode::Default; }(); NSWindowProxy * const proxy = ensureWindowProxy(windowId); @@ -516,6 +528,56 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, return true; } +QString Utils::getWallpaperFilePath() +{ +#if 0 + const NSWorkspace * const sharedWorkspace = [NSWorkspace sharedWorkspace]; + if (!sharedWorkspace) { + WARNING << "Failed to retrieve the shared workspace."; + return {}; + } + NSScreen * const mainScreen = [NSScreen mainScreen]; + if (!mainScreen) { + WARNING << "Failed to retrieve the main screen."; + return {}; + } + const NSURL * const url = [sharedWorkspace desktopImageURLForScreen:mainScreen]; + if (!url) { + WARNING << "Failed to retrieve the desktop image URL."; + return {}; + } + const QUrl path = QUrl::fromNSURL(url); + if (!path.isValid()) { + WARNING << "The converted QUrl is not valid."; + return {}; + } + return path.toLocalFile(); +#else + // ### TODO + return {}; +#endif +} + +WallpaperAspectStyle Utils::getWallpaperAspectStyle() +{ + return WallpaperAspectStyle::Stretch; +} + +bool Utils::isBlurBehindWindowSupported() +{ + static const bool result = []() -> bool { + if (FramelessConfig::instance()->isSet(Option::ForceNonNativeBackgroundBlur)) { + return false; + } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) + return (QOperatingSystemVersion::current() >= QOperatingSystemVersion::OSXYosemite); +#else + return (QSysInfo::macVersion() >= QSysInfo::MV_YOSEMITE); +#endif + }(); + return result; +} + FRAMELESSHELPER_END_NAMESPACE #include "utils_mac.moc" diff --git a/src/core/utils_win.cpp b/src/core/utils_win.cpp index 12c6503..2725bc2 100644 --- a/src/core/utils_win.cpp +++ b/src/core/utils_win.cpp @@ -44,29 +44,19 @@ Q_DECLARE_METATYPE(QMargins) FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcUtilsWin, "wangwenx190.framelesshelper.core.utils.win") +#define INFO qCInfo(lcUtilsWin) +#define DEBUG qCDebug(lcUtilsWin) +#define WARNING qCWarning(lcUtilsWin) +#define CRITICAL qCCritical(lcUtilsWin) + using namespace Global; -struct Win32UtilsHelperData -{ - WNDPROC originalWindowProc = nullptr; - IsWindowFixedSizeCallback isWindowFixedSize = nullptr; - IsInsideTitleBarDraggableAreaCallback isInTitleBarArea = nullptr; - GetWindowDevicePixelRatioCallback getDevicePixelRatio = nullptr; -}; - -struct Win32UtilsHelper -{ - QMutex mutex; - QHash data = {}; -}; - -Q_GLOBAL_STATIC(Win32UtilsHelper, g_utilsHelper) - static constexpr const wchar_t kDummyWindowClassName[] = L"FRAMELESSHELPER_DUMMY_WINDOW_CLASS"; static const QString qDwmColorKeyName = QString::fromWCharArray(kDwmColorKeyName); FRAMELESSHELPER_STRING_CONSTANT2(SuccessMessageText, "The operation completed successfully.") -FRAMELESSHELPER_STRING_CONSTANT2(FormatMessageEmptyResult, "\"FormatMessageW()\" returned empty string.") -FRAMELESSHELPER_STRING_CONSTANT2(ErrorMessageTemplate, "Function \"%1()\" failed with error code %2: %3.") +FRAMELESSHELPER_STRING_CONSTANT2(EmptyMessageText, "FormatMessageW() returned empty string.") +FRAMELESSHELPER_STRING_CONSTANT2(ErrorMessageTemplate, "Function %1() failed with error code %2: %3.") FRAMELESSHELPER_STRING_CONSTANT(Composition) FRAMELESSHELPER_STRING_CONSTANT(ColorizationColor) FRAMELESSHELPER_STRING_CONSTANT(AppsUseLightTheme) @@ -140,6 +130,24 @@ FRAMELESSHELPER_STRING_CONSTANT(RegisterClassExW) FRAMELESSHELPER_STRING_CONSTANT(CreateWindowExW) FRAMELESSHELPER_STRING_CONSTANT(AccentColor) FRAMELESSHELPER_STRING_CONSTANT(GetScaleFactorForMonitor) +FRAMELESSHELPER_STRING_CONSTANT(WallpaperStyle) +FRAMELESSHELPER_STRING_CONSTANT(TileWallpaper) + +struct Win32UtilsHelperData +{ + WNDPROC originalWindowProc = nullptr; + IsWindowFixedSizeCallback isWindowFixedSize = nullptr; + IsInsideTitleBarDraggableAreaCallback isInTitleBarArea = nullptr; + GetWindowDevicePixelRatioCallback getDevicePixelRatio = nullptr; +}; + +struct Win32UtilsHelper +{ + QMutex mutex; + QHash data = {}; +}; + +Q_GLOBAL_STATIC(Win32UtilsHelper, g_utilsHelper) struct SYSTEM_METRIC { @@ -241,12 +249,18 @@ private: return key; } +[[nodiscard]] static inline QString desktopRegistryKey() +{ + static const QString key = (hkcuRegistryKey() + u'\\' + QString::fromWCharArray(kDesktopRegistryKey)); + return key; +} + [[nodiscard]] static inline HWND ensureDummyWindow() { static const HWND hwnd = []() -> HWND { const HMODULE instance = GetModuleHandleW(nullptr); if (!instance) { - qWarning() << Utils::getSystemErrorMessage(kGetModuleHandleW); + WARNING << Utils::getSystemErrorMessage(kGetModuleHandleW); return nullptr; } WNDCLASSEXW wcex; @@ -257,13 +271,13 @@ private: wcex.lpszClassName = kDummyWindowClassName; const ATOM atom = RegisterClassExW(&wcex); if (!atom) { - qWarning() << Utils::getSystemErrorMessage(kRegisterClassExW); + WARNING << Utils::getSystemErrorMessage(kRegisterClassExW); return nullptr; } const HWND window = CreateWindowExW(0, kDummyWindowClassName, nullptr, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, nullptr, nullptr, instance, nullptr); if (!window) { - qWarning() << Utils::getSystemErrorMessage(kCreateWindowExW); + WARNING << Utils::getSystemErrorMessage(kCreateWindowExW); return nullptr; } return window; @@ -325,7 +339,7 @@ private: LPWSTR buf = nullptr; if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&buf), 0, nullptr) == 0) { - return kFormatMessageEmptyResult; + return kEmptyMessageText; } const QString errorText = QString::fromWCharArray(buf).trimmed(); LocalFree(buf); @@ -366,7 +380,7 @@ private: return 0; } if (!g_systemMetricsTable.contains(index)) { - qWarning() << "FIXME: Add SYSTEM_METRIC value for index" << index; + WARNING << "FIXME: Add SYSTEM_METRIC value for index" << index; return 0; } const SYSTEM_METRIC systemMetric = g_systemMetricsTable.value(index); @@ -393,7 +407,7 @@ private: DPI_CASE(432) DPI_CASE(480) default: - qWarning() << "Unsupported DPI value:" << dpi; + WARNING << "Unsupported DPI value:" << dpi; return 0; } #undef DPI_CASE @@ -447,7 +461,7 @@ private: const auto getNativeGlobalPosFromKeyboard = [hWnd, windowId]() -> QPoint { RECT windowPos = {}; if (GetWindowRect(hWnd, &windowPos) == FALSE) { - qWarning() << Utils::getSystemErrorMessage(kGetWindowRect); + WARNING << Utils::getSystemErrorMessage(kGetWindowRect); return {}; } const bool maxOrFull = (IsMaximized(hWnd) || Utils::isFullScreen(windowId)); @@ -485,7 +499,7 @@ private: if (data.isInTitleBarArea(qtScenePos)) { POINT pos = {nativeLocalPos.x(), nativeLocalPos.y()}; if (ClientToScreen(hWnd, &pos) == FALSE) { - qWarning() << Utils::getSystemErrorMessage(kClientToScreen); + WARNING << Utils::getSystemErrorMessage(kClientToScreen); break; } shouldShowSystemMenu = true; @@ -561,7 +575,7 @@ bool Utils::isDwmCompositionEnabled() BOOL enabled = FALSE; const HRESULT hr = API_CALL_FUNCTION(DwmIsCompositionEnabled, &enabled); if (FAILED(hr)) { - qWarning() << __getSystemErrorMessage(kDwmIsCompositionEnabled, hr); + WARNING << __getSystemErrorMessage(kDwmIsCompositionEnabled, hr); return resultFromRegistry(); } return (enabled != FALSE); @@ -576,7 +590,7 @@ void Utils::triggerFrameChange(const WId windowId) const auto hwnd = reinterpret_cast(windowId); static constexpr const UINT flags = (SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER); if (SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, flags) == FALSE) { - qWarning() << getSystemErrorMessage(kSetWindowPos); + WARNING << getSystemErrorMessage(kSetWindowPos); } } @@ -604,7 +618,7 @@ void Utils::updateWindowFrameMargins(const WId windowId, const bool reset) const auto hwnd = reinterpret_cast(windowId); const HRESULT hr = API_CALL_FUNCTION(DwmExtendFrameIntoClientArea, hwnd, &margins); if (FAILED(hr)) { - qWarning() << __getSystemErrorMessage(kDwmExtendFrameIntoClientArea, hr); + WARNING << __getSystemErrorMessage(kDwmExtendFrameIntoClientArea, hr); return; } triggerFrameChange(windowId); @@ -636,14 +650,14 @@ void Utils::updateInternalWindowFrameMargins(QWindow *window, const bool enable) if (QPlatformWindow *platformWindow = window->handle()) { QGuiApplication::platformNativeInterface()->setWindowProperty(platformWindow, kWindowsCustomMargins, marginsVar); } else { - qWarning() << "Failed to retrieve the platform window."; + WARNING << "Failed to retrieve the platform window."; return; } #else if (const auto platformWindow = dynamic_cast(window->handle())) { platformWindow->setCustomMargins(margins); } else { - qWarning() << "Failed to retrieve the platform window."; + WARNING << "Failed to retrieve the platform window."; return; } #endif @@ -678,7 +692,7 @@ QColor Utils::getDwmColorizationColor() BOOL opaque = FALSE; const HRESULT hr = API_CALL_FUNCTION(DwmGetColorizationColor, &color, &opaque); if (FAILED(hr)) { - qWarning() << __getSystemErrorMessage(kDwmGetColorizationColor, hr); + WARNING << __getSystemErrorMessage(kDwmGetColorizationColor, hr); return resultFromRegistry(); } return QColor::fromRgba(color); @@ -762,7 +776,7 @@ void Utils::showSystemMenu(const WId windowId, const QPoint &pos, const bool sel // Send the command that the user choses to the corresponding window. if (PostMessageW(hWnd, WM_SYSCOMMAND, result, 0) == FALSE) { - qWarning() << getSystemErrorMessage(kPostMessageW); + WARNING << getSystemErrorMessage(kPostMessageW); } } @@ -775,21 +789,21 @@ bool Utils::isFullScreen(const WId windowId) const auto hwnd = reinterpret_cast(windowId); RECT wndRect = {}; if (GetWindowRect(hwnd, &wndRect) == FALSE) { - qWarning() << getSystemErrorMessage(kGetWindowRect); + WARNING << getSystemErrorMessage(kGetWindowRect); return false; } // According to Microsoft Docs, we should compare to the primary screen's geometry // (if we can't determine the correct screen of our window). const HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); if (!mon) { - qWarning() << getSystemErrorMessage(kMonitorFromWindow); + WARNING << getSystemErrorMessage(kMonitorFromWindow); return false; } MONITORINFO mi; SecureZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); if (GetMonitorInfoW(mon, &mi) == FALSE) { - qWarning() << getSystemErrorMessage(kGetMonitorInfoW); + WARNING << getSystemErrorMessage(kGetMonitorInfoW); return false; } // Compare to the full area of the screen, not the work area. @@ -809,7 +823,7 @@ bool Utils::isWindowNoState(const WId windowId) SecureZeroMemory(&wp, sizeof(wp)); wp.length = sizeof(wp); // This line is important! Don't miss it! if (GetWindowPlacement(hwnd, &wp) == FALSE) { - qWarning() << getSystemErrorMessage(kGetWindowPlacement); + WARNING << getSystemErrorMessage(kGetWindowPlacement); return false; } return ((wp.showCmd == SW_NORMAL) || (wp.showCmd == SW_RESTORE)); @@ -836,22 +850,22 @@ void Utils::syncWmPaintWithDwm() // Dirty hack to workaround the resize flicker caused by DWM. LARGE_INTEGER freq = {}; if (QueryPerformanceFrequency(&freq) == FALSE) { - qWarning() << getSystemErrorMessage(kQueryPerformanceFrequency); + WARNING << getSystemErrorMessage(kQueryPerformanceFrequency); return; } TIMECAPS tc = {}; if (API_CALL_FUNCTION(timeGetDevCaps, &tc, sizeof(tc)) != MMSYSERR_NOERROR) { - qWarning() << "timeGetDevCaps() failed."; + WARNING << "timeGetDevCaps() failed."; return; } const UINT ms_granularity = tc.wPeriodMin; if (API_CALL_FUNCTION(timeBeginPeriod, ms_granularity) != TIMERR_NOERROR) { - qWarning() << "timeBeginPeriod() failed."; + WARNING << "timeBeginPeriod() failed."; return; } LARGE_INTEGER now0 = {}; if (QueryPerformanceCounter(&now0) == FALSE) { - qWarning() << getSystemErrorMessage(kQueryPerformanceCounter); + WARNING << getSystemErrorMessage(kQueryPerformanceCounter); return; } // ask DWM where the vertical blank falls @@ -860,12 +874,12 @@ void Utils::syncWmPaintWithDwm() dti.cbSize = sizeof(dti); const HRESULT hr = API_CALL_FUNCTION(DwmGetCompositionTimingInfo, nullptr, &dti); if (FAILED(hr)) { - qWarning() << getSystemErrorMessage(kDwmGetCompositionTimingInfo); + WARNING << getSystemErrorMessage(kDwmGetCompositionTimingInfo); return; } LARGE_INTEGER now1 = {}; if (QueryPerformanceCounter(&now1) == FALSE) { - qWarning() << getSystemErrorMessage(kQueryPerformanceCounter); + WARNING << getSystemErrorMessage(kQueryPerformanceCounter); return; } // - DWM told us about SOME vertical blank @@ -887,7 +901,7 @@ void Utils::syncWmPaintWithDwm() const qreal m_ms = (1000.0 * qreal(m) / qreal(freq.QuadPart)); Sleep(static_cast(qRound(m_ms))); if (API_CALL_FUNCTION(timeEndPeriod, ms_granularity) != TIMERR_NOERROR) { - qWarning() << "timeEndPeriod() failed."; + WARNING << "timeEndPeriod() failed."; } } @@ -897,7 +911,7 @@ bool Utils::isHighContrastModeEnabled() SecureZeroMemory(&hc, sizeof(hc)); hc.cbSize = sizeof(hc); if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, 0) == FALSE) { - qWarning() << getSystemErrorMessage(kSystemParametersInfoW); + WARNING << getSystemErrorMessage(kSystemParametersInfoW); return false; } return (hc.dwFlags & HCF_HIGHCONTRASTON); @@ -921,10 +935,10 @@ quint32 Utils::getPrimaryScreenDpi(const bool horizontal) if (SUCCEEDED(hr) && (dpiX > 0) && (dpiY > 0)) { return (horizontal ? dpiX : dpiY); } else { - qWarning() << __getSystemErrorMessage(kGetDpiForMonitor, hr); + WARNING << __getSystemErrorMessage(kGetDpiForMonitor, hr); } } else { - qWarning() << getSystemErrorMessage(kMonitorFromPoint); + WARNING << getSystemErrorMessage(kMonitorFromPoint); } } if (API_SHCORE_AVAILABLE(GetScaleFactorForMonitor)) { @@ -935,10 +949,10 @@ quint32 Utils::getPrimaryScreenDpi(const bool horizontal) if (SUCCEEDED(hr) && (factor != DEVICE_SCALE_FACTOR_INVALID)) { return quint32(qRound(qreal(USER_DEFAULT_SCREEN_DPI) * qreal(factor) / qreal(100))); } else { - qWarning() << __getSystemErrorMessage(kGetScaleFactorForMonitor, hr); + WARNING << __getSystemErrorMessage(kGetScaleFactorForMonitor, hr); } } else { - qWarning() << getSystemErrorMessage(kMonitorFromPoint); + WARNING << getSystemErrorMessage(kMonitorFromPoint); } } if (API_D2D_AVAILABLE(D2D1CreateFactory)) { @@ -960,13 +974,13 @@ quint32 Utils::getPrimaryScreenDpi(const bool horizontal) if ((dpiX > 0.0f) && (dpiY > 0.0f)) { return (horizontal ? quint32(qRound(dpiX)) : quint32(qRound(dpiY))); } else { - qWarning() << "GetDesktopDpi() failed."; + WARNING << "GetDesktopDpi() failed."; } } else { - qWarning() << __getSystemErrorMessage(kReloadSystemMetrics, hr); + WARNING << __getSystemErrorMessage(kReloadSystemMetrics, hr); } } else { - qWarning() << __getSystemErrorMessage(kD2D1CreateFactory, hr); + WARNING << __getSystemErrorMessage(kD2D1CreateFactory, hr); } } const HDC hdc = GetDC(nullptr); @@ -977,16 +991,16 @@ quint32 Utils::getPrimaryScreenDpi(const bool horizontal) if ((dpiX > 0) && (dpiY > 0)) { valid = true; } else { - qWarning() << getSystemErrorMessage(kGetDeviceCaps); + WARNING << getSystemErrorMessage(kGetDeviceCaps); } if (ReleaseDC(nullptr, hdc) == 0) { - qWarning() << getSystemErrorMessage(kReleaseDC); + WARNING << getSystemErrorMessage(kReleaseDC); } if (valid) { return (horizontal ? dpiX : dpiY); } } else { - qWarning() << getSystemErrorMessage(kGetDC); + WARNING << getSystemErrorMessage(kGetDC); } return USER_DEFAULT_SCREEN_DPI; } @@ -1003,7 +1017,7 @@ quint32 Utils::getWindowDpi(const WId windowId, const bool horizontal) if (dpi > 0) { return dpi; } else { - qWarning() << getSystemErrorMessage(kGetDpiForWindow); + WARNING << getSystemErrorMessage(kGetDpiForWindow); } } if (API_USER_AVAILABLE(GetSystemDpiForProcess)) { @@ -1011,7 +1025,7 @@ quint32 Utils::getWindowDpi(const WId windowId, const bool horizontal) if (dpi > 0) { return dpi; } else { - qWarning() << getSystemErrorMessage(kGetSystemDpiForProcess); + WARNING << getSystemErrorMessage(kGetSystemDpiForProcess); } } if (API_USER_AVAILABLE(GetDpiForSystem)) { @@ -1019,7 +1033,7 @@ quint32 Utils::getWindowDpi(const WId windowId, const bool horizontal) if (dpi > 0) { return dpi; } else { - qWarning() << getSystemErrorMessage(kGetDpiForSystem); + WARNING << getSystemErrorMessage(kGetDpiForSystem); } } return getPrimaryScreenDpi(horizontal); @@ -1127,7 +1141,7 @@ void Utils::updateWindowFrameBorderColor(const WId windowId, const bool dark) const BOOL value = (dark ? TRUE : FALSE); const HRESULT hr = API_CALL_FUNCTION(DwmSetWindowAttribute, hwnd, mode, &value, sizeof(value)); if (FAILED(hr)) { - qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); + WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); } } @@ -1141,7 +1155,7 @@ void Utils::fixupQtInternals(const WId windowId) SetLastError(ERROR_SUCCESS); const auto oldClassStyle = static_cast(GetClassLongPtrW(hwnd, GCL_STYLE)); if (oldClassStyle == 0) { - qWarning() << getSystemErrorMessage(kGetClassLongPtrW); + WARNING << getSystemErrorMessage(kGetClassLongPtrW); return; } // CS_HREDRAW/CS_VREDRAW will trigger a repaint event when the window size changes @@ -1152,13 +1166,13 @@ void Utils::fixupQtInternals(const WId windowId) const DWORD newClassStyle = (oldClassStyle & ~(CS_HREDRAW | CS_VREDRAW)); SetLastError(ERROR_SUCCESS); if (SetClassLongPtrW(hwnd, GCL_STYLE, static_cast(newClassStyle)) == 0) { - qWarning() << getSystemErrorMessage(kSetClassLongPtrW); + WARNING << getSystemErrorMessage(kSetClassLongPtrW); return; } SetLastError(ERROR_SUCCESS); const auto oldWindowStyle = static_cast(GetWindowLongPtrW(hwnd, GWL_STYLE)); if (oldWindowStyle == 0) { - qWarning() << getSystemErrorMessage(kGetWindowLongPtrW); + WARNING << getSystemErrorMessage(kGetWindowLongPtrW); return; } // Qt by default adds the "WS_POPUP" flag to all Win32 windows it created and maintained, @@ -1171,7 +1185,7 @@ void Utils::fixupQtInternals(const WId windowId) const DWORD newWindowStyle = ((oldWindowStyle & ~WS_POPUP) | WS_OVERLAPPED); SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(hwnd, GWL_STYLE, static_cast(newWindowStyle)) == 0) { - qWarning() << getSystemErrorMessage(kSetWindowLongPtrW); + WARNING << getSystemErrorMessage(kSetWindowLongPtrW); return; } triggerFrameChange(windowId); @@ -1188,12 +1202,12 @@ void Utils::startSystemMove(QWindow *window, const QPoint &globalPos) window->startSystemMove(); #else if (ReleaseCapture() == FALSE) { - qWarning() << getSystemErrorMessage(kReleaseCapture); + WARNING << getSystemErrorMessage(kReleaseCapture); return; } const auto hwnd = reinterpret_cast(window->winId()); if (PostMessageW(hwnd, WM_SYSCOMMAND, 0xF012 /*SC_DRAGMOVE*/, 0) == FALSE) { - qWarning() << getSystemErrorMessage(kPostMessageW); + WARNING << getSystemErrorMessage(kPostMessageW); } #endif } @@ -1212,12 +1226,12 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoi window->startSystemResize(edges); #else if (ReleaseCapture() == FALSE) { - qWarning() << getSystemErrorMessage(kReleaseCapture); + WARNING << getSystemErrorMessage(kReleaseCapture); return; } const auto hwnd = reinterpret_cast(window->winId()); if (PostMessageW(hwnd, WM_SYSCOMMAND, qtEdgesToWin32Orientation(edges), 0) == FALSE) { - qWarning() << getSystemErrorMessage(kPostMessageW); + WARNING << getSystemErrorMessage(kPostMessageW); } #endif } @@ -1273,12 +1287,12 @@ void Utils::installSystemMenuHook(const WId windowId, const auto originalWindowProc = reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_WNDPROC)); Q_ASSERT(originalWindowProc); if (!originalWindowProc) { - qWarning() << getSystemErrorMessage(kGetWindowLongPtrW); + WARNING << getSystemErrorMessage(kGetWindowLongPtrW); return; } SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast(SystemMenuHookWindowProc)) == 0) { - qWarning() << getSystemErrorMessage(kSetWindowLongPtrW); + WARNING << getSystemErrorMessage(kSetWindowLongPtrW); return; } //triggerFrameChange(windowId); // Crash @@ -1308,7 +1322,7 @@ void Utils::uninstallSystemMenuHook(const WId windowId) const auto hwnd = reinterpret_cast(windowId); SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast(data.originalWindowProc)) == 0) { - qWarning() << getSystemErrorMessage(kSetWindowLongPtrW); + WARNING << getSystemErrorMessage(kSetWindowLongPtrW); return; } //triggerFrameChange(windowId); // Crash @@ -1330,7 +1344,7 @@ void Utils::tryToBeCompatibleWithQtFramelessWindowHint(const WId windowId, SetLastError(ERROR_SUCCESS); const LONG_PTR originalWindowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); if (originalWindowStyle == 0) { - qWarning() << getSystemErrorMessage(kGetWindowLongPtrW); + WARNING << getSystemErrorMessage(kGetWindowLongPtrW); return; } const Qt::WindowFlags originalWindowFlags = getWindowFlags(); @@ -1341,7 +1355,7 @@ void Utils::tryToBeCompatibleWithQtFramelessWindowHint(const WId windowId, // flag to the extended window style. SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(hwnd, GWL_STYLE, originalWindowStyle) == 0) { - qWarning() << getSystemErrorMessage(kSetWindowLongPtrW); + WARNING << getSystemErrorMessage(kSetWindowLongPtrW); return; } triggerFrameChange(windowId); @@ -1357,7 +1371,7 @@ void Utils::setAeroSnappingEnabled(const WId windowId, const bool enable) SetLastError(ERROR_SUCCESS); const auto oldWindowStyle = static_cast(GetWindowLongPtrW(hwnd, GWL_STYLE)); if (oldWindowStyle == 0) { - qWarning() << getSystemErrorMessage(kGetWindowLongPtrW); + WARNING << getSystemErrorMessage(kGetWindowLongPtrW); return; } // The key is the existence of the "WS_THICKFRAME" flag. @@ -1372,7 +1386,7 @@ void Utils::setAeroSnappingEnabled(const WId windowId, const bool enable) }(); SetLastError(ERROR_SUCCESS); if (SetWindowLongPtrW(hwnd, GWL_STYLE, static_cast(newWindowStyle)) == 0) { - qWarning() << getSystemErrorMessage(kSetWindowLongPtrW); + WARNING << getSystemErrorMessage(kSetWindowLongPtrW); return; } triggerFrameChange(windowId); @@ -1394,11 +1408,11 @@ void Utils::tryToEnableHighestDpiAwarenessLevel() // Any attempt to change the DPI awareness level through API will always fail, // so we treat this situation as succeeded. if (dwError == ERROR_ACCESS_DENIED) { - qDebug() << "FramelessHelper doesn't have access to change the current process's DPI awareness level," - " most likely due to it has been set externally already."; + DEBUG << "FramelessHelper doesn't have access to change the current process's DPI awareness level," + " most likely due to it has been set externally already."; return true; } - qWarning() << __getSystemErrorMessage(kSetProcessDpiAwarenessContext, dwError); + WARNING << __getSystemErrorMessage(kSetProcessDpiAwarenessContext, dwError); return false; }; if (SetProcessDpiAwarenessContext2(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) { @@ -1424,11 +1438,11 @@ void Utils::tryToEnableHighestDpiAwarenessLevel() // Any attempt to change the DPI awareness level through API will always fail, // so we treat this situation as succeeded. if (hr == E_ACCESSDENIED) { - qDebug() << "FramelessHelper doesn't have access to change the current process's DPI awareness level," - " most likely due to it has been set externally already."; + DEBUG << "FramelessHelper doesn't have access to change the current process's DPI awareness level," + " most likely due to it has been set externally already."; return true; } - qWarning() << __getSystemErrorMessage(kSetProcessDpiAwareness, hr); + WARNING << __getSystemErrorMessage(kSetProcessDpiAwareness, hr); return false; }; if (SetProcessDpiAwareness2(PROCESS_PER_MONITOR_DPI_AWARE_V2)) { @@ -1445,7 +1459,7 @@ void Utils::tryToEnableHighestDpiAwarenessLevel() // issue by always load it dynamically at runtime. if (API_USER_AVAILABLE(SetProcessDPIAware)) { if (API_CALL_FUNCTION(SetProcessDPIAware) == FALSE) { - qWarning() << getSystemErrorMessage(kSetProcessDPIAware); + WARNING << getSystemErrorMessage(kSetProcessDPIAware); } } } @@ -1480,7 +1494,7 @@ void Utils::updateGlobalWin32ControlsTheme(const WId windowId, const bool dark) const HRESULT hr = API_CALL_FUNCTION(SetWindowTheme, hwnd, (dark ? kSystemDarkThemeResourceName : kSystemLightThemeResourceName), nullptr); if (FAILED(hr)) { - qWarning() << __getSystemErrorMessage(kSetWindowTheme, hr); + WARNING << __getSystemErrorMessage(kSetWindowTheme, hr); } } @@ -1527,7 +1541,7 @@ void Utils::forceSquareCornersForWindow(const WId windowId, const bool force) const _DWM_WINDOW_CORNER_PREFERENCE wcp = (force ? _DWMWCP_DONOTROUND : _DWMWCP_ROUND); const HRESULT hr = API_CALL_FUNCTION(DwmSetWindowAttribute, hwnd, _DWMWA_WINDOW_CORNER_PREFERENCE, &wcp, sizeof(wcp)); if (FAILED(hr)) { - qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); + WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); } } @@ -1552,8 +1566,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, const auto pSetWindowCompositionAttribute = reinterpret_cast( SysApiLoader::instance()->get(kSetWindowCompositionAttribute)); - //static const bool isWin11Insider2OrGreater = doCompareWindowsVersion({10, 0, 22557}); - static const bool isWin11Insider1OrGreater = doCompareWindowsVersion({10, 0, 22523}); + static const bool isWin1122H2OrGreater = isWindowsVersionOrGreater(WindowsVersion::_11_22H2); static const bool isWin11OrGreater = isWindowsVersionOrGreater(WindowsVersion::_11_21H2); static const bool isWin10OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1507); const BlurMode blurMode = [mode]() -> BlurMode { @@ -1561,15 +1574,15 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, return mode; } if ((mode == BlurMode::Windows_Mica) && !isWin11OrGreater) { - qWarning() << "The Mica material is not supported on your system, fallback to the Acrylic blur instead..."; + WARNING << "The Mica material is not supported on your system, fallback to the Acrylic blur instead..."; if (isWin10OrGreater) { return BlurMode::Windows_Acrylic; } - qWarning() << "The Acrylic blur is not supported on your system, fallback to the traditional DWM blur instead..."; + WARNING << "The Acrylic blur is not supported on your system, fallback to the traditional DWM blur instead..."; return BlurMode::Windows_Aero; } if ((mode == BlurMode::Windows_Acrylic) && !isWin10OrGreater) { - qWarning() << "The Acrylic blur is not supported on your system, fallback to the traditional DWM blur instead..."; + WARNING << "The Acrylic blur is not supported on your system, fallback to the traditional DWM blur instead..."; return BlurMode::Windows_Aero; } if (mode == BlurMode::Default) { @@ -1585,12 +1598,12 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, return mode; }(); if (blurMode == BlurMode::Disable) { - if (isWin11Insider1OrGreater) { + if (isWin1122H2OrGreater) { const _DWM_SYSTEMBACKDROP_TYPE dwmsbt = _DWMSBT_NONE; const HRESULT hr = API_CALL_FUNCTION(DwmSetWindowAttribute, hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &dwmsbt, sizeof(dwmsbt)); if (FAILED(hr)) { - qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); + WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); } } if (isWin11OrGreater) { @@ -1598,47 +1611,44 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, HRESULT hr = API_CALL_FUNCTION(DwmSetWindowAttribute, hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable)); if (FAILED(hr)) { - qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); + WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); } const MARGINS margins = {0, 0, 0, 0}; hr = API_CALL_FUNCTION(DwmExtendFrameIntoClientArea, hwnd, &margins); if (FAILED(hr)) { - qWarning() << __getSystemErrorMessage(kDwmExtendFrameIntoClientArea, hr); + WARNING << __getSystemErrorMessage(kDwmExtendFrameIntoClientArea, hr); + } + } else { + ACCENT_POLICY policy; + SecureZeroMemory(&policy, sizeof(policy)); + policy.State = ACCENT_DISABLED; + WINDOWCOMPOSITIONATTRIBDATA wcad; + SecureZeroMemory(&wcad, sizeof(wcad)); + wcad.Attrib = WCA_ACCENT_POLICY; + wcad.pvData = &policy; + wcad.cbData = sizeof(policy); + if (pSetWindowCompositionAttribute(hwnd, &wcad) == FALSE) { + WARNING << getSystemErrorMessage(kSetWindowCompositionAttribute); } - } - ACCENT_POLICY policy; - SecureZeroMemory(&policy, sizeof(policy)); - policy.State = ACCENT_DISABLED; - WINDOWCOMPOSITIONATTRIBDATA wcad; - SecureZeroMemory(&wcad, sizeof(wcad)); - wcad.Attrib = WCA_ACCENT_POLICY; - wcad.pvData = &policy; - wcad.cbData = sizeof(policy); - if (pSetWindowCompositionAttribute(hwnd, &wcad) == FALSE) { - qWarning() << getSystemErrorMessage(kSetWindowCompositionAttribute); } return true; } else { if (blurMode == BlurMode::Windows_Mica) { + // By giving a negative value, DWM will extend the window frame into the whole + // client area. We need this step because the Mica material can only be applied + // to the non-client area of a window. Without this step, you'll get a window + // with a pure black background. const MARGINS margins = {-1, -1, -1, -1}; HRESULT hr = API_CALL_FUNCTION(DwmExtendFrameIntoClientArea, hwnd, &margins); if (SUCCEEDED(hr)) { - if (isWin11Insider1OrGreater) { - // ### FIXME: Is it necessary to enable the host backdrop brush in the first place? To be checked. - const BOOL enable = TRUE; + if (isWin1122H2OrGreater) { + const _DWM_SYSTEMBACKDROP_TYPE dwmsbt = _DWMSBT_MAINWINDOW; // Mica hr = API_CALL_FUNCTION(DwmSetWindowAttribute, hwnd, - _DWMWA_USE_HOSTBACKDROPBRUSH, &enable, sizeof(enable)); + _DWMWA_SYSTEMBACKDROP_TYPE, &dwmsbt, sizeof(dwmsbt)); if (SUCCEEDED(hr)) { - const _DWM_SYSTEMBACKDROP_TYPE dwmsbt = _DWMSBT_MAINWINDOW; // Mica - hr = API_CALL_FUNCTION(DwmSetWindowAttribute, hwnd, - _DWMWA_SYSTEMBACKDROP_TYPE, &dwmsbt, sizeof(dwmsbt)); - if (SUCCEEDED(hr)) { - return true; - } else { - qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); - } + return true; } else { - qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); + WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); } } else { const BOOL enable = TRUE; @@ -1647,11 +1657,11 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, if (SUCCEEDED(hr)) { return true; } else { - qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); + WARNING << __getSystemErrorMessage(kDwmSetWindowAttribute, hr); } } } else { - qWarning() << __getSystemErrorMessage(kDwmExtendFrameIntoClientArea, hr); + WARNING << __getSystemErrorMessage(kDwmExtendFrameIntoClientArea, hr); } } else { ACCENT_POLICY policy; @@ -1664,7 +1674,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, return color; } QColor clr = (shouldAppsUseDarkMode() ? kDefaultSystemDarkColor : kDefaultSystemLightColor); - clr.setAlpha(230); // 90% opacity. + clr.setAlphaF(0.9f); return clr; }(); // This API expects the #AABBGGRR format. @@ -1682,15 +1692,15 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, wcad.cbData = sizeof(policy); if (pSetWindowCompositionAttribute(hwnd, &wcad) != FALSE) { if (!isWin11OrGreater) { - qDebug() << "Enabling the Acrylic blur for Win32 windows on Windows 10 " - "is very buggy. The only recommended way by Microsoft is to " - "use the XAML Island technology or use pure UWP instead. If " - "you find your window becomes very laggy during moving and " - "resizing, please disable the Acrylic blur immediately."; + DEBUG << "Enabling the Acrylic blur for Win32 windows on Windows 10 " + "is very buggy. The only recommended way by Microsoft is to " + "use the XAML Island technology or use pure UWP instead. If " + "you find your window becomes very laggy during moving and " + "resizing, please disable the Acrylic blur immediately."; } return true; } - qWarning() << getSystemErrorMessage(kSetWindowCompositionAttribute); + WARNING << getSystemErrorMessage(kSetWindowCompositionAttribute); } } } else { @@ -1705,7 +1715,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, return FALSE; } if ((mode != BlurMode::Default) && (mode != BlurMode::Windows_Aero)) { - qWarning() << "The only supported blur mode on Windows 7 is the traditional DWM blur."; + WARNING << "The only supported blur mode on Windows 7 is the traditional DWM blur."; } return TRUE; }(); @@ -1713,7 +1723,7 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, if (SUCCEEDED(hr)) { return true; } - qWarning() << __getSystemErrorMessage(kDwmEnableBlurBehindWindow, hr); + WARNING << __getSystemErrorMessage(kDwmEnableBlurBehindWindow, hr); } } return false; @@ -1721,6 +1731,12 @@ bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, QColor Utils::getDwmAccentColor() { + // According to my test, this AccentColor will be exactly the same with + // ColorizationColor, what's the meaning of it? But Microsoft products + // usually read this setting instead of using DwmGetColorizationColor(), + // so we'd better also do the same thing. + // There's no Windows API to get this value, so we can only read it + // directly from the registry. const QSettings registry(dwmRegistryKey(), QSettings::NativeFormat); bool ok = false; const DWORD value = registry.value(kAccentColor).toULongLong(&ok); @@ -1733,4 +1749,57 @@ QColor Utils::getDwmAccentColor() return QColor(abgr.blue(), abgr.green(), abgr.red(), abgr.alpha()); } +QString Utils::getWallpaperFilePath() +{ + wchar_t path[MAX_PATH] = {}; + if (SystemParametersInfoW(SPI_GETDESKWALLPAPER, MAX_PATH, path, 0) == FALSE) { + WARNING << getSystemErrorMessage(kSystemParametersInfoW); + return {}; + } + return QString::fromWCharArray(path); +} + +WallpaperAspectStyle Utils::getWallpaperAspectStyle() +{ + static constexpr const auto defaultStyle = WallpaperAspectStyle::Fill; + const QSettings registry(desktopRegistryKey(), QSettings::NativeFormat); + bool ok = false; + const DWORD wallpaperStyle = registry.value(kWallpaperStyle).toULongLong(&ok); + if (!ok) { + return defaultStyle; + } + switch (wallpaperStyle) { + case 0: { + ok = false; + const DWORD tileWallpaper = registry.value(kTileWallpaper).toULongLong(&ok); + if (ok && (tileWallpaper != 0)) { + return WallpaperAspectStyle::Tile; + } + return WallpaperAspectStyle::Center; + } + case 2: + return WallpaperAspectStyle::Stretch; // Ignore aspect ratio to fill. + case 6: + return WallpaperAspectStyle::Fit; // Keep aspect ratio to fill, but don't expand/crop. + case 10: + return WallpaperAspectStyle::Fill; // Keep aspect ratio to fill, expand/crop if necessary. + case 22: + return WallpaperAspectStyle::Span; // ??? + default: + return defaultStyle; + } +} + +bool Utils::isBlurBehindWindowSupported() +{ + static const bool result = []() -> bool { + if (FramelessConfig::instance()->isSet(Option::ForceNonNativeBackgroundBlur)) { + return false; + } + static const bool isWin11OrGreater = isWindowsVersionOrGreater(WindowsVersion::_11_21H2); + return isWin11OrGreater; + }(); + return result; +} + FRAMELESSHELPER_END_NAMESPACE diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt index 81fd6a2..7125ff3 100644 --- a/src/quick/CMakeLists.txt +++ b/src/quick/CMakeLists.txt @@ -37,6 +37,7 @@ set(PUBLIC_HEADERS ${INCLUDE_PREFIX}/framelessquickhelper.h ${INCLUDE_PREFIX}/framelessquickutils.h ${INCLUDE_PREFIX}/quickchromepalette.h + ${INCLUDE_PREFIX}/quickmicamaterial.h ) set(PUBLIC_HEADERS_ALIAS @@ -45,6 +46,7 @@ set(PUBLIC_HEADERS_ALIAS ${INCLUDE_PREFIX}/FramelessQuickHelper ${INCLUDE_PREFIX}/FramelessQuickUtils ${INCLUDE_PREFIX}/QuickChromePalette + ${INCLUDE_PREFIX}/QuickMicaMaterial ) set(PRIVATE_HEADERS @@ -53,6 +55,7 @@ set(PRIVATE_HEADERS ${INCLUDE_PREFIX}/private/framelessquickhelper_p.h ${INCLUDE_PREFIX}/private/framelessquickwindow_p.h ${INCLUDE_PREFIX}/private/framelessquickwindow_p_p.h + ${INCLUDE_PREFIX}/private/quickmicamaterial_p.h ) set(SOURCES @@ -64,6 +67,7 @@ set(SOURCES framelessquickhelper.cpp quickchromepalette.cpp global.cpp + quickmicamaterial.cpp ) if(WIN32 AND NOT FRAMELESSHELPER_BUILD_STATIC) @@ -134,38 +138,9 @@ if(FRAMELESSHELPER_BUILD_STATIC) endif() target_compile_definitions(${SUB_PROJ_NAME} PRIVATE - QT_NO_CAST_FROM_ASCII - QT_NO_CAST_TO_ASCII - QT_NO_URL_CAST_FROM_STRING - QT_NO_CAST_FROM_BYTEARRAY - #QT_NO_KEYWORDS # Some private Qt Quick headers are not keyword-clean, so sad :( - QT_NO_NARROWING_CONVERSIONS_IN_CONNECT - QT_NO_FOREACH - QT_USE_QSTRINGBUILDER - QT_DEPRECATED_WARNINGS - QT_DISABLE_DEPRECATED_BEFORE=0x060500 FRAMELESSHELPER_QUICK_LIBRARY ) -if(MSVC) - set(_WIN32_WINNT_WIN10 0x0A00) - set(NTDDI_WIN10_CO 0x0A00000B) - target_compile_definitions(${SUB_PROJ_NAME} PRIVATE - _CRT_NON_CONFORMING_SWPRINTFS _CRT_SECURE_NO_WARNINGS - _ENABLE_EXTENDED_ALIGNED_STORAGE NOMINMAX UNICODE - _UNICODE WIN32_LEAN_AND_MEAN WINRT_LEAN_AND_MEAN - WINVER=${_WIN32_WINNT_WIN10} _WIN32_WINNT=${_WIN32_WINNT_WIN10} - _WIN32_IE=${_WIN32_WINNT_WIN10} NTDDI_VERSION=${NTDDI_WIN10_CO} - ) - target_compile_options(${SUB_PROJ_NAME} PRIVATE - /utf-8 /W3 /WX # Cannot use /W4 here, Qt's own headers are not warning-clean. - ) -else() - target_compile_options(${SUB_PROJ_NAME} PRIVATE - -Wall -Wextra -Werror - ) -endif() - target_link_libraries(${SUB_PROJ_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::QuickPrivate Qt${QT_VERSION_MAJOR}::QuickTemplates2Private @@ -183,25 +158,6 @@ target_include_directories(${SUB_PROJ_NAME} PUBLIC "$" ) -install(TARGETS ${SUB_PROJ_NAME} - EXPORT ${SUB_PROJ_NAME}Targets - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH} -) - -export(EXPORT ${SUB_PROJ_NAME}Targets - FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${SUB_PROJ_NAME}Targets.cmake" - NAMESPACE ${PROJECT_NAME}:: -) - -install(FILES ${PUBLIC_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}) -install(FILES ${PUBLIC_HEADERS_ALIAS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}) -install(FILES ${PRIVATE_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}/private) - -install(EXPORT ${SUB_PROJ_NAME}Targets - FILE ${SUB_PROJ_NAME}Targets.cmake - NAMESPACE ${PROJECT_NAME}:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} -) +include(../core/cmakehelper.cmake) +setup_compile_params(${SUB_PROJ_NAME}) +setup_package_export(${SUB_PROJ_NAME} ${SUB_PROJ_PATH} "${PUBLIC_HEADERS}" "${PUBLIC_HEADERS_ALIAS}" "${PRIVATE_HEADERS}") diff --git a/src/quick/framelessquickhelper.cpp b/src/quick/framelessquickhelper.cpp index 1dfb770..82ba11f 100644 --- a/src/quick/framelessquickhelper.cpp +++ b/src/quick/framelessquickhelper.cpp @@ -24,6 +24,7 @@ #include "framelessquickhelper.h" #include "framelessquickhelper_p.h" +#include "quickmicamaterial.h" #include #include #include @@ -33,6 +34,7 @@ # include // For QWINDOWSIZE_MAX #endif #include +#include #include #include #include @@ -40,6 +42,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcFramelessQuickHelper, "wangwenx190.framelesshelper.quick.framelessquickhelper") +#define INFO qCInfo(lcFramelessQuickHelper) +#define DEBUG qCDebug(lcFramelessQuickHelper) +#define WARNING qCWarning(lcFramelessQuickHelper) +#define CRITICAL qCCritical(lcFramelessQuickHelper) + using namespace Global; struct QuickHelperData @@ -63,6 +71,24 @@ struct QuickHelper Q_GLOBAL_STATIC(QuickHelper, g_quickHelper) +[[nodiscard]] static inline QuickMicaMaterial *findMicaMaterialItem(const QQuickWindow * const window) +{ + Q_ASSERT(window); + if (!window) { + return nullptr; + } + QQuickItem * const rootItem = window->contentItem(); + if (const auto item = rootItem->findChild()) { + return item; + } + const auto item = new QuickMicaMaterial; + item->setParent(rootItem); + item->setParentItem(rootItem); + item->setZ(-999); // Make sure it always stays on the bottom. + QQuickItemPrivate::get(item)->anchors()->setFill(rootItem); + return item; +} + FramelessQuickHelperPrivate::FramelessQuickHelperPrivate(FramelessQuickHelper *q) : QObject(q) { Q_ASSERT(q); @@ -393,26 +419,32 @@ void FramelessQuickHelperPrivate::setBlurBehindWindowEnabled(const bool value, c if (m_blurBehindWindowEnabled == value) { return; } - QuickGlobal::BlurMode mode = QuickGlobal::BlurMode::Disable; - if (value) { - if (!m_savedWindowBackgroundColor.isValid()) { - m_savedWindowBackgroundColor = window->color(); + if (Utils::isBlurBehindWindowSupported()) { + QuickGlobal::BlurMode mode = QuickGlobal::BlurMode::Disable; + if (value) { + if (!m_savedWindowBackgroundColor.isValid()) { + m_savedWindowBackgroundColor = window->color(); + } + window->setColor(kDefaultTransparentColor); + mode = QuickGlobal::BlurMode::Default; + } else { + if (m_savedWindowBackgroundColor.isValid()) { + window->setColor(m_savedWindowBackgroundColor); + m_savedWindowBackgroundColor = {}; + } + mode = QuickGlobal::BlurMode::Disable; + } + if (Utils::setBlurBehindWindowEnabled(window->winId(), + FRAMELESSHELPER_ENUM_QUICK_TO_CORE(BlurMode, mode), color)) { + m_blurBehindWindowEnabled = value; + Q_EMIT q->blurBehindWindowEnabledChanged(); + } else { + WARNING << "Failed to enable/disable blur behind window."; } - window->setColor(kDefaultTransparentColor); - mode = QuickGlobal::BlurMode::Default; } else { - if (m_savedWindowBackgroundColor.isValid()) { - window->setColor(m_savedWindowBackgroundColor); - m_savedWindowBackgroundColor = {}; - } - mode = QuickGlobal::BlurMode::Disable; - } - if (Utils::setBlurBehindWindowEnabled(window->winId(), - FRAMELESSHELPER_ENUM_QUICK_TO_CORE(BlurMode, mode), color)) { m_blurBehindWindowEnabled = value; + findMicaMaterialItem(window)->setVisible(m_blurBehindWindowEnabled); Q_EMIT q->blurBehindWindowEnabledChanged(); - } else { - qWarning() << "Failed to enable/disable blur behind window."; } } diff --git a/src/quick/framelessquickmodule.cpp b/src/quick/framelessquickmodule.cpp index 2d36f9a..0c0757f 100644 --- a/src/quick/framelessquickmodule.cpp +++ b/src/quick/framelessquickmodule.cpp @@ -26,12 +26,14 @@ #include "framelessquickhelper.h" #include "framelessquickutils.h" #include "quickchromepalette.h" +#include "quickmicamaterial.h" #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) # include "quickstandardsystembutton_p.h" # include "quickstandardtitlebar_p.h" # include "framelessquickwindow_p.h" # include "framelessquickwindow_p_p.h" # include "framelessquickhelper_p.h" +# include "quickmicamaterial_p.h" #else // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) # include #endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) @@ -50,6 +52,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcQuickModule, "wangwenx190.framelesshelper.quick.quickmodule") +#define INFO qCInfo(lcQuickModule) +#define DEBUG qCDebug(lcQuickModule) +#define WARNING qCWarning(lcQuickModule) +#define CRITICAL qCCritical(lcQuickModule) + void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) { Q_ASSERT(engine); @@ -74,6 +82,7 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) qRegisterMetaType(); qRegisterMetaType(); + #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) qRegisterMetaType(); qRegisterMetaType(); @@ -83,6 +92,8 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); #endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) qmlRegisterUncreatableType(QUICK_URI_FULL, "FramelessHelperConstants", @@ -100,6 +111,8 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine) qmlRegisterRevision(QUICK_URI_FULL); qmlRegisterType(QUICK_URI_EXPAND("FramelessHelper")); + qmlRegisterType(QUICK_URI_EXPAND("MicaMaterial")); + #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) qmlRegisterType(QUICK_URI_EXPAND("StandardSystemButton")); qmlRegisterType(QUICK_URI_EXPAND("StandardTitleBar")); diff --git a/src/quick/framelessquickutils.cpp b/src/quick/framelessquickutils.cpp index 38a72cd..aa224d5 100644 --- a/src/quick/framelessquickutils.cpp +++ b/src/quick/framelessquickutils.cpp @@ -28,6 +28,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcFramelessQuickUtils, "wangwenx190.framelesshelper.quick.framelessquickutils") +#define INFO qCInfo(lcFramelessQuickUtils) +#define DEBUG qCDebug(lcFramelessQuickUtils) +#define WARNING qCWarning(lcFramelessQuickUtils) +#define CRITICAL qCCritical(lcFramelessQuickUtils) + using namespace Global; FramelessQuickUtils::FramelessQuickUtils(QObject *parent) : QObject(parent) @@ -73,7 +79,7 @@ QuickGlobal::SystemTheme FramelessQuickUtils::systemTheme() const QColor FramelessQuickUtils::systemAccentColor() const { #ifdef Q_OS_WINDOWS - return Utils::getDwmColorizationColor(); + return Utils::getDwmAccentColor(); #elif defined(Q_OS_LINUX) return Utils::getWmThemeColor(); #elif defined(Q_OS_MACOS) diff --git a/src/quick/framelessquickwindow.cpp b/src/quick/framelessquickwindow.cpp index 2279597..4c9c48d 100644 --- a/src/quick/framelessquickwindow.cpp +++ b/src/quick/framelessquickwindow.cpp @@ -34,6 +34,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcFramelessQuickWindow, "wangwenx190.framelesshelper.quick.framelessquickwindow") +#define INFO qCInfo(lcFramelessQuickWindow) +#define DEBUG qCDebug(lcFramelessQuickWindow) +#define WARNING qCWarning(lcFramelessQuickWindow) +#define CRITICAL qCCritical(lcFramelessQuickWindow) + using namespace Global; FramelessQuickWindowPrivate::FramelessQuickWindowPrivate(FramelessQuickWindow *q) : QObject(q) diff --git a/src/quick/global.cpp b/src/quick/global.cpp index 0da7b47..0af03a3 100644 --- a/src/quick/global.cpp +++ b/src/quick/global.cpp @@ -26,6 +26,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcQuickGlobal, "wangwenx190.framelesshelper.quick.global") +#define INFO qCInfo(lcQuickGlobal) +#define DEBUG qCDebug(lcQuickGlobal) +#define WARNING qCWarning(lcQuickGlobal) +#define CRITICAL qCCritical(lcQuickGlobal) + namespace FramelessHelper::Quick { diff --git a/src/quick/quickchromepalette.cpp b/src/quick/quickchromepalette.cpp index ce51bb7..ea0dbea 100644 --- a/src/quick/quickchromepalette.cpp +++ b/src/quick/quickchromepalette.cpp @@ -26,6 +26,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcQuickChromePalette, "wangwenx190.framelesshelper.quick.quickchromepalette") +#define INFO qCInfo(lcQuickChromePalette) +#define DEBUG qCDebug(lcQuickChromePalette) +#define WARNING qCWarning(lcQuickChromePalette) +#define CRITICAL qCCritical(lcQuickChromePalette) + QuickChromePalette::QuickChromePalette(QObject *parent) : ChromePalette(parent) { } diff --git a/src/quick/quickmicamaterial.cpp b/src/quick/quickmicamaterial.cpp new file mode 100644 index 0000000..ee68d9e --- /dev/null +++ b/src/quick/quickmicamaterial.cpp @@ -0,0 +1,261 @@ +/* + * MIT License + * + * Copyright (C) 2022 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 "quickmicamaterial.h" +#include "quickmicamaterial_p.h" +#include +#include +#include +#include +#include +#include +#include +#include + +FRAMELESSHELPER_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcQuickMicaMaterial, "wangwenx190.framelesshelper.quick.quickmicamaterial") +#define INFO qCInfo(lcQuickMicaMaterial) +#define DEBUG qCDebug(lcQuickMicaMaterial) +#define WARNING qCWarning(lcQuickMicaMaterial) +#define CRITICAL qCCritical(lcQuickMicaMaterial) + +using namespace Global; + +struct QuickMicaData +{ + QMutex mutex; +}; + +Q_GLOBAL_STATIC(QuickMicaData, g_data) + +class WallpaperImageNode : public QObject, public QSGTransformNode +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(WallpaperImageNode) + +public: + explicit WallpaperImageNode(QuickMicaMaterial *item); + ~WallpaperImageNode() override; + +public Q_SLOTS: + void maybeUpdateWallpaperImageClipRect(); + void maybeGenerateWallpaperImageCache(const bool force = false); + +private: + void initialize(); + +private: + QScopedPointer m_texture; + QPointer m_item = nullptr; + QSGSimpleTextureNode *m_node = nullptr; + QPixmap m_pixmapCache = {}; + QPointer m_micaMaterial = nullptr; +}; + +WallpaperImageNode::WallpaperImageNode(QuickMicaMaterial *item) +{ + Q_ASSERT(item); + if (!item) { + return; + } + m_item = item; + initialize(); +} + +WallpaperImageNode::~WallpaperImageNode() = default; + +void WallpaperImageNode::initialize() +{ + g_data()->mutex.lock(); + + QQuickWindow * const window = m_item->window(); + m_micaMaterial = MicaMaterial::attach(window); + + m_node = new QSGSimpleTextureNode; + m_node->setFiltering(QSGTexture::Linear); + + g_data()->mutex.unlock(); + + maybeGenerateWallpaperImageCache(); + maybeUpdateWallpaperImageClipRect(); + + appendChildNode(m_node); + + connect(m_micaMaterial, &MicaMaterial::shouldRedraw, this, [this](){ + maybeGenerateWallpaperImageCache(true); + }); + connect(window, &QQuickWindow::beforeRendering, this, + &WallpaperImageNode::maybeUpdateWallpaperImageClipRect, Qt::DirectConnection); + + QuickMicaMaterialPrivate::get(m_item)->appendNode(this); +} + +void WallpaperImageNode::maybeGenerateWallpaperImageCache(const bool force) +{ + QMutexLocker locker(&g_data()->mutex); + if (!m_pixmapCache.isNull() && !force) { + return; + } + const QSize desktopSize = QGuiApplication::primaryScreen()->virtualSize(); + static constexpr const QPoint originPoint = {0, 0}; + m_pixmapCache = QPixmap(desktopSize); + m_pixmapCache.fill(kDefaultTransparentColor); + QPainter painter(&m_pixmapCache); + m_micaMaterial->paint(&painter, desktopSize, originPoint); + m_texture.reset(m_item->window()->createTextureFromImage(m_pixmapCache.toImage())); + m_node->setTexture(m_texture.data()); +} + +void WallpaperImageNode::maybeUpdateWallpaperImageClipRect() +{ + QMutexLocker locker(&g_data()->mutex); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + const QSizeF itemSize = m_item->size(); +#else + const QSizeF itemSize = {m_item->width(), m_item->height()}; +#endif + m_node->setRect(QRectF(QPointF(0.0, 0.0), itemSize)); + m_node->setSourceRect(QRectF(m_item->mapToGlobal(QPointF(0.0, 0.0)), itemSize)); +} + +QuickMicaMaterialPrivate::QuickMicaMaterialPrivate(QuickMicaMaterial *q) : QObject(q) +{ + Q_ASSERT(q); + if (!q) { + return; + } + q_ptr = q; + initialize(); +} + +QuickMicaMaterialPrivate::~QuickMicaMaterialPrivate() = default; + +QuickMicaMaterialPrivate *QuickMicaMaterialPrivate::get(QuickMicaMaterial *q) +{ + Q_ASSERT(q); + if (!q) { + return nullptr; + } + return q->d_func(); +} + +const QuickMicaMaterialPrivate *QuickMicaMaterialPrivate::get(const QuickMicaMaterial *q) +{ + Q_ASSERT(q); + if (!q) { + return nullptr; + } + return q->d_func(); +} + +void QuickMicaMaterialPrivate::initialize() +{ + Q_Q(QuickMicaMaterial); + q->setFlag(QuickMicaMaterial::ItemHasContents); + q->setClip(true); +} + +void QuickMicaMaterialPrivate::rebindWindow() +{ + Q_Q(QuickMicaMaterial); + QQuickWindow * const window = q->window(); + if (!window) { + return; + } + if (m_rootWindowXChangedConnection) { + disconnect(m_rootWindowXChangedConnection); + m_rootWindowXChangedConnection = {}; + } + if (m_rootWindowYChangedConnection) { + disconnect(m_rootWindowYChangedConnection); + m_rootWindowYChangedConnection = {}; + } + m_rootWindowXChangedConnection = connect(window, &QQuickWindow::xChanged, q, [q](){ q->update(); }); + m_rootWindowYChangedConnection = connect(window, &QQuickWindow::yChanged, q, [q](){ q->update(); }); +} + +void QuickMicaMaterialPrivate::forceRegenerateWallpaperImageCache() +{ + if (m_nodes.isEmpty()) { + return; + } + for (auto &&node : qAsConst(m_nodes)) { + if (node) { + node->maybeGenerateWallpaperImageCache(true); + } + } +} + +void QuickMicaMaterialPrivate::appendNode(WallpaperImageNode *node) +{ + Q_ASSERT(node); + if (!node) { + return; + } + if (m_nodes.contains(node)) { + return; + } + m_nodes.append(node); +} + +QuickMicaMaterial::QuickMicaMaterial(QQuickItem *parent) + : QQuickItem(parent), d_ptr(new QuickMicaMaterialPrivate(this)) +{ +} + +QuickMicaMaterial::~QuickMicaMaterial() = default; + +void QuickMicaMaterial::itemChange(const ItemChange change, const ItemChangeData &value) +{ + QQuickItem::itemChange(change, value); + Q_D(QuickMicaMaterial); + switch (change) { + case ItemDevicePixelRatioHasChanged: { + d->forceRegenerateWallpaperImageCache(); + update(); // Force re-paint immediately. + } break; + case ItemSceneChange: { + if (value.window) { + d->rebindWindow(); + } + } break; + default: + break; + } +} + +QSGNode *QuickMicaMaterial::updatePaintNode(QSGNode *old, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + auto node = static_cast(old); + if (!node) { + node = new WallpaperImageNode(this); + } + return node; +} + +FRAMELESSHELPER_END_NAMESPACE + +#include "quickmicamaterial.moc" diff --git a/src/quick/quickmicamaterial.h b/src/quick/quickmicamaterial.h new file mode 100644 index 0000000..cff5c54 --- /dev/null +++ b/src/quick/quickmicamaterial.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Quick/quickmicamaterial.h" diff --git a/src/quick/quickmicamaterial_p.h b/src/quick/quickmicamaterial_p.h new file mode 100644 index 0000000..3ed20c1 --- /dev/null +++ b/src/quick/quickmicamaterial_p.h @@ -0,0 +1 @@ +#include "../../include/FramelessHelper/Quick/private/quickmicamaterial_p.h" diff --git a/src/quick/quickstandardsystembutton.cpp b/src/quick/quickstandardsystembutton.cpp index e57119b..d032341 100644 --- a/src/quick/quickstandardsystembutton.cpp +++ b/src/quick/quickstandardsystembutton.cpp @@ -34,6 +34,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcQuickStandardSystemButton, "wangwenx190.framelesshelper.quick.quickstandardsystembutton") +#define INFO qCInfo(lcQuickStandardSystemButton) +#define DEBUG qCDebug(lcQuickStandardSystemButton) +#define WARNING qCWarning(lcQuickStandardSystemButton) +#define CRITICAL qCCritical(lcQuickStandardSystemButton) + using namespace Global; QuickStandardSystemButton::QuickStandardSystemButton(QQuickItem *parent) : QQuickButton(parent) diff --git a/src/quick/quickstandardtitlebar.cpp b/src/quick/quickstandardtitlebar.cpp index 95dbad7..7b2126a 100644 --- a/src/quick/quickstandardtitlebar.cpp +++ b/src/quick/quickstandardtitlebar.cpp @@ -34,6 +34,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcQuickStandardTitleBar, "wangwenx190.framelesshelper.quick.quickstandardtitlebar") +#define INFO qCInfo(lcQuickStandardTitleBar) +#define DEBUG qCDebug(lcQuickStandardTitleBar) +#define WARNING qCWarning(lcQuickStandardTitleBar) +#define CRITICAL qCCritical(lcQuickStandardTitleBar) + using namespace Global; QuickStandardTitleBar::QuickStandardTitleBar(QQuickItem *parent) : QQuickRectangle(parent) diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index d53163e..ac9b0de 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -92,29 +92,10 @@ if(FRAMELESSHELPER_BUILD_STATIC) endif() target_compile_definitions(${SUB_PROJ_NAME} PRIVATE - QT_NO_CAST_FROM_ASCII - QT_NO_CAST_TO_ASCII - QT_NO_URL_CAST_FROM_STRING - QT_NO_CAST_FROM_BYTEARRAY QT_NO_KEYWORDS - QT_NO_NARROWING_CONVERSIONS_IN_CONNECT - QT_NO_FOREACH - QT_USE_QSTRINGBUILDER - QT_DEPRECATED_WARNINGS - QT_DISABLE_DEPRECATED_BEFORE=0x060500 FRAMELESSHELPER_WIDGETS_LIBRARY ) -if(MSVC) - target_compile_options(${SUB_PROJ_NAME} PRIVATE - /utf-8 /W3 /WX # Cannot use /W4 here, Qt's own headers are not warning-clean. - ) -else() - target_compile_options(${SUB_PROJ_NAME} PRIVATE - -Wall -Wextra -Werror - ) -endif() - target_link_libraries(${SUB_PROJ_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::WidgetsPrivate ) @@ -130,25 +111,6 @@ target_include_directories(${SUB_PROJ_NAME} PUBLIC "$" ) -install(TARGETS ${SUB_PROJ_NAME} - EXPORT ${SUB_PROJ_NAME}Targets - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH} -) - -export(EXPORT ${SUB_PROJ_NAME}Targets - FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${SUB_PROJ_NAME}Targets.cmake" - NAMESPACE ${PROJECT_NAME}:: -) - -install(FILES ${PUBLIC_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}) -install(FILES ${PUBLIC_HEADERS_ALIAS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}) -install(FILES ${PRIVATE_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}/private) - -install(EXPORT ${SUB_PROJ_NAME}Targets - FILE ${SUB_PROJ_NAME}Targets.cmake - NAMESPACE ${PROJECT_NAME}:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} -) +include(../core/cmakehelper.cmake) +setup_compile_params(${SUB_PROJ_NAME}) +setup_package_export(${SUB_PROJ_NAME} ${SUB_PROJ_PATH} "${PUBLIC_HEADERS}" "${PUBLIC_HEADERS_ALIAS}" "${PRIVATE_HEADERS}") diff --git a/src/widgets/framelessmainwindow.cpp b/src/widgets/framelessmainwindow.cpp index 5322ae9..ea99f37 100644 --- a/src/widgets/framelessmainwindow.cpp +++ b/src/widgets/framelessmainwindow.cpp @@ -30,6 +30,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcFramelessMainWindow, "wangwenx190.framelesshelper.widgets.framelessmainwindow") +#define INFO qCInfo(lcFramelessMainWindow) +#define DEBUG qCDebug(lcFramelessMainWindow) +#define WARNING qCWarning(lcFramelessMainWindow) +#define CRITICAL qCCritical(lcFramelessMainWindow) + using namespace Global; FramelessMainWindowPrivate::FramelessMainWindowPrivate(FramelessMainWindow *q) : QObject(q) @@ -103,6 +109,11 @@ void FramelessMainWindowPrivate::toggleFullScreen() } } +WidgetsSharedHelper *FramelessMainWindowPrivate::widgetsSharedHelper() const +{ + return (m_helper.isNull() ? nullptr : m_helper.data()); +} + FramelessMainWindow::FramelessMainWindow(QWidget *parent, const Qt::WindowFlags flags) : QMainWindow(parent, flags), d_ptr(new FramelessMainWindowPrivate(this)) { diff --git a/src/widgets/framelesswidget.cpp b/src/widgets/framelesswidget.cpp index d45e44c..f028c87 100644 --- a/src/widgets/framelesswidget.cpp +++ b/src/widgets/framelesswidget.cpp @@ -30,6 +30,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcFramelessWidget, "wangwenx190.framelesshelper.widgets.framelesswidget") +#define INFO qCInfo(lcFramelessWidget) +#define DEBUG qCDebug(lcFramelessWidget) +#define WARNING qCWarning(lcFramelessWidget) +#define CRITICAL qCCritical(lcFramelessWidget) + using namespace Global; FramelessWidgetPrivate::FramelessWidgetPrivate(FramelessWidget *q) : QObject(q) @@ -103,6 +109,11 @@ void FramelessWidgetPrivate::toggleFullScreen() } } +WidgetsSharedHelper *FramelessWidgetPrivate::widgetsSharedHelper() const +{ + return (m_helper.isNull() ? nullptr : m_helper.data()); +} + FramelessWidget::FramelessWidget(QWidget *parent) : QWidget(parent), d_ptr(new FramelessWidgetPrivate(this)) { diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index 78782c5..41a8f5a 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -24,6 +24,11 @@ #include "framelesswidgetshelper.h" #include "framelesswidgetshelper_p.h" +#include "framelesswidget.h" +#include "framelesswidget_p.h" +#include "framelessmainwindow.h" +#include "framelessmainwindow_p.h" +#include "widgetssharedhelper_p.h" #include #include #include @@ -37,6 +42,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcFramelessWidgetsHelper, "wangwenx190.framelesshelper.widgets.framelesswidgetshelper") +#define INFO qCInfo(lcFramelessWidgetsHelper) +#define DEBUG qCDebug(lcFramelessWidgetsHelper) +#define WARNING qCWarning(lcFramelessWidgetsHelper) +#define CRITICAL qCCritical(lcFramelessWidgetsHelper) + using namespace Global; struct WidgetsHelperData @@ -60,6 +71,25 @@ struct WidgetsHelper Q_GLOBAL_STATIC(WidgetsHelper, g_widgetsHelper) +[[nodiscard]] static inline WidgetsSharedHelper *findWidgetsSharedHelper(QWidget *window) +{ + Q_ASSERT(window); + if (!window) { + return nullptr; + } + if (const auto widget = qobject_cast(window)) { + if (const auto widgetPriv = FramelessWidgetPrivate::get(widget)) { + return widgetPriv->widgetsSharedHelper(); + } + } + if (const auto mainWindow = qobject_cast(window)) { + if (const auto mainWindowPriv = FramelessMainWindowPrivate::get(mainWindow)) { + return mainWindowPriv->widgetsSharedHelper(); + } + } + return nullptr; +} + FramelessWidgetsHelperPrivate::FramelessWidgetsHelperPrivate(FramelessWidgetsHelper *q) : QObject(q) { Q_ASSERT(q); @@ -164,28 +194,39 @@ void FramelessWidgetsHelperPrivate::setBlurBehindWindowEnabled(const bool enable if (m_blurBehindWindowEnabled == enable) { return; } - BlurMode mode = BlurMode::Disable; - QPalette palette = window->palette(); - if (enable) { - if (!m_savedWindowBackgroundColor.isValid()) { - m_savedWindowBackgroundColor = palette.color(QPalette::Window); + if (Utils::isBlurBehindWindowSupported()) { + BlurMode mode = BlurMode::Disable; + QPalette palette = window->palette(); + if (enable) { + if (!m_savedWindowBackgroundColor.isValid()) { + m_savedWindowBackgroundColor = palette.color(QPalette::Window); + } + palette.setColor(QPalette::Window, kDefaultTransparentColor); + mode = BlurMode::Default; + } else { + if (m_savedWindowBackgroundColor.isValid()) { + palette.setColor(QPalette::Window, m_savedWindowBackgroundColor); + m_savedWindowBackgroundColor = {}; + } + mode = BlurMode::Disable; } - palette.setColor(QPalette::Window, kDefaultTransparentColor); - mode = BlurMode::Default; - } else { - if (m_savedWindowBackgroundColor.isValid()) { - palette.setColor(QPalette::Window, m_savedWindowBackgroundColor); - m_savedWindowBackgroundColor = {}; + window->setPalette(palette); + if (Utils::setBlurBehindWindowEnabled(window->winId(), mode, color)) { + m_blurBehindWindowEnabled = enable; + Q_Q(FramelessWidgetsHelper); + Q_EMIT q->blurBehindWindowEnabledChanged(); + } else { + WARNING << "Failed to enable/disable blur behind window."; } - mode = BlurMode::Disable; - } - window->setPalette(palette); - if (Utils::setBlurBehindWindowEnabled(window->winId(), mode, color)) { - m_blurBehindWindowEnabled = enable; - Q_Q(FramelessWidgetsHelper); - Q_EMIT q->blurBehindWindowEnabledChanged(); } else { - qWarning() << "Failed to enable/disable blur behind window."; + if (WidgetsSharedHelper * const helper = findWidgetsSharedHelper(window)) { + m_blurBehindWindowEnabled = enable; + helper->setMicaEnabled(m_blurBehindWindowEnabled); + Q_Q(FramelessWidgetsHelper); + Q_EMIT q->blurBehindWindowEnabledChanged(); + } else { + DEBUG << "Blur behind window is not supported on current platform."; + } } } diff --git a/src/widgets/global.cpp b/src/widgets/global.cpp index daf1d63..8db3dd3 100644 --- a/src/widgets/global.cpp +++ b/src/widgets/global.cpp @@ -37,6 +37,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcWidgetsGlobal, "wangwenx190.framelesshelper.widgets.global") +#define INFO qCInfo(lcWidgetsGlobal) +#define DEBUG qCDebug(lcWidgetsGlobal) +#define WARNING qCWarning(lcWidgetsGlobal) +#define CRITICAL qCCritical(lcWidgetsGlobal) + namespace FramelessHelper::Widgets { diff --git a/src/widgets/standardsystembutton.cpp b/src/widgets/standardsystembutton.cpp index 0398965..f2779b4 100644 --- a/src/widgets/standardsystembutton.cpp +++ b/src/widgets/standardsystembutton.cpp @@ -31,6 +31,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcStandardSystemButton, "wangwenx190.framelesshelper.widgets.standardsystembutton") +#define INFO qCInfo(lcStandardSystemButton) +#define DEBUG qCDebug(lcStandardSystemButton) +#define WARNING qCWarning(lcStandardSystemButton) +#define CRITICAL qCCritical(lcStandardSystemButton) + using namespace Global; static constexpr const QRect g_buttonRect = {QPoint(0, 0), kDefaultSystemButtonSize}; diff --git a/src/widgets/standardtitlebar.cpp b/src/widgets/standardtitlebar.cpp index 0e8d0b2..a149611 100644 --- a/src/widgets/standardtitlebar.cpp +++ b/src/widgets/standardtitlebar.cpp @@ -32,6 +32,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcStandardTitleBar, "wangwenx190.framelesshelper.widgets.standardtitlebar") +#define INFO qCInfo(lcStandardTitleBar) +#define DEBUG qCDebug(lcStandardTitleBar) +#define WARNING qCWarning(lcStandardTitleBar) +#define CRITICAL qCCritical(lcStandardTitleBar) + using namespace Global; StandardTitleBarPrivate::StandardTitleBarPrivate(StandardTitleBar *q) : QObject(q) diff --git a/src/widgets/widgetssharedhelper.cpp b/src/widgets/widgetssharedhelper.cpp index 91941c0..6c8eaac 100644 --- a/src/widgets/widgetssharedhelper.cpp +++ b/src/widgets/widgetssharedhelper.cpp @@ -26,11 +26,20 @@ #include #include #include +#include #include +#include +#include #include FRAMELESSHELPER_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcWidgetsSharedHelper, "wangwenx190.framelesshelper.widgets.widgetssharedhelper") +#define INFO qCInfo(lcWidgetsSharedHelper) +#define DEBUG qCDebug(lcWidgetsSharedHelper) +#define WARNING qCWarning(lcWidgetsSharedHelper) +#define CRITICAL qCCritical(lcWidgetsSharedHelper) + using namespace Global; WidgetsSharedHelper::WidgetsSharedHelper(QObject *parent) : QObject(parent) @@ -49,9 +58,44 @@ void WidgetsSharedHelper::setup(QWidget *widget) return; } m_targetWidget = widget; + m_micaMaterial = MicaMaterial::attach(m_targetWidget); + if (m_micaRedrawConnection) { + disconnect(m_micaRedrawConnection); + m_micaRedrawConnection = {}; + } + m_micaRedrawConnection = connect(m_micaMaterial, &MicaMaterial::shouldRedraw, + this, [this](){ + if (m_targetWidget) { + m_targetWidget->update(); + } + }); m_targetWidget->installEventFilter(this); updateContentsMargins(); m_targetWidget->update(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QScreen *screen = m_targetWidget->screen(); +#else + QScreen *screen = m_targetWidget->windowHandle()->screen(); +#endif + handleScreenChanged(screen); + connect(m_targetWidget->windowHandle(), &QWindow::screenChanged, this, &WidgetsSharedHelper::handleScreenChanged); +} + +bool WidgetsSharedHelper::isMicaEnabled() const +{ + return m_micaEnabled; +} + +void WidgetsSharedHelper::setMicaEnabled(const bool value) +{ + if (m_micaEnabled == value) { + return; + } + m_micaEnabled = value; + if (m_targetWidget) { + m_targetWidget->update(); + } + Q_EMIT micaEnabledChanged(); } bool WidgetsSharedHelper::eventFilter(QObject *object, QEvent *event) @@ -61,6 +105,9 @@ bool WidgetsSharedHelper::eventFilter(QObject *object, QEvent *event) if (!object || !event) { return false; } + if (!m_targetWidget) { + return QObject::eventFilter(object, event); + } if (!object->isWidgetType()) { return QObject::eventFilter(object, event); } @@ -76,6 +123,12 @@ bool WidgetsSharedHelper::eventFilter(QObject *object, QEvent *event) case QEvent::WindowStateChange: { changeEventHandler(event); } break; + case QEvent::Move: + case QEvent::Resize: { + if (m_micaEnabled) { + m_targetWidget->update(); + } + } break; default: break; } @@ -99,28 +152,27 @@ void WidgetsSharedHelper::changeEventHandler(QEvent *event) void WidgetsSharedHelper::paintEventHandler(QPaintEvent *event) { -#ifdef Q_OS_WINDOWS - Q_ASSERT(event); - if (!event) { - return; - } - if (!shouldDrawFrameBorder()) { - return; - } - QPainter painter(m_targetWidget); - painter.save(); - QPen pen = {}; - pen.setColor(Utils::getFrameBorderColor(m_targetWidget->isActiveWindow())); - pen.setWidth(kDefaultWindowFrameBorderThickness); - painter.setPen(pen); - // In fact, we should use "m_targetWidget->width() - 1" here but we can't - // because Qt's drawing system has some rounding errors internally and if - // we minus one here we'll get a one pixel gap, so sad. But drawing a line - // with a little extra pixels won't hurt anyway. - painter.drawLine(0, 0, m_targetWidget->width(), 0); - painter.restore(); -#else Q_UNUSED(event); + if (m_micaEnabled && m_micaMaterial) { + QPainter painter(m_targetWidget); + m_micaMaterial->paint(&painter, m_targetWidget->size(), + m_targetWidget->mapToGlobal(QPoint(0, 0))); + } +#ifdef Q_OS_WINDOWS + if (shouldDrawFrameBorder()) { + QPainter painter(m_targetWidget); + painter.save(); + QPen pen = {}; + pen.setColor(Utils::getFrameBorderColor(m_targetWidget->isActiveWindow())); + pen.setWidth(kDefaultWindowFrameBorderThickness); + painter.setPen(pen); + // In fact, we should use "m_targetWidget->width() - 1" here but we can't + // because Qt's drawing system has some rounding errors internally and if + // we minus one here we'll get a one pixel gap, so sad. But drawing a line + // with a little extra pixels won't hurt anyway. + painter.drawLine(0, 0, m_targetWidget->width(), 0); + painter.restore(); + } #endif } @@ -135,6 +187,39 @@ bool WidgetsSharedHelper::shouldDrawFrameBorder() const #endif } +void WidgetsSharedHelper::handleScreenChanged(QScreen *screen) +{ + Q_ASSERT(m_targetWidget); + if (!m_targetWidget) { + return; + } + // The QScreen handle can be null if a window was moved out of a screen. + if (!screen) { + return; + } + if (m_screen == screen) { + return; + } + m_screen = screen; + m_screenDpr = m_screen->devicePixelRatio(); + if (m_screenDpiChangeConnection) { + disconnect(m_screenDpiChangeConnection); + m_screenDpiChangeConnection = {}; + } + m_screenDpiChangeConnection = connect(m_screen, &QScreen::physicalDotsPerInchChanged, + this, [this](const qreal dpi){ + Q_UNUSED(dpi); + const qreal currentDpr = m_screen->devicePixelRatio(); + if (m_screenDpr == currentDpr) { + return; + } + m_screenDpr = currentDpr; + if (m_micaEnabled && m_micaMaterial) { + MicaMaterialPrivate::get(m_micaMaterial)->maybeGenerateBlurredWallpaper(true); + } + }); +} + void WidgetsSharedHelper::updateContentsMargins() { #ifdef Q_OS_WINDOWS