execution_character_set

分类: 科技创建于: 7/24/2025
1#pragma execution_character_set("utf-8") // 这是一个预处理指令,指示编译器使用UTF-8字符集来解释源代码中的字符串字面量。
2                                        // 这有助于确保在代码中使用中文字符或其他非ASCII字符时不会出现乱码问题。
3#include "mainwindow.h"                 // 包含MainWindow类的头文件,定义了MainWindow的接口。
4#include "ui_mainwindow.h"              // 包含由Qt Designer生成的UI文件对应的头文件,定义了UI元素。
5#include <QGraphicsDropShadowEffect>    // 包含QGraphicsDropShadowEffect类,用于为控件添加阴影效果。
6#include <QColor>                       // 包含QColor类,用于定义颜色。
7#include <QMouseEvent>                  // 包含QMouseEvent类,用于处理鼠标事件。
8#include <QPushButton>                  // 包含QPushButton类,一个标准按钮控件。
9#include <QMenuBar>                     // 包含QMenuBar类,用于创建菜单栏。
10#include <QMenu>                        // 包含QMenu类,用于创建菜单。
11#include <QDebug>                       // 包含QDebug类,用于调试输出信息。
12#include <QPropertyAnimation>           // 包含QPropertyAnimation类,用于属性动画。
13#include <QScreen>                      // 包含QScreen类,用于获取屏幕信息。
14#include <QCoreApplication>             // 包含QCoreApplication类,提供事件循环和核心功能。
15#include <QTimer>                       // 包含QTimer类,用于定时器功能。
16#include <QDateTime>                    // 包含QDateTime类,用于处理日期和时间。
17#include <QClipboard>                   // 包含QClipboard类,用于访问系统剪贴板。
18#include <QParallelAnimationGroup>      // 包含QParallelAnimationGroup类,用于并行播放多个动画。
19#include <Windows.h>                    // 包含Windows API头文件,提供访问Windows底层功能的接口。
20#include <qt_windows.h>                 // 包含Qt对Windows平台特定功能的封装头文件。
21#include <QSystemTrayIcon>              // 包含QSystemTrayIcon类,用于创建系统托盘图标。
22#include <QFile>                        // 包含QFile类,用于文件操作。
23#include <QJsonDocument>                // 包含QJsonDocument类,用于读写JSON文档。
24#include <QJsonObject>                  // 包含QJsonObject类,用于表示JSON对象。
25#include <QJsonValue>                   // 包含QJsonValue类,用于表示JSON值。
26#include <QJsonArray>                   // 包含QJsonArray类,用于表示JSON数组。
27#include <QShortcut>                    // 包含QShortcut类,用于定义键盘快捷键。
28#include <QDesktopServices>             // 包含QDesktopServices类,用于打开文件或URL。
29// #include <QFile> // 重复包含,已在上面包含过。
30#include <QListView>                    // 包含QListView类,用于显示列表数据。
31#include <QFormLayout>                  // 包含QFormLayout类,用于创建表单布局。
32#include <QDesktopWidget>               // 包含QDesktopWidget类(在Qt6中已被QScreen取代),用于获取桌面信息。
33#include "windows.h"                    // 重复包含Windows API头文件。
34#include "windowsx.h"                   // 包含Windowsx.h,提供一些宏,如GET_X_LPARAM等。
35
36// MainWindow类的构造函数,用于初始化窗口。
37MainWindow::MainWindow(QWidget *parent) :
38    QMainWindow(parent),                // 调用父类QMainWindow的构造函数。
39    ui(new Ui::MainWindow)              // 初始化ui指针,指向Ui::MainWindow的实例,用于访问UI界面元素。
40{
41    ui->setupUi(this);                  // 调用ui对象的setupUi方法,根据.ui文件设置窗口的用户界面。
42
43    // 设置窗口标志:
44    // Qt::FramelessWindowHint:移除窗口的边框和标题栏,实现完全自定义的窗口外观。
45    // Qt::WindowMinimizeButtonHint:虽然是无边框窗口,但允许在任务栏上显示最小化按钮(对于最小化功能)。
46    this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint);
47    // QMainWindow透明显示,当设置主显示窗口的外边距时,防止外边距显示出来。
48    // Qt::WA_TranslucentBackground:设置窗口背景透明。这对于创建阴影效果和自定义边框是必要的,
49    // 因为它允许阴影绘制在窗口内容之外,而不会被非透明背景覆盖。
50    this->setAttribute(Qt::WA_TranslucentBackground, true);
51
52    // 设置窗口内容的边距。
53    // 对于自定义无边框窗口,通常需要设置外部边距来为阴影效果或拉伸区域预留空间。
54    // 这里的10像素边距是为阴影效果和鼠标拖拽窗口边缘调整大小的功能留出的空间。
55    setContentsMargins(10, 10, 10, 10);
56    // 设置最大化按钮的图标。":lib/Icon_max4.png"是Qt资源文件中的路径。
57    ui->toolButton_max->setIcon(QIcon(":lib/Icon_max4.png"));
58
59    // 设置主窗口的阴影效果
60    defaultShadow = new QGraphicsDropShadowEffect(); // 创建一个QGraphicsDropShadowEffect对象。
61    defaultShadow->setBlurRadius(15);                // 设置阴影的模糊半径为15像素,值越大阴影越模糊。
62    defaultShadow->setColor(QColor(0, 0, 0));        // 设置阴影颜色为黑色(RGB 0,0,0)。
63    defaultShadow->setOffset(0, 0);                  // 设置阴影的偏移量为(0,0),表示阴影向四周均匀发散。
64    // 不要直接给this(MainWindow)设置,会报UpdateLayeredWindowIndirect failed错误。
65    // 这是因为WA_TranslucentBackground和QGraphicsDropShadowEffect在某些情况下直接应用于顶层窗口可能引起Windows API的冲突。
66    // 将阴影效果应用于中央控件(ui->centralWidget)可以规避这个问题,并且阴影会正确地显示在窗口外部。
67    ui->centralWidget->setGraphicsEffect(defaultShadow);
68
69    // 双击标题栏事件在这里处理。
70    // 以下代码使用Windows API来修改窗口样式,以启用标准的Windows窗口行为,例如双击标题栏最大化/还原,以及通过边框调整大小。
71    HWND hwnd = (HWND)this->winId(); // 获取当前Qt窗口的Windows句柄(HWND)。
72    DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); // 获取当前窗口的样式。
73    // 设置新的窗口样式:
74    // WS_MAXIMIZEBOX:允许窗口有最大化按钮(尽管我们用自定义按钮,但这个标志有助于Windows内部处理)。
75    // WS_THICKFRAME:允许窗口通过拖拽边框进行调整大小(这是拉伸功能的基础)。
76    // WS_CAPTION:允许窗口有标题栏的行为(即使是自定义的,例如双击最大化)。
77    // CS_DBLCLKS:允许窗口接收双击消息(如WM_LBUTTONDBLCLK),用于双击标题栏最大化。
78    SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION | CS_DBLCLKS);
79
80
81    // 设置侧边栏(ui->widget_side)的阴影效果
82    QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect(); // 创建另一个阴影效果对象。
83    effect->setOffset(2, 0);                    // 设置阴影向右偏移2像素,垂直方向无偏移。
84    effect->setColor(QColor(10, 31, 57));       // 设置阴影颜色为深蓝色。
85    effect->setBlurRadius(15);                  // 设置阴影模糊半径为15像素。
86
87    ui->widget_side->setGraphicsEffect(effect); // 将阴影效果应用到侧边栏widget_side。
88
89    // 快捷键 F11 全屏功能
90    QShortcut *shortcut = new QShortcut(QKeySequence(Qt::Key_F11), this); // 创建一个F11键的快捷方式。
91    // 连接快捷方式的activated()信号到rece_toolButton_fullScreen_sign()槽函数,
92    // 当按下F11键时,将触发全屏/退出全屏操作。
93    connect(shortcut, SIGNAL(activated()), this, SLOT(rece_toolButton_fullScreen_sign()));
94
95    // 设置窗口支持拉伸(调整大小)功能。
96    setSupportStretch(true);
97}
98
99// 计算当前窗口的拉伸(调整大小)区域的矩形范围。
100// 这些矩形定义了鼠标指针可以改变窗口大小的特定区域(四个角和四条边)。
101void MainWindow::calculateCurrentStrechRect()
102{
103    // STRETCH_RECT_WIDTH和STRETCH_RECT_HEIGHT是定义拉伸区域宽度的常量,通常是一个小值,
104    // 例如10像素,用于在窗口边缘或角落形成一个可拖拽的“热区”。
105
106    // 四个角Rect;
107    // 左上角:(0, 0)到(STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT)
108    m_leftTopRect = QRect(0, 0, STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT);
109    // 左下角:(0, 窗口高度 - STRETCH_RECT_HEIGHT)到(STRETCH_RECT_WIDTH, 窗口高度)
110    m_leftBottomRect = QRect(0, this->height() - STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, STRETCH_RECT_WIDTH); // 注意这里宽度也用了STRETCH_RECT_WIDTH
111    // 右上角:(窗口宽度 - STRETCH_RECT_WIDTH, 0)到(窗口宽度, STRETCH_RECT_HEIGHT)
112    m_rightTopRect = QRect(this->width() - STRETCH_RECT_WIDTH, 0, STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT);
113    // 右下角:(窗口宽度 - STRETCH_RECT_WIDTH, 窗口高度 - STRETCH_RECT_HEIGHT)到(窗口宽度, 窗口高度)
114    m_rightBottomRect = QRect(this->width() - STRETCH_RECT_WIDTH, this->height() - STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT);
115
116    // 四条边Rect;
117    // 顶部边框:(STRETCH_RECT_WIDTH, 0)到(窗口宽度 - STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT)
118    // 避开了左右两个角,只处理纯粹的边。
119    m_topBorderRect = QRect(STRETCH_RECT_WIDTH, 0, this->width() - STRETCH_RECT_WIDTH * 2, STRETCH_RECT_HEIGHT);
120    // 右边框:(窗口宽度 - STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT)到(窗口宽度, 窗口高度 - STRETCH_RECT_HEIGHT)
121    m_rightBorderRect = QRect(this->width() - STRETCH_RECT_WIDTH, STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, this->height() - STRETCH_RECT_HEIGHT * 2);
122    // 底部边框:(STRETCH_RECT_WIDTH, 窗口高度 - STRETCH_RECT_HEIGHT)到(窗口宽度 - STRETCH_RECT_WIDTH, 窗口高度)
123    m_bottomBorderRect = QRect(STRETCH_RECT_WIDTH, this->height() - STRETCH_RECT_HEIGHT, this->width() - STRETCH_RECT_WIDTH * 2, STRETCH_RECT_HEIGHT);
124    // 左边框:(0, STRETCH_RECT_HEIGHT)到(STRETCH_RECT_WIDTH, 窗口高度 - STRETCH_RECT_HEIGHT)
125    m_leftBorderRect = QRect(0, STRETCH_RECT_HEIGHT, STRETCH_RECT_WIDTH, this->height() - STRETCH_RECT_HEIGHT * 2);
126}
127
128// 根据当前鼠标光标的位置,判断它处于哪个拉伸区域。
129// 参数:cursorPos - 鼠标在窗口坐标系中的当前位置。
130// 返回:WindowStretchRectState枚举值,表示鼠标所在的拉伸区域类型。
131WindowStretchRectState MainWindow::getCurrentStretchState(QPoint cursorPos)
132{
133    WindowStretchRectState stretchState; // 声明一个变量来存储拉伸状态。
134    // 依次检查鼠标光标是否包含在预定义的各个拉伸区域矩形中。
135    if (m_leftTopRect.contains(cursorPos)) {
136        stretchState = LEFT_TOP_RECT; // 左上角
137    } else if (m_rightTopRect.contains(cursorPos)) {
138        stretchState = RIGHT_TOP_RECT; // 右上角
139    } else if (m_rightBottomRect.contains(cursorPos)) {
140        stretchState = RIGHT_BOTTOM_RECT; // 右下角
141    } else if (m_leftBottomRect.contains(cursorPos)) {
142        stretchState = LEFT_BOTTOM_RECT; // 左下角
143    }  else if (m_topBorderRect.contains(cursorPos)) {
144        stretchState = TOP_BORDER; // 顶部边框
145    } else if (m_rightBorderRect.contains(cursorPos)) {
146        stretchState = RIGHT_BORDER; // 右边框
147    } else if (m_bottomBorderRect.contains(cursorPos)) {
148        stretchState = BOTTOM_BORDER; // 底部边框
149    } else if (m_leftBorderRect.contains(cursorPos)) {
150        stretchState = LEFT_BORDER; // 左边框
151    } else {
152        stretchState = NO_SELECT; // 不在任何拉伸区域内
153    }
154    return stretchState; // 返回确定的拉伸状态。
155}
156
157// 根据当前的拉伸状态,更新鼠标光标的样式。
158// 参数:stretchState - 当前的拉伸区域类型。
159void MainWindow::updateMouseStyle(WindowStretchRectState stretchState)
160{
161    // 根据拉伸状态设置不同的鼠标光标图标,以提示用户可以进行哪种类型的拖拽。
162    switch (stretchState)
163    {
164    case NO_SELECT:
165        setCursor(Qt::ArrowCursor); // 默认箭头光标,表示不可拉伸。
166        break;
167    case LEFT_TOP_RECT:
168    case RIGHT_BOTTOM_RECT:
169        setCursor(Qt::SizeFDiagCursor); // 对角线拉伸光标(左上-右下方向)。
170        break;
171    case TOP_BORDER:
172    case BOTTOM_BORDER:
173        setCursor(Qt::SizeVerCursor); // 垂直方向拉伸光标。
174        break;
175    case RIGHT_TOP_RECT:
176    case LEFT_BOTTOM_RECT:
177        setCursor(Qt::SizeBDiagCursor); // 对角线拉伸光标(右上-左下方向)。
178        break;
179    case LEFT_BORDER:
180    case RIGHT_BORDER:
181        setCursor(Qt::SizeHorCursor); // 水平方向拉伸光标。
182        break;
183    default:
184        setCursor(Qt::ArrowCursor); // 默认情况,设置为箭头光标。
185        break;
186    }
187}
188
189// 根据当前的拉伸状态和鼠标移动量,更新窗口的大小和位置。
190void MainWindow::updateWindowSize()
191{
192    // 拉伸时要注意设置窗口最小值; 避免窗口被缩小到不可见或不合理的尺寸。
193    QRect windowRect = m_windowRectBeforeStretch; // 获取鼠标按下时记录的窗口原始几何尺寸。
194    // 计算鼠标从按下点(m_startPoint)到当前点(m_endPoint)的X和Y方向的位移。
195    int delValue_X = m_startPoint.x() - m_endPoint.x(); // 负值表示鼠标向右移动,正值表示向左移动。
196    int delValue_Y = m_startPoint.y() - m_endPoint.y(); // 负值表示鼠标向下移动,正值表示向上移动。
197    int m_windowMinWidth = 600; // 定义窗口的最小宽度。
198    int m_windowMinHeight = 600; // 定义窗口的最小高度。
199
200    // 根据不同的拉伸状态调整窗口的尺寸和位置。
201    if (m_stretchRectState == LEFT_BORDER) { // 拖拽左边框
202        // 检查窗口宽度是否已经达到最小值,并且鼠标是向左拉伸(delValue_X > 0)或向右缩小(delValue_X <= 0)。
203        // 如果已达最小宽度且是缩小操作,则返回,不再缩小。
204        if (this->geometry().width() <= m_windowMinWidth && delValue_X <= 0) {
205            return;
206        }
207        QPoint bottomLeftPoint = windowRect.bottomLeft(); // 获取原始窗口的左下角点。
208        bottomLeftPoint.setX(bottomLeftPoint.x() - delValue_X); // 根据X方向位移更新左下角的X坐标。
209        windowRect.setBottomLeft(bottomLeftPoint); // 更新窗口矩形的左下角点,从而调整宽度和X位置。
210        this->setGeometry(windowRect); // 应用新的窗口几何尺寸。
211    } else if (m_stretchRectState == RIGHT_BORDER) { // 拖拽右边框
212        QPoint bottomRightPoint = windowRect.bottomRight(); // 获取原始窗口的右下角点。
213        bottomRightPoint.setX(bottomRightPoint.x() - delValue_X); // 根据X方向位移更新右下角的X坐标。
214        windowRect.setBottomRight(bottomRightPoint); // 更新窗口矩形的右下角点,从而调整宽度。
215        this->setGeometry(windowRect); // 应用新的窗口几何尺寸。
216    } else if (m_stretchRectState == TOP_BORDER) { // 拖拽上边框
217        // 检查窗口高度是否已经达到最小值,并且鼠标是向上拉伸(delValue_Y > 0)或向下缩小(delValue_Y <= 0)。
218        if (this->geometry().height() <= m_windowMinHeight && delValue_Y <= 0) {
219            return;
220        }
221        QPoint topLeftPoint = windowRect.topLeft(); // 获取原始窗口的左上角点。
222        topLeftPoint.setY(topLeftPoint.y() - delValue_Y); // 根据Y方向位移更新左上角的Y坐标。
223        windowRect.setTopLeft(topLeftPoint); // 更新窗口矩形的左上角点,从而调整高度和Y位置。
224        this->setGeometry(windowRect); // 应用新的窗口几何尺寸。
225    } else if (m_stretchRectState == BOTTOM_BORDER)  { // 拖拽下边框
226        QPoint bottomRightPoint = windowRect.bottomRight(); // 获取原始窗口的右下角点。
227        bottomRightPoint.setY(bottomRightPoint.y() - delValue_Y); // 根据Y方向位移更新右下角的Y坐标。
228        windowRect.setBottomRight(bottomRightPoint); // 更新窗口矩形的右下角点,从而调整高度。
229        this->setGeometry(windowRect); // 应用新的窗口几何尺寸。
230    } else if (m_stretchRectState == LEFT_TOP_RECT) { // 拖拽左上角
231        // 检查宽度和高度是否同时达到最小值,并且都在缩小。
232        if (this->geometry().width() - 3 <= m_windowMinWidth && delValue_X <= 0
233                && this->geometry().height() -3 <= m_windowMinHeight && delValue_Y <= 0) {
234            return;
235        }
236        int a = 0; // 用于标记是否宽度/高度达到最小值。
237        if (this->geometry().width() - 3 <= m_windowMinWidth && delValue_X <= 0) {
238            a = 1; // 宽度达到最小值,并且是向右缩小。
239        }
240        if (this->geometry().height() - 3 <= m_windowMinHeight && delValue_Y <= 0) {
241            a = 2; // 高度达到最小值,并且是向下缩小。
242        }
243        // 根据a的值,决定是同时调整宽度和高度,还是只调整其中一个。
244        if (a == 0) { // 宽度和高度都未达到最小值,可以自由拉伸。
245            QPoint topLeftPoint = windowRect.topLeft();
246            topLeftPoint.setX(topLeftPoint.x() - delValue_X);
247            topLeftPoint.setY(topLeftPoint.y() - delValue_Y);
248            windowRect.setTopLeft(topLeftPoint);
249            this->setGeometry(windowRect);
250        } else if (a == 1) { // 宽度已达最小值,只调整高度。
251            QPoint topLeftPoint = windowRect.topLeft();
252            topLeftPoint.setX(this->geometry().x()); // 保持当前X坐标不变。
253            topLeftPoint.setY(topLeftPoint.y() - delValue_Y);
254            windowRect.setTopLeft(topLeftPoint);
255            this->setGeometry(windowRect);
256        } else if (a == 2) { // 高度已达最小值,只调整宽度。
257            //qDebug() << "走3"; // 调试输出。
258            QPoint topLeftPoint = windowRect.topLeft();
259            topLeftPoint.setX(topLeftPoint.x() - delValue_X);
260            topLeftPoint.setY(this->geometry().y()); // 保持当前Y坐标不变。
261            windowRect.setTopLeft(topLeftPoint);
262            this->setGeometry(windowRect);
263        }
264    } else if (m_stretchRectState == RIGHT_TOP_RECT) { // 拖拽右上角
265        // 检查宽度和高度是否同时达到最小值,并且都在缩小。
266        if (this->geometry().width() - 3 <= m_windowMinWidth && delValue_X <= 0
267                && this->geometry().height() -3 <= m_windowMinHeight && delValue_Y <= 0) {
268            return;
269        }
270        int a = 0;
271        if (this->geometry().width() - 3 <= m_windowMinWidth && delValue_X <= 0) {
272            a = 1;
273        }
274        if (this->geometry().height() - 3 <= m_windowMinHeight && delValue_Y <= 0) {
275            a = 2;
276        }
277
278        if (a == 0) { // 宽度和高度都未达到最小值。
279            QPoint topRightPoint = windowRect.topRight();
280            topRightPoint.setX(topRightPoint.x() - delValue_X); // 根据X位移调整宽度。
281            topRightPoint.setY(topRightPoint.y() - delValue_Y); // 根据Y位移调整高度和Y位置。
282            windowRect.setTopRight(topRightPoint);
283            this->setGeometry(windowRect);
284        } else if (a == 1) { // 宽度已达最小值,只调整高度。
285            QPoint topRightPoint = windowRect.topRight();
286            topRightPoint.setX(this->geometry().x()); // 保持当前X坐标不变。
287            topRightPoint.setY(topRightPoint.y() - delValue_Y);
288            windowRect.setTopRight(topRightPoint);
289            this->setGeometry(windowRect);
290        } else if (a == 2) { // 高度已达最小值,只调整宽度。
291            //qDebug() << "走3";
292            QPoint topRightPoint = windowRect.topRight();
293            topRightPoint.setX(topRightPoint.x() - delValue_X);
294            topRightPoint.setY(this->geometry().y()); // 保持当前Y坐标不变。
295            windowRect.setTopRight(topRightPoint);
296            this->setGeometry(windowRect);
297        }
298    } else if (m_stretchRectState == RIGHT_BOTTOM_RECT) { // 拖拽右下角
299        // 右下角拉伸只改变宽度和高度,不改变窗口位置。
300        QPoint bottomRightPoint = windowRect.bottomRight();
301        bottomRightPoint.setX(bottomRightPoint.x() - delValue_X);
302        bottomRightPoint.setY(bottomRightPoint.y() - delValue_Y);
303        windowRect.setBottomRight(bottomRightPoint);
304        this->setGeometry(windowRect);
305    } else if (m_stretchRectState == LEFT_BOTTOM_RECT) { // 拖拽左下角
306        // 检查宽度和高度是否同时达到最小值,并且都在缩小。
307        if (this->geometry().width() - 3 <= m_windowMinWidth && delValue_X <= 0
308                && this->geometry().height() -3 <= m_windowMinHeight && delValue_Y <= 0) {
309            return;
310        }
311        int a = 0;
312        if (this->geometry().width() - 3 <= m_windowMinWidth && delValue_X <= 0) {
313            a = 1;
314        }
315        if (this->geometry().height() - 3 <= m_windowMinHeight && delValue_Y <= 0) {
316            a = 2;
317        }
318        if (a == 0) { // 宽度和高度都未达到最小值。
319            QPoint bottomLeftPoint = windowRect.bottomLeft();
320            bottomLeftPoint.setX(bottomLeftPoint.x() - delValue_X); // 根据X位移调整宽度和X位置。
321            bottomLeftPoint.setY(bottomLeftPoint.y() - delValue_Y); // 根据Y位移调整高度。
322            windowRect.setBottomLeft(bottomLeftPoint);
323            this->setGeometry(windowRect);
324        } else if (a == 1) { // 宽度已达最小值,只调整高度。
325            QPoint bottomLeftPoint = windowRect.bottomLeft();
326            bottomLeftPoint.setX(this->geometry().x()); // 保持当前X坐标不变。
327            bottomLeftPoint.setY(bottomLeftPoint.y() - delValue_Y);
328            windowRect.setBottomLeft(bottomLeftPoint);
329            this->setGeometry(windowRect);
330        } else if (a == 2) { // 高度已达最小值,只调整宽度。
331            QPoint bottomLeftPoint = windowRect.bottomLeft();
332            bottomLeftPoint.setX(bottomLeftPoint.x() - delValue_X);
333            bottomLeftPoint.setY(this->geometry().y()); // 保持当前Y坐标不变。
334            windowRect.setBottomLeft(bottomLeftPoint);
335            this->setGeometry(windowRect);
336        }
337    }
338}
339
340// 设置是否支持窗口拉伸(调整大小)功能。
341// 参数:isSupportStretch - 布尔值,true表示支持拉伸,false表示不支持。
342void MainWindow::setSupportStretch(bool isSupportStretch)
343{
344    // 因为需要在鼠标未按下的情况下通过mouseMoveEvent事件捕捉鼠标位置,所以需要设置setMouseTracking为true(如果窗口支持拉伸);
345    m_isSupportStretch = isSupportStretch; // 更新成员变量m_isSupportStretch。
346    this->setMouseTracking(isSupportStretch); // 启用/禁用窗口的鼠标跟踪。
347                                              // 当鼠标跟踪启用时,即使没有按住任何鼠标按钮,
348                                              // QWidget也会在其几何区域内生成QMouseEvent::MouseMove事件。
349                                              // 这对于实时更新鼠标光标样式以指示可拉伸区域至关重要。
350
351    // 这里对子控件也进行了设置,是因为如果不对子控件设置,当鼠标移动到子控件上时,
352    // 不会发送mouseMoveEvent事件,也就获取不到当前鼠标位置,无法判断鼠标状态及显示样式了。
353    QList<QWidget*> widgetList = this->findChildren<QWidget*>(); // 获取当前窗口的所有子控件。
354    for(int i = 0; i < widgetList.length(); i ++) {
355        widgetList[i]->setMouseTracking(isSupportStretch); // 对每个子控件也启用/禁用鼠标跟踪。
356    }
357}
358
359// 鼠标按下事件处理函数。
360// 参数:event - QMouseEvent对象,包含鼠标事件的信息。
361void MainWindow::mousePressEvent(QMouseEvent *event)
362{
363    // 检查鼠标是否在标题栏区域(ui->widget_title)且当前未处于最大化状态(!showFlag)。
364    // 如果是,则将isPressedWidget标记为true,表示正在拖拽整个窗口。
365    // showFlag在这里通常与窗口的最大化状态有关,最大化时通常不允许直接拖拽。
366    if(ui->widget_title->underMouse() && !showFlag) {
367        isPressedWidget = true; // 当前鼠标按下的即是QWidget而非界面上布局的其它控件
368    }
369    last = event->globalPos(); // 记录鼠标按下时的全局坐标,用于后续计算窗口移动量。
370
371    // 当前鼠标进入了以上指定的8个区域(拉伸区域),并且是左键按下时才开始进行窗口拉伸;
372    if (m_stretchRectState != NO_SELECT && event->button() == Qt::LeftButton)
373    {
374        m_isMousePressed = true; // 标记鼠标左键已按下,准备进行拉伸操作。
375        // 记录下当前鼠标位置的全局坐标,为后面计算拉伸位置提供起始点。
376        m_startPoint = this->mapToGlobal(event->pos());
377        // 保存下拉伸前的窗口位置及大小,以便在拉伸过程中基于原始尺寸进行计算。
378        m_windowRectBeforeStretch = this->geometry();
379    }
380}
381
382// 鼠标移动事件处理函数。
383// 参数:event - QMouseEvent对象,包含鼠标事件的信息。
384void MainWindow::mouseMoveEvent(QMouseEvent *event) {
385    // 如果isPressedWidget为true(表示正在拖拽标题栏移动窗口)
386    if (isPressedWidget) {
387        // 计算鼠标相对于上次移动的位移量。
388        int dx = event->globalX() - last.x();
389        int dy = event->globalY() - last.y();
390        last = event->globalPos(); // 更新last为当前鼠标的全局坐标。
391        move(x()+dx, y()+dy); // 移动窗口到新位置。
392    }
393
394    // 如果鼠标左键没有按下(即没有在拖拽或拉伸),则更新鼠标光标样式。
395    if (!m_isMousePressed) {
396        QPoint cursorPos = event->pos(); // 获取鼠标在窗口坐标系中的当前位置。
397        // 根据当前鼠标的位置显示不同的样式;
398        m_stretchRectState = getCurrentStretchState(cursorPos); // 判断鼠标处于哪个拉伸区域。
399        updateMouseStyle(m_stretchRectState); // 更新鼠标光标样式。
400    } else {
401        // 如果当前鼠标左键已经按下(正在进行拉伸操作),则记录下第二个点的位置,并更新窗口的大小;
402        m_endPoint = this->mapToGlobal(event->pos()); // 记录当前鼠标的全局坐标作为拉伸的结束点。
403        updateWindowSize(); // 根据起始点和结束点以及拉伸状态更新窗口尺寸。
404    }
405}
406
407// 鼠标释放事件处理函数。
408// 参数:event - QMouseEvent对象,包含鼠标事件的信息。
409void MainWindow::mouseReleaseEvent(QMouseEvent *event)
410{
411    isPressedWidget = false; // 鼠标松开时,将拖拽窗口的标志置为false。
412    m_isMousePressed = false; // 鼠标松开时,将正在拉伸的标志置为false。
413    calculateCurrentStrechRect(); // 重新计算拉伸区域矩形,以应对窗口大小可能已经改变的情况。
414}
415
416// 鼠标双击事件处理函数。
417// 此处为空,因为双击标题栏最大化/还原的逻辑是在nativeEvent中通过Windows API处理的。
418void MainWindow::mouseDoubleClickEvent(QMouseEvent *event)
419{
420    //不调用 (此函数为空,表示不直接通过Qt事件处理双击,而是依赖原生事件WM_NCHITTEST和WM_GETMINMAXINFO)
421}
422
423// 窗口显示事件处理函数。
424// 参数:event - QShowEvent对象。
425void MainWindow::showEvent(QShowEvent *event)
426{
427    calculateCurrentStrechRect(); // 窗口显示时,重新计算拉伸区域,确保正确。
428
429    // 防止假死。
430    // Qt::WA_Mapped属性通常在窗口显示后设置,确保窗口句柄已经创建并且窗口已被映射到屏幕。
431    // 有时,在某些复杂的图形效果或窗口管理下,这可以避免一些显示问题或假死状态。
432    setAttribute(Qt::WA_Mapped);
433    QMainWindow::showEvent(event); // 调用基类的showEvent确保标准行为。
434
435    // 这段代码是一个常见的技巧,用于强制Qt重新计算窗口的布局和阴影,
436    // 特别是在使用QGraphicsDropShadowEffect时,有时需要先稍微改变窗口大小再恢复,
437    // 以确保阴影正确渲染,避免阴影显示不全或延迟显示的问题。
438    QSize oldSize = this->size();
439    resize(oldSize + QSize(10, 10)); // 临时增大窗口尺寸。
440    resize(oldSize); // 恢复窗口到原始尺寸。
441}
442
443// 窗口状态改变事件处理函数。
444// 当窗口最小化、最大化、全屏或从这些状态恢复时触发。
445// 参数:event - QEvent对象。
446void MainWindow::changeEvent(QEvent *event)
447{
448    // 检查事件类型是否为窗口状态改变事件。
449    if(QEvent::WindowStateChange == event->type())
450    {
451        // 将QEvent转换为QWindowStateChangeEvent,以便获取更多状态信息。
452        QWindowStateChangeEvent * stateEvent = dynamic_cast<QWindowStateChangeEvent*>(event);
453        if(Q_NULLPTR != stateEvent) // 确保转换成功。
454        {
455            // 判断当前窗口状态。
456            if(this->windowState() == Qt::WindowMinimized)
457            {
458                //qDebug() << "当前最小化"; // 窗口最小化。
459            } else if (this->windowState() == Qt::WindowNoState && stateEvent->oldState() == Qt::WindowMaximized) {
460                //qDebug() << "当前正常"; // 窗口从最大化状态恢复到正常状态。
461                // 恢复centralWidget的样式,包括背景图和圆角。
462                ui->centralWidget->setStyleSheet("#centralWidget {background-color: rgb(67, 67, 67);border-image: url(:/lib/back1.png);border-radius:10px;}");
463                // 恢复widget_side的样式,包括圆角。
464                ui->widget_side->setStyleSheet("#widget_side { \
465                                                    color: rgb(255, 255, 255); \
466                                                    border-top-left-radius: 10px; \
467                                                    border-bottom-left-radius: 10px; \
468                                                    background-color: rgb(10, 31, 57); \
469                                                }");
470                // 恢复关闭按钮的样式,包括圆角和鼠标悬停效果。
471                ui->toolButton_close->setStyleSheet("QToolButton {\
472                                                        color: rgb(255, 255, 255);\
473                                                        border-top-right-radius: 9px;\
474                                                        background-color: rgba(94, 255, 210, 0);\
475                                                        border: none;\
476                                                    }\
477                                                    QToolButton::menu-indicator { \
478                                                        image: None;\
479                                                    }\
480                                                    QToolButton:hover {\
481                                                        color: rgb(255, 255, 255);\
482                                                        background-color: rgb(200, 0, 0);\
483                                                        border: none;\
484                                                    }");
485                // 恢复最大化按钮的图标为“最大化”图标。
486                ui->toolButton_max->setIcon(QIcon(":lib/Icon_max4.png"));
487            } else if (this->windowState() == Qt::WindowMaximized && stateEvent->oldState() == Qt::WindowNoState) {
488                //qDebug() << "当前最大化"; // 窗口从正常状态变为最大化状态。
489                // 移除centralWidget的圆角,使之铺满整个屏幕。
490                ui->centralWidget->setStyleSheet("#centralWidget {background-color: rgb(67, 67, 67);border-image: url(:/lib/back1.png);border-radius:0px;}");
491                // 移除widget_side的圆角。
492                ui->widget_side->setStyleSheet("#widget_side { \
493                                                    color: rgb(255, 255, 255); \
494                                                    border-top-left-radius: 0px; \
495                                                    border-bottom-left-radius: 0px; \
496                                                    background-color: rgb(10, 31, 57); \
497                                                }");
498                // 移除关闭按钮的圆角。
499                ui->toolButton_close->setStyleSheet("QToolButton {\
500                                                        color: rgb(255, 255, 255);\
501                                                        border-top-right-radius: 0px;\
502                                                        background-color: rgba(94, 255, 210, 0);\
503                                                        border: none;\
504                                                    }\
505                                                    QToolButton::menu-indicator { \
506                                                        image: None;\
507                                                    }\
508                                                    QToolButton:hover {\
509                                                        color: rgb(255, 255, 255);\
510                                                        background-color: rgb(200, 0, 0);\
511                                                        border: none;\
512                                                    }");
513                // 更改最大化按钮的图标为“还原”图标。
514                ui->toolButton_max->setIcon(QIcon(":lib/icon-copy.png"));
515            }
516        }
517    }
518}
519
520// 处理Windows原生事件。
521// 这是实现自定义无边框窗口行为的关键部分,如拖拽、最大化、调整大小等。
522// 参数:eventType - 事件类型(QByteArray),message - 消息结构体指针,result - 结果指针。
523// 返回值:如果事件已处理,则返回true;否则返回false,让Qt继续处理。
524bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
525{
526    MSG* msg = (MSG*)message; // 将void*消息指针转换为Windows MSG结构体指针。
527    switch (msg->message) {
528    // 处理WM_NCCALCSIZE消息:在窗口的非客户区(标题栏、边框)需要计算大小时发送。
529    // 没有这一段,将不会显示窗口(或显示为默认样式,无法自定义)。
530    // 返回true并设置*result为0,告诉系统我们自己负责绘制非客户区,不使用默认边框和标题栏。
531    case WM_NCCALCSIZE:
532        *result = 0; // 告诉系统不需要默认的非客户区计算,完全由我们自己绘制。
533        return true;
534
535    // 处理WM_NCHITTEST消息:当鼠标光标移动时,或者鼠标按钮按下/松开时发送,
536    // 用于确定鼠标光标在哪一部分窗口区域(如标题栏、边框、客户区)。
537    case WM_NCHITTEST:
538    {
539        //qDebug() << "触发WM_NCHITTEST"; // 调试输出。
540        qreal ratio = 1.0; // DPI缩放比例,此处默认为1.0。
541        // 获取鼠标光标的屏幕坐标,并考虑DPI缩放。
542        long x = GET_X_LPARAM(msg->lParam) / ratio;
543        long y = GET_Y_LPARAM(msg->lParam) / ratio;
544        // 将屏幕坐标转换为窗口的局部坐标。
545        QPoint pos = mapFromGlobal(QPoint(x, y));
546        //qDebug() << "pos = " << pos; // 调试输出鼠标在窗口中的位置。
547
548        // 如果鼠标Y坐标大于10像素(避开顶部的拉伸区域),并且鼠标在ui->widget_title2(通常是自定义标题栏)的矩形范围内。
549        // 这里的10像素是为了避免与顶部边框的拉伸区域冲突。
550        if (pos.y() > 10 && ui->widget_title2->rect().contains(pos)) {
551            //qDebug() << "标题栏被按下"; // 调试输出。
552            // 根据当前鼠标的位置显示不同的样式;
553            // 设置*result为HTCAPTION,告诉Windows,鼠标当前位于标题栏区域。
554            // 这将启用标准的标题栏行为,如拖拽窗口移动,以及双击标题栏最大化/还原。
555            *result = HTCAPTION;
556            return true; // 表示已处理此消息。
557        }
558    }
559    // 处理WM_GETMINMAXINFO消息:当窗口最大化或最小化时,或者当窗口需要计算其最大化/最小化大小和位置时发送。
560    case WM_GETMINMAXINFO:
561    {
562        // 检查窗口当前是否处于最大化状态。
563        if (::IsZoomed(msg->hwnd)) { // ::IsZoomed是Windows API函数,检查窗口是否最大化。
564            isMaxShow = true; // 设置最大化状态标志。
565            showFlag = true; // 设置另一个状态标志,可能用于控制UI元素的显示。
566            // 最大化时会超出屏幕,所以填充边框间距。
567            // 当自定义无边框窗口最大化时,Windows默认会将窗口扩展到整个屏幕,包括任务栏区域,导致窗口边缘超出屏幕。
568            // AdjustWindowRectEx用于计算一个窗口矩形(frame)所需的尺寸,以便客户区达到指定大小。
569            // 这里反过来使用它来获取Windows默认的窗口边框大小。
570            RECT frame = { 0, 0, 0, 0 };
571            // WS_OVERLAPPEDWINDOW是标准重叠窗口的样式,FALSE表示不含菜单,0表示无扩展样式。
572            AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0);
573            frame.left = abs(frame.left); // 获取左边框的宽度(通常为负值,取绝对值)。
574            frame.top = abs(frame.bottom); // 获取上边框的高度(通常bottom为负值,取绝对值)。
575                                          // 注意:这里用frame.bottom来获取top边距可能是历史遗留或特定平台行为,
576                                          // 正常应为abs(frame.top)。如果出现显示问题,此处值得检查。
577            // 设置窗口内容的边距,抵消系统边框,使窗口内容在最大化时正好占据可用屏幕区域。
578            this->setContentsMargins(frame.left, frame.top, frame.right, frame.bottom);
579        } else {
580            isMaxShow = false; // 不处于最大化状态。
581            showFlag = false; // 对应状态标志设为false。
582        }
583        // 调用默认的窗口过程处理此消息,以确保系统继续处理最大化/最小化的其他方面。
584        *result = ::DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
585        return true; // 表示已处理此消息。
586    }
587    }
588    // 对于其他未特殊处理的Windows消息,交由QMainWindow的默认nativeEvent处理。
589    return QMainWindow::nativeEvent(eventType, message, result);
590}
591
592// 创建系统托盘图标和菜单。
593void MainWindow::createSystemTray()
594{
595    // 创建系统托盘图标。
596    // QIcon(":lib/icon9.png")设置托盘图标的图片。
597    // this指定父对象为MainWindow,确保在MainWindow销毁时托盘图标也被正确销毁。
598    trayIcon = new QSystemTrayIcon(QIcon(":lib/icon9.png"), this);
599    trayIcon->setToolTip("Fdog-Kit"); // 设置鼠标悬停在托盘图标上时显示的提示文本。
600    trayIcon->show(); // 显示系统托盘图标。
601
602    // 创建一个菜单。
603    QMenu* menu = new QMenu();
604    // 设置菜单的窗口标志,使其为无边框窗口,并移除默认阴影,以实现自定义外观。
605    // 这对于菜单的自定义样式通常是必要的,例如实现圆角或自定义背景。
606    menu->setWindowFlags(menu->windowFlags()  | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint);
607    menu->setAttribute(Qt::WA_TranslucentBackground); // 设置菜单背景透明。
608    QAction* openAction = new QAction("打开"); // 创建“打开”菜单项。
609    QAction* closeAction = new QAction("退出"); // 创建“退出”菜单项。
610    menu->addAction(openAction); // 将“打开”动作添加到菜单。
611    menu->addAction(closeAction); // 将“退出”动作添加到菜单。
612    // 将菜单设置给系统托盘图标,使其在点击托盘图标时弹出。
613    trayIcon->setContextMenu(menu);
614
615    // 连接菜单项的triggered()信号到槽函数。
616    // 当点击“打开”或“退出”菜单项时,都会触发rece_systemTrayMenu()槽函数。
617    connect(openAction, SIGNAL(triggered()), this, SLOT(rece_systemTrayMenu()));
618    connect(closeAction, SIGNAL(triggered()), this, SLOT(rece_systemTrayMenu()));
619}
620
621// 根据配置文件设置窗口属性。
622// 这个函数当前是空的,表示此功能尚未实现或处于待开发状态。
623void MainWindow::setWindowsByConf()
624{
625    // 待实现:从配置文件加载窗口位置、大小、主题等设置。
626}
627
628// 关闭按钮点击事件槽函数。
629void MainWindow::on_toolButton_close_clicked()
630{
631    this->close(); // 关闭当前窗口。
632}
633
634// 最小化按钮点击事件槽函数。
635void MainWindow::on_toolButton_min_clicked()
636{
637    this->showMinimized(); // 最小化当前窗口。
638}
639
640// 最大化/还原按钮点击事件槽函数。
641void MainWindow::on_toolButton_max_clicked()
642{
643    // showFlag是一个状态标志,通常用于指示窗口是否处于最大化或全屏状态。
644    // 如果showFlag为false(表示当前未最大化或全屏,处于正常状态)
645    if (!showFlag) {
646        setContentsMargins(0, 0, 0, 0); // 移除内容边距,使窗口内容铺满整个屏幕(无边框,无阴影空间)。
647        this->setWindowState(Qt::WindowState::WindowMaximized); // 将窗口设置为最大化状态。
648        isMaxShow = true; // 更新最大化状态标志。
649        showFlag = true; // 更新通用状态标志。
650    } else { // 如果showFlag为true(表示当前已最大化或全屏)
651        setContentsMargins(10, 10, 10, 10); // 恢复内容边距,为阴影和拉伸区域留出空间。
652        this->setWindowState(Qt::WindowState::WindowNoState); // 将窗口恢复到正常状态。
653        isMaxShow = false; // 更新最大化状态标志。
654        showFlag = false; // 更新通用状态标志。
655    }
656}
657
658// 槽函数:关闭窗口。
659// 此函数功能与on_toolButton_close_clicked()类似,但这里调用的是showMinimized()而不是close()。
660// 这意味着点击这个关闭按钮(如果实际使用它)会将窗口最小化到任务栏或托盘,而不是完全退出程序。
661void MainWindow::closeWindow()
662{
663    this->showMinimized(); // 将窗口最小化。
664}
665
666// 槽函数:最小化窗口。
667// 此函数功能与on_toolButton_min_clicked()完全相同,可能是为了不同的信号连接或未来的扩展。
668void MainWindow::minWindow()
669{
670    this->showMinimized(); // 将窗口最小化。
671}
672
673// 槽函数:最大化窗口。
674// 此函数体为空,可能是预留的接口或尚未实现的逻辑。
675void MainWindow::maxWindow()
676{
677    //mWindow // 占位符或未完成的代码。
678}
679
680// 槽函数:处理全屏切换。
681// 通常由F11快捷键或特定的全屏按钮触发。
682void MainWindow::rece_toolButton_fullScreen_sign()
683{
684    if (!isFullScreen) { // 如果当前不是全屏状态
685        setContentsMargins(0, 0, 0, 0); // 移除内容边距,确保全屏时内容完全填充屏幕。
686        // 移除centralWidget的圆角,使其铺满整个屏幕。
687        ui->centralWidget->setStyleSheet("#centralWidget {border-image: url(:/lib/back1.png);border-radius:0px;}");
688        ui->toolButton_max->setIcon(QIcon(":lib/icon-copy.png")); // 更改最大化按钮图标为“还原”样式。
689        //this->showNormal(); // 注释掉,因为接下来会直接showFullScreen。
690        ui->toolButton_min->hide(); // 隐藏最小化按钮。
691        ui->toolButton_max->hide(); // 隐藏最大化按钮。
692        ui->toolButton_close->hide(); // 隐藏关闭按钮。
693        this->showFullScreen(); // 将窗口设置为全屏模式。
694        isFullScreen = true; // 更新全屏状态标志。
695    } else { // 如果当前是全屏状态
696        ui->toolButton_min->show(); // 显示最小化按钮。
697        ui->toolButton_max->show(); // 显示最大化按钮。
698        ui->toolButton_close->show(); // 显示关闭按钮。
699        setContentsMargins(10, 10, 10, 10); // 恢复内容边距。
700        ui->centralWidget->setStyleSheet("#centralWidget {border-image: url(:/lib/back1.png);border-radius:10px;}"); // 恢复centralWidget的圆角样式。
701        ui->toolButton_max->setIcon(QIcon(":lib/Icon_max4.png")); // 恢复最大化按钮图标为“最大化”样式。
702
703        if (showFlag) { // 如果在进入全屏前是最大化状态
704            ui->centralWidget->setStyleSheet("#centralWidget {border-image: url(:/lib/back1.png);border-radius:0px;}"); // 保持centralWidget无圆角(因为是最大化)。
705            setContentsMargins(0, 0, 0, 0); // 保持无边距(因为是最大化)。
706            ui->toolButton_max->setIcon(QIcon(":lib/icon-copy.png")); // 保持最大化图标(因为是最大化)。
707            this->showMaximized(); // 恢复到最大化状态。
708        } else { // 如果在进入全屏前是正常状态
709            this->showNormal(); // 恢复到正常窗口状态。
710        }
711        isFullScreen = false; // 更新全屏状态标志。
712    }
713}
714
715// MainWindow类的析构函数。
716MainWindow::~MainWindow()
717{
718    delete ui; // 释放由new Ui::MainWindow创建的ui对象内存,防止内存泄漏。
719}