diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cde407..310bd10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ qt_add_executable(FancyInputQt src/main.cpp ) -# ============ QML 模块 1:FancyInput.Controls(用户自定义控件) ============ +# ============ QML 模块 1:FancyInput.Controls(用户自定义控件,≈ WPF UserControl) ============ # 控件文件放在 src/qml/controls/ 目录下,GLOB 自动收集: # 以后新增控件只需放 .qml 文件,无需修改本文件。 file(GLOB CONTROLS_QML_FILES CONFIGURE_DEPENDS @@ -53,20 +53,40 @@ qt_add_qml_module(FancyInputControls ${CONTROLS_QML_FILES} ) -# ============ QML 模块 2:FancyInputQt(App 主界面) ============ +# ============ QML 模块 2:FancyInput.Windows(窗口组件,≈ WPF Window) ============ +# 窗口文件放在 src/qml/windows/ 目录下,GLOB 自动收集: +# 以后新增窗口只需放 .qml 文件,无需修改本文件。 +file(GLOB WINDOWS_QML_FILES CONFIGURE_DEPENDS + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + "src/qml/windows/*.qml" +) + +qt_add_qml_module(FancyInputWindows + STATIC # 编译为静态库,链接进 exe + URI FancyInput.Windows + VERSION 1.0 + QML_FILES + ${WINDOWS_QML_FILES} + DEPENDENCIES + FancyInputControls # 窗口里会用自定义控件 +) + +# ============ QML 模块 3:FancyInputQt(App 主界面) ============ qt_add_qml_module(FancyInputQt URI FancyInputQt VERSION 1.0 QML_FILES - src/qml/Main.qml + src/qml/Main.qml # 主界面内容(Rectangle 根,可可视化编辑) + src/qml/MainWindow.qml # 主窗口包装(Window 根) DEPENDENCIES FancyInputControls + FancyInputWindows ) # ============ 链接 ============ target_link_libraries(FancyInputQt PRIVATE FancyInputControls # 控件模块(含 QML 资源) - FancyInputControlsplugin # 控件模块的静态插件,必须链接,否则运行时找不到模块 + FancyInputWindows # 窗口模块(含 QML 资源) Qt6::Core Qt6::Gui Qt6::Qml Qt6::Quick Qt6::QuickControls2 ) @@ -94,7 +114,7 @@ if(WIN32) # 2. 体积优化:删除未使用的 Qt 组件 set(DEPLOY_DIR "${CMAKE_SOURCE_DIR}/bin/${CMAKE_BUILD_TYPE}") - # 2.1 未使用的 QuickControls2 风格(默认只需 Windows/Basic/Fusion) + # 2.1 未使用的 QuickControls2 风格(程序显式锁定 Basic,其余全部可删) set(UNUSED_STYLES "${DEPLOY_DIR}/qml/QtQuick/Controls/FluentWinUI3" "${DEPLOY_DIR}/qml/QtQuick/Controls/Material" diff --git a/src/main.cpp b/src/main.cpp index fed3cd2..989c1a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,6 +9,11 @@ int main(int argc, char *argv[]) app.setApplicationName("FancyInput"); app.setApplicationVersion("1.2.0"); + // 显式锁定 QuickControls2 风格为 Basic: + // 部署时裁剪掉了 Material/Universal/Imagine/FluentWinUI3, + // 若不显式指定,Qt 6.7+ 默认 FluentWinUI3,找不到会直接崩溃。 + qputenv("QT_QUICK_CONTROLS_STYLE", "Basic"); + // Qt 6 默认启用高 DPI 缩放,叠层/遮罩坐标后续用 devicePixelRatio 归一化 QQmlApplicationEngine engine; @@ -24,7 +29,9 @@ int main(int argc, char *argv[]) &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection); - engine.loadFromModule("FancyInputQt", "Main"); + // 加载 MainWindow.qml(Window 壳,内含 Rectangle 根的主界面 Main.qml): + // 窗口级属性与内容自适应由 QML 侧处理,C++ 保持最简。 + engine.loadFromModule("FancyInputQt", "MainWindow"); return app.exec(); } diff --git a/src/qml/Main.qml b/src/qml/Main.qml index 9b426fe..cdc91e5 100644 --- a/src/qml/Main.qml +++ b/src/qml/Main.qml @@ -2,18 +2,66 @@ import QtQuick.Controls import QtQuick.Layouts import FancyInput.Controls +import FancyInput.Windows -// 阶段 0:Hello World 骨架 -// 窗口尺寸沿用 WPF 主窗 600x480,主题色 #651F65 -ApplicationWindow { +// 主界面(根元素为 Rectangle —— Qt Creator 设计模式可可视化编辑) +// 由 main.cpp 用 QQuickWindow 包装成主窗口。 +Rectangle { id: root width: 600 height: 480 - visible: true - title: qsTr("FancyInput · Qt 迁移版(阶段 0)") - color: Theme.background + property int clickCount: 0 + + // 弹独立窗口:统一包装器现包装(等价 WPF 的 new SettingsWindow().Show()) + // 关键:createObject 的 parent 传 root(主窗口)→ FancyWindow 自动成为 + // transient parent 窗口(= WPF 的 Owner)。有了它: + // · 非模态:随主窗口最小化/关闭,任务栏不单列 + // · 模态:Qt.WindowModal 在同一窗口组内生效,主窗口被系统禁用, + // 点击主窗口时 Windows 自带标题栏闪烁反馈(同 WPF ShowDialog) + function openSettingsWindow(modal) { + var win = settingsWindowComp.createObject(modal ? root : null, { modal: modal }) + if (win) win.show() + } + // 打开无边框透明悬浮窗(WPF WindowStyle=None + AllowsTransparency + Topmost) + function openOverlayWindow() { + var win = overlayWindowComp.createObject(null, {}) + if (win) win.show() + } + // 设置窗口:FancyWindow 包装 SettingsWindow 内容 + Component { + id: settingsWindowComp + FancyWindow { + width: 480 + height: 360 + windowTitle: qsTr("设置 · FancyInput") + + SettingsWindow { + anchors.fill: parent + onCloseRequested: rootWindow.close() + } + } + } + + // 悬浮窗:无边框 + 透明 + 置顶 + 工具窗(FancyWindow 一键配置) + Component { + id: overlayWindowComp + FancyWindow { + width: 280 + height: 180 + frameless: true + transparent: true + alwaysOnTop: true + toolWindow: true + + OverlayWindow { + anchors.fill: parent + onCloseRequested: rootWindow.close() + } + } + } + ColumnLayout { anchors.centerIn: parent spacing: 16 @@ -63,7 +111,29 @@ ApplicationWindow { console.log("点击次数:", root.clickCount) } } - } - property int clickCount: 0 + // 打开独立窗口(WPF Window.Show 等价) + Button { + Layout.alignment: Qt.AlignHCenter + text: qsTr("打开设置窗口") + font.family: Theme.fontFamily + onClicked: root.openSettingsWindow(false) + } + + // 打开模态窗口(WPF Window.ShowDialog 等价) + Button { + Layout.alignment: Qt.AlignHCenter + text: qsTr("打开模态窗口") + font.family: Theme.fontFamily + onClicked: root.openSettingsWindow(true) + } + + // 打开无边框透明悬浮窗(WPF 悬浮窗等价) + Button { + Layout.alignment: Qt.AlignHCenter + text: qsTr("打开悬浮窗") + font.family: Theme.fontFamily + onClicked: root.openOverlayWindow() + } + } } diff --git a/src/qml/MainWindow.qml b/src/qml/MainWindow.qml new file mode 100644 index 0000000..9c6db70 --- /dev/null +++ b/src/qml/MainWindow.qml @@ -0,0 +1,19 @@ +import QtQuick +import FancyInput.Controls +import FancyInput.Windows + +// 主窗口:用统一包装器 FancyWindow 包装主界面内容 +// 内容在 Main.qml(Rectangle 根,Qt Creator 可可视化编辑) +// main.cpp 通过 loadFromModule("FancyInputQt", "MainWindow") 加载本文件。 +FancyWindow { + id: win + width: 600 + height: 480 + visible: true + windowTitle: qsTr("FancyInput · Qt 迁移版(阶段 0)") + + // 内容组件:anchors.fill 跟随窗口缩放 + Main { + anchors.fill: parent + } +} diff --git a/src/qml/windows/FancyWindow.qml b/src/qml/windows/FancyWindow.qml new file mode 100644 index 0000000..f50e42f --- /dev/null +++ b/src/qml/windows/FancyWindow.qml @@ -0,0 +1,49 @@ +import QtQuick + +// 统一窗口包装器(等价 WPF 的 Window 类) +// +// 用法:内容组件一律保持 Rectangle 根(Qt Creator 设计模式可可视化编辑), +// 需要窗口时用本包装器现包装即可: +// +// FancyWindow { +// width: 480 +// height: 360 +// windowTitle: qsTr("设置") +// modal: true // 模态(WPF ShowDialog 等价) +// SettingsWindow { anchors.fill: parent } +// } +// +// // 无边框透明悬浮窗(WPF WindowStyle=None + AllowsTransparency + Topmost) +// FancyWindow { +// frameless: true +// transparent: true +// alwaysOnTop: true +// toolWindow: true +// OverlayWindow { anchors.fill: parent } +// } +// +// 子项(内容组件)直接写在包装器里即可 —— Window 的 data 默认行为 +// 会自动把 Item 子项挂到内容区(contentItem)。 +Window { + id: root + + // 内容组件可通过 rootWindow 访问包装器(如 rootWindow.close() 关窗) + // 固定别名,统一命名,避免每个包装处都要 id 转发 + property alias rootWindow: root + + // ===== 窗口级属性(对齐 WPF Window) ===== + property string windowTitle: "" // 标题(WPF Title) + property bool modal: false // true=ShowDialog(WindowModal),false=Show + property bool frameless: false // 无边框(WPF WindowStyle=None) + property bool transparent: false // 透明背景(WPF AllowsTransparency=True) + property bool alwaysOnTop: false // 置顶(WPF Topmost) + property bool toolWindow: false // 工具窗口:任务栏不显示、随 Owner 关闭 + + title: windowTitle + modality: modal ? Qt.WindowModal : Qt.NonModal + flags: Qt.Window + | (frameless ? Qt.FramelessWindowHint : 0) + | (alwaysOnTop ? Qt.WindowStaysOnTopHint : 0) + | (toolWindow ? Qt.Tool : 0) + color: transparent ? "transparent" : "#FFFFFF" +} diff --git a/src/qml/windows/OverlayWindow.qml b/src/qml/windows/OverlayWindow.qml new file mode 100644 index 0000000..5a87b31 --- /dev/null +++ b/src/qml/windows/OverlayWindow.qml @@ -0,0 +1,90 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import FancyInput.Controls + +// 悬浮窗内容(根元素 Rectangle —— Qt Creator 设计模式可可视化编辑) +// 使用场景:无边框透明窗口,只呈现内容本身,对应 WPF: +// · WindowStyle=None → 包装层 flags: Qt.FramelessWindowHint +// · AllowsTransparency=True → 包装层 color: "transparent" +// · Topmost= true → 包装层 Qt.WindowStaysOnTopHint +// 由调用方(Main.qml)用 Window 现包装,包装层负责窗口级属性。 +Rectangle { + id: content + width: 280 + height: 180 + color: "transparent" // 内容自身透明,只有卡片可见 + + // 请求关闭(Window 包装层监听后调用 close()) + signal closeRequested + + // 无边框窗口没有标题栏,需自行实现拖拽移动。 + // 用系统原生拖拽(startSystemMove,等同拖标题栏): + // 窗口系统接管整个移动过程(DWM 合成,移动的是窗口快照), + // 流畅无闪烁 —— 手动跟踪鼠标坐标逐帧改 x/y 在透明窗口上会 + // 触发重绘同步,造成卡顿和残影(Windows 合成器下尤其明显)。 + MouseArea { + id: dragArea + anchors.fill: parent + z: 0 + cursorShape: Qt.SizeAllCursor + + onPressed: { + var w = content.Window.window + if (w) w.startSystemMove() + } + } + + // 半透明圆角卡片(真正呈现的内容) + Rectangle { + id: card + anchors.centerIn: parent + width: 240 + height: 140 + radius: 16 + color: "#CC1E1E24" // 半透明主题 surface 色(alpha=0xCC) + border.color: Theme.border + border.width: 1 + z: 1 + + // 浅色描边让卡片在任意背景下都可见 + Rectangle { + anchors.fill: parent + radius: 16 + color: "transparent" + border.color: "#33FFFFFF" + border.width: 1 + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 16 + spacing: 10 + + Label { + Layout.alignment: Qt.AlignHCenter + text: qsTr("悬浮窗") + font.family: Theme.fontFamily + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + } + + Label { + Layout.alignment: Qt.AlignHCenter + text: qsTr("无边框 · 透明 · 置顶\n拖拽本窗口移动") + font.family: Theme.fontFamily + font.pixelSize: 12 + horizontalAlignment: Text.AlignHCenter + color: Theme.textSecondary + } + + Button { + Layout.alignment: Qt.AlignHCenter + text: qsTr("关闭") + font.family: Theme.fontFamily + onClicked: content.closeRequested() + } + } + } +} diff --git a/src/qml/windows/SettingsWindow.qml b/src/qml/windows/SettingsWindow.qml new file mode 100644 index 0000000..130cc8f --- /dev/null +++ b/src/qml/windows/SettingsWindow.qml @@ -0,0 +1,38 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import FancyInput.Controls + +// 设置窗口界面(根元素为 Rectangle —— Qt Creator 设计模式可可视化编辑) +// 需要弹窗时由调用方(Main.qml)现场用 Window 包装,本组件只负责内容。 +Rectangle { + id: content + width: 480 + height: 360 + color: Theme.background + + // 请求关闭(Window 包装层监听后调用 close()) + signal closeRequested + + ColumnLayout { + anchors.centerIn: parent + spacing: 16 + + Label { + Layout.alignment: Qt.AlignHCenter + text: qsTr("设置窗口(来自 FancyInput.Windows)") + font.family: Theme.fontFamily + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + } + + // 复用 controls 模块的自定义控件,验证跨模块引用 + Button { + Layout.alignment: Qt.AlignHCenter + text: qsTr("关闭") + font.family: Theme.fontFamily + onClicked: content.closeRequested() + } + } +}