Implement homemade Mica material & other improvements

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-07-11 17:39:50 +08:00
parent 7f9f2c8543
commit 7616161699
95 changed files with 2193 additions and 1261 deletions

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 515 KiB

After

Width:  |  Height:  |  Size: 686 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 714 KiB

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View File

@ -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()

View File

@ -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)

View File

@ -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();

View File

@ -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)

View File

@ -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);

View File

@ -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
$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>
)
include(../deployqt.cmake)
deploy_qt_libraries(QuickBlur)

View File

@ -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"
}
}
}

View File

@ -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 <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::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<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

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <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

@ -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
$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>: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)

View File

@ -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 {

View File

@ -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")) {

View File

@ -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)

View File

@ -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 <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::Widgets::initialize();
QApplication application(argc, argv);
FramelessConfig::instance()->set(Global::Option::WindowUseRoundCorners);
Widget widget;
widget.show();
return QCoreApplication::exec();
}

View File

@ -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 <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>
#include <ChromePalette>
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->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<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

@ -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)

View File

@ -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();

View File

@ -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();
}

View File

@ -0,0 +1 @@
#include <micamaterial.h>

View File

@ -26,9 +26,12 @@
#include "framelesshelpercore_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qloggingcategory.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcChromePalette)
class ChromePalettePrivate;
class FRAMELESSHELPER_CORE_API ChromePalette : public QObject

View File

@ -26,9 +26,12 @@
#include "framelesshelpercore_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qloggingcategory.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcFramelessHelperQt)
class FRAMELESSHELPER_CORE_API FramelessHelperQt : public QObject
{
Q_OBJECT

View File

@ -26,9 +26,12 @@
#include "framelesshelpercore_global.h"
#include <QtCore/qabstractnativeeventfilter.h>
#include <QtCore/qloggingcategory.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcFramelessHelperWin)
class FRAMELESSHELPER_CORE_API FramelessHelperWin : public QAbstractNativeEventFilter
{
Q_DISABLE_COPY_MOVE(FramelessHelperWin)

View File

@ -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)";

View File

@ -28,6 +28,7 @@
#include <QtCore/qpoint.h>
#include <QtCore/qsize.h>
#include <QtCore/qobject.h>
#include <QtCore/qloggingcategory.h>
#include <QtGui/qcolor.h>
#include <QtGui/qwindowdefs.h>
#include <functional>
@ -185,6 +186,8 @@ QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcCoreGlobal)
#include <framelesshelper.version>
[[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;

View File

@ -26,9 +26,12 @@
#include "framelesshelpercore_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qloggingcategory.h>
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 &params);
Q_SIGNALS:
void systemThemeChanged();
void wallpaperChanged();
private:
QScopedPointer<FramelessManagerPrivate> d_ptr;

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.
*/
#pragma once
#include "framelesshelpercore_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qloggingcategory.h>
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<MicaMaterialPrivate> d_ptr;
};
FRAMELESSHELPER_END_NAMESPACE
Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(MicaMaterial))

View File

@ -27,6 +27,7 @@
#include "framelesshelpercore_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qvariant.h>
#include <optional>
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<QVariant> setInternal(const QString &key, const QVariant &value);
Q_NODISCARD std::optional<QVariant> getInternal(const QString &key) const;
template<typename T>
Q_NODISCARD T getInternal(const QString &key) const
Q_NODISCARD std::optional<T> getInternal(const QString &key) const
{
return qvariant_cast<T>(getInternal(key));
const std::optional<QVariant> var = getInternal(key);
if (var.has_value()) {
return qvariant_cast<T>(var.value());
}
return std::nullopt;
}
};

View File

@ -25,12 +25,12 @@
#pragma once
#include "framelesshelpercore_global.h"
#include "framelessmanager.h"
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
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 &params);
Q_INVOKABLE void notifySystemThemeHasChangedOrNot();
Q_INVOKABLE void notifyWallpaperHasChangedOrNot();
private:
void initialize();
private:
FramelessManager *q_ptr = nullptr;
QPointer<FramelessManager> 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

View File

@ -24,38 +24,45 @@
#pragma once
#include "framelesshelpercore_global.h"
#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
#include <QtCore/qpointer.h>
#include <QtGui/qbrush.h>
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<QSettings> m_settings;
void initialize();
private:
QPointer<MicaMaterial> 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))

View File

@ -26,9 +26,19 @@
#include "framelesshelpercore_global.h"
#include <QtGui/qwindowdefs.h>
#include <QtCore/qloggingcategory.h>
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);

View File

@ -0,0 +1 @@
#include <quickmicamaterial.h>

View File

@ -25,6 +25,7 @@
#pragma once
#include <framelesshelpercore_global.h>
#include <QtCore/qloggingcategory.h>
#if __has_include(<QtQml/qqmlregistration.h>)
# include <QtQml/qqmlregistration.h>
#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

View File

@ -26,9 +26,12 @@
#include "framelesshelperquick_global.h"
#include <QtQuick/qquickitem.h>
#include <QtCore/qloggingcategory.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcFramelessQuickHelper)
class FramelessQuickHelperPrivate;
class FRAMELESSHELPER_QUICK_API FramelessQuickHelper : public QQuickItem

View File

@ -25,6 +25,7 @@
#pragma once
#include "framelesshelperquick_global.h"
#include <QtCore/qloggingcategory.h>
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);

View File

@ -26,6 +26,7 @@
#include "framelesshelperquick_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qloggingcategory.h>
#include <QtGui/qcolor.h>
#include <QtQml/qqml.h>
@ -35,6 +36,8 @@ QT_END_NAMESPACE
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcFramelessQuickUtils)
class FRAMELESSHELPER_QUICK_API FramelessQuickUtils : public QObject
{
Q_OBJECT

View File

@ -75,7 +75,7 @@ private Q_SLOTS:
void updateTopBorderHeight();
private:
FramelessQuickWindow *q_ptr = nullptr;
QPointer<FramelessQuickWindow> q_ptr = nullptr;
QScopedPointer<QQuickRectangle> m_topBorderRectangle;
QScopedPointer<QQuickAnchors> m_topBorderAnchors;
QQuickWindow::Visibility m_savedVisibility = QQuickWindow::Windowed;

View File

@ -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 <QtCore/qobject.h>
#include <QtCore/qpointer.h>
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<QuickMicaMaterial> q_ptr = nullptr;
QMetaObject::Connection m_rootWindowXChangedConnection = {};
QMetaObject::Connection m_rootWindowYChangedConnection = {};
QList<QPointer<WallpaperImageNode>> m_nodes = {};
};
FRAMELESSHELPER_END_NAMESPACE
Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickMicaMaterialPrivate))

View File

@ -25,11 +25,14 @@
#pragma once
#include "framelesshelperquick_global.h"
#include <QtCore/qloggingcategory.h>
#include <QtQml/qqml.h>
#include <chromepalette.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQuickChromePalette)
class FRAMELESSHELPER_QUICK_API QuickChromePalette : public ChromePalette
{
Q_OBJECT

View File

@ -24,36 +24,38 @@
#pragma once
#include <FramelessWidget>
QT_BEGIN_NAMESPACE
class QLabel;
QT_END_NAMESPACE
#include "framelesshelperquick_global.h"
#include <QtCore/qloggingcategory.h>
#include <QtQuick/qquickitem.h>
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<QLabel> m_clockLabel;
QScopedPointer<FRAMELESSHELPER_PREPEND_NAMESPACE(StandardTitleBar)> m_titleBar;
QScopedPointer<QuickMicaMaterialPrivate> d_ptr;
};
FRAMELESSHELPER_END_NAMESPACE
Q_DECLARE_METATYPE2(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickMicaMaterial))
QML_DECLARE_TYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(QuickMicaMaterial))

View File

@ -25,6 +25,7 @@
#pragma once
#include <framelesshelpercore_global.h>
#include <QtCore/qloggingcategory.h>
#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();

View File

@ -25,10 +25,13 @@
#pragma once
#include "framelesshelperwidgets_global.h"
#include <QtCore/qloggingcategory.h>
#include <QtWidgets/qmainwindow.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcFramelessMainWindow)
class FramelessMainWindowPrivate;
class FRAMELESSHELPER_WIDGETS_API FramelessMainWindow : public QMainWindow

View File

@ -25,10 +25,13 @@
#pragma once
#include "framelesshelperwidgets_global.h"
#include <QtCore/qloggingcategory.h>
#include <QtWidgets/qwidget.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcFramelessWidget)
class FramelessWidgetPrivate;
class FRAMELESSHELPER_WIDGETS_API FramelessWidget : public QWidget

View File

@ -26,9 +26,12 @@
#include "framelesshelperwidgets_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qloggingcategory.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcFramelessWidgetsHelper)
class FramelessWidgetsHelperPrivate;
class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelper : public QObject

View File

@ -26,6 +26,7 @@
#include "framelesshelperwidgets_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
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<FramelessMainWindow> q_ptr = nullptr;
Qt::WindowState m_savedWindowState = Qt::WindowNoState;
QScopedPointer<WidgetsSharedHelper> m_helper;
};

View File

@ -26,6 +26,7 @@
#include "framelesshelperwidgets_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
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<FramelessWidget> q_ptr = nullptr;
Qt::WindowState m_savedWindowState = Qt::WindowNoState;
QScopedPointer<WidgetsSharedHelper> m_helper;
};

View File

@ -27,6 +27,7 @@
#include "framelesshelperwidgets_global.h"
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
#include <QtGui/qscreen.h>
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<QWidget> m_targetWidget = nullptr;
bool m_micaEnabled = false;
QPointer<MicaMaterial> m_micaMaterial;
QMetaObject::Connection m_micaRedrawConnection = {};
QPointer<QScreen> m_screen = nullptr;
qreal m_screenDpr = 0.0;
QMetaObject::Connection m_screenDpiChangeConnection = {};
};
FRAMELESSHELPER_END_NAMESPACE

View File

@ -25,10 +25,13 @@
#pragma once
#include "framelesshelperwidgets_global.h"
#include <QtCore/qloggingcategory.h>
#include <QtWidgets/qabstractbutton.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcStandardSystemButton)
class StandardSystemButtonPrivate;
class FRAMELESSHELPER_WIDGETS_API StandardSystemButton : public QAbstractButton

View File

@ -25,11 +25,14 @@
#pragma once
#include "framelesshelperwidgets_global.h"
#include <QtCore/qloggingcategory.h>
#include <chromepalette.h>
#include <QtWidgets/qwidget.h>
FRAMELESSHELPER_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcStandardTitleBar)
class StandardTitleBarPrivate;
class StandardSystemButton;

View File

@ -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_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}/private>"
)
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}")

View File

@ -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)

View File

@ -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)

View File

@ -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<QVariant> 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<QVariant> 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<QVariant> 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

View File

@ -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.

View File

@ -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<FramelessHelperWin> nativeEventFilter;
QHash<WId, Win32HelperData> data = {};
QHash<WId, WId> 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<FramelessHelperWin> nativeEventFilter;
QHash<WId, Win32HelperData> data = {};
QHash<WId, WId> 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<SystemButtonType> 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<HWND>(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<HWND>(parentWindowId);
const auto instance = static_cast<HINSTANCE>(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<HBRUSH>(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<WId>(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 &params)
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 &params)
// 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<DWORD>(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<LONG_PTR>(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<LONG_PTR>(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<LPCWSTR>(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;

View File

@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/org.wangwenx190.FramelessHelper">
<file>resources/fonts/Micon.ttf</file>
<file>resources/images/noise.png</file>
</qresource>
</RCC>

View File

@ -22,7 +22,6 @@
* SOFTWARE.
*/
#include "framelessmanager.h"
#include "framelessmanager_p.h"
#include <QtCore/qdebug.h>
#include <QtCore/qmutex.h>
@ -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 &params)
{
Q_ASSERT(params.isValid());
@ -203,11 +227,11 @@ void FramelessManagerPrivate::addWindow(const SystemParameters &params)
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 &params)
{
Q_D(FramelessManager);

View File

@ -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 <QtGui/qguiapplication.h>
#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<WindowsVersion>();
qRegisterMetaType<ApplicationType>();
qRegisterMetaType<BlurMode>();
qRegisterMetaType<WallpaperAspectStyle>();
qRegisterMetaType<VersionNumber>();
qRegisterMetaType<SystemParameters>();
qRegisterMetaType<VersionInfo>();
@ -155,6 +164,8 @@ void initialize()
qRegisterMetaType<FramelessManagerPrivate>();
qRegisterMetaType<FramelessConfig>();
qRegisterMetaType<ChromePalettePrivate>();
qRegisterMetaType<MicaMaterial>();
qRegisterMetaType<MicaMaterialPrivate>();
#endif
}

684
src/core/micamaterial.cpp Normal file
View File

@ -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 <QtCore/qdebug.h>
#include <QtCore/qsysinfo.h>
#include <QtCore/qmutex.h>
#include <QtGui/qpixmap.h>
#include <QtGui/qimage.h>
#include <QtGui/qpainter.h>
#include <QtGui/qscreen.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qmemrotate_p.h>
// 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<const int shift>
[[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<const int aprec, const int zprec>
static inline void qt_blurinner(uchar *bptr, int &zR, int &zG, int &zB, int &zA, const int alpha)
{
const auto pixel = reinterpret_cast<QRgb *>(bptr);
#define Z_MASK (0xff << zprec)
const int A_zprec = (qt_static_shift<zprec - 24>(*pixel) & Z_MASK);
const int R_zprec = (qt_static_shift<zprec - 16>(*pixel) & Z_MASK);
const int G_zprec = (qt_static_shift<zprec - 8>(*pixel) & Z_MASK);
const int B_zprec = (qt_static_shift<zprec>(*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<const int aprec, const int zprec>
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<const int aprec, const int zprec, const bool alphaOnly>
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<aprec, zprec>(bptr, zA, alpha);
} else {
qt_blurinner<aprec, zprec>(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<aprec, zprec>(bptr, zA, alpha);
} else {
qt_blurinner<aprec, zprec>(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<const int aprec, const int zprec, const bool alphaOnly>
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<aprec, zprec, alphaOnly>(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<const quint8 *>(img.bits()),
img.width(), img.height(), img.bytesPerLine(),
reinterpret_cast<quint8 *>(temp.bits()),
temp.bytesPerLine());
} else {
qt_memrotate270(reinterpret_cast<const quint32 *>(img.bits()),
img.width(), img.height(), img.bytesPerLine(),
reinterpret_cast<quint32 *>(temp.bits()),
temp.bytesPerLine());
}
} else {
if (img.depth() == 8) {
qt_memrotate90(reinterpret_cast<const quint8 *>(img.bits()),
img.width(), img.height(), img.bytesPerLine(),
reinterpret_cast<quint8 *>(temp.bits()),
temp.bytesPerLine());
} else {
qt_memrotate90(reinterpret_cast<const quint32 *>(img.bits()),
img.width(), img.height(), img.bytesPerLine(),
reinterpret_cast<quint32 *>(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<aprec, zprec, alphaOnly>(temp, row, alpha);
}
}
if (transposed == 0) {
if (img.depth() == 8) {
qt_memrotate90(reinterpret_cast<const quint8 *>(temp.bits()),
temp.width(), temp.height(), temp.bytesPerLine(),
reinterpret_cast<quint8 *>(img.bits()),
img.bytesPerLine());
} else {
qt_memrotate90(reinterpret_cast<const quint32 *>(temp.bits()),
temp.width(), temp.height(), temp.bytesPerLine(),
reinterpret_cast<quint32 *>(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 uchar *>(const_cast<const QImage &>(srcImage).bits());
const qsizetype sx = srcImage.bytesPerLine();
const qsizetype sx2 = (sx << 1);
auto dst = reinterpret_cast<uchar *>(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 uchar *>(const_cast<const QImage &>(srcImage).bits());
const qsizetype sx = srcImage.bytesPerLine();
const qsizetype sx2 = (sx << 1);
auto dst = reinterpret_cast<uchar *>(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 quint32 *>(const_cast<const QImage &>(srcImage).bits());
const qsizetype sx = (srcImage.bytesPerLine() >> 2);
const qsizetype sx2 = (sx << 1);
auto dst = reinterpret_cast<quint32 *>(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<MicaMaterial *>()) {
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

1
src/core/micamaterial.h Normal file
View File

@ -0,0 +1 @@
#include "../../include/FramelessHelper/Core/micamaterial.h"

View File

@ -0,0 +1 @@
#include "../../include/FramelessHelper/Core/private/micamaterial_p.h"

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

View File

@ -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;
}

View File

@ -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<int>(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();

View File

@ -23,6 +23,7 @@
*/
#include "utils.h"
#include "framelessconfig_p.h"
#include <QtCore/qdebug.h>
#include <QtCore/qregularexpression.h>
#include <QtGui/qwindow.h>
@ -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

View File

@ -24,10 +24,16 @@
#include "utils.h"
#include "framelessmanager.h"
#include "framelessconfig_p.h"
#include <QtCore/qdebug.h>
#include <QtCore/qhash.h>
#include <QtCore/qmutex.h>
#include <QtCore/qcoreapplication.h>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
# include <QtCore/qoperatingsystemversion.h>
#else
# include <QtCore/qsysinfo.h>
#endif
#include <QtGui/qwindow.h>
#include <objc/runtime.h>
#include <AppKit/AppKit.h>
@ -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"

View File

@ -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<WId, Win32UtilsHelperData> 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<WId, Win32UtilsHelperData> 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<LPWSTR>(&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<HWND>(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<HWND>(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<QNativeInterface::Private::QWindowsWindow *>(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<HWND>(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<DWORD>(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<DWORD>(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<LONG_PTR>(newClassStyle)) == 0) {
qWarning() << getSystemErrorMessage(kSetClassLongPtrW);
WARNING << getSystemErrorMessage(kSetClassLongPtrW);
return;
}
SetLastError(ERROR_SUCCESS);
const auto oldWindowStyle = static_cast<DWORD>(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<LONG_PTR>(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<HWND>(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<HWND>(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<WNDPROC>(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<LONG_PTR>(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<HWND>(windowId);
SetLastError(ERROR_SUCCESS);
if (SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(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<DWORD>(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<LONG_PTR>(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<SetWindowCompositionAttributePtr>(
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

View File

@ -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_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}/private>"
)
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}")

View File

@ -24,6 +24,7 @@
#include "framelessquickhelper.h"
#include "framelessquickhelper_p.h"
#include "quickmicamaterial.h"
#include <QtCore/qmutex.h>
#include <QtCore/qtimer.h>
#include <QtCore/qdebug.h>
@ -33,6 +34,7 @@
# include <QtGui/private/qwindow_p.h> // For QWINDOWSIZE_MAX
#endif
#include <QtQuick/qquickwindow.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuickTemplates2/private/qquickabstractbutton_p.h>
#include <framelessmanager.h>
#include <framelessconfig_p.h>
@ -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<QuickMicaMaterial *>()) {
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.";
}
}

View File

@ -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 <QtQuick/qquickwindow.h>
#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<QuickGlobal::BlurMode>();
qRegisterMetaType<QuickGlobal>();
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
qRegisterMetaType<FramelessQuickUtils>();
qRegisterMetaType<QuickChromePalette>();
@ -83,6 +92,8 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine)
qRegisterMetaType<FramelessQuickWindow>();
qRegisterMetaType<FramelessQuickWindowPrivate>();
qRegisterMetaType<FramelessQuickHelperPrivate>();
qRegisterMetaType<QuickMicaMaterial>();
qRegisterMetaType<QuickMicaMaterialPrivate>();
#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
qmlRegisterUncreatableType<QuickGlobal>(QUICK_URI_FULL, "FramelessHelperConstants",
@ -100,6 +111,8 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine)
qmlRegisterRevision<QQuickItem, 254>(QUICK_URI_FULL);
qmlRegisterType<FramelessQuickHelper>(QUICK_URI_EXPAND("FramelessHelper"));
qmlRegisterType<QuickMicaMaterial>(QUICK_URI_EXPAND("MicaMaterial"));
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
qmlRegisterType<QuickStandardSystemButton>(QUICK_URI_EXPAND("StandardSystemButton"));
qmlRegisterType<QuickStandardTitleBar>(QUICK_URI_EXPAND("StandardTitleBar"));

View File

@ -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)

View File

@ -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)

View File

@ -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
{

View File

@ -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)
{
}

View File

@ -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 <micamaterial.h>
#include <QtCore/qdebug.h>
#include <QtCore/qmutex.h>
#include <QtGui/qscreen.h>
#include <QtGui/qpainter.h>
#include <QtGui/qguiapplication.h>
#include <QtQuick/qquickwindow.h>
#include <QtQuick/qsgsimpletexturenode.h>
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<QSGTexture> m_texture;
QPointer<QuickMicaMaterial> m_item = nullptr;
QSGSimpleTextureNode *m_node = nullptr;
QPixmap m_pixmapCache = {};
QPointer<MicaMaterial> 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<WallpaperImageNode *>(old);
if (!node) {
node = new WallpaperImageNode(this);
}
return node;
}
FRAMELESSHELPER_END_NAMESPACE
#include "quickmicamaterial.moc"

View File

@ -0,0 +1 @@
#include "../../include/FramelessHelper/Quick/quickmicamaterial.h"

View File

@ -0,0 +1 @@
#include "../../include/FramelessHelper/Quick/private/quickmicamaterial_p.h"

View File

@ -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)

View File

@ -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)

View File

@ -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_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SUB_PROJ_PATH}/private>"
)
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}")

View File

@ -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))
{

View File

@ -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))
{

View File

@ -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 <QtCore/qmutex.h>
#include <QtCore/qhash.h>
#include <QtCore/qtimer.h>
@ -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<FramelessWidget *>(window)) {
if (const auto widgetPriv = FramelessWidgetPrivate::get(widget)) {
return widgetPriv->widgetsSharedHelper();
}
}
if (const auto mainWindow = qobject_cast<FramelessMainWindow *>(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.";
}
}
}

View File

@ -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
{

View File

@ -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};

View File

@ -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)

View File

@ -26,11 +26,20 @@
#include <QtCore/qcoreevent.h>
#include <QtGui/qevent.h>
#include <QtGui/qpainter.h>
#include <QtGui/qwindow.h>
#include <QtWidgets/qwidget.h>
#include <micamaterial.h>
#include <micamaterial_p.h>
#include <utils.h>
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