diff --git a/example/example_en_US.ts b/example/example_en_US.ts
index 1e7b9ce7..a1edb73f 100644
--- a/example/example_en_US.ts
+++ b/example/example_en_US.ts
@@ -639,7 +639,7 @@
-
+
@@ -659,57 +659,57 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -2264,6 +2264,21 @@ Some contents...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
T_TimePicker
diff --git a/example/example_zh_CN.ts b/example/example_zh_CN.ts
index 88b8c04d..a5086d73 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,21 @@ Some contents...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
T_TimePicker
diff --git a/example/qml/page/New_TableView.qml b/example/qml/page/New_TableView.qml
new file mode 100644
index 00000000..0bef204a
--- /dev/null
+++ b/example/qml/page/New_TableView.qml
@@ -0,0 +1,263 @@
+import QtQuick 2.15
+import FluentUI 1.0
+import "../component"
+
+FluPage{
+ title:"表格测试"
+ TableView{
+ columnSource: [
+ {
+ title: "全选",
+ dataIndex: 'patientsex',
+ width:60,
+ readOnly:true,
+ position: 0,
+ delegate: com_checkbox,
+ headerDelegate: com_header_checkbox,
+ frozen: true
+ },
+
+ {
+ title: "检测日期",
+ dataIndex: 'testdate',
+ readOnly:true,
+ // width:150,
+ // delegate: com_action
+ },
+ {
+ title: "条码号",
+ dataIndex: 'barcode',
+ width:80,
+ readOnly:true,
+ position: 0,
+ movable: false,
+ frozen: true
+ // delegate: com_checkbox,
+ // headerDelegate: com_header_checkbox
+ },
+ {
+ title: "样本号",
+ dataIndex: 'sampleid',
+ width:100,
+ position: 1,
+ minimumWidth:100,
+ maximumWidth:100,
+ },
+ {
+ title: "姓名",
+ dataIndex: 'patientname',
+ width:220,
+ minimumWidth:100,
+ maximumWidth:250
+ },
+ {
+ title:"操作",
+ dataIndex:"",
+ delegate:com_action,
+ frozen: true,
+ width: 250
+ },
+ {
+ title: "头像",
+ dataIndex: 'imageurl',
+ width:120,
+ minimumWidth:80,
+ maximumWidth:250,
+ delegate:com_avatar,
+ frozen: true
+ },
+ {
+ title: "性别",
+ dataIndex: 'patientsex',
+ width:130,
+ minimumWidth:50,
+ maximumWidth:250,
+ // delegate:com_avatar
+ },
+
+ {
+ title: "年龄",
+ dataIndex: 'patientage',
+ width:100,
+ minimumWidth:80,
+ maximumWidth:330
+ },
+ {
+ title: "电话",
+ dataIndex: 'patienttel',
+ width:200,
+ minimumWidth:100,
+ maximumWidth:300,
+ editMultiline: true
+ },
+ {
+ title: "身份证号",
+ dataIndex: 'patientidenno',
+ width:120,
+ minimumWidth:100,
+ maximumWidth:250
+ },
+ {
+ title: "检测项目",
+ dataIndex: 'hisitemnamelist',
+ width:120,
+ minimumWidth:100,
+ maximumWidth:250
+ },
+ {
+ title: "开单科室",
+ dataIndex: 'deptname',
+ width:150,
+ minimumWidth:100,
+ maximumWidth:250
+ },
+ {
+ title: "开单医生",
+ dataIndex: 'doctorname',
+ width:140,
+ minimumWidth:100,
+ maximumWidth:250
+ }
+ ,
+ {
+ title: "接收时间",
+ dataIndex: 'incepttime',
+ width:120,
+ minimumWidth:100,
+ maximumWidth:250
+ },
+ {
+ title: "核收时间",
+ dataIndex: 'accepttime',
+ width:220,
+ minimumWidth:100,
+ maximumWidth:250
+ }
+ ]
+ model: ListModel{
+ id: customModel
+ }
+ }
+ Component.onCompleted: {
+ // generateTestData(1000) // 生成100条测试数据
+ for (var i = 0; i < 100000; i++) {
+ customModel.append(generateTestData(i))
+ }
+ // uvRecord.setProperty()
+ }
+ Component{
+ id:com_avatar
+ Item{
+ anchors.fill: parent
+ FluClip{
+ anchors.centerIn: parent
+ width: Math.min(parent.width,parent.height)
+ height: width
+ radius: [width/2,width/2,width/2,width/2]
+ Image{
+ anchors.fill: parent
+ source: display
+ sourceSize: Qt.size(80,80)
+ fillMode: Image.PreserveAspectFit
+ }
+ }
+ }
+ }
+ Component {
+ id: com_checkbox
+ Item{
+ FluCheckBox {
+ width: 15
+ height: 15
+ anchors.centerIn: parent
+ }
+ }
+ }
+ Component {
+ id: com_header_checkbox
+ Item{
+ FluCheckBox {
+ width: 15
+ height: 15
+ anchors.centerIn: parent
+ }
+ }
+ }
+ Component{
+ id: com_action
+ Item{
+ Row{
+ anchors.centerIn: parent
+ FluTextButton{
+ text:"插入"
+ onClicked: {
+ // uvRecord.insertRecord(row)
+ uvRecord.insert(row,generateTestData(row))
+ }
+ }
+ FluTextButton{
+ text:"上移"
+ onClicked:{
+ if (row > 0) {
+ uvRecord.move(row, row - 1, 1)
+ }
+ }
+ }
+ FluTextButton{
+ text:"下移"
+ onClicked:{
+ if (row < uvRecord.rowCount() - 1) {
+ uvRecord.move(row, row + 2, 1) // 注意这里是 row + 2
+ }
+ }
+ }
+ FluTextButton{
+ text:"查看"
+ onClicked: {
+ showSuccess(JSON.stringify(control.model.get(row)))
+ }
+ }
+ FluTextButton{
+ text:"删除"
+ onClicked: {
+ uvRecord.remove(row)
+ }
+ }
+ }
+ }
+ }
+
+ function generateTestData(i) {
+ var sexes = ["男", "女"]
+ var departments = ["内科", "外科", "儿科", "妇科", "眼科"]
+ var doctors = ["张医生", "李医生", "王医生", "刘医生", "陈医生"]
+ var images = [
+ "qrc:/res/image/pages/exchange.png",
+ "qrc:/res/image/pages/nuclear.png",
+ "qrc:/res/image/pages/ocr.png",
+ "qrc:/res/image/pages/room-temperature.png",
+ "qrc:/res/image/pages/rt-pcr-machine.png"
+ ]
+ return {
+ testdate: new Date().toLocaleDateString(),
+ barcode: "BC" + (1000000 + i).toString(),
+ sampleid: "S" + (100000 + i).toString(),
+ patientname: "患者" + (i + 1),
+ patientsex: sexes[Math.floor(Math.random() * sexes.length)],
+ patientage: Math.floor(Math.random() * 80 + 1) + "岁",
+ patienttel: "1" + Math.floor(Math.random() * 9000000000 + 1000000000),
+ patientidenno: (310000000000000000 + i).toString(),
+ hisitemnamelist: "项目1,项目2,项目3",
+ deptname: departments[Math.floor(Math.random() * departments.length)],
+ doctorname: doctors[Math.floor(Math.random() * doctors.length)],
+ incepttime: new Date().toLocaleString(),
+ accepttime: new Date().toLocaleString(),
+ imageurl: images[Math.floor(Math.random() * images.length)],
+ _minimumHeight: 42,
+ _maximumHeight: 800,
+ height: 42
+ }
+
+ }
+
+}
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..76a75f37 100644
--- a/src/FluFrameless.cpp
+++ b/src/FluFrameless.cpp
@@ -1,4 +1,5 @@
#include "FluFrameless.h"
+#include "FluTheme.h"
#include
#include
@@ -8,12 +9,69 @@
#ifdef Q_OS_WIN
-#pragma comment (lib, "user32.lib")
-#pragma comment (lib, "dwmapi.lib")
+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;
+}
-#include
-#include
-#include
+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";
@@ -48,6 +106,138 @@ static inline void setShadow(HWND hwnd) {
}
}
+static inline bool setWindowDarkMode(HWND hwnd, const BOOL enable) {
+ return bool(DwmSetWindowAttribute(hwnd, DWMWINDOWATTRIBUTE::DWMWA_USE_IMMERSIVE_DARK_MODE,
+ &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()) {
+ return false;
+ }
+ if (enable) {
+ DwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
+ if (isWin1122H2OrGreater()) {
+ const DWM_SYSTEMBACKDROP_TYPE backdropType = DWMSBT_MAINWINDOW;
+ DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &backdropType,
+ sizeof(backdropType));
+ } else {
+ const BOOL enable = TRUE;
+ DwmSetWindowAttribute(hwnd, 1029, &enable, sizeof(enable));
+ }
+ } else {
+ if (isWin1122H2OrGreater()) {
+ const DWM_SYSTEMBACKDROP_TYPE backdropType = DWMSBT_AUTO;
+ DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &backdropType,
+ sizeof(backdropType));
+ } else {
+ const BOOL enable = FALSE;
+ DwmSetWindowAttribute(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) {
+ DwmExtendFrameIntoClientArea(hwnd, &extendedMargins);
+ const DWM_SYSTEMBACKDROP_TYPE backdropType = DWMSBT_TABBEDWINDOW;
+ DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &backdropType,
+ sizeof(backdropType));
+ } else {
+ const DWM_SYSTEMBACKDROP_TYPE backdropType = DWMSBT_AUTO;
+ DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &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};
+ DwmExtendFrameIntoClientArea(hwnd, &margins);
+ DWM_SYSTEMBACKDROP_TYPE system_backdrop_type =
+ DWM_SYSTEMBACKDROP_TYPE::DWMSBT_TRANSIENTWINDOW;
+ DwmSetWindowAttribute(hwnd, DWMWINDOWATTRIBUTE::DWMWA_SYSTEMBACKDROP_TYPE,
+ &system_backdrop_type, sizeof(DWM_SYSTEMBACKDROP_TYPE));
+ } else {
+ const DWM_SYSTEMBACKDROP_TYPE backdropType = DWMSBT_AUTO;
+ DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &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);
+ typedef HRESULT(WINAPI * SetWindowCompositionAttributePtr)(HWND,
+ PWINDOWCOMPOSITIONATTRIBDATA);
+ SetWindowCompositionAttributePtr SetWindowCompositionAttribute;
+ if (isWin8OrGreater()) {
+ HMODULE module = LoadLibraryW(L"user32.dll");
+ SetWindowCompositionAttribute = reinterpret_cast(
+ GetProcAddress(module, "SetWindowCompositionAttribute"));
+ if (!SetWindowCompositionAttribute) {
+ 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);
+ SetWindowCompositionAttribute(hwnd, &wcad);
+ } else {
+ DWM_BLURBEHIND bb{};
+ bb.fEnable = TRUE;
+ bb.dwFlags = DWM_BB_ENABLE;
+ DwmEnableBlurBehindWindow(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);
+ SetWindowCompositionAttribute(hwnd, &wcad);
+ } else {
+ DWM_BLURBEHIND bb{};
+ bb.fEnable = FALSE;
+ bb.dwFlags = DWM_BB_ENABLE;
+ DwmEnableBlurBehindWindow(hwnd, &bb);
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
#endif
bool containsCursorToItem(QQuickItem *item) {
@@ -70,6 +260,7 @@ FluFrameless::FluFrameless(QQuickItem *parent) : QQuickItem{parent} {
_closeButton = nullptr;
_topmost = false;
_disabled = false;
+ _effect = "normal";
_isWindows11OrGreater = FluTools::getInstance()->isWindows11OrGreater();
}
@@ -107,6 +298,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 +319,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 +464,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..db095210 100644
--- a/src/FluFrameless.h
+++ b/src/FluFrameless.h
@@ -6,6 +6,82 @@
#include
#include "stdafx.h"
+#ifdef Q_OS_WIN
+
+#pragma comment (lib, "user32.lib")
+#pragma comment (lib, "dwmapi.lib")
+
+#include
+#include
+#include
+
+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 *;
+
+
+#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 +99,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 +154,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" }