diff --git a/example/example_en_US.ts b/example/example_en_US.ts
index 1e7b9ce7..70fa49cb 100644
--- a/example/example_en_US.ts
+++ b/example/example_en_US.ts
@@ -639,7 +639,7 @@
-
+
@@ -659,57 +659,57 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -2264,6 +2264,26 @@ Some contents...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
T_TimePicker
diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts
index 88b8c04d..6214065e 100644
--- a/example/example_zh_CN.ts
+++ b/example/example_zh_CN.ts
@@ -616,7 +616,7 @@
MainWindow
-
+
夜间模式
@@ -648,7 +648,7 @@
-
+
取消
@@ -668,52 +668,52 @@
搜索
-
+
完成
-
+
下一步
-
+
上一步
-
+
在这里,您可以切换到夜间模式。
-
+
隐藏彩蛋
-
+
再试几下!!
-
+
升级提示
-
+
FluentUI 目前最新版本
-
+
-- 当前应用版本
-
+
-
+
确定
-
+
当前版本已经是最新版本
-
+
网络异常
@@ -2446,6 +2446,26 @@ Some contents...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
T_TimePicker
diff --git a/example/qml/page/T_Theme.qml b/example/qml/page/T_Theme.qml
index c1e5f280..9a01f989 100644
--- a/example/qml/page/T_Theme.qml
+++ b/example/qml/page/T_Theme.qml
@@ -13,7 +13,7 @@ FluScrollablePage{
FluFrame{
Layout.fillWidth: true
- Layout.preferredHeight: 408
+ Layout.fillHeight: true
padding: 10
ColumnLayout{
@@ -124,12 +124,69 @@ FluScrollablePage{
Layout.topMargin: 20
}
FluToggleSwitch{
+ id: toggle_blur
Layout.topMargin: 5
checked: FluTheme.blurBehindWindowEnabled
onClicked: {
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{
diff --git a/example/qml/window/MainWindow.qml b/example/qml/window/MainWindow.qml
index 610f1f3f..7f3e0fbb 100644
--- a/example/qml/window/MainWindow.qml
+++ b/example/qml/window/MainWindow.qml
@@ -236,6 +236,7 @@ FluWindow {
id: reveal
target: window.containerItem()
anchors.fill: parent
+ darkToLight: FluTheme.dark
onAnimationFinished:{
//动画结束后释放资源
loader_reveal.sourceComponent = undefined
@@ -256,17 +257,14 @@ FluWindow {
}
function handleDarkChanged(button){
- if(!FluTheme.animationEnabled || window.fitsAppBarWindows === false){
+ if(FluTools.isMacos() || !FluTheme.animationEnabled){
changeDark()
}else{
- if(loader_reveal.sourceComponent){
- return
- }
loader_reveal.sourceComponent = com_reveal
var target = window.containerItem()
var pos = button.mapToItem(target,0,0)
- var mouseX = pos.x
- var mouseY = pos.y
+ var mouseX = pos.x + button.width / 2
+ 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 reveal = loader_reveal.item
reveal.start(reveal.width*Screen.devicePixelRatio,reveal.height*Screen.devicePixelRatio,Qt.point(mouseX,mouseY),radius)
diff --git a/example/src/component/CircularReveal.cpp b/example/src/component/CircularReveal.cpp
index 65de3f88..d0d652e3 100644
--- a/example/src/component/CircularReveal.cpp
+++ b/example/src/component/CircularReveal.cpp
@@ -25,13 +25,32 @@ void CircularReveal::paint(QPainter *painter) {
path.moveTo(_center.x(), _center.y());
path.addEllipse(QPointF(_center.x(), _center.y()), _radius, _radius);
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();
}
[[maybe_unused]] void CircularReveal::start(int w, int h, const QPoint ¢er, int radius) {
- _anim->setStartValue(0);
- _anim->setEndValue(radius);
+ if (_anim->state() == QAbstractAnimation::Running) {
+ _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;
_grabResult = _target->grabToImage(QSize(w, h));
connect(_grabResult.data(), &QQuickItemGrabResult::ready, this,
diff --git a/example/src/component/CircularReveal.h b/example/src/component/CircularReveal.h
index 62db7c97..2c1adde2 100644
--- a/example/src/component/CircularReveal.h
+++ b/example/src/component/CircularReveal.h
@@ -10,6 +10,7 @@ class CircularReveal : public QQuickPaintedItem {
Q_OBJECT
Q_PROPERTY_AUTO_P(QQuickItem *, target)
Q_PROPERTY_AUTO(int, radius)
+ Q_PROPERTY_AUTO(bool, darkToLight)
public:
explicit CircularReveal(QQuickItem *parent = nullptr);
void paint(QPainter *painter) override;
diff --git a/src/FluFrameless.cpp b/src/FluFrameless.cpp
index 94ef70d3..165f7212 100644
--- a/src/FluFrameless.cpp
+++ b/src/FluFrameless.cpp
@@ -1,4 +1,5 @@
#include "FluFrameless.h"
+#include "FluTheme.h"
#include
#include
@@ -8,12 +9,75 @@
#ifdef Q_OS_WIN
-#pragma comment (lib, "user32.lib")
-#pragma comment (lib, "dwmapi.lib")
+static DwmSetWindowAttributeFunc pDwmSetWindowAttribute = nullptr;
+static DwmExtendFrameIntoClientAreaFunc pDwmExtendFrameIntoClientArea = nullptr;
+static DwmIsCompositionEnabledFunc pDwmIsCompositionEnabled = nullptr;
+static DwmEnableBlurBehindWindowFunc pDwmEnableBlurBehindWindow = nullptr;
+static SetWindowCompositionAttributeFunc pSetWindowCompositionAttribute = nullptr;
-#include
-#include
-#include
+static RTL_OSVERSIONINFOW GetRealOSVersionImpl() {
+ HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
+ using RtlGetVersionPtr = NTSTATUS(WINAPI *)(PRTL_OSVERSIONINFOW);
+ auto pRtlGetVersion =
+ reinterpret_cast(::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 const auto result = "windows_generic_MSG";
@@ -21,14 +85,12 @@ static inline QByteArray qtNativeEventType() {
}
static inline bool isCompositionEnabled() {
- typedef HRESULT (WINAPI *DwmIsCompositionEnabledPtr)(BOOL *pfEnabled);
HMODULE module = ::LoadLibraryW(L"dwmapi.dll");
if (module) {
BOOL composition_enabled = false;
- DwmIsCompositionEnabledPtr dwm_is_composition_enabled;
- dwm_is_composition_enabled = reinterpret_cast(::GetProcAddress(module, "DwmIsCompositionEnabled"));
- if (dwm_is_composition_enabled) {
- dwm_is_composition_enabled(&composition_enabled);
+ pDwmIsCompositionEnabled = reinterpret_cast(::GetProcAddress(module, "DwmIsCompositionEnabled"));
+ if (pDwmIsCompositionEnabled) {
+ pDwmIsCompositionEnabled(&composition_enabled);
}
return composition_enabled;
}
@@ -37,17 +99,172 @@ static inline bool isCompositionEnabled() {
static inline void setShadow(HWND hwnd) {
const MARGINS shadow = {1, 0, 0, 0};
- typedef HRESULT (WINAPI *DwmExtendFrameIntoClientAreaPtr)(HWND hWnd, const MARGINS *pMarInset);
HMODULE module = LoadLibraryW(L"dwmapi.dll");
if (module) {
- DwmExtendFrameIntoClientAreaPtr dwm_extendframe_into_client_area_;
- dwm_extendframe_into_client_area_ = reinterpret_cast(GetProcAddress(module, "DwmExtendFrameIntoClientArea"));
- if (dwm_extendframe_into_client_area_) {
- dwm_extendframe_into_client_area_(hwnd, &shadow);
+ pDwmExtendFrameIntoClientArea = reinterpret_cast(GetProcAddress(module, "DwmExtendFrameIntoClientArea"));
+ if (pDwmExtendFrameIntoClientArea) {
+ pDwmExtendFrameIntoClientArea(hwnd, &shadow);
}
}
}
+static inline bool setWindowDarkMode(HWND hwnd, const BOOL enable) {
+ if (!pDwmSetWindowAttribute) {
+ HMODULE module = LoadLibraryW(L"dwmapi.dll");
+ if (module) {
+ pDwmSetWindowAttribute = reinterpret_cast(
+ GetProcAddress(module, "DwmSetWindowAttribute"));
+ }
+ if (!pDwmSetWindowAttribute) {
+ 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};
+ HMODULE module = LoadLibraryW(L"dwmapi.dll");
+ if (module) {
+ pDwmExtendFrameIntoClientArea = reinterpret_cast(
+ GetProcAddress(module, "DwmExtendFrameIntoClientArea"));
+ if (!pDwmExtendFrameIntoClientArea) {
+ return false;
+ }
+ }
+ if (key == QStringLiteral("mica")) {
+ if (!isWin11OrGreater()) {
+ 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()) {
+ 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()) {
+ return false;
+ }
+ if (enable) {
+ MARGINS margins{-1, -1, -1, -1};
+ pDwmExtendFrameIntoClientArea(hwnd, &margins);
+ 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()) {
+ return false;
+ }
+ BOOL isDark = FluTheme::getInstance()->dark();
+ setWindowDarkMode(hwnd, isDark && enable);
+ if (isWin8OrGreater() && !pSetWindowCompositionAttribute) {
+ HMODULE module = LoadLibraryW(L"user32.dll");
+ pSetWindowCompositionAttribute = reinterpret_cast(
+ GetProcAddress(module, "SetWindowCompositionAttribute"));
+ if (!pSetWindowCompositionAttribute) {
+ return false;
+ }
+ }
+ 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;
+ if (!pDwmEnableBlurBehindWindow) {
+ HMODULE module = LoadLibraryW(L"user32.dll");
+ pDwmEnableBlurBehindWindow =
+ reinterpret_cast(
+ GetProcAddress(module, "DwmEnableBlurBehindWindowFunc"));
+ if (!pDwmEnableBlurBehindWindow) {
+ return false;
+ }
+ }
+ 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;
+ if (!pDwmEnableBlurBehindWindow) {
+ HMODULE module = LoadLibraryW(L"user32.dll");
+ pDwmEnableBlurBehindWindow =
+ reinterpret_cast(
+ GetProcAddress(module, "DwmEnableBlurBehindWindowFunc"));
+ if (!pDwmEnableBlurBehindWindow) {
+ return false;
+ }
+ }
+ pDwmEnableBlurBehindWindow(hwnd, &bb);
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
#endif
bool containsCursorToItem(QQuickItem *item) {
@@ -70,6 +287,7 @@ FluFrameless::FluFrameless(QQuickItem *parent) : QQuickItem{parent} {
_closeButton = nullptr;
_topmost = false;
_disabled = false;
+ _effect = "normal";
_isWindows11OrGreater = FluTools::getInstance()->isWindows11OrGreater();
}
@@ -107,6 +325,9 @@ void FluFrameless::componentComplete() {
#endif
HWND hwnd = reinterpret_cast(window()->winId());
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) {
::SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_THICKFRAME | WS_CAPTION);;
for (int i = 0; i <= QGuiApplication::screens().count() - 1; ++i) {
@@ -125,6 +346,42 @@ void FluFrameless::componentComplete() {
if (!window()->property("_hideShadow").toBool()) {
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
auto appBarHeight = _appbar->height();
h = qRound(h + appBarHeight);
@@ -234,6 +491,9 @@ void FluFrameless::componentComplete() {
*result = FALSE;
return false;
} else if (uMsg == WM_NCACTIVATE) {
+ if (effective() || (!effect().isEmpty() && _currentEffect!="normal")) {
+ return false;
+ }
*result = TRUE;
return true;
} else if (_isWindows11OrGreater && (uMsg == WM_NCLBUTTONDBLCLK || uMsg == WM_NCLBUTTONDOWN)) {
diff --git a/src/FluFrameless.h b/src/FluFrameless.h
index 332fb33d..80897270 100644
--- a/src/FluFrameless.h
+++ b/src/FluFrameless.h
@@ -6,6 +6,97 @@
#include
#include "stdafx.h"
+#ifdef Q_OS_WIN
+
+#pragma comment (lib, "user32.lib")
+#pragma comment (lib, "dwmapi.lib")
+
+#include
+#include
+#include
+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))
using QT_NATIVE_EVENT_RESULT_TYPE = qintptr;
using QT_ENTER_EVENT_TYPE = QEnterEvent;
@@ -23,6 +114,9 @@ class FluFrameless : public QQuickItem, QAbstractNativeEventFilter {
Q_PROPERTY_AUTO(bool, topmost)
Q_PROPERTY_AUTO(bool, disabled)
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)
public:
explicit FluFrameless(QQuickItem *parent = nullptr);
@@ -75,4 +169,5 @@ private:
quint64 _clickTimer = 0;
bool _isWindows11OrGreater = false;
QList> _hitTestList;
+ QString _currentEffect;
};
diff --git a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml
index ae9e7f4f..9ab23dab 100644
--- a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml
+++ b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml
@@ -13,6 +13,11 @@ Window {
property bool fixSize: false
property Component loadingItem: com_loading
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 {
title: window.title
height: 30
@@ -24,6 +29,15 @@ Window {
icon: window.windowIcon
}
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){
return FluTheme.windowActiveBackgroundColor
}
@@ -107,6 +121,11 @@ Window {
Component.onDestruction: {
frameless.onDestruction()
}
+ onEffectiveChanged: {
+ if(effective){
+ FluTheme.blurBehindWindowEnabled = false
+ }
+ }
}
Component{
id:com_background
@@ -162,8 +181,8 @@ Window {
FluAcrylic{
anchors.fill: parent
target: img_back
- tintOpacity: FluTheme.dark ? 0.80 : 0.75
- blurRadius: 64
+ tintOpacity: window.tintOpacity
+ blurRadius: window.blurRadius
visible: window.active && FluTheme.blurBehindWindowEnabled
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)
diff --git a/src/Qt5/imports/FluentUI/plugins.qmltypes b/src/Qt5/imports/FluentUI/plugins.qmltypes
index 66365ab0..5390b832 100644
--- a/src/Qt5/imports/FluentUI/plugins.qmltypes
+++ b/src/Qt5/imports/FluentUI/plugins.qmltypes
@@ -150,6 +150,9 @@ Module {
Property { name: "topmost"; type: "bool" }
Property { name: "disabled"; 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: "showMaximized" }
Method { name: "showMinimized" }
@@ -396,6 +399,7 @@ Module {
Property { name: "nativeText"; type: "bool" }
Property { name: "animationEnabled"; type: "bool" }
Property { name: "blurBehindWindowEnabled"; type: "bool" }
+ Property { name: "micaBackgroundColor"; type: "QColor" }
}
Component {
name: "FluThemeType"
@@ -4150,6 +4154,12 @@ Module {
Property { name: "autoVisible"; type: "bool" }
Property { name: "autoCenter"; 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: "resizeBorderColor"; type: "QColor" }
Property { name: "resizeBorderWidth"; type: "int" }
diff --git a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml
index 4dd9aed5..ba9bdf5b 100644
--- a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml
+++ b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml
@@ -12,6 +12,11 @@ Window {
property bool fixSize: false
property Component loadingItem: com_loading
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 {
title: window.title
height: 30
@@ -23,6 +28,15 @@ Window {
icon: window.windowIcon
}
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){
return FluTheme.windowActiveBackgroundColor
}
@@ -106,6 +120,11 @@ Window {
Component.onDestruction: {
frameless.onDestruction()
}
+ onEffectiveChanged: {
+ if(effective){
+ FluTheme.blurBehindWindowEnabled = false
+ }
+ }
}
Component{
id:com_background
@@ -161,8 +180,8 @@ Window {
FluAcrylic{
anchors.fill: parent
target: img_back
- tintOpacity: FluTheme.dark ? 0.80 : 0.75
- blurRadius: 64
+ tintOpacity: window.tintOpacity
+ blurRadius: window.blurRadius
visible: window.active && FluTheme.blurBehindWindowEnabled
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)
@@ -274,7 +293,7 @@ Window {
sourceComponent: window.useSystemAppBar ? undefined : com_app_bar
}
Item{
- id:layout_content
+ id: layout_content
anchors{
top: loader_app_bar.bottom
left: parent.left
@@ -293,7 +312,6 @@ Window {
id:info_bar
root: layout_container
}
-
FluLoader{
id:loader_border
anchors.fill: parent