add blurBehindWindowEnabled property

Current only implements for Windows,
macOS will be implemented later,
Linux won't be supported in the near future.

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-07-03 15:49:10 +08:00
parent f90b49538a
commit 71f8a5aab1
33 changed files with 1313 additions and 17 deletions

View File

@ -8,6 +8,7 @@ You can join our [Discord channel](https://discord.gg/grrM4Tmesy) to communicate
## Roadmap for 2.2 ## Roadmap for 2.2
- Common: Add cross-platform blur behind window feature.
- Common: Add cross-platform customizable system menu for both Qt Widgets and Qt Quick. Also supports both light and dark theme. - Common: Add cross-platform customizable system menu for both Qt Widgets and Qt Quick. Also supports both light and dark theme.
- Common: Migrate to categorized logging output. - Common: Migrate to categorized logging output.
- Examples: Add QtWebEngine based demo projects for both Qt Widgets and Qt Quick. The whole user interface will be written in HTML instead of C++/QML. - Examples: Add QtWebEngine based demo projects for both Qt Widgets and Qt Quick. The whole user interface will be written in HTML instead of C++/QML.
@ -18,7 +19,7 @@ You can join our [Discord channel](https://discord.gg/grrM4Tmesy) to communicate
## Highlights compared to 2.0 ## Highlights compared to 2.0
- Windows: Added support for the snap layouts feature introduced in Windows 11. - Windows: Added support for the snap layout feature introduced in Windows 11.
- Widgets: Redesigned the public interface, the use of FramelessHelper is now more elegant. - Widgets: Redesigned the public interface, the use of FramelessHelper is now more elegant.
- Quick: Redesigned the public interface, the use of FramelessHelper is now more elegant. - Quick: Redesigned the public interface, the use of FramelessHelper is now more elegant.
- Common: Redesigned the standard title bar interface, it's now possible to customize it from outside. Previously there's no standard title bar in the widgets module, it's now added and exported. - Common: Redesigned the standard title bar interface, it's now possible to customize it from outside. Previously there's no standard title bar in the widgets module, it's now added and exported.
@ -279,8 +280,10 @@ Please refer to the demo projects to see more detailed usages: [examples](./exam
If you are lucky enough, one of them may fix the issue for you. If not, you may try to use multiple solutions together. But I can't guarantee the issue can 100% be fixed. If you are lucky enough, one of them may fix the issue for you. If not, you may try to use multiple solutions together. But I can't guarantee the issue can 100% be fixed.
- Due to there are many sub-versions of Windows 10, it's highly recommended to use the latest version of Windows 10, at least no older than Windows 10 1809. If you try to use this framework on some very old Windows 10 versions such as 1507 or 1607, there may be some compatibility issues. Using this framework on Windows 7 is also supported but not recommended. To get the most stable behavior and the best appearance, you should use it on the latest version of Windows 10 or Windows 11. - Due to there are many sub-versions of Windows 10, it's highly recommended to use the latest version of Windows 10, at least no older than Windows 10 1809. If you try to use this framework on some very old Windows 10 versions such as 1507 or 1607, there may be some compatibility issues. Using this framework on Windows 7 is also supported but not recommended. To get the most stable behavior and the best appearance, you should use it on the latest version of Windows 10 or Windows 11.
- To make the snap layout work as expected, there are some additional rules for your homemade system buttons to follow: - To make the snap layout work as expected, there are some additional rules for your homemade system buttons to follow:
- Add a manifest file to your application, in the manifest file, you need to claim your application supports Windows 11 explicitly. This step is very important. Without this step, the snap layout feature can't be enabled.
- Make sure there are two public invokable functions (slot functions are always invokable): `void setHovered(bool)` and `void setPressed(bool)`. These two functions will be invoked by FramelessHelper when the button is being hovered or pressed. You should change the button's visual state inside these functions. If you need to show tooltips, you'll have to do it manually in these functions. - Make sure there are two public invokable functions (slot functions are always invokable): `void setHovered(bool)` and `void setPressed(bool)`. These two functions will be invoked by FramelessHelper when the button is being hovered or pressed. You should change the button's visual state inside these functions. If you need to show tooltips, you'll have to do it manually in these functions.
- Make sure there's a public signal: `void clicked()`. When the button is being clicked, that signal will be triggered by FramelessHelper. You should connect your event handler to that signal. - Make sure there's a public signal: `void clicked()`. When the button is being clicked, that signal will be triggered by FramelessHelper. You should connect your event handler to that signal.
- For Qt Quick applications, for the C++ side, you need to inherit your button from the `QQuickAbstractButton` class, for the QML side, you need to inherit your button from the `Button` type (from the `QtQuick.Controls.Basic` module). They have all the invokable functions and signals we need, so no more extra work is needed.
- Don't forget to call `setSystemButton()` for each button to let FramelessHelper know which is the minimize/maximize/close button. - Don't forget to call `setSystemButton()` for each button to let FramelessHelper know which is the minimize/maximize/close button.
- System buttons will not be able to receive any actual mouse and keyboard events so there's no need to handle these events inside these buttons. That's also why we need to set the button's visual state manually. - System buttons will not be able to receive any actual mouse and keyboard events so there's no need to handle these events inside these buttons. That's also why we need to set the button's visual state manually.
- I know this is making everything complicated but unfortunately we can't avoid this mess if we need to support the snap layout feature. Snap layout is really only designed for the original standard window frame, so if we want to forcely support it without a standard window frame, many black magic will be needed. - I know this is making everything complicated but unfortunately we can't avoid this mess if we need to support the snap layout feature. Snap layout is really only designed for the original standard window frame, so if we want to forcely support it without a standard window frame, many black magic will be needed.

View File

@ -26,8 +26,10 @@ if(FRAMELESSHELPER_BUILD_WIDGETS AND TARGET Qt${QT_VERSION_MAJOR}::Widgets)
add_subdirectory(widget) add_subdirectory(widget)
add_subdirectory(mainwindow) add_subdirectory(mainwindow)
add_subdirectory(openglwidget) add_subdirectory(openglwidget)
add_subdirectory(widget-blur)
endif() endif()
if(FRAMELESSHELPER_BUILD_QUICK AND TARGET Qt${QT_VERSION_MAJOR}::Quick AND ${QT_VERSION_MAJOR} GREATER_EQUAL 6) if(FRAMELESSHELPER_BUILD_QUICK AND TARGET Qt${QT_VERSION_MAJOR}::Quick AND ${QT_VERSION_MAJOR} GREATER_EQUAL 6)
add_subdirectory(quick) add_subdirectory(quick)
add_subdirectory(quick-blur)
endif() endif()

View File

@ -23,6 +23,7 @@
*/ */
#include <QtWidgets/qapplication.h> #include <QtWidgets/qapplication.h>
#include <framelessconfig_p.h>
#include "mainwindow.h" #include "mainwindow.h"
FRAMELESSHELPER_USE_NAMESPACE FRAMELESSHELPER_USE_NAMESPACE
@ -35,6 +36,8 @@ int main(int argc, char *argv[])
QApplication application(argc, argv); QApplication application(argc, argv);
FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners);
MainWindow mainWindow; MainWindow mainWindow;
mainWindow.show(); mainWindow.show();

View File

@ -52,6 +52,7 @@
#include <QApplication> #include <QApplication>
#include <QSurfaceFormat> #include <QSurfaceFormat>
#include <QOpenGLContext> #include <QOpenGLContext>
#include <framelessconfig_p.h>
#include "mainwindow.h" #include "mainwindow.h"
// This example demonstrates easy, cross-platform usage of OpenGL ES 3.0 functions via // This example demonstrates easy, cross-platform usage of OpenGL ES 3.0 functions via
@ -72,6 +73,8 @@ int main(int argc, char *argv[])
QApplication application(argc, argv); QApplication application(argc, argv);
FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners);
QSurfaceFormat fmt = {}; QSurfaceFormat fmt = {};
fmt.setDepthBufferSize(24); fmt.setDepthBufferSize(24);

View File

@ -0,0 +1,84 @@
#[[
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
$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>
)

View File

@ -0,0 +1,83 @@
/*
* 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 layouts 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.blurBehindWindow = 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
}
useAlternativeBackground: true
titleLabelAlignment: Qt.AlignCenter
color: window.color
}
}

View File

@ -0,0 +1,137 @@
/*
* 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 <QtGui/qguiapplication.h>
#include <QtQml/qqmlapplicationengine.h>
#include <QtQuick/qquickwindow.h>
#include <QtQuickControls2/qquickstyle.h>
#include <framelessquickmodule.h>
#include <framelessconfig_p.h>
#include "settings.h"
#if QMLTC_ENABLED
# include <mainwindow.h>
#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::Core::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<Settings>("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> mainWindow(new MainWindow(&engine));
mainWindow->show();
#endif
return QCoreApplication::exec();
}

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/Demo">
<file>MainWindow.qml</file>
</qresource>
</RCC>

View File

@ -0,0 +1,28 @@
:: 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

View File

@ -0,0 +1,28 @@
:: 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

View File

@ -0,0 +1,28 @@
:: 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

View File

@ -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.
*/
#include "settings.h"
#include <QtCore/qsettings.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
#include <QtCore/qdatastream.h>
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;
}

View File

@ -0,0 +1,61 @@
/*
* 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 <QtCore/qobject.h>
#include <QtGui/qwindow.h>
#include <framelesshelpercore_global.h>
#if __has_include(<QtQml/qqmlregistration.h>)
# include <QtQml/qqmlregistration.h>
#else
# include <QtQml/qqml.h>
#endif
QT_BEGIN_NAMESPACE
class QSettings;
QT_END_NAMESPACE
class Settings : public QObject
{
Q_OBJECT
#ifdef QML_ELEMENT
QML_ELEMENT
#endif
#ifdef QML_SINGLETON
QML_SINGLETON
#endif
Q_DISABLE_COPY_MOVE(Settings)
public:
explicit Settings(QObject *parent = nullptr);
~Settings() override;
public Q_SLOTS:
void saveGeometry(QWindow *window);
Q_NODISCARD bool restoreGeometry(QWindow *window);
private:
QScopedPointer<QSettings> m_settings;
};

View File

@ -32,6 +32,7 @@
#include <QtQuick/qquickwindow.h> #include <QtQuick/qquickwindow.h>
#include <QtQuickControls2/qquickstyle.h> #include <QtQuickControls2/qquickstyle.h>
#include <framelessquickmodule.h> #include <framelessquickmodule.h>
#include <framelessconfig_p.h>
#include "settings.h" #include "settings.h"
#if QMLTC_ENABLED #if QMLTC_ENABLED
# include <mainwindow.h> # include <mainwindow.h>
@ -47,6 +48,8 @@ int main(int argc, char *argv[])
QGuiApplication application(argc, argv); QGuiApplication application(argc, argv);
FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners);
// Enable QtRHI debug output if not explicitly requested by the user. // Enable QtRHI debug output if not explicitly requested by the user.
if (!qEnvironmentVariableIsSet("QSG_INFO")) { if (!qEnvironmentVariableIsSet("QSG_INFO")) {
qputenv("QSG_INFO", FRAMELESSHELPER_BYTEARRAY_LITERAL("1")); qputenv("QSG_INFO", FRAMELESSHELPER_BYTEARRAY_LITERAL("1"));

View File

@ -0,0 +1,58 @@
#[[
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
)

View File

@ -0,0 +1,45 @@
/*
* 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 <QtWidgets/qapplication.h>
#include <framelessconfig_p.h>
#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::Core::initialize();
QApplication application(argc, argv);
FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners);
Widget widget;
widget.show();
return QCoreApplication::exec();
}

View File

@ -0,0 +1,129 @@
/*
* 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 <QtCore/qdatetime.h>
#include <QtCore/qsettings.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qdir.h>
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qboxlayout.h>
#include <FramelessManager>
#include <Utils>
#include <FramelessWidgetsHelper>
#include <StandardTitleBar>
#include <StandardSystemButton>
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<QSettings> 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->setUseAlternativeBackground(true);
m_titleBar->setStyleSheet(FRAMELESSHELPER_STRING_LITERAL("background-color: transparent;"));
m_titleBar->setTitleLabelAlignment(Qt::AlignCenter);
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<QSettings> 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();
}

View File

@ -0,0 +1,59 @@
/*
* 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 <FramelessWidget>
QT_BEGIN_NAMESPACE
class QLabel;
QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBar;
FRAMELESSHELPER_END_NAMESPACE
class Widget : public FRAMELESSHELPER_PREPEND_NAMESPACE(FramelessWidget)
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(Widget)
public:
explicit Widget(QWidget *parent = nullptr);
~Widget() override;
protected:
void timerEvent(QTimerEvent *event) override;
void closeEvent(QCloseEvent *event) override;
private:
void initialize();
private Q_SLOTS:
void updateStyleSheet();
private:
QScopedPointer<QLabel> m_clockLabel;
QScopedPointer<FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar)> m_titleBar;
};

View File

@ -23,6 +23,7 @@
*/ */
#include <QtWidgets/qapplication.h> #include <QtWidgets/qapplication.h>
#include <framelessconfig_p.h>
#include "widget.h" #include "widget.h"
FRAMELESSHELPER_USE_NAMESPACE FRAMELESSHELPER_USE_NAMESPACE
@ -35,6 +36,8 @@ int main(int argc, char *argv[])
QApplication application(argc, argv); QApplication application(argc, argv);
FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners);
Widget widget; Widget widget;
widget.show(); widget.show();

View File

@ -137,7 +137,7 @@
using MMRESULT = UINT; using MMRESULT = UINT;
using TIMECAPS = struct timecaps_tag using TIMECAPS = struct TIMECAPS
{ {
UINT wPeriodMin; // minimum period supported UINT wPeriodMin; // minimum period supported
UINT wPeriodMax; // maximum period supported UINT wPeriodMax; // maximum period supported
@ -164,12 +164,91 @@ using MONITOR_DPI_TYPE = enum MONITOR_DPI_TYPE
using _DWM_WINDOW_CORNER_PREFERENCE = enum _DWM_WINDOW_CORNER_PREFERENCE using _DWM_WINDOW_CORNER_PREFERENCE = enum _DWM_WINDOW_CORNER_PREFERENCE
{ {
_DWMWCP_DEFAULT = 0, _DWMWCP_DEFAULT = 0, // Let the system decide whether or not to round window corners
_DWMWCP_DONOTROUND = 1, _DWMWCP_DONOTROUND = 1, // Never round window corners
_DWMWCP_ROUND = 2, _DWMWCP_ROUND = 2, // Round the corners if appropriate
_DWMWCP_ROUNDSMALL = 3 _DWMWCP_ROUNDSMALL = 3 // Round the corners if appropriate, with a small radius
}; };
using _DWM_SYSTEMBACKDROP_TYPE = enum _DWM_SYSTEMBACKDROP_TYPE
{
_DWMSBT_AUTO = 0, // [Default] Let DWM automatically decide the system-drawn backdrop for this window.
_DWMSBT_NONE = 1, // Do not draw any system backdrop.
_DWMSBT_MAINWINDOW = 2, /* Mica */ // Draw the backdrop material effect corresponding to a long-lived window.
_DWMSBT_TRANSIENTWINDOW = 3, /* Acrylic */ // Draw the backdrop material effect corresponding to a transient window.
_DWMSBT_TABBEDWINDOW = 4 /* Aero */ // Draw the backdrop material effect corresponding to a window with a tabbed title bar.
};
using WINDOWCOMPOSITIONATTRIB = enum WINDOWCOMPOSITIONATTRIB
{
WCA_UNDEFINED = 0,
WCA_NCRENDERING_ENABLED = 1,
WCA_NCRENDERING_POLICY = 2,
WCA_TRANSITIONS_FORCEDISABLED = 3,
WCA_ALLOW_NCPAINT = 4,
WCA_CAPTION_BUTTON_BOUNDS = 5,
WCA_NONCLIENT_RTL_LAYOUT = 6,
WCA_FORCE_ICONIC_REPRESENTATION = 7,
WCA_EXTENDED_FRAME_BOUNDS = 8,
WCA_HAS_ICONIC_BITMAP = 9,
WCA_THEME_ATTRIBUTES = 10,
WCA_NCRENDERING_EXILED = 11,
WCA_NCADORNMENTINFO = 12,
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
WCA_VIDEO_OVERLAY_ACTIVE = 14,
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
WCA_DISALLOW_PEEK = 16,
WCA_CLOAK = 17,
WCA_CLOAKED = 18,
WCA_ACCENT_POLICY = 19,
WCA_FREEZE_REPRESENTATION = 20,
WCA_EVER_UNCLOAKED = 21,
WCA_VISUAL_OWNER = 22,
WCA_HOLOGRAPHIC = 23,
WCA_EXCLUDED_FROM_DDA = 24,
WCA_PASSIVEUPDATEMODE = 25,
WCA_USEDARKMODECOLORS = 26,
WCA_CORNER_STYLE = 27,
WCA_PART_COLOR = 28,
WCA_DISABLE_MOVESIZE_FEEDBACK = 29,
WCA_LAST = 30
};
using ACCENT_STATE = enum ACCENT_STATE
{
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803
ACCENT_USE_HOST_BACKDROP = 5, // RS5 1809
ACCENT_INVALID_STATE = 6
};
using ACCENT_POLICY = struct ACCENT_POLICY
{
ACCENT_STATE State;
DWORD Flags;
DWORD GradientColor; // #AABBGGRR
DWORD AnimationId;
};
using PACCENT_POLICY = ACCENT_POLICY *;
using NPACCENT_POLICY = ACCENT_POLICY NEAR *;
using LPACCENT_POLICY = ACCENT_POLICY FAR *;
using WINDOWCOMPOSITIONATTRIBDATA = struct WINDOWCOMPOSITIONATTRIBDATA
{
WINDOWCOMPOSITIONATTRIB Attrib;
PVOID pvData;
SIZE_T cbData;
};
using PWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA *;
using NPWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA NEAR *;
using LPWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA FAR *;
using GetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA);
using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA);
EXTERN_C MMRESULT WINAPI EXTERN_C MMRESULT WINAPI
timeGetDevCaps( timeGetDevCaps(
_Out_writes_bytes_(cbtc) LPTIMECAPS ptc, _Out_writes_bytes_(cbtc) LPTIMECAPS ptc,
@ -208,7 +287,10 @@ GetDpiForMonitor(
[[maybe_unused]] static constexpr const wchar_t kSystemDarkThemeResourceName[] = L"DarkMode_Explorer"; [[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 kSystemLightThemeResourceName[] = L"Explorer";
[[maybe_unused]] static constexpr const DWORD _DWMWA_USE_HOSTBACKDROPBRUSH = 17;
[[maybe_unused]] static constexpr const DWORD _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19; [[maybe_unused]] static constexpr const DWORD _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
[[maybe_unused]] static constexpr const DWORD _DWMWA_USE_IMMERSIVE_DARK_MODE = 20; [[maybe_unused]] static constexpr const DWORD _DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
[[maybe_unused]] static constexpr const DWORD _DWMWA_WINDOW_CORNER_PREFERENCE = 33; [[maybe_unused]] static constexpr const DWORD _DWMWA_WINDOW_CORNER_PREFERENCE = 33;
[[maybe_unused]] static constexpr const DWORD _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37; [[maybe_unused]] static constexpr const DWORD _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37;
[[maybe_unused]] static constexpr const DWORD _DWMWA_SYSTEMBACKDROP_TYPE = 38;
[[maybe_unused]] static constexpr const DWORD _DWMWA_MICA_EFFECT = 1029;

View File

@ -98,6 +98,10 @@ QT_END_NAMESPACE
# define QUtf8String(str) QString::fromUtf8(str) # define QUtf8String(str) QString::fromUtf8(str)
#endif #endif
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
using namespace Qt::StringLiterals;
#endif
#ifndef FRAMELESSHELPER_BYTEARRAY_LITERAL #ifndef FRAMELESSHELPER_BYTEARRAY_LITERAL
# if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) # if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
# define FRAMELESSHELPER_BYTEARRAY_LITERAL(ba) ba##_ba # define FRAMELESSHELPER_BYTEARRAY_LITERAL(ba) ba##_ba
@ -215,7 +219,8 @@ enum class Option
ForceShowWindowFrameBorder = 2, ForceShowWindowFrameBorder = 2,
DisableWindowsSnapLayouts = 3, DisableWindowsSnapLayouts = 3,
WindowUseRoundCorners = 4, WindowUseRoundCorners = 4,
CenterWindowBeforeShow = 5 CenterWindowBeforeShow = 5,
EnableBlurBehindWindow = 6
}; };
Q_ENUM_NS(Option) Q_ENUM_NS(Option)
@ -310,6 +315,16 @@ enum class ApplicationType
}; };
Q_ENUM_NS(ApplicationType) Q_ENUM_NS(ApplicationType)
enum class BlurMode
{
Disable = 0, // Do not enable blur behind window
Default = 1, // Use platform default blur mode
Windows_Aero = 2, // Windows only, use the traditional DWM blur
Windows_Acrylic = 3, // Windows only, use the Acrylic blur
Windows_Mica = 4 // Windows only, use the Mica material
};
Q_ENUM_NS(BlurMode)
struct VersionNumber struct VersionNumber
{ {
int major = 0; int major = 0;

View File

@ -53,6 +53,8 @@ FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter(
const Global::SystemButtonType button, const Global::ButtonState state); const Global::SystemButtonType button, const Global::ButtonState state);
[[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool shouldAppsUseDarkMode();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isTitleBarColorized(); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isTitleBarColorized();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool
setBlurBehindWindowEnabled(const WId windowId, const Global::BlurMode mode, const QColor &color);
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowsVersionOrGreater(const Global::WindowsVersion version); [[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowsVersionOrGreater(const Global::WindowsVersion version);

View File

@ -170,6 +170,16 @@ struct FRAMELESSHELPER_QUICK_API QuickGlobal
}; };
Q_ENUM(ApplicationType) Q_ENUM(ApplicationType)
enum class BlurMode
{
FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Disable)
FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Default)
FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Windows_Aero)
FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Windows_Acrylic)
FRAMELESSHELPER_QUICK_ENUM_VALUE(BlurMode, Windows_Mica)
};
Q_ENUM(BlurMode)
private: private:
Q_GADGET Q_GADGET
#ifdef QML_NAMED_ELEMENT #ifdef QML_NAMED_ELEMENT

View File

@ -44,6 +44,7 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickHelper : public QQuickItem
Q_DISABLE_COPY_MOVE(FramelessQuickHelper) Q_DISABLE_COPY_MOVE(FramelessQuickHelper)
Q_PROPERTY(QQuickItem* titleBarItem READ titleBarItem WRITE setTitleBarItem NOTIFY titleBarItemChanged FINAL) Q_PROPERTY(QQuickItem* titleBarItem READ titleBarItem WRITE setTitleBarItem NOTIFY titleBarItemChanged FINAL)
Q_PROPERTY(bool windowFixedSize READ isWindowFixedSize WRITE setWindowFixedSize NOTIFY windowFixedSizeChanged FINAL) Q_PROPERTY(bool windowFixedSize READ isWindowFixedSize WRITE setWindowFixedSize NOTIFY windowFixedSizeChanged FINAL)
Q_PROPERTY(bool blurBehindWindowEnabled READ isBlurBehindWindowEnabled WRITE setBlurBehindWindowEnabled NOTIFY blurBehindWindowEnabledChanged FINAL)
public: public:
explicit FramelessQuickHelper(QQuickItem *parent = nullptr); explicit FramelessQuickHelper(QQuickItem *parent = nullptr);
@ -54,6 +55,7 @@ public:
Q_NODISCARD QQuickItem *titleBarItem() const; Q_NODISCARD QQuickItem *titleBarItem() const;
Q_NODISCARD bool isWindowFixedSize() const; Q_NODISCARD bool isWindowFixedSize() const;
Q_NODISCARD bool isBlurBehindWindowEnabled() const;
public Q_SLOTS: public Q_SLOTS:
void extendsContentIntoTitleBar(); void extendsContentIntoTitleBar();
@ -69,6 +71,7 @@ public Q_SLOTS:
void moveWindowToDesktopCenter(); void moveWindowToDesktopCenter();
void bringWindowToFront(); void bringWindowToFront();
void setWindowFixedSize(const bool value); void setWindowFixedSize(const bool value);
void setBlurBehindWindowEnabled(const bool value);
protected: protected:
void itemChange(const ItemChange change, const ItemChangeData &value) override; void itemChange(const ItemChange change, const ItemChangeData &value) override;
@ -76,6 +79,7 @@ protected:
Q_SIGNALS: Q_SIGNALS:
void titleBarItemChanged(); void titleBarItemChanged();
void windowFixedSizeChanged(); void windowFixedSizeChanged();
void blurBehindWindowEnabledChanged();
void ready(); void ready();
private: private:

View File

@ -26,6 +26,7 @@
#include "framelesshelperquick_global.h" #include "framelesshelperquick_global.h"
#include <QtCore/qobject.h> #include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QQuickItem; class QQuickItem;
@ -67,6 +68,9 @@ public:
void emitSignalForAllInstances(const QByteArray &signal); void emitSignalForAllInstances(const QByteArray &signal);
Q_NODISCARD bool isBlurBehindWindowEnabled() const;
void setBlurBehindWindowEnabled(const bool value, const QColor &color);
private: private:
Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const; Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const;
Q_NODISCARD bool isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const; Q_NODISCARD bool isInSystemButtons(const QPoint &pos, QuickGlobal::SystemButtonType *button) const;
@ -77,7 +81,9 @@ private:
Q_NODISCARD QuickHelperData *getWindowDataMutable() const; Q_NODISCARD QuickHelperData *getWindowDataMutable() const;
private: private:
FramelessQuickHelper *q_ptr = nullptr; QPointer<FramelessQuickHelper> q_ptr = nullptr;
QColor m_savedWindowBackgroundColor = {};
bool m_blurBehindWindowEnabled = false;
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -38,6 +38,7 @@ class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelper : public QObject
Q_DISABLE_COPY_MOVE(FramelessWidgetsHelper) Q_DISABLE_COPY_MOVE(FramelessWidgetsHelper)
Q_PROPERTY(QWidget* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL) Q_PROPERTY(QWidget* titleBarWidget READ titleBarWidget WRITE setTitleBarWidget NOTIFY titleBarWidgetChanged FINAL)
Q_PROPERTY(bool windowFixedSize READ isWindowFixedSize WRITE setWindowFixedSize NOTIFY windowFixedSizeChanged FINAL) Q_PROPERTY(bool windowFixedSize READ isWindowFixedSize WRITE setWindowFixedSize NOTIFY windowFixedSizeChanged FINAL)
Q_PROPERTY(bool blurBehindWindowEnabled READ isBlurBehindWindowEnabled WRITE setBlurBehindWindowEnabled NOTIFY blurBehindWindowEnabledChanged FINAL)
public: public:
explicit FramelessWidgetsHelper(QObject *parent = nullptr); explicit FramelessWidgetsHelper(QObject *parent = nullptr);
@ -47,6 +48,7 @@ public:
Q_NODISCARD QWidget *titleBarWidget() const; Q_NODISCARD QWidget *titleBarWidget() const;
Q_NODISCARD bool isWindowFixedSize() const; Q_NODISCARD bool isWindowFixedSize() const;
Q_NODISCARD bool isBlurBehindWindowEnabled() const;
public Q_SLOTS: public Q_SLOTS:
void extendsContentIntoTitleBar(); void extendsContentIntoTitleBar();
@ -62,10 +64,12 @@ public Q_SLOTS:
void moveWindowToDesktopCenter(); void moveWindowToDesktopCenter();
void bringWindowToFront(); void bringWindowToFront();
void setWindowFixedSize(const bool value); void setWindowFixedSize(const bool value);
void setBlurBehindWindowEnabled(const bool value);
Q_SIGNALS: Q_SIGNALS:
void titleBarWidgetChanged(); void titleBarWidgetChanged();
void windowFixedSizeChanged(); void windowFixedSizeChanged();
void blurBehindWindowEnabledChanged();
void ready(); void ready();
private: private:

View File

@ -26,6 +26,7 @@
#include "framelesshelperwidgets_global.h" #include "framelesshelperwidgets_global.h"
#include <QtCore/qobject.h> #include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
@ -63,6 +64,9 @@ public:
void emitSignalForAllInstances(const QByteArray &signal); void emitSignalForAllInstances(const QByteArray &signal);
Q_NODISCARD bool isBlurBehindWindowEnabled() const;
void setBlurBehindWindowEnabled(const bool enable, const QColor &color);
private: private:
Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const; Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const;
Q_NODISCARD bool isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const; Q_NODISCARD bool isInSystemButtons(const QPoint &pos, Global::SystemButtonType *button) const;
@ -74,7 +78,9 @@ private:
Q_NODISCARD WidgetsHelperData *getWindowDataMutable() const; Q_NODISCARD WidgetsHelperData *getWindowDataMutable() const;
private: private:
FramelessWidgetsHelper *q_ptr = nullptr; QPointer<FramelessWidgetsHelper> q_ptr = nullptr;
QColor m_savedWindowBackgroundColor = {};
bool m_blurBehindWindowEnabled = false;
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -50,7 +50,9 @@ static const struct
{FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_WINDOW_USE_ROUND_CORNERS"), {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_WINDOW_USE_ROUND_CORNERS"),
FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/WindowUseRoundCorners")}, FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/WindowUseRoundCorners")},
{FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_CENTER_WINDOW_BEFORE_SHOW"), {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_CENTER_WINDOW_BEFORE_SHOW"),
FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/CenterWindowBeforeShow")} FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/CenterWindowBeforeShow")},
{FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_ENABLE_BLUR_BEHIND_WINDOW"),
FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/EnableBlurBehindWindow")}
}; };
static constexpr const auto OptionCount = std::size(OptionsTable); static constexpr const auto OptionCount = std::size(OptionsTable);

View File

@ -346,6 +346,7 @@ void FramelessHelper::Core::initialize()
qRegisterMetaType<ButtonState>(); qRegisterMetaType<ButtonState>();
qRegisterMetaType<WindowsVersion>(); qRegisterMetaType<WindowsVersion>();
qRegisterMetaType<ApplicationType>(); qRegisterMetaType<ApplicationType>();
qRegisterMetaType<BlurMode>();
qRegisterMetaType<VersionNumber>(); qRegisterMetaType<VersionNumber>();
qRegisterMetaType<SystemParameters>(); qRegisterMetaType<SystemParameters>();
qRegisterMetaType<VersionInfo>(); qRegisterMetaType<VersionInfo>();

View File

@ -125,6 +125,8 @@ FRAMELESSHELPER_STRING_CONSTANT(HiliteMenuItem)
FRAMELESSHELPER_STRING_CONSTANT(TrackPopupMenu) FRAMELESSHELPER_STRING_CONSTANT(TrackPopupMenu)
FRAMELESSHELPER_STRING_CONSTANT(ClientToScreen) FRAMELESSHELPER_STRING_CONSTANT(ClientToScreen)
FRAMELESSHELPER_STRING_CONSTANT2(HKEY_CURRENT_USER, "HKEY_CURRENT_USER") FRAMELESSHELPER_STRING_CONSTANT2(HKEY_CURRENT_USER, "HKEY_CURRENT_USER")
FRAMELESSHELPER_STRING_CONSTANT(DwmEnableBlurBehindWindow)
FRAMELESSHELPER_STRING_CONSTANT(SetWindowCompositionAttribute)
template <typename T> template <typename T>
class HumbleComPtr class HumbleComPtr
@ -404,7 +406,8 @@ bool Utils::isDwmCompositionEnabled()
return true; return true;
} }
const auto resultFromRegistry = []() -> bool { const auto resultFromRegistry = []() -> bool {
const QSettings registry(kHKEY_CURRENT_USER + u'\\' + qDwmRegistryKey, QSettings::NativeFormat); static const QString keyPath = kHKEY_CURRENT_USER + u'\\' + qDwmRegistryKey;
const QSettings registry(keyPath, QSettings::NativeFormat);
bool ok = false; bool ok = false;
const DWORD value = registry.value(kComposition).toULongLong(&ok); const DWORD value = registry.value(kComposition).toULongLong(&ok);
return (ok && (value != 0)); return (ok && (value != 0));
@ -413,6 +416,7 @@ bool Utils::isDwmCompositionEnabled()
reinterpret_cast<decltype(&DwmIsCompositionEnabled)>( reinterpret_cast<decltype(&DwmIsCompositionEnabled)>(
QSystemLibrary::resolve(kdwmapi, "DwmIsCompositionEnabled")); QSystemLibrary::resolve(kdwmapi, "DwmIsCompositionEnabled"));
if (!pDwmIsCompositionEnabled) { if (!pDwmIsCompositionEnabled) {
qWarning() << "Failed to resolve DwmIsCompositionEnabled() from DWMAPI.DLL.";
return resultFromRegistry(); return resultFromRegistry();
} }
BOOL enabled = FALSE; BOOL enabled = FALSE;
@ -452,6 +456,7 @@ void Utils::updateWindowFrameMargins(const WId windowId, const bool reset)
reinterpret_cast<decltype(&DwmExtendFrameIntoClientArea)>( reinterpret_cast<decltype(&DwmExtendFrameIntoClientArea)>(
QSystemLibrary::resolve(kdwmapi, "DwmExtendFrameIntoClientArea")); QSystemLibrary::resolve(kdwmapi, "DwmExtendFrameIntoClientArea"));
if (!pDwmExtendFrameIntoClientArea) { if (!pDwmExtendFrameIntoClientArea) {
qWarning() << "Failed to resolve DwmExtendFrameIntoClientArea() from DWMAPI.DLL.";
return; return;
} }
const MARGINS margins = [reset]() -> MARGINS { const MARGINS margins = [reset]() -> MARGINS {
@ -526,7 +531,8 @@ QString Utils::getSystemErrorMessage(const QString &function)
QColor Utils::getDwmColorizationColor() QColor Utils::getDwmColorizationColor()
{ {
const auto resultFromRegistry = []() -> QColor { const auto resultFromRegistry = []() -> QColor {
const QSettings registry(kHKEY_CURRENT_USER + u'\\' + qDwmRegistryKey, QSettings::NativeFormat); static const QString keyPath = kHKEY_CURRENT_USER + u'\\' + qDwmRegistryKey;
const QSettings registry(keyPath, QSettings::NativeFormat);
bool ok = false; bool ok = false;
const DWORD value = registry.value(kColorizationColor).toULongLong(&ok); const DWORD value = registry.value(kColorizationColor).toULongLong(&ok);
return (ok ? QColor::fromRgba(value) : kDefaultDarkGrayColor); return (ok ? QColor::fromRgba(value) : kDefaultDarkGrayColor);
@ -535,6 +541,7 @@ QColor Utils::getDwmColorizationColor()
reinterpret_cast<decltype(&DwmGetColorizationColor)>( reinterpret_cast<decltype(&DwmGetColorizationColor)>(
QSystemLibrary::resolve(kdwmapi, "DwmGetColorizationColor")); QSystemLibrary::resolve(kdwmapi, "DwmGetColorizationColor"));
if (!pDwmGetColorizationColor) { if (!pDwmGetColorizationColor) {
qWarning() << "Failed to resolve DwmGetColorizationColor() from DWMAPI.DLL.";
return resultFromRegistry(); return resultFromRegistry();
} }
DWORD color = 0; DWORD color = 0;
@ -554,10 +561,12 @@ DwmColorizationArea Utils::getDwmColorizationArea()
if (!isWin10OrGreater) { if (!isWin10OrGreater) {
return DwmColorizationArea::None_; return DwmColorizationArea::None_;
} }
const QSettings themeRegistry(kHKEY_CURRENT_USER + u'\\' + qPersonalizeRegistryKey, QSettings::NativeFormat); static const QString themeKeyPath = kHKEY_CURRENT_USER + u'\\' + qPersonalizeRegistryKey;
const QSettings themeRegistry(themeKeyPath, QSettings::NativeFormat);
bool themeOk = false; bool themeOk = false;
const DWORD themeValue = themeRegistry.value(qDwmColorKeyName).toULongLong(&themeOk); const DWORD themeValue = themeRegistry.value(qDwmColorKeyName).toULongLong(&themeOk);
const QSettings dwmRegistry(kHKEY_CURRENT_USER + u'\\' + qDwmRegistryKey, QSettings::NativeFormat); static const QString dwmKeyPath = kHKEY_CURRENT_USER + u'\\' + qDwmRegistryKey;
const QSettings dwmRegistry(dwmKeyPath, QSettings::NativeFormat);
bool dwmOk = false; bool dwmOk = false;
const DWORD dwmValue = dwmRegistry.value(qDwmColorKeyName).toULongLong(&dwmOk); const DWORD dwmValue = dwmRegistry.value(qDwmColorKeyName).toULongLong(&dwmOk);
const bool theme = (themeOk && (themeValue != 0)); const bool theme = (themeOk && (themeValue != 0));
@ -687,14 +696,27 @@ void Utils::syncWmPaintWithDwm()
QSystemLibrary winmmLib(kwinmm); QSystemLibrary winmmLib(kwinmm);
static const auto ptimeGetDevCaps = static const auto ptimeGetDevCaps =
reinterpret_cast<decltype(&timeGetDevCaps)>(winmmLib.resolve("timeGetDevCaps")); reinterpret_cast<decltype(&timeGetDevCaps)>(winmmLib.resolve("timeGetDevCaps"));
if (!ptimeGetDevCaps) {
qWarning() << "Failed to resolve timeGetDevCaps() from WINMM.DLL.";
return;
}
static const auto ptimeBeginPeriod = static const auto ptimeBeginPeriod =
reinterpret_cast<decltype(&timeBeginPeriod)>(winmmLib.resolve("timeBeginPeriod")); reinterpret_cast<decltype(&timeBeginPeriod)>(winmmLib.resolve("timeBeginPeriod"));
if (!ptimeBeginPeriod) {
qWarning() << "Failed to resolve timeBeginPeriod() from WINMM.DLL.";
return;
}
static const auto ptimeEndPeriod = static const auto ptimeEndPeriod =
reinterpret_cast<decltype(&timeEndPeriod)>(winmmLib.resolve("timeEndPeriod")); reinterpret_cast<decltype(&timeEndPeriod)>(winmmLib.resolve("timeEndPeriod"));
if (!ptimeEndPeriod) {
qWarning() << "Failed to resolve timeEndPeriod() from WINMM.DLL.";
return;
}
static const auto pDwmGetCompositionTimingInfo = static const auto pDwmGetCompositionTimingInfo =
reinterpret_cast<decltype(&DwmGetCompositionTimingInfo)>( reinterpret_cast<decltype(&DwmGetCompositionTimingInfo)>(
QSystemLibrary::resolve(kdwmapi, "DwmGetCompositionTimingInfo")); QSystemLibrary::resolve(kdwmapi, "DwmGetCompositionTimingInfo"));
if (!ptimeGetDevCaps || !ptimeBeginPeriod || !ptimeEndPeriod || !pDwmGetCompositionTimingInfo) { if (!pDwmGetCompositionTimingInfo) {
qWarning() << "Failed to resolve DwmGetCompositionTimingInfo() from DWMAPI.DLL.";
return; return;
} }
// Dirty hack to workaround the resize flicker caused by DWM. // Dirty hack to workaround the resize flicker caused by DWM.
@ -922,6 +944,7 @@ quint32 Utils::getFrameBorderThickness(const WId windowId, const bool scaled)
reinterpret_cast<decltype(&DwmGetWindowAttribute)>( reinterpret_cast<decltype(&DwmGetWindowAttribute)>(
QSystemLibrary::resolve(kdwmapi, "DwmGetWindowAttribute")); QSystemLibrary::resolve(kdwmapi, "DwmGetWindowAttribute"));
if (!pDwmGetWindowAttribute) { if (!pDwmGetWindowAttribute) {
qWarning() << "Failed to resolve DwmGetWindowAttribute() from DWMAPI.DLL.";
return 0; return 0;
} }
const UINT dpi = getWindowDpi(windowId, true); const UINT dpi = getWindowDpi(windowId, true);
@ -972,6 +995,7 @@ void Utils::updateWindowFrameBorderColor(const WId windowId, const bool dark)
reinterpret_cast<decltype(&DwmSetWindowAttribute)>( reinterpret_cast<decltype(&DwmSetWindowAttribute)>(
QSystemLibrary::resolve(kdwmapi, "DwmSetWindowAttribute")); QSystemLibrary::resolve(kdwmapi, "DwmSetWindowAttribute"));
if (!pDwmSetWindowAttribute) { if (!pDwmSetWindowAttribute) {
qWarning() << "Failed to resolve DwmSetWindowAttribute() from DWMAPI.DLL.";
return; return;
} }
const auto hwnd = reinterpret_cast<HWND>(windowId); const auto hwnd = reinterpret_cast<HWND>(windowId);
@ -1332,6 +1356,7 @@ void Utils::updateGlobalWin32ControlsTheme(const WId windowId, const bool dark)
reinterpret_cast<decltype(&SetWindowTheme)>( reinterpret_cast<decltype(&SetWindowTheme)>(
QSystemLibrary::resolve(kuxtheme, "SetWindowTheme")); QSystemLibrary::resolve(kuxtheme, "SetWindowTheme"));
if (!pSetWindowTheme) { if (!pSetWindowTheme) {
qWarning() << "Failed to resolve SetWindowTheme() from UXTHEME.DLL.";
return; return;
} }
const auto hwnd = reinterpret_cast<HWND>(windowId); const auto hwnd = reinterpret_cast<HWND>(windowId);
@ -1349,7 +1374,8 @@ bool Utils::shouldAppsUseDarkMode_windows()
return false; return false;
} }
const auto resultFromRegistry = []() -> bool { const auto resultFromRegistry = []() -> bool {
const QSettings registry(kHKEY_CURRENT_USER + u'\\' + qPersonalizeRegistryKey, QSettings::NativeFormat); static const QString keyPath = kHKEY_CURRENT_USER + u'\\' + qPersonalizeRegistryKey;
const QSettings registry(keyPath, QSettings::NativeFormat);
bool ok = false; bool ok = false;
const DWORD value = registry.value(kAppsUseLightTheme).toULongLong(&ok); const DWORD value = registry.value(kAppsUseLightTheme).toULongLong(&ok);
return (ok && (value == 0)); return (ok && (value == 0));
@ -1387,6 +1413,7 @@ void Utils::forceSquareCornersForWindow(const WId windowId, const bool force)
reinterpret_cast<decltype(&DwmSetWindowAttribute)>( reinterpret_cast<decltype(&DwmSetWindowAttribute)>(
QSystemLibrary::resolve(kdwmapi, "DwmSetWindowAttribute")); QSystemLibrary::resolve(kdwmapi, "DwmSetWindowAttribute"));
if (!pDwmSetWindowAttribute) { if (!pDwmSetWindowAttribute) {
qWarning() << "Failed to resolve DwmSetWindowAttribute() from DWMAPI.DLL.";
return; return;
} }
const auto hwnd = reinterpret_cast<HWND>(windowId); const auto hwnd = reinterpret_cast<HWND>(windowId);
@ -1397,4 +1424,195 @@ void Utils::forceSquareCornersForWindow(const WId windowId, const bool force)
} }
} }
bool Utils::setBlurBehindWindowEnabled(const WId windowId, const BlurMode mode, const QColor &color)
{
Q_ASSERT(windowId);
if (!windowId) {
return false;
}
const auto hwnd = reinterpret_cast<HWND>(windowId);
static const bool isWin8OrGreater = isWindowsVersionOrGreater(WindowsVersion::_8);
if (isWin8OrGreater) {
static const auto pSetWindowCompositionAttribute =
reinterpret_cast<SetWindowCompositionAttributePtr>(
QSystemLibrary::resolve(kuser32, "SetWindowCompositionAttribute"));
if (!pSetWindowCompositionAttribute) {
qWarning() << "Failed to resolve SetWindowCompositionAttribute() from USER32.DLL.";
return false;
}
static const auto pDwmSetWindowAttribute =
reinterpret_cast<decltype(&DwmSetWindowAttribute)>(
QSystemLibrary::resolve(kdwmapi, "DwmSetWindowAttribute"));
if (!pDwmSetWindowAttribute) {
qWarning() << "Failed to resolve DwmSetWindowAttribute() from DWMAPI.DLL.";
return false;
}
static const auto pDwmExtendFrameIntoClientArea =
reinterpret_cast<decltype(&DwmExtendFrameIntoClientArea)>(
QSystemLibrary::resolve(kdwmapi, "DwmExtendFrameIntoClientArea"));
if (!pDwmExtendFrameIntoClientArea) {
qWarning() << "Failed to resolve DwmExtendFrameIntoClientArea() from DWMAPI.DLL.";
return false;
}
static const bool isBuild22523OrGreater = doCompareWindowsVersion(10, 0, 22523);
static const bool isWin11OrGreater = isWindowsVersionOrGreater(WindowsVersion::_11_21H2);
static const bool isWin10OrGreater = isWindowsVersionOrGreater(WindowsVersion::_10_1507);
const BlurMode blurMode = [mode]() -> BlurMode {
if ((mode == BlurMode::Disable) || (mode == BlurMode::Windows_Aero)) {
return mode;
}
if ((mode == BlurMode::Windows_Mica) && !isWin11OrGreater) {
qWarning() << "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...";
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...";
return BlurMode::Windows_Aero;
}
if (mode == BlurMode::Default) {
if (isWin11OrGreater) {
return BlurMode::Windows_Mica;
}
if (isWin10OrGreater) {
return BlurMode::Windows_Acrylic;
}
return BlurMode::Windows_Aero;
}
Q_ASSERT(false); // Really should NOT go here.
return mode;
}();
if (blurMode == BlurMode::Disable) {
if (isBuild22523OrGreater) {
const _DWM_SYSTEMBACKDROP_TYPE dwmsbt = _DWMSBT_NONE;
const HRESULT hr = pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &dwmsbt, sizeof(dwmsbt));
if (FAILED(hr)) {
qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
}
}
if (isWin11OrGreater) {
const BOOL enable = FALSE;
const HRESULT hr = pDwmSetWindowAttribute(hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable));
if (FAILED(hr)) {
qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
}
}
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) {
const MARGINS margins = {-1, -1, -1, -1};
HRESULT hr = pDwmExtendFrameIntoClientArea(hwnd, &margins);
if (SUCCEEDED(hr)) {
if (isBuild22523OrGreater) {
// ### FIXME: Is it necessary to enable the host backdrop brush in the first place? To be checked.
const BOOL enable = TRUE;
hr = pDwmSetWindowAttribute(hwnd, _DWMWA_USE_HOSTBACKDROPBRUSH, &enable, sizeof(enable));
if (SUCCEEDED(hr)) {
const _DWM_SYSTEMBACKDROP_TYPE dwmsbt = _DWMSBT_MAINWINDOW; // Mica
hr = pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &dwmsbt, sizeof(dwmsbt));
if (SUCCEEDED(hr)) {
return true;
} else {
qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
}
} else {
qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
}
} else {
const BOOL enable = TRUE;
hr = pDwmSetWindowAttribute(hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable));
if (SUCCEEDED(hr)) {
return true;
} else {
qWarning() << __getSystemErrorMessage(kDwmSetWindowAttribute, hr);
}
}
} else {
qWarning() << __getSystemErrorMessage(kDwmExtendFrameIntoClientArea, hr);
}
} else {
ACCENT_POLICY policy;
SecureZeroMemory(&policy, sizeof(policy));
if (blurMode == BlurMode::Windows_Acrylic) {
policy.State = ACCENT_ENABLE_ACRYLICBLURBEHIND;
policy.Flags = 2; // Magic number, this member must be set to 2.
const QColor gradientColor = [&color]() -> QColor {
if (color.isValid()) {
return color;
}
QColor clr = (shouldAppsUseDarkMode() ? kDefaultSystemDarkColor : kDefaultSystemLightColor);
clr.setAlpha(230); // 90% opacity.
return clr;
}();
// This API expects the #AABBGGRR format.
policy.GradientColor = DWORD(qRgba(gradientColor.blue(),
gradientColor.green(), gradientColor.red(), gradientColor.alpha()));
} else if (blurMode == BlurMode::Windows_Aero) {
policy.State = ACCENT_ENABLE_BLURBEHIND;
} else {
Q_ASSERT(false); // Really should NOT go here.
}
WINDOWCOMPOSITIONATTRIBDATA wcad;
SecureZeroMemory(&wcad, sizeof(wcad));
wcad.Attrib = WCA_ACCENT_POLICY;
wcad.pvData = &policy;
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.";
}
return true;
}
qWarning() << getSystemErrorMessage(kSetWindowCompositionAttribute);
}
}
} else {
// We prefer to use "DwmEnableBlurBehindWindow" on Windows 7 because it behaves
// better than the undocumented API.
static const auto pDwmEnableBlurBehindWindow =
reinterpret_cast<decltype(&DwmEnableBlurBehindWindow)>(
QSystemLibrary::resolve(kdwmapi, "DwmEnableBlurBehindWindow"));
if (pDwmEnableBlurBehindWindow) {
DWM_BLURBEHIND dwmbb;
SecureZeroMemory(&dwmbb, sizeof(dwmbb));
dwmbb.dwFlags = DWM_BB_ENABLE;
dwmbb.fEnable = [mode]() -> BOOL {
if (mode == BlurMode::Disable) {
return FALSE;
}
if ((mode != BlurMode::Default) && (mode != BlurMode::Windows_Aero)) {
qWarning() << "The only supported blur mode on Windows 7 is the traditional DWM blur.";
}
return TRUE;
}();
const HRESULT hr = pDwmEnableBlurBehindWindow(hwnd, &dwmbb);
if (SUCCEEDED(hr)) {
return true;
}
qWarning() << __getSystemErrorMessage(kDwmEnableBlurBehindWindow, hr);
} else {
qWarning() << "Failed to resolve DwmEnableBlurBehindWindow() from DWMAPI.DLL.";
}
}
return false;
}
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -184,6 +184,9 @@ void FramelessQuickHelperPrivate::attachToWindow()
if (FramelessConfig::instance()->isSet(Option::CenterWindowBeforeShow)) { if (FramelessConfig::instance()->isSet(Option::CenterWindowBeforeShow)) {
moveWindowToDesktopCenter(); moveWindowToDesktopCenter();
} }
if (FramelessConfig::instance()->isSet(Option::EnableBlurBehindWindow)) {
setBlurBehindWindowEnabled(true, {});
}
emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("ready")); emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("ready"));
}); });
} }
@ -374,6 +377,44 @@ void FramelessQuickHelperPrivate::emitSignalForAllInstances(const QByteArray &si
} }
} }
bool FramelessQuickHelperPrivate::isBlurBehindWindowEnabled() const
{
return m_blurBehindWindowEnabled;
}
void FramelessQuickHelperPrivate::setBlurBehindWindowEnabled(const bool value, const QColor &color)
{
Q_Q(FramelessQuickHelper);
QQuickWindow * const window = q->window();
if (!window) {
return;
}
if (m_blurBehindWindowEnabled == value) {
return;
}
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 {
qWarning() << "Failed to enable/disable blur behind window.";
}
}
QRect FramelessQuickHelperPrivate::mapItemGeometryToScene(const QQuickItem * const item) const QRect FramelessQuickHelperPrivate::mapItemGeometryToScene(const QQuickItem * const item) const
{ {
Q_ASSERT(item); Q_ASSERT(item);
@ -664,6 +705,12 @@ bool FramelessQuickHelper::isWindowFixedSize() const
return d->isWindowFixedSize(); return d->isWindowFixedSize();
} }
bool FramelessQuickHelper::isBlurBehindWindowEnabled() const
{
Q_D(const FramelessQuickHelper);
return d->isBlurBehindWindowEnabled();
}
void FramelessQuickHelper::extendsContentIntoTitleBar() void FramelessQuickHelper::extendsContentIntoTitleBar()
{ {
// Intentionally not doing anything here. // Intentionally not doing anything here.
@ -739,6 +786,12 @@ void FramelessQuickHelper::setWindowFixedSize(const bool value)
d->setWindowFixedSize(value); d->setWindowFixedSize(value);
} }
void FramelessQuickHelper::setBlurBehindWindowEnabled(const bool value)
{
Q_D(FramelessQuickHelper);
d->setBlurBehindWindowEnabled(value, {});
}
void FramelessQuickHelper::itemChange(const ItemChange change, const ItemChangeData &value) void FramelessQuickHelper::itemChange(const ItemChange change, const ItemChangeData &value)
{ {
QQuickItem::itemChange(change, value); QQuickItem::itemChange(change, value);

View File

@ -60,6 +60,7 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine)
qRegisterMetaType<QuickGlobal::ButtonState>(); qRegisterMetaType<QuickGlobal::ButtonState>();
qRegisterMetaType<QuickGlobal::WindowsVersion>(); qRegisterMetaType<QuickGlobal::WindowsVersion>();
qRegisterMetaType<QuickGlobal::ApplicationType>(); qRegisterMetaType<QuickGlobal::ApplicationType>();
qRegisterMetaType<QuickGlobal::BlurMode>();
qmlRegisterUncreatableType<QuickGlobal>(QUICK_URI_FULL, "FramelessHelperConstants", qmlRegisterUncreatableType<QuickGlobal>(QUICK_URI_FULL, "FramelessHelperConstants",
FRAMELESSHELPER_STRING_LITERAL("The FramelessHelperConstants namespace is not creatable, you can only use it to access it's enums.")); FRAMELESSHELPER_STRING_LITERAL("The FramelessHelperConstants namespace is not creatable, you can only use it to access it's enums."));
qmlRegisterSingletonType<FramelessQuickUtils>(QUICK_URI_EXPAND("FramelessUtils"), qmlRegisterSingletonType<FramelessQuickUtils>(QUICK_URI_EXPAND("FramelessUtils"),

View File

@ -26,9 +26,9 @@
#include "framelesswidgetshelper_p.h" #include "framelesswidgetshelper_p.h"
#include <QtCore/qmutex.h> #include <QtCore/qmutex.h>
#include <QtCore/qhash.h> #include <QtCore/qhash.h>
#include <QtCore/qpointer.h>
#include <QtCore/qtimer.h> #include <QtCore/qtimer.h>
#include <QtGui/qwindow.h> #include <QtGui/qwindow.h>
#include <QtGui/qpalette.h>
#include <QtWidgets/qwidget.h> #include <QtWidgets/qwidget.h>
#include <framelessmanager.h> #include <framelessmanager.h>
#include <framelessconfig_p.h> #include <framelessconfig_p.h>
@ -149,6 +149,45 @@ void FramelessWidgetsHelperPrivate::emitSignalForAllInstances(const QByteArray &
} }
} }
bool FramelessWidgetsHelperPrivate::isBlurBehindWindowEnabled() const
{
return m_blurBehindWindowEnabled;
}
void FramelessWidgetsHelperPrivate::setBlurBehindWindowEnabled(const bool enable, const QColor &color)
{
QWidget * const window = getWindow();
if (!window) {
return;
}
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);
}
palette.setColor(QPalette::Window, kDefaultTransparentColor);
mode = BlurMode::Default;
} else {
if (m_savedWindowBackgroundColor.isValid()) {
palette.setColor(QPalette::Window, m_savedWindowBackgroundColor);
m_savedWindowBackgroundColor = {};
}
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.";
}
}
void FramelessWidgetsHelperPrivate::setTitleBarWidget(QWidget *widget) void FramelessWidgetsHelperPrivate::setTitleBarWidget(QWidget *widget)
{ {
Q_ASSERT(widget); Q_ASSERT(widget);
@ -268,6 +307,9 @@ void FramelessWidgetsHelperPrivate::attachToWindow()
if (FramelessConfig::instance()->isSet(Option::CenterWindowBeforeShow)) { if (FramelessConfig::instance()->isSet(Option::CenterWindowBeforeShow)) {
moveWindowToDesktopCenter(); moveWindowToDesktopCenter();
} }
if (FramelessConfig::instance()->isSet(Option::EnableBlurBehindWindow)) {
setBlurBehindWindowEnabled(true, {});
}
emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("ready")); emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("ready"));
}); });
} }
@ -657,6 +699,12 @@ bool FramelessWidgetsHelper::isWindowFixedSize() const
return d->isWindowFixedSize(); return d->isWindowFixedSize();
} }
bool FramelessWidgetsHelper::isBlurBehindWindowEnabled() const
{
Q_D(const FramelessWidgetsHelper);
return d->isBlurBehindWindowEnabled();
}
void FramelessWidgetsHelper::extendsContentIntoTitleBar() void FramelessWidgetsHelper::extendsContentIntoTitleBar()
{ {
// Intentionally not doing anything here. // Intentionally not doing anything here.
@ -732,4 +780,10 @@ void FramelessWidgetsHelper::setWindowFixedSize(const bool value)
d->setWindowFixedSize(value); d->setWindowFixedSize(value);
} }
void FramelessWidgetsHelper::setBlurBehindWindowEnabled(const bool value)
{
Q_D(FramelessWidgetsHelper);
d->setBlurBehindWindowEnabled(value, {});
}
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE