diff --git a/example/example_en_US.ts b/example/example_en_US.ts index af16a652..e49fa5ec 100644 --- a/example/example_en_US.ts +++ b/example/example_en_US.ts @@ -2190,6 +2190,11 @@ Some contents... Open Animation + + + Open Blur Window + + T_TimePicker diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts index ce01e5a8..5f4c183e 100644 --- a/example/example_zh_CN.ts +++ b/example/example_zh_CN.ts @@ -2280,6 +2280,11 @@ Some contents... Open Animation 开启动画 + + + Open Blur Window + + T_TimePicker diff --git a/example/qml/page/T_Theme.qml b/example/qml/page/T_Theme.qml index a7e4a01d..308c1e92 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: 340 + Layout.preferredHeight: 400 padding: 10 ColumnLayout{ @@ -119,6 +119,17 @@ FluScrollablePage{ FluTheme.animationEnabled = !FluTheme.animationEnabled } } + FluText{ + text: qsTr("Open Blur Window") + Layout.topMargin: 20 + } + FluToggleSwitch{ + Layout.topMargin: 5 + checked: FluTheme.blurBehindWindowEnabled + onClicked: { + FluTheme.blurBehindWindowEnabled = !FluTheme.blurBehindWindowEnabled + } + } } } CodeExpander{ diff --git a/src/FluTheme.cpp b/src/FluTheme.cpp index 4948f3e8..ff2b760b 100644 --- a/src/FluTheme.cpp +++ b/src/FluTheme.cpp @@ -2,9 +2,11 @@ #include #include +#include #include "Def.h" #include "FluentIconDef.h" #include "FluColors.h" +#include "FluTools.h" bool systemDark() { QPalette palette = QGuiApplication::palette(); @@ -18,13 +20,20 @@ FluTheme::FluTheme(QObject *parent) : QObject{parent} { _nativeText = false; _animationEnabled = true; _systemDark = systemDark(); + _desktopImagePath = ""; + _blurBehindWindowEnabled = false; QGuiApplication::instance()->installEventFilter(this); + refreshColors(); + updateDesktopImage(); connect(this, &FluTheme::darkModeChanged, this, [=] { Q_EMIT darkChanged(); }); connect(this, &FluTheme::darkChanged, this, [=] { refreshColors(); }); connect(this, &FluTheme::accentColorChanged, this, [=] { refreshColors(); }); - refreshColors(); + connect(&_watcher, &QFileSystemWatcher::fileChanged, this, [=](const QString &path){ + Q_EMIT desktopImagePathChanged(); + }); + startTimer(500); } void FluTheme::refreshColors() { @@ -38,6 +47,8 @@ void FluTheme::refreshColors() { fontSecondaryColor(isDark ? QColor(222, 222, 222, 255) : QColor(102, 102, 102, 255)); fontTertiaryColor(isDark ? QColor(200, 200, 200, 255) : QColor(153, 153, 153, 255)); itemNormalColor(isDark ? QColor(255, 255, 255, 0) : QColor(0, 0, 0, 0)); + frameColor(isDark ? QColor(255, 255, 255, qRound(255 * 0.12)) : QColor(0, 0, 0, qRound(255 * 0.09))); + frameActiveColor(isDark ? QColor(32, 32, 32, qRound(255 * 0.8)) : QColor(255, 255, 255, qRound(255 * 0.6))); itemHoverColor(isDark ? QColor(255, 255, 255, qRound(255 * 0.06)) : QColor(0, 0, 0, qRound(255 * 0.03))); itemPressColor(isDark ? QColor(255, 255, 255, qRound(255 * 0.09)) : QColor(0, 0, 0, qRound(255 * 0.06))); itemCheckColor(isDark ? QColor(255, 255, 255, qRound(255 * 0.12)) : QColor(0, 0, 0, qRound(255 * 0.09))); @@ -78,3 +89,19 @@ bool FluTheme::dark() const { return false; } } + +void FluTheme::updateDesktopImage(){ + auto path = FluTools::getInstance()->getWallpaperFilePath(); + if(_desktopImagePath != path){ + if(!_desktopImagePath.isEmpty()){ + _watcher.removePath(_desktopImagePath); + } + desktopImagePath(path); + _watcher.addPath(path); + } +} + +void FluTheme::timerEvent(QTimerEvent *event) +{ + updateDesktopImage(); +} diff --git a/src/FluTheme.h b/src/FluTheme.h index e1b15ee8..2d95a39b 100644 --- a/src/FluTheme.h +++ b/src/FluTheme.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include "FluAccentColor.h" #include "stdafx.h" #include "singleton.h" @@ -15,7 +17,7 @@ */ class FluTheme : public QObject { Q_OBJECT - Q_PROPERTY(bool dark READ dark NOTIFY darkChanged) +Q_PROPERTY(bool dark READ dark NOTIFY darkChanged) Q_PROPERTY_AUTO_P(FluAccentColor*, accentColor); Q_PROPERTY_AUTO(QColor, primaryColor); Q_PROPERTY_AUTO(QColor, backgroundColor); @@ -26,12 +28,16 @@ Q_PROPERTY_AUTO(QColor, fontPrimaryColor); Q_PROPERTY_AUTO(QColor, fontSecondaryColor); Q_PROPERTY_AUTO(QColor, fontTertiaryColor); Q_PROPERTY_AUTO(QColor, itemNormalColor); +Q_PROPERTY_AUTO(QColor, frameColor); +Q_PROPERTY_AUTO(QColor, frameActiveColor); Q_PROPERTY_AUTO(QColor, itemHoverColor); Q_PROPERTY_AUTO(QColor, itemPressColor); Q_PROPERTY_AUTO(QColor, itemCheckColor); +Q_PROPERTY_AUTO(QString, desktopImagePath); Q_PROPERTY_AUTO(int, darkMode); Q_PROPERTY_AUTO(bool, nativeText); Q_PROPERTY_AUTO(bool, animationEnabled); +Q_PROPERTY_AUTO(bool, blurBehindWindowEnabled); QML_NAMED_ELEMENT(FluTheme) QML_SINGLETON @@ -42,6 +48,14 @@ private: void refreshColors(); + void updateBackgroundMainColor(); + +protected: + + void timerEvent(QTimerEvent *event) override; + + void updateDesktopImage(); + public: SINGLETON(FluTheme) @@ -55,6 +69,7 @@ SINGLETON(FluTheme) private: bool _systemDark; + QFileSystemWatcher _watcher; }; #endif // FLUTHEME_H diff --git a/src/FluTools.cpp b/src/FluTools.cpp index bed2f651..fbea119c 100644 --- a/src/FluTools.cpp +++ b/src/FluTools.cpp @@ -16,6 +16,14 @@ #include #include +#ifdef Q_OS_WIN +#pragma comment (lib, "user32.lib") + +#include +#include + +#endif + FluTools::FluTools(QObject *parent) : QObject{parent} { } @@ -244,3 +252,32 @@ bool FluTools::isWindows10OrGreater() { QRect FluTools::desktopAvailableGeometry(QQuickWindow *window) { return window->screen()->availableGeometry(); } + +QString FluTools::getWallpaperFilePath() { +#if defined(Q_OS_WIN) + wchar_t path[MAX_PATH] = {}; + if (::SystemParametersInfoW(SPI_GETDESKWALLPAPER, MAX_PATH, path, FALSE) == FALSE) { + return {}; + } + return QString::fromWCharArray(path); +#endif + return {}; +} + +QColor FluTools::imageMainColor(const QImage& image, double bright) { + int step = 20; + int t = 0; + int r = 0, g = 0, b = 0; + for (int i = 0; i < image.width(); i += step) { + for (int j = 0; j < image.height(); j += step) { + if (image.valid(i, j)) { + t++; + QColor c = image.pixel(i, j); + r += c.red(); + b += c.blue(); + g += c.green(); + } + } + } + return QColor(int(bright * r / t) > 255 ? 255 : int(bright * r / t), int(bright * g / t) > 255 ? 255 : int(bright * g / t), int(bright * b / t) > 255 ? 255 : int(bright * b / t)); +} diff --git a/src/FluTools.h b/src/FluTools.h index f1b4bb95..2ba82887 100644 --- a/src/FluTools.h +++ b/src/FluTools.h @@ -92,4 +92,8 @@ SINGLETON(FluTools) Q_INVOKABLE bool isWindows10OrGreater(); Q_INVOKABLE QRect desktopAvailableGeometry(QQuickWindow *window); -}; \ No newline at end of file + + Q_INVOKABLE QString getWallpaperFilePath(); + + Q_INVOKABLE QColor imageMainColor(const QImage& image, double bright = 1); +}; diff --git a/src/Qt5/imports/FluentUI/Controls/FluAcrylic.qml b/src/Qt5/imports/FluentUI/Controls/FluAcrylic.qml index 66d06fb1..58137df7 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluAcrylic.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluAcrylic.qml @@ -8,7 +8,7 @@ Item { property real tintOpacity: 0.65 property real luminosity: 0.01 property real noiseOpacity: 0.02 - property alias target: effect_source.sourceItem + property var target property int blurRadius: 32 property rect targetRect: Qt.rect(control.x, control.y, control.width,control.height) ShaderEffectSource { @@ -16,6 +16,7 @@ Item { anchors.fill: parent visible: false sourceRect: control.targetRect + sourceItem: control.target } FastBlur { id: fast_blur @@ -33,7 +34,7 @@ Item { } Image { anchors.fill: parent - source: "../Image/noise.png" + source: "qrc:/qt/qml/FluentUI/Image/noise.png" fillMode: Image.Tile opacity: control.noiseOpacity } diff --git a/src/Qt5/imports/FluentUI/Controls/FluCalendarPicker.qml b/src/Qt5/imports/FluentUI/Controls/FluCalendarPicker.qml index 8badce7a..5b1e932a 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluCalendarPicker.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluCalendarPicker.qml @@ -76,10 +76,11 @@ FluButton { } contentItem: Item{ clip: true - FluFrame{ + Rectangle{ id:container width: 300 height: 360 + color: FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(248/255,250/255,253/255,1) ColumnLayout { anchors.fill: parent spacing: 0 diff --git a/src/Qt5/imports/FluentUI/Controls/FluExpander.qml b/src/Qt5/imports/FluentUI/Controls/FluExpander.qml index d43d9991..b076b3f9 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluExpander.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluExpander.qml @@ -27,7 +27,12 @@ Item { height: 45 radius: 4 border.color: FluTheme.dividerColor - color: FluTheme.dark ? Window.active ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1) + color: { + if(Window.active){ + return FluTheme.frameActiveColor + } + return FluTheme.frameColor + } MouseArea{ id:control_mouse anchors.fill: parent @@ -84,12 +89,18 @@ Item { height: contentHeight+container.anchors.topMargin width: parent.width z:-999 + clip: true Rectangle{ id:container anchors.fill: parent radius: 4 clip: true - color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1) + color: { + if(Window.active){ + return FluTheme.frameActiveColor + } + return FluTheme.frameColor + } border.color: FluTheme.dividerColor anchors.topMargin: -contentHeight states: [ diff --git a/src/Qt5/imports/FluentUI/Controls/FluFrame.qml b/src/Qt5/imports/FluentUI/Controls/FluFrame.qml index 963b2af0..b18609d2 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluFrame.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluFrame.qml @@ -19,6 +19,11 @@ T.Frame { id:d radius: 4 border.color: FluTheme.dividerColor - color: FluTheme.dark ? Window.active ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(251/255,251/255,253/255,1) + color: { + if(Window.active){ + return FluTheme.frameActiveColor + } + return FluTheme.frameColor + } } } diff --git a/src/Qt5/imports/FluentUI/Controls/FluGroupBox.qml b/src/Qt5/imports/FluentUI/Controls/FluGroupBox.qml index 443651f1..f602a7b6 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluGroupBox.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluGroupBox.qml @@ -8,8 +8,13 @@ import FluentUI 1.0 T.GroupBox { id: control property int borderWidth : 1 - property color borderColor : FluTheme.dark ? Window.active ? Qt.rgba(55/255,55/255,55/255,1):Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1) - property color color: FluTheme.dark ? Window.active ? Qt.rgba(38/255,44/255,54/255,1) : Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1) + property color borderColor : FluTheme.dividerColor + property color color: { + if(Window.active){ + return FluTheme.frameActiveColor + } + return FluTheme.frameColor + } property int radius: 4 implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding, diff --git a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml index 4cfd90bb..3be637de 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluWindow.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluWindow.qml @@ -109,8 +109,53 @@ Window { } Component{ id:com_background - Rectangle{ - color: window.backgroundColor + Item{ + Rectangle{ + anchors.fill: parent + color: window.backgroundColor + } + Image{ + id:img_back + visible: false + cache: false + Component.onCompleted: { + var geometry = FluTools.desktopAvailableGeometry(window) + width = geometry.width + height = geometry.height + sourceSize = Qt.size(width,height) + source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath) + } + Connections{ + target: FluTheme + function onDesktopImagePathChanged(){ + timer_update_image.restart() + } + function onBlurBehindWindowEnabledChanged(){ + if(FluTheme.blurBehindWindowEnabled){ + img_back.source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath) + }else{ + img_back.source = "" + } + } + } + Timer{ + id:timer_update_image + interval: 500 + onTriggered: { + img_back.source = "" + img_back.source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath) + } + } + } + FluAcrylic{ + anchors.fill: parent + target: img_back + tintOpacity: FluTheme.dark ? 0.80 : 0.75 + blurRadius: 64 + 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.y,window.width,window.height) + } } } Component{ diff --git a/src/Qt6/imports/FluentUI/Controls/FluAcrylic.qml b/src/Qt6/imports/FluentUI/Controls/FluAcrylic.qml index 7a76e91f..9dd417e7 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluAcrylic.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluAcrylic.qml @@ -8,7 +8,7 @@ Item { property real tintOpacity: 0.65 property real luminosity: 0.01 property real noiseOpacity: 0.02 - property alias target: effect_source.sourceItem + property var target property int blurRadius: 32 property rect targetRect: Qt.rect(control.x, control.y, control.width,control.height) ShaderEffectSource { @@ -16,6 +16,7 @@ Item { anchors.fill: parent visible: false sourceRect: control.targetRect + sourceItem: control.target } FastBlur { id: fast_blur @@ -33,7 +34,7 @@ Item { } Image { anchors.fill: parent - source: "../Image/noise.png" + source: "qrc:/qt/qml/FluentUI/Image/noise.png" fillMode: Image.Tile opacity: control.noiseOpacity } diff --git a/src/Qt6/imports/FluentUI/Controls/FluCalendarPicker.qml b/src/Qt6/imports/FluentUI/Controls/FluCalendarPicker.qml index ab11b054..444bdd7a 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluCalendarPicker.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluCalendarPicker.qml @@ -75,10 +75,11 @@ FluButton { } contentItem: Item{ clip: true - FluFrame{ + Rectangle{ id:container width: 300 height: 360 + color: FluTheme.dark ? Qt.rgba(51/255,48/255,48/255,1) : Qt.rgba(248/255,250/255,253/255,1) ColumnLayout { anchors.fill: parent spacing: 0 diff --git a/src/Qt6/imports/FluentUI/Controls/FluExpander.qml b/src/Qt6/imports/FluentUI/Controls/FluExpander.qml index 24018e34..4e6d8d86 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluExpander.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluExpander.qml @@ -27,7 +27,12 @@ Item { height: 45 radius: 4 border.color: FluTheme.dividerColor - color: FluTheme.dark ? Window.active ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1) + color: { + if(Window.active){ + return FluTheme.frameActiveColor + } + return FluTheme.frameColor + } MouseArea{ id:control_mouse anchors.fill: parent @@ -84,12 +89,18 @@ Item { height: contentHeight+container.anchors.topMargin width: parent.width z:-999 + clip: true Rectangle{ id:container anchors.fill: parent radius: 4 clip: true - color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1) + color: { + if(Window.active){ + return FluTheme.frameActiveColor + } + return FluTheme.frameColor + } border.color: FluTheme.dividerColor anchors.topMargin: -contentHeight states: [ diff --git a/src/Qt6/imports/FluentUI/Controls/FluFrame.qml b/src/Qt6/imports/FluentUI/Controls/FluFrame.qml index f830d529..a98edff7 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluFrame.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluFrame.qml @@ -17,6 +17,11 @@ T.Frame { id:d radius: 4 border.color: FluTheme.dividerColor - color: FluTheme.dark ? Window.active ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(251/255,251/255,253/255,1) + color: { + if(Window.active){ + return FluTheme.frameActiveColor + } + return FluTheme.frameColor + } } } diff --git a/src/Qt6/imports/FluentUI/Controls/FluGroupBox.qml b/src/Qt6/imports/FluentUI/Controls/FluGroupBox.qml index b93f576e..4ee5e444 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluGroupBox.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluGroupBox.qml @@ -8,8 +8,13 @@ import FluentUI T.GroupBox { id: control property int borderWidth : 1 - property color borderColor : FluTheme.dark ? Window.active ? Qt.rgba(55/255,55/255,55/255,1):Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1) - property color color: FluTheme.dark ? Window.active ? Qt.rgba(38/255,44/255,54/255,1) : Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1) + property color borderColor : FluTheme.dividerColor + property color color: { + if(Window.active){ + return FluTheme.frameActiveColor + } + return FluTheme.frameColor + } property int radius: 4 implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding, diff --git a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml index ea046575..4474285a 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluWindow.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluWindow.qml @@ -108,8 +108,53 @@ Window { } Component{ id:com_background - Rectangle{ - color: window.backgroundColor + Item{ + Rectangle{ + anchors.fill: parent + color: window.backgroundColor + } + Image{ + id:img_back + visible: false + cache: false + Component.onCompleted: { + var geometry = FluTools.desktopAvailableGeometry(window) + width = geometry.width + height = geometry.height + sourceSize = Qt.size(width,height) + source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath) + } + Connections{ + target: FluTheme + function onDesktopImagePathChanged(){ + timer_update_image.restart() + } + function onBlurBehindWindowEnabledChanged(){ + if(FluTheme.blurBehindWindowEnabled){ + img_back.source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath) + }else{ + img_back.source = "" + } + } + } + Timer{ + id:timer_update_image + interval: 500 + onTriggered: { + img_back.source = "" + img_back.source = FluTools.getUrlByFilePath(FluTheme.desktopImagePath) + } + } + } + FluAcrylic{ + anchors.fill: parent + target: img_back + tintOpacity: FluTheme.dark ? 0.80 : 0.75 + blurRadius: 64 + 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.y,window.width,window.height) + } } } Component{