弹窗功能

This commit is contained in:
2026-08-20 23:33:33 +08:00
parent aca98a8b55
commit d7794ce630
7 changed files with 307 additions and 14 deletions
+25 -5
View File
@@ -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"
+8 -1
View File
@@ -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();
}
+77 -7
View File
@@ -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)
}
}
// 打开独立窗口(WPF Window.Show 等价)
Button {
Layout.alignment: Qt.AlignHCenter
text: qsTr("打开设置窗口")
font.family: Theme.fontFamily
onClicked: root.openSettingsWindow(false)
}
property int clickCount: 0
// 打开模态窗口(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()
}
}
}
+19
View File
@@ -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
}
}
+49
View File
@@ -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"
}
+90
View File
@@ -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()
}
}
}
}
+38
View File
@@ -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()
}
}
}