Compare commits

..

10 Commits

Author SHA1 Message Date
zhuzichu
3e28c42e1c
Merge pull request #549 from jeffrey0326/main
整理dwmapi相关代码,修复Win11设置效果出错的bug
2024-08-22 00:44:45 +08:00
jeffrey0326
50b89e7eb2 整理代码,修复win11设置效果崩溃的错误 2024-08-21 22:50:43 +08:00
jeffrey0326
893000e40f
Merge branch 'zhuzichu520:main' into main 2024-08-21 22:08:49 +08:00
jeffrey0326
f2eca9a2b9 修复win11设置效果崩溃的错误 2024-08-21 22:08:13 +08:00
jeffrey0326
76f40a6265 修复win11设置效果崩溃的错误 2024-08-21 22:07:20 +08:00
zhuzichu
86f347edad
Merge pull request #548 from jeffrey0326/main
添加windows窗口背景效果,修改夜间模式动画
2024-08-21 19:06:51 +08:00
jeffrey0326
b6c7afc744 调整支持mingw编译 2024-08-21 17:56:23 +08:00
jeffrey0326
f099d3c737 调整支持mingw编译 2024-08-21 17:16:39 +08:00
jeffrey0326
cfbaf44a05 update 2024-08-21 16:16:47 +08:00
jeffrey0326
0f5e16464c 添加支持windows系统窗口win7-win10(area、blur)、win11(mica、mica-alt)效果切换,修改夜间模式切换动画效果,支持动画打断; 2024-08-21 16:12:35 +08:00
11 changed files with 580 additions and 67 deletions

View File

@ -639,7 +639,7 @@
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="95"/> <location filename="qml/window/MainWindow.qml" line="95"/>
<location filename="qml/window/MainWindow.qml" line="339"/> <location filename="qml/window/MainWindow.qml" line="337"/>
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -659,57 +659,57 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="304"/> <location filename="qml/window/MainWindow.qml" line="302"/>
<source>Finish</source> <source>Finish</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="305"/> <location filename="qml/window/MainWindow.qml" line="303"/>
<source>Next</source> <source>Next</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="306"/> <location filename="qml/window/MainWindow.qml" line="304"/>
<source>Previous</source> <source>Previous</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="310"/> <location filename="qml/window/MainWindow.qml" line="308"/>
<source>Dark Mode</source> <source>Dark Mode</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="310"/> <location filename="qml/window/MainWindow.qml" line="308"/>
<source>Here you can switch to night mode.</source> <source>Here you can switch to night mode.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="312"/> <location filename="qml/window/MainWindow.qml" line="310"/>
<source>Hide Easter eggs</source> <source>Hide Easter eggs</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="312"/> <location filename="qml/window/MainWindow.qml" line="310"/>
<source>Try a few more clicks!!</source> <source>Try a few more clicks!!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="336"/> <location filename="qml/window/MainWindow.qml" line="334"/>
<source>Upgrade Tips</source> <source>Upgrade Tips</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="337"/> <location filename="qml/window/MainWindow.qml" line="335"/>
<source>FluentUI is currently up to date </source> <source>FluentUI is currently up to date </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="337"/> <location filename="qml/window/MainWindow.qml" line="335"/>
<source> -- The current app version</source> <source> -- The current app version</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="337"/> <location filename="qml/window/MainWindow.qml" line="335"/>
<source> <source>
Now go and download the new version Now go and download the new version
@ -718,17 +718,17 @@ Updated content:
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="340"/> <location filename="qml/window/MainWindow.qml" line="338"/>
<source>OK</source> <source>OK</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="367"/> <location filename="qml/window/MainWindow.qml" line="365"/>
<source>The current version is already the latest</source> <source>The current version is already the latest</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="374"/> <location filename="qml/window/MainWindow.qml" line="372"/>
<source>The network is abnormal</source> <source>The network is abnormal</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -2264,6 +2264,26 @@ Some contents...</source>
<source>Open Blur Window</source> <source>Open Blur Window</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="qml/page/T_Theme.qml" line="136"/>
<source>window tintOpacity</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_Theme.qml" line="153"/>
<source>window blurRadius</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_Theme.qml" line="169"/>
<source>window effect</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_Theme.qml" line="178"/>
<source></source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>T_TimePicker</name> <name>T_TimePicker</name>

View File

@ -616,7 +616,7 @@
<context> <context>
<name>MainWindow</name> <name>MainWindow</name>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="310"/> <location filename="qml/window/MainWindow.qml" line="308"/>
<source>Dark Mode</source> <source>Dark Mode</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -648,7 +648,7 @@
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="95"/> <location filename="qml/window/MainWindow.qml" line="95"/>
<location filename="qml/window/MainWindow.qml" line="339"/> <location filename="qml/window/MainWindow.qml" line="337"/>
<source>Cancel</source> <source>Cancel</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -668,52 +668,52 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="304"/> <location filename="qml/window/MainWindow.qml" line="302"/>
<source>Finish</source> <source>Finish</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="305"/> <location filename="qml/window/MainWindow.qml" line="303"/>
<source>Next</source> <source>Next</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="306"/> <location filename="qml/window/MainWindow.qml" line="304"/>
<source>Previous</source> <source>Previous</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="310"/> <location filename="qml/window/MainWindow.qml" line="308"/>
<source>Here you can switch to night mode.</source> <source>Here you can switch to night mode.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="312"/> <location filename="qml/window/MainWindow.qml" line="310"/>
<source>Hide Easter eggs</source> <source>Hide Easter eggs</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="312"/> <location filename="qml/window/MainWindow.qml" line="310"/>
<source>Try a few more clicks!!</source> <source>Try a few more clicks!!</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="336"/> <location filename="qml/window/MainWindow.qml" line="334"/>
<source>Upgrade Tips</source> <source>Upgrade Tips</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="337"/> <location filename="qml/window/MainWindow.qml" line="335"/>
<source>FluentUI is currently up to date </source> <source>FluentUI is currently up to date </source>
<translation type="unfinished">FluentUI </translation> <translation type="unfinished">FluentUI </translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="337"/> <location filename="qml/window/MainWindow.qml" line="335"/>
<source> -- The current app version</source> <source> -- The current app version</source>
<translation type="unfinished"> -- </translation> <translation type="unfinished"> -- </translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="337"/> <location filename="qml/window/MainWindow.qml" line="335"/>
<source> <source>
Now go and download the new version Now go and download the new version
@ -726,17 +726,17 @@ Updated content:
</translation> </translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="340"/> <location filename="qml/window/MainWindow.qml" line="338"/>
<source>OK</source> <source>OK</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="367"/> <location filename="qml/window/MainWindow.qml" line="365"/>
<source>The current version is already the latest</source> <source>The current version is already the latest</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="qml/window/MainWindow.qml" line="374"/> <location filename="qml/window/MainWindow.qml" line="372"/>
<source>The network is abnormal</source> <source>The network is abnormal</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -2446,6 +2446,26 @@ Some contents...</source>
<source>Open Blur Window</source> <source>Open Blur Window</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="qml/page/T_Theme.qml" line="136"/>
<source>window tintOpacity</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_Theme.qml" line="153"/>
<source>window blurRadius</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_Theme.qml" line="169"/>
<source>window effect</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="qml/page/T_Theme.qml" line="178"/>
<source></source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>T_TimePicker</name> <name>T_TimePicker</name>

View File

@ -13,7 +13,7 @@ FluScrollablePage{
FluFrame{ FluFrame{
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 408 Layout.fillHeight: true
padding: 10 padding: 10
ColumnLayout{ ColumnLayout{
@ -124,12 +124,69 @@ FluScrollablePage{
Layout.topMargin: 20 Layout.topMargin: 20
} }
FluToggleSwitch{ FluToggleSwitch{
id: toggle_blur
Layout.topMargin: 5 Layout.topMargin: 5
checked: FluTheme.blurBehindWindowEnabled checked: FluTheme.blurBehindWindowEnabled
onClicked: { onClicked: {
FluTheme.blurBehindWindowEnabled = !FluTheme.blurBehindWindowEnabled FluTheme.blurBehindWindowEnabled = !FluTheme.blurBehindWindowEnabled
} }
} }
FluText{
visible: FluTheme.blurBehindWindowEnabled || window.effect === "dwm-blur"
text: qsTr("window tintOpacity")
Layout.topMargin: 20
}
FluSlider{
visible: FluTheme.blurBehindWindowEnabled || window.effect === "dwm-blur"
Layout.topMargin: 5
to:1
stepSize:0.1
onValueChanged: {
window.tintOpacity = value
}
Component.onCompleted: {
value = window.tintOpacity
}
}
FluText{
visible: FluTheme.blurBehindWindowEnabled
text: qsTr("window blurRadius")
Layout.topMargin: 20
}
FluSlider{
visible: FluTheme.blurBehindWindowEnabled
Layout.topMargin: 5
to:100
stepSize:1
onValueChanged: {
window.blurRadius = value
}
Component.onCompleted: {
value = window.blurRadius
}
}
FluText{
text: qsTr("window effect")
Layout.topMargin: 20
}
Row{
spacing: 10
Repeater{
model: window.availableEffects
delegate: FluRadioButton{
checked: window.effect === modelData
text: qsTr(`${modelData}`)
clickListener:function(){
window.effect = modelData
if(window.effective){
FluTheme.blurBehindWindowEnabled = false
toggle_blur.checked = Qt.binding( function() {return FluTheme.blurBehindWindowEnabled})
}
}
}
}
}
} }
} }
CodeExpander{ CodeExpander{

View File

@ -236,6 +236,7 @@ FluWindow {
id: reveal id: reveal
target: window.containerItem() target: window.containerItem()
anchors.fill: parent anchors.fill: parent
darkToLight: FluTheme.dark
onAnimationFinished:{ onAnimationFinished:{
// //
loader_reveal.sourceComponent = undefined loader_reveal.sourceComponent = undefined
@ -256,17 +257,14 @@ FluWindow {
} }
function handleDarkChanged(button){ function handleDarkChanged(button){
if(!FluTheme.animationEnabled || window.fitsAppBarWindows === false){ if(FluTools.isMacos() || !FluTheme.animationEnabled){
changeDark() changeDark()
}else{ }else{
if(loader_reveal.sourceComponent){
return
}
loader_reveal.sourceComponent = com_reveal loader_reveal.sourceComponent = com_reveal
var target = window.containerItem() var target = window.containerItem()
var pos = button.mapToItem(target,0,0) var pos = button.mapToItem(target,0,0)
var mouseX = pos.x var mouseX = pos.x + button.width / 2
var mouseY = pos.y var mouseY = pos.y + button.height / 2
var radius = Math.max(distance(mouseX,mouseY,0,0),distance(mouseX,mouseY,target.width,0),distance(mouseX,mouseY,0,target.height),distance(mouseX,mouseY,target.width,target.height)) var radius = Math.max(distance(mouseX,mouseY,0,0),distance(mouseX,mouseY,target.width,0),distance(mouseX,mouseY,0,target.height),distance(mouseX,mouseY,target.width,target.height))
var reveal = loader_reveal.item var reveal = loader_reveal.item
reveal.start(reveal.width*Screen.devicePixelRatio,reveal.height*Screen.devicePixelRatio,Qt.point(mouseX,mouseY),radius) reveal.start(reveal.width*Screen.devicePixelRatio,reveal.height*Screen.devicePixelRatio,Qt.point(mouseX,mouseY),radius)

View File

@ -25,13 +25,32 @@ void CircularReveal::paint(QPainter *painter) {
path.moveTo(_center.x(), _center.y()); path.moveTo(_center.x(), _center.y());
path.addEllipse(QPointF(_center.x(), _center.y()), _radius, _radius); path.addEllipse(QPointF(_center.x(), _center.y()), _radius, _radius);
painter->setCompositionMode(QPainter::CompositionMode_Clear); painter->setCompositionMode(QPainter::CompositionMode_Clear);
painter->fillPath(path, Qt::black); if(_darkToLight){
painter->fillPath(path, Qt::white);
}else{
QPainterPath outerRect;
outerRect.addRect(0, 0, width(), height());
outerRect = outerRect.subtracted(path);
painter->fillPath(outerRect, Qt::black);
}
painter->restore(); painter->restore();
} }
[[maybe_unused]] void CircularReveal::start(int w, int h, const QPoint &center, int radius) { [[maybe_unused]] void CircularReveal::start(int w, int h, const QPoint &center, int radius) {
_anim->setStartValue(0); if (_anim->state() == QAbstractAnimation::Running) {
_anim->setEndValue(radius); _anim->stop();
int currentRadius = _radius;
_anim->setStartValue(currentRadius);
_anim->setEndValue(_darkToLight ? 0 : radius);
} else {
if(_darkToLight){
_anim->setStartValue(radius);
_anim->setEndValue(0);
}else{
_anim->setStartValue(0);
_anim->setEndValue(radius);
}
}
_center = center; _center = center;
_grabResult = _target->grabToImage(QSize(w, h)); _grabResult = _target->grabToImage(QSize(w, h));
connect(_grabResult.data(), &QQuickItemGrabResult::ready, this, connect(_grabResult.data(), &QQuickItemGrabResult::ready, this,

View File

@ -10,6 +10,7 @@ class CircularReveal : public QQuickPaintedItem {
Q_OBJECT Q_OBJECT
Q_PROPERTY_AUTO_P(QQuickItem *, target) Q_PROPERTY_AUTO_P(QQuickItem *, target)
Q_PROPERTY_AUTO(int, radius) Q_PROPERTY_AUTO(int, radius)
Q_PROPERTY_AUTO(bool, darkToLight)
public: public:
explicit CircularReveal(QQuickItem *parent = nullptr); explicit CircularReveal(QQuickItem *parent = nullptr);
void paint(QPainter *painter) override; void paint(QPainter *painter) override;

View File

@ -1,4 +1,5 @@
#include "FluFrameless.h" #include "FluFrameless.h"
#include "FluTheme.h"
#include <QQuickWindow> #include <QQuickWindow>
#include <QGuiApplication> #include <QGuiApplication>
@ -8,28 +9,132 @@
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#pragma comment (lib, "user32.lib") static DwmSetWindowAttributeFunc pDwmSetWindowAttribute = nullptr;
#pragma comment (lib, "dwmapi.lib") static DwmExtendFrameIntoClientAreaFunc pDwmExtendFrameIntoClientArea = nullptr;
static DwmIsCompositionEnabledFunc pDwmIsCompositionEnabled = nullptr;
static DwmEnableBlurBehindWindowFunc pDwmEnableBlurBehindWindow = nullptr;
static SetWindowCompositionAttributeFunc pSetWindowCompositionAttribute = nullptr;
#include <windows.h> static RTL_OSVERSIONINFOW GetRealOSVersionImpl() {
#include <windowsx.h> HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
#include <dwmapi.h> using RtlGetVersionPtr = NTSTATUS(WINAPI *)(PRTL_OSVERSIONINFOW);
auto pRtlGetVersion =
reinterpret_cast<RtlGetVersionPtr>(::GetProcAddress(hMod, "RtlGetVersion"));
RTL_OSVERSIONINFOW rovi{};
rovi.dwOSVersionInfoSize = sizeof(rovi);
pRtlGetVersion(&rovi);
return rovi;
}
RTL_OSVERSIONINFOW GetRealOSVersion() {
static const auto result = GetRealOSVersionImpl();
return result;
}
static inline bool isWin8OrGreater() {
RTL_OSVERSIONINFOW rovi = GetRealOSVersion();
return (rovi.dwMajorVersion > 6) || (rovi.dwMajorVersion == 6 && rovi.dwMinorVersion >= 2);
}
static inline bool isWin8Point1OrGreater() {
RTL_OSVERSIONINFOW rovi = GetRealOSVersion();
return (rovi.dwMajorVersion > 6) || (rovi.dwMajorVersion == 6 && rovi.dwMinorVersion >= 3);
}
static inline bool isWin10OrGreater() {
RTL_OSVERSIONINFOW rovi = GetRealOSVersion();
return (rovi.dwMajorVersion > 10) || (rovi.dwMajorVersion == 10 && rovi.dwMinorVersion >= 0);
}
static inline bool isWin101809OrGreater() {
RTL_OSVERSIONINFOW rovi = GetRealOSVersion();
return (rovi.dwMajorVersion > 10) ||
(rovi.dwMajorVersion == 10 && rovi.dwMinorVersion >= 0 && rovi.dwBuildNumber >= 17763);
}
static inline bool isWin101903OrGreater() {
RTL_OSVERSIONINFOW rovi = GetRealOSVersion();
return (rovi.dwMajorVersion > 10) ||
(rovi.dwMajorVersion == 10 && rovi.dwMinorVersion >= 0 && rovi.dwBuildNumber >= 18362);
}
static inline bool isWin11OrGreater() {
RTL_OSVERSIONINFOW rovi = GetRealOSVersion();
return (rovi.dwMajorVersion > 10) ||
(rovi.dwMajorVersion == 10 && rovi.dwMinorVersion >= 0 && rovi.dwBuildNumber >= 22000);
}
static inline bool isWin1122H2OrGreater() {
RTL_OSVERSIONINFOW rovi = GetRealOSVersion();
return (rovi.dwMajorVersion > 10) ||
(rovi.dwMajorVersion == 10 && rovi.dwMinorVersion >= 0 && rovi.dwBuildNumber >= 22621);
}
static inline bool isWin10Only() {
return isWin10OrGreater() && !isWin11OrGreater();
}
static inline bool isWin7Only() {
RTL_OSVERSIONINFOW rovi = GetRealOSVersion();
return rovi.dwMajorVersion = 7;
}
static inline QByteArray qtNativeEventType() { static inline QByteArray qtNativeEventType() {
static const auto result = "windows_generic_MSG"; static const auto result = "windows_generic_MSG";
return result; return result;
} }
static inline bool isCompositionEnabled() { static inline bool initializeFunctionPointers() {
typedef HRESULT (WINAPI *DwmIsCompositionEnabledPtr)(BOOL *pfEnabled); HMODULE module = LoadLibraryW(L"dwmapi.dll");
HMODULE module = ::LoadLibraryW(L"dwmapi.dll");
if (module) { if (module) {
BOOL composition_enabled = false; if (!pDwmSetWindowAttribute) {
DwmIsCompositionEnabledPtr dwm_is_composition_enabled; pDwmSetWindowAttribute = reinterpret_cast<DwmSetWindowAttributeFunc>(
dwm_is_composition_enabled = reinterpret_cast<DwmIsCompositionEnabledPtr>(::GetProcAddress(module, "DwmIsCompositionEnabled")); GetProcAddress(module, "DwmSetWindowAttribute"));
if (dwm_is_composition_enabled) { if (!pDwmSetWindowAttribute) {
dwm_is_composition_enabled(&composition_enabled); return false;
}
} }
if (!pDwmExtendFrameIntoClientArea) {
pDwmExtendFrameIntoClientArea = reinterpret_cast<DwmExtendFrameIntoClientAreaFunc>(
GetProcAddress(module, "DwmExtendFrameIntoClientArea"));
if (!pDwmExtendFrameIntoClientArea) {
return false;
}
}
if (!pDwmIsCompositionEnabled) {
pDwmIsCompositionEnabled = reinterpret_cast<DwmIsCompositionEnabledFunc>(
::GetProcAddress(module, "DwmIsCompositionEnabled"));
if (!pDwmIsCompositionEnabled) {
return false;
}
}
if (!pDwmEnableBlurBehindWindow) {
pDwmEnableBlurBehindWindow =
reinterpret_cast<DwmEnableBlurBehindWindowFunc>(
GetProcAddress(module, "DwmEnableBlurBehindWindow"));
if (!pDwmEnableBlurBehindWindow) {
return false;
}
}
if (!pSetWindowCompositionAttribute) {
HMODULE user32 = LoadLibraryW(L"user32.dll");
if (!user32) {
return false;
}
pSetWindowCompositionAttribute = reinterpret_cast<SetWindowCompositionAttributeFunc>(
GetProcAddress(user32, "SetWindowCompositionAttribute"));
if (!pSetWindowCompositionAttribute) {
return false;
}
}
}
return true;
}
static inline bool isCompositionEnabled() {
if(initializeFunctionPointers()){
BOOL composition_enabled = false;
pDwmIsCompositionEnabled(&composition_enabled);
return composition_enabled; return composition_enabled;
} }
return false; return false;
@ -37,17 +142,125 @@ static inline bool isCompositionEnabled() {
static inline void setShadow(HWND hwnd) { static inline void setShadow(HWND hwnd) {
const MARGINS shadow = {1, 0, 0, 0}; const MARGINS shadow = {1, 0, 0, 0};
typedef HRESULT (WINAPI *DwmExtendFrameIntoClientAreaPtr)(HWND hWnd, const MARGINS *pMarInset); if (initializeFunctionPointers()) {
HMODULE module = LoadLibraryW(L"dwmapi.dll"); pDwmExtendFrameIntoClientArea(hwnd, &shadow);
if (module) {
DwmExtendFrameIntoClientAreaPtr dwm_extendframe_into_client_area_;
dwm_extendframe_into_client_area_ = reinterpret_cast<DwmExtendFrameIntoClientAreaPtr>(GetProcAddress(module, "DwmExtendFrameIntoClientArea"));
if (dwm_extendframe_into_client_area_) {
dwm_extendframe_into_client_area_(hwnd, &shadow);
}
} }
} }
static inline bool setWindowDarkMode(HWND hwnd, const BOOL enable) {
if (!initializeFunctionPointers()) {
return false;
}
return bool(pDwmSetWindowAttribute(hwnd, 20, &enable, sizeof(BOOL)));
}
static inline bool setWindowEffect(HWND hwnd, const QString &key, const bool &enable) {
static constexpr const MARGINS extendedMargins = {-1, -1, -1, -1};
if (key == QStringLiteral("mica")) {
if (!isWin11OrGreater() || !initializeFunctionPointers()) {
return false;
}
if (enable) {
pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
if (isWin1122H2OrGreater()) {
const DWORD backdropType = _DWMSBT_MAINWINDOW;
pDwmSetWindowAttribute(hwnd, 38, &backdropType, sizeof(backdropType));
} else {
const BOOL enable = TRUE;
pDwmSetWindowAttribute(hwnd, 1029, &enable, sizeof(enable));
}
} else {
if (isWin1122H2OrGreater()) {
const DWORD backdropType = _DWMSBT_AUTO;
pDwmSetWindowAttribute(hwnd, 38, &backdropType, sizeof(backdropType));
} else {
const BOOL enable = FALSE;
pDwmSetWindowAttribute(hwnd, 1029, &enable, sizeof(enable));
}
}
BOOL isDark = FluTheme::getInstance()->dark();
setWindowDarkMode(hwnd, isDark);
return true;
}
if (key == QStringLiteral("mica-alt")) {
if (!isWin1122H2OrGreater() || !initializeFunctionPointers()) {
return false;
}
if (enable) {
pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
const DWORD backdropType = _DWMSBT_TABBEDWINDOW;
pDwmSetWindowAttribute(hwnd, 38, &backdropType, sizeof(backdropType));
} else {
const DWORD backdropType = _DWMSBT_AUTO;
pDwmSetWindowAttribute(hwnd, 38, &backdropType, sizeof(backdropType));
}
BOOL isDark = FluTheme::getInstance()->dark();
setWindowDarkMode(hwnd, isDark);
return true;
}
if (key == QStringLiteral("acrylic")) {
if (!isWin11OrGreater() || !initializeFunctionPointers()) {
return false;
}
if (enable) {
pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
DWORD system_backdrop_type = _DWMSBT_TRANSIENTWINDOW;
pDwmSetWindowAttribute(hwnd, 38, &system_backdrop_type, sizeof(DWORD));
} else {
const DWORD backdropType = _DWMSBT_AUTO;
pDwmSetWindowAttribute(hwnd, 38, &backdropType, sizeof(backdropType));
}
BOOL isDark = FluTheme::getInstance()->dark();
setWindowDarkMode(hwnd, isDark);
return true;
}
if (key == QStringLiteral("dwm-blur")) {
if ((isWin7Only() && !isCompositionEnabled()) || !initializeFunctionPointers()) {
return false;
}
BOOL isDark = FluTheme::getInstance()->dark();
setWindowDarkMode(hwnd, isDark && enable);
if (enable) {
if (isWin8OrGreater()) {
ACCENT_POLICY policy{};
policy.dwAccentState = ACCENT_ENABLE_BLURBEHIND;
policy.dwAccentFlags = ACCENT_NONE;
WINDOWCOMPOSITIONATTRIBDATA wcad{};
wcad.Attrib = WCA_ACCENT_POLICY;
wcad.pvData = &policy;
wcad.cbData = sizeof(policy);
pSetWindowCompositionAttribute(hwnd, &wcad);
} else {
DWM_BLURBEHIND bb{};
bb.fEnable = TRUE;
bb.dwFlags = DWM_BB_ENABLE;
pDwmEnableBlurBehindWindow(hwnd, &bb);
}
} else {
if (isWin8OrGreater()) {
ACCENT_POLICY policy{};
policy.dwAccentState = ACCENT_DISABLED;
policy.dwAccentFlags = ACCENT_NONE;
WINDOWCOMPOSITIONATTRIBDATA wcad{};
wcad.Attrib = WCA_ACCENT_POLICY;
wcad.pvData = &policy;
wcad.cbData = sizeof(policy);
pSetWindowCompositionAttribute(hwnd, &wcad);
} else {
DWM_BLURBEHIND bb{};
bb.fEnable = FALSE;
bb.dwFlags = DWM_BB_ENABLE;
pDwmEnableBlurBehindWindow(hwnd, &bb);
}
}
return true;
}
return false;
}
#endif #endif
bool containsCursorToItem(QQuickItem *item) { bool containsCursorToItem(QQuickItem *item) {
@ -70,6 +283,7 @@ FluFrameless::FluFrameless(QQuickItem *parent) : QQuickItem{parent} {
_closeButton = nullptr; _closeButton = nullptr;
_topmost = false; _topmost = false;
_disabled = false; _disabled = false;
_effect = "normal";
_isWindows11OrGreater = FluTools::getInstance()->isWindows11OrGreater(); _isWindows11OrGreater = FluTools::getInstance()->isWindows11OrGreater();
} }
@ -107,6 +321,9 @@ void FluFrameless::componentComplete() {
#endif #endif
HWND hwnd = reinterpret_cast<HWND>(window()->winId()); HWND hwnd = reinterpret_cast<HWND>(window()->winId());
DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE); DWORD style = ::GetWindowLongPtr(hwnd, GWL_STYLE);
# if (QT_VERSION == QT_VERSION_CHECK(6, 7, 2))
style &= ~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU);
# endif
if (_fixSize) { if (_fixSize) {
::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME | WS_CAPTION);; ::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME | WS_CAPTION);;
for (int i = 0; i <= QGuiApplication::screens().count() - 1; ++i) { for (int i = 0; i <= QGuiApplication::screens().count() - 1; ++i) {
@ -125,6 +342,42 @@ void FluFrameless::componentComplete() {
if (!window()->property("_hideShadow").toBool()) { if (!window()->property("_hideShadow").toBool()) {
setShadow(hwnd); setShadow(hwnd);
} }
if (isWin11OrGreater()) {
availableEffects({"mica", "mica-alt", "acrylic", "dwm-blur", "normal"});
} else if (isWin7Only()) {
availableEffects({"dwm-blur","normal"});
}
if (!_effect.isEmpty()) {
effective(setWindowEffect(hwnd, _effect, true));
if (effective()) {
_currentEffect = effect();
}
}
connect(this, &FluFrameless::effectChanged, this, [hwnd, this] {
if (effect() == _currentEffect) {
return;
}
if (effective()) {
setWindowEffect(hwnd, _currentEffect, false);
}
effective(setWindowEffect(hwnd, effect(), true));
if (effective()) {
_currentEffect = effect();
} else {
_effect = "normal";
_currentEffect = "normal";
}
});
connect(FluTheme::getInstance(), &FluTheme::blurBehindWindowEnabledChanged, this, [this] {
if (FluTheme::getInstance()->blurBehindWindowEnabled()) {
effect("normal");
}
});
connect(FluTheme::getInstance(), &FluTheme::darkChanged, this, [hwnd, this] {
if (effective() && !_currentEffect.isEmpty() && _currentEffect != "normal") {
setWindowDarkMode(hwnd, FluTheme::getInstance()->dark());
}
});
#endif #endif
auto appBarHeight = _appbar->height(); auto appBarHeight = _appbar->height();
h = qRound(h + appBarHeight); h = qRound(h + appBarHeight);
@ -234,6 +487,9 @@ void FluFrameless::componentComplete() {
*result = FALSE; *result = FALSE;
return false; return false;
} else if (uMsg == WM_NCACTIVATE) { } else if (uMsg == WM_NCACTIVATE) {
if (effective() || (!effect().isEmpty() && _currentEffect!="normal")) {
return false;
}
*result = TRUE; *result = TRUE;
return true; return true;
} else if (_isWindows11OrGreater && (uMsg == WM_NCLBUTTONDBLCLK || uMsg == WM_NCLBUTTONDOWN)) { } else if (_isWindows11OrGreater && (uMsg == WM_NCLBUTTONDBLCLK || uMsg == WM_NCLBUTTONDOWN)) {

View File

@ -6,6 +6,97 @@
#include <QQmlProperty> #include <QQmlProperty>
#include "stdafx.h" #include "stdafx.h"
#ifdef Q_OS_WIN
#pragma comment (lib, "user32.lib")
#pragma comment (lib, "dwmapi.lib")
#include <windows.h>
#include <windowsx.h>
#include <dwmapi.h>
enum _DWM_SYSTEMBACKDROP_TYPE {
_DWMSBT_AUTO, // [Default] Let DWM automatically decide the system-drawn backdrop for this
// window.
_DWMSBT_NONE, // [Disable] Do not draw any system backdrop.
_DWMSBT_MAINWINDOW, // [Mica] Draw the backdrop material effect corresponding to a
// long-lived window.
_DWMSBT_TRANSIENTWINDOW, // [Acrylic] Draw the backdrop material effect corresponding to a
// transient window.
_DWMSBT_TABBEDWINDOW, // [Mica Alt] Draw the backdrop material effect corresponding to a
// window with a tabbed title bar.
};
enum WINDOWCOMPOSITIONATTRIB {
WCA_UNDEFINED = 0,
WCA_NCRENDERING_ENABLED = 1,
WCA_NCRENDERING_POLICY = 2,
WCA_TRANSITIONS_FORCEDISABLED = 3,
WCA_ALLOW_NCPAINT = 4,
WCA_CAPTION_BUTTON_BOUNDS = 5,
WCA_NONCLIENT_RTL_LAYOUT = 6,
WCA_FORCE_ICONIC_REPRESENTATION = 7,
WCA_EXTENDED_FRAME_BOUNDS = 8,
WCA_HAS_ICONIC_BITMAP = 9,
WCA_THEME_ATTRIBUTES = 10,
WCA_NCRENDERING_EXILED = 11,
WCA_NCADORNMENTINFO = 12,
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
WCA_VIDEO_OVERLAY_ACTIVE = 14,
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
WCA_DISALLOW_PEEK = 16,
WCA_CLOAK = 17,
WCA_CLOAKED = 18,
WCA_ACCENT_POLICY = 19,
WCA_FREEZE_REPRESENTATION = 20,
WCA_EVER_UNCLOAKED = 21,
WCA_VISUAL_OWNER = 22,
WCA_HOLOGRAPHIC = 23,
WCA_EXCLUDED_FROM_DDA = 24,
WCA_PASSIVEUPDATEMODE = 25,
WCA_USEDARKMODECOLORS = 26,
WCA_CORNER_STYLE = 27,
WCA_PART_COLOR = 28,
WCA_DISABLE_MOVESIZE_FEEDBACK = 29,
WCA_LAST = 30
};
enum ACCENT_STATE {
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3, // Traditional DWM blur
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803
ACCENT_ENABLE_HOST_BACKDROP = 5, // RS5 1809
ACCENT_INVALID_STATE = 6 // Using this value will remove the window background
};
enum ACCENT_FLAG {
ACCENT_NONE = 0,
ACCENT_ENABLE_ACRYLIC = 1,
ACCENT_ENABLE_ACRYLIC_WITH_LUMINOSITY = 482
};
struct ACCENT_POLICY {
DWORD dwAccentState;
DWORD dwAccentFlags;
DWORD dwGradientColor; // #AABBGGRR
DWORD dwAnimationId;
};
using PACCENT_POLICY = ACCENT_POLICY *;
struct WINDOWCOMPOSITIONATTRIBDATA {
WINDOWCOMPOSITIONATTRIB Attrib;
PVOID pvData;
SIZE_T cbData;
};
using PWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA *;
typedef HRESULT (WINAPI *DwmSetWindowAttributeFunc)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
typedef HRESULT (WINAPI *DwmExtendFrameIntoClientAreaFunc)(HWND hwnd, const MARGINS *pMarInset);
typedef HRESULT (WINAPI *DwmIsCompositionEnabledFunc)(BOOL *pfEnabled);
typedef HRESULT (WINAPI *DwmEnableBlurBehindWindowFunc)(HWND hWnd, const DWM_BLURBEHIND *pBlurBehind);
typedef BOOL (WINAPI *SetWindowCompositionAttributeFunc)(HWND hwnd, const WINDOWCOMPOSITIONATTRIBDATA *);
#endif
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
using QT_NATIVE_EVENT_RESULT_TYPE = qintptr; using QT_NATIVE_EVENT_RESULT_TYPE = qintptr;
using QT_ENTER_EVENT_TYPE = QEnterEvent; using QT_ENTER_EVENT_TYPE = QEnterEvent;
@ -23,6 +114,9 @@ class FluFrameless : public QQuickItem, QAbstractNativeEventFilter {
Q_PROPERTY_AUTO(bool, topmost) Q_PROPERTY_AUTO(bool, topmost)
Q_PROPERTY_AUTO(bool, disabled) Q_PROPERTY_AUTO(bool, disabled)
Q_PROPERTY_AUTO(bool, fixSize) Q_PROPERTY_AUTO(bool, fixSize)
Q_PROPERTY_AUTO(QString, effect)
Q_PROPERTY_READONLY_AUTO(bool, effective)
Q_PROPERTY_READONLY_AUTO(QStringList, availableEffects)
QML_NAMED_ELEMENT(FluFrameless) QML_NAMED_ELEMENT(FluFrameless)
public: public:
explicit FluFrameless(QQuickItem *parent = nullptr); explicit FluFrameless(QQuickItem *parent = nullptr);
@ -75,4 +169,5 @@ private:
quint64 _clickTimer = 0; quint64 _clickTimer = 0;
bool _isWindows11OrGreater = false; bool _isWindows11OrGreater = false;
QList<QPointer<QQuickItem>> _hitTestList; QList<QPointer<QQuickItem>> _hitTestList;
QString _currentEffect;
}; };

View File

@ -13,6 +13,11 @@ Window {
property bool fixSize: false property bool fixSize: false
property Component loadingItem: com_loading property Component loadingItem: com_loading
property bool fitsAppBarWindows: false property bool fitsAppBarWindows: false
property var tintOpacity: FluTheme.dark ? 0.80 : 0.75
property int blurRadius: 60
property alias effect: frameless.effect
readonly property alias effective: frameless.effective
readonly property var availableEffects: frameless.availableEffects
property Item appBar: FluAppBar { property Item appBar: FluAppBar {
title: window.title title: window.title
height: 30 height: 30
@ -24,6 +29,15 @@ Window {
icon: window.windowIcon icon: window.windowIcon
} }
property color backgroundColor: { property color backgroundColor: {
if(frameless.effective && active){
var backcolor
if(frameless.effect==="dwm-blur"){
backcolor = FluTools.withOpacity(FluTheme.windowActiveBackgroundColor, window.tintOpacity)
}else{
backcolor = "transparent"
}
return backcolor
}
if(active){ if(active){
return FluTheme.windowActiveBackgroundColor return FluTheme.windowActiveBackgroundColor
} }
@ -107,6 +121,11 @@ Window {
Component.onDestruction: { Component.onDestruction: {
frameless.onDestruction() frameless.onDestruction()
} }
onEffectiveChanged: {
if(effective){
FluTheme.blurBehindWindowEnabled = false
}
}
} }
Component{ Component{
id:com_background id:com_background
@ -162,8 +181,8 @@ Window {
FluAcrylic{ FluAcrylic{
anchors.fill: parent anchors.fill: parent
target: img_back target: img_back
tintOpacity: FluTheme.dark ? 0.80 : 0.75 tintOpacity: window.tintOpacity
blurRadius: 64 blurRadius: window.blurRadius
visible: window.active && FluTheme.blurBehindWindowEnabled visible: window.active && FluTheme.blurBehindWindowEnabled
tintColor: FluTheme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1) tintColor: FluTheme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1)
targetRect: Qt.rect(window.x-window.screen.virtualX,window.y-window.screen.virtualY,window.width,window.height) targetRect: Qt.rect(window.x-window.screen.virtualX,window.y-window.screen.virtualY,window.width,window.height)

View File

@ -150,6 +150,9 @@ Module {
Property { name: "topmost"; type: "bool" } Property { name: "topmost"; type: "bool" }
Property { name: "disabled"; type: "bool" } Property { name: "disabled"; type: "bool" }
Property { name: "fixSize"; type: "bool" } Property { name: "fixSize"; type: "bool" }
Property { name: "effect"; type: "string" }
Property { name: "effective"; type: "bool" }
Property {name: "availableEffects"; type: "QVariant"}
Method { name: "showFullScreen" } Method { name: "showFullScreen" }
Method { name: "showMaximized" } Method { name: "showMaximized" }
Method { name: "showMinimized" } Method { name: "showMinimized" }
@ -396,6 +399,7 @@ Module {
Property { name: "nativeText"; type: "bool" } Property { name: "nativeText"; type: "bool" }
Property { name: "animationEnabled"; type: "bool" } Property { name: "animationEnabled"; type: "bool" }
Property { name: "blurBehindWindowEnabled"; type: "bool" } Property { name: "blurBehindWindowEnabled"; type: "bool" }
Property { name: "micaBackgroundColor"; type: "QColor" }
} }
Component { Component {
name: "FluThemeType" name: "FluThemeType"
@ -4150,6 +4154,12 @@ Module {
Property { name: "autoVisible"; type: "bool" } Property { name: "autoVisible"; type: "bool" }
Property { name: "autoCenter"; type: "bool" } Property { name: "autoCenter"; type: "bool" }
Property { name: "autoDestroy"; type: "bool" } Property { name: "autoDestroy"; type: "bool" }
Property { name: "effect"; type: "string" }
Property { name: "effective"; type: "bool" }
Property { name: "blurRadius"; type: "int" }
Property { name: "tintOpacity"; type: "QVariant" }
Property { name: "useSystemAppBar"; type: "bool" } Property { name: "useSystemAppBar"; type: "bool" }
Property { name: "resizeBorderColor"; type: "QColor" } Property { name: "resizeBorderColor"; type: "QColor" }
Property { name: "resizeBorderWidth"; type: "int" } Property { name: "resizeBorderWidth"; type: "int" }

View File

@ -12,6 +12,11 @@ Window {
property bool fixSize: false property bool fixSize: false
property Component loadingItem: com_loading property Component loadingItem: com_loading
property bool fitsAppBarWindows: false property bool fitsAppBarWindows: false
property var tintOpacity: FluTheme.dark ? 0.80 : 0.75
property int blurRadius: 60
property alias effect: frameless.effect
readonly property alias effective: frameless.effective
readonly property var availableEffects: frameless.availableEffects
property Item appBar: FluAppBar { property Item appBar: FluAppBar {
title: window.title title: window.title
height: 30 height: 30
@ -23,6 +28,15 @@ Window {
icon: window.windowIcon icon: window.windowIcon
} }
property color backgroundColor: { property color backgroundColor: {
if(frameless.effective && active){
var backcolor
if(frameless.effect==="dwm-blur"){
backcolor = FluTools.withOpacity(FluTheme.windowActiveBackgroundColor, window.tintOpacity)
}else{
backcolor = "transparent"
}
return backcolor
}
if(active){ if(active){
return FluTheme.windowActiveBackgroundColor return FluTheme.windowActiveBackgroundColor
} }
@ -106,6 +120,11 @@ Window {
Component.onDestruction: { Component.onDestruction: {
frameless.onDestruction() frameless.onDestruction()
} }
onEffectiveChanged: {
if(effective){
FluTheme.blurBehindWindowEnabled = false
}
}
} }
Component{ Component{
id:com_background id:com_background
@ -161,8 +180,8 @@ Window {
FluAcrylic{ FluAcrylic{
anchors.fill: parent anchors.fill: parent
target: img_back target: img_back
tintOpacity: FluTheme.dark ? 0.80 : 0.75 tintOpacity: window.tintOpacity
blurRadius: 64 blurRadius: window.blurRadius
visible: window.active && FluTheme.blurBehindWindowEnabled visible: window.active && FluTheme.blurBehindWindowEnabled
tintColor: FluTheme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1) tintColor: FluTheme.dark ? Qt.rgba(0, 0, 0, 1) : Qt.rgba(1, 1, 1, 1)
targetRect: Qt.rect(window.x-window.screen.virtualX,window.y-window.screen.virtualY,window.width,window.height) targetRect: Qt.rect(window.x-window.screen.virtualX,window.y-window.screen.virtualY,window.width,window.height)
@ -274,7 +293,7 @@ Window {
sourceComponent: window.useSystemAppBar ? undefined : com_app_bar sourceComponent: window.useSystemAppBar ? undefined : com_app_bar
} }
Item{ Item{
id:layout_content id: layout_content
anchors{ anchors{
top: loader_app_bar.bottom top: loader_app_bar.bottom
left: parent.left left: parent.left
@ -293,7 +312,6 @@ Window {
id:info_bar id:info_bar
root: layout_container root: layout_container
} }
FluLoader{ FluLoader{
id:loader_border id:loader_border
anchors.fill: parent anchors.fill: parent