解码 Qt 交互:滑动交互、窗口拖拽

QPoint类

QPoint类用于表示平面中的一个点,采用整数精度存储x、y坐标,是Qt中处理坐标、位置的基础类,支持坐标直接操作、加减乘除等向量运算,常用于描述控件位置、鼠标坐标、动画位移等场景。

核心方法与代码示例

cpp 复制代码
/**
 * @brief QPoint构造函数:创建指定坐标的点对象
 * @param xpos x轴坐标(整数),水平方向位置,向右为正
 * @param ypos y轴坐标(整数),垂直方向位置,向下为正
 * @return 无返回值,构造对象
 * @note 默认构造函数QPoint()创建(0,0)的空点,isNull()返回true
 */
QPoint point(100, 200); // 创建x=100,y=200的点

/**
 * @brief x():获取x轴坐标
 * @param 无参数
 * @return int 返回当前点的x坐标值
 * @note 配套setX(int x)可修改x坐标,rx()返回x坐标的引用,支持直接修改
 */
int x = point.x(); // x = 100
point.setX(150);   // 修改x坐标为150
point.rx()++;      // 直接操作x引用,x变为151

/**
 * @brief y():获取y轴坐标
 * @param 无参数
 * @return int 返回当前点的y坐标值
 * @note 配套setY(int y)可修改y坐标,ry()返回y坐标的引用,支持直接修改
 */
int y = point.y(); // y = 200
point.setY(250);   // 修改y坐标为250
point.ry()--;      // 直接操作y引用,y变为249

/**
 * @brief manhattanLength():计算曼哈顿长度(x绝对值+y绝对值)
 * @param 无参数
 * @return int 返回曼哈顿长度值,是欧几里得长度的快速近似
 * @note 常用于判断鼠标移动距离、简化碰撞检测等场景,计算效率远高于平方根运算
 */
int mLen = point.manhattanLength(); // |151| + |249| = 400

/**
 * @brief operator+=:点的加法运算(向量相加)
 * @param point 待相加的QPoint对象
 * @return QPoint& 返回当前点对象的引用(自身)
 * @note 对应operator-=为向量相减,operator*=为坐标乘系数(整数/浮点数)
 */
QPoint offset(10, 20);
point += offset;   // point变为(161, 269)

/**
 * @brief dotProduct:静态方法,计算两个点的点积(向量点乘)
 * @param p1 第一个QPoint操作数
 * @param p2 第二个QPoint操作数
 * @return int 点积结果(p1.x*p2.x + p1.y*p2.y)
 * @note 点积可用于判断向量夹角、投影计算等几何运算
 */
QPoint p(3,7), q(-1,4);
int dot = QPoint::dotProduct(p, q); // 3*(-1) +7*4 = 25

QEvent类

QEvent是Qt所有事件的基类,封装了事件的类型、接受状态等核心信息,Qt的事件循环会将系统事件转换为QEvent对象,再分发给对应的QObject对象处理。事件可分为系统事件(如鼠标、键盘、窗口变化)和自定义事件,支持事件的接受/忽略、事件过滤等机制。

核心成员

  • Type枚举:定义所有事件类型,如鼠标事件(MouseMove/Press/Release)、窗口事件(Move/Resize/Close)、自定义事件(User/MaxUser区间);
  • accepted属性:标记事件是否被处理,accept()设为true,ignore()设为false,未处理的事件会向上传递给父对象;
  • 核心方法:type()获取事件类型、spontaneous()判断是否为系统原生事件、registerEventType()注册自定义事件类型。

代码示例(事件处理与自定义事件)

slideevent.h(自定义事件头文件)

cpp 复制代码
#ifndef SLIDEEVENT_H
#define SLIDEEVENT_H

// 头文件保护宏:防止重复包含
#include <QEvent>
#include <QPoint>

// 声明自定义事件类型(extern保证跨文件可见)
extern const int CustomSlideEvent;

/**
 * @brief 自定义滑动事件类:继承QEvent,携带滑动偏移量
 * @note 仅声明类和核心方法,实现直接写在头文件(简单类无需单独cpp)
 */
class SlideEvent : public QEvent {
public:
    // 构造函数:初始化事件类型和滑动偏移量
    SlideEvent(QPoint delta) : QEvent((QEvent::Type)CustomSlideEvent), m_delta(delta) {}
    
    // 获取滑动偏移量的接口
    QPoint delta() const { return m_delta; }

private:
    QPoint m_delta; // 存储滑动偏移量
};

#endif // SLIDEEVENT_H

mywidget.h(自定义窗口头文件)

cpp 复制代码
#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include <QMouseEvent>
// 引入自定义事件头文件
#include "slideevent.h"

/**
 * @brief 自定义窗口类:处理鼠标事件和自定义滑动事件
 */
class MyWidget : public QWidget {
    Q_OBJECT // Qt的信号槽/元对象系统必须的宏

public:
    // 构造函数:显式声明,方便后续扩展
    explicit MyWidget(QWidget *parent = nullptr);

protected:
    // 重写事件总入口:处理自定义SlideEvent和系统MouseMove
    bool event(QEvent *event) override;

    // 重写鼠标按下事件:记录鼠标偏移
    void mousePressEvent(QMouseEvent *event) override;

    // 重写鼠标释放事件:触发自定义SlideEvent
    void mouseReleaseEvent(QMouseEvent *event) override;

private:
    QPoint m_lastMousePos;      // 鼠标按下时与窗口的偏移
    QPoint m_pressGlobalPos;    // 鼠标按下时的全局坐标
};

#endif // MYWIDGET_H

mywidget.cpp(自定义窗口实现文件)

cpp 复制代码
#include "mywidget.h"
#include <QCoreApplication>
#include <QDebug>

// 定义自定义事件类型(赋值,与头文件的extern对应)
const int CustomSlideEvent = QEvent::registerEventType(QEvent::User + 1);

/**
 * @brief MyWidget构造函数:初始化成员变量
 */
MyWidget::MyWidget(QWidget *parent) : QWidget(parent) {
    // 可选:设置窗口默认属性
    setWindowTitle("自定义滑动事件示例");
    resize(300, 200);
}

/**
 * @brief 事件总处理函数:识别并处理自定义事件和系统事件
 */
bool MyWidget::event(QEvent *event) {
    // 处理自定义SlideEvent
    if (event->type() == (QEvent::Type)CustomSlideEvent) {
        SlideEvent *slideEvent = static_cast<SlideEvent*>(event);
        QPoint delta = slideEvent->delta();
        
        // 核心逻辑:根据偏移量移动窗口
        move(pos() + delta);
        qDebug() << "SlideEvent触发!窗口偏移量:" << delta;
        
        event->accept(); // 标记事件已处理
        return true;
    }

    // 处理系统MouseMove事件
    if (event->type() == QEvent::MouseMove) {
        QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
        QPoint mousePos = mouseEvent->pos();
        qDebug() << "鼠标位置(相对窗口):" << mousePos;
        
        // 不拦截,交给父类处理(保证鼠标事件正常传递)
        return QWidget::event(event);
    }

    // 其他事件交给父类处理
    return QWidget::event(event);
}

/**
 * @brief 鼠标按下事件:记录鼠标位置偏移
 */
void MyWidget::mousePressEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {
        m_lastMousePos = event->globalPos() - pos(); // 鼠标与窗口的偏移
        m_pressGlobalPos = event->globalPos();       // 鼠标全局坐标
        event->accept();
    }
    // 交给父类处理(保证其他按键逻辑正常)
    QWidget::mousePressEvent(event);
}

/**
 * @brief 鼠标释放事件:手动发送自定义SlideEvent
 */
void MyWidget::mouseReleaseEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {
        // 计算鼠标滑动的总偏移量
        QPoint delta = event->globalPos() - m_pressGlobalPos;
        
        // 核心:手动发送自定义事件(同步发送,栈对象)
        SlideEvent slideEvent(delta);
        QCoreApplication::sendEvent(this, &slideEvent);
    }
    // 交给父类处理
    QWidget::mouseReleaseEvent(event);
}

main.cpp(程序入口文件)

cpp 复制代码
#include <QApplication>
#include "mywidget.h"

/**
 * @brief 主函数:Qt程序入口
 */
int main(int argc, char *argv[]) {
    // 创建Qt应用对象
    QApplication a(argc, argv);
    
    // 创建自定义窗口并显示
    MyWidget w;
    w.show();
    
    // 运行应用事件循环
    return a.exec();
}

QPropertyAnimation类

QPropertyAnimation是Qt的属性动画类,用于对QObject的属性进行平滑动画(如位置、大小、透明度等),继承自QVariantAnimation,支持设置动画时长、起始/结束值、插值方式等,是Qt动画框架的核心类。使用前提:目标对象必须是QObject子类,且动画的属性必须有对应的setter方法(如geometry属性对应setGeometry())。

触摸事件逻辑(用开发板时替换下述示例中的鼠标事件)

cpp 复制代码
// 重写event
bool Widget::event(QEvent *event)
{
    if(event->type() == QEvent::TouchBegin)
    {
        QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
        startPos = touchEvent->touchPoints().first().pos().toPoint();
				// 处理事件
        return true;
    }
    else if(event->type() == QEvent::TouchUpdate &&m_isSliding)
    {
        QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
        QPoint currentPos = touchEvent->touchPoints().first().pos().toPoint();
				// 处理事件
        return true;
    }
    else if(event->type() == QEvent::TouchEnd)
    {
        // 处理事件
        return true;
    }
    return QWidget::event(event);
}

场景:屏幕滑动交互(仿手机列表滑动)

slidewidget.h(头文件)

cpp 复制代码
#ifndef SLIDEWIDGET_H
#define SLIDEWIDGET_H

#include <QWidget>
#include <QPropertyAnimation>
#include <QScrollArea>

class SlideWidget : public QWidget {
    Q_OBJECT
public:
    explicit SlideWidget(QWidget *parent = nullptr);
    ~SlideWidget() override; // 析构函数释放动画对象

protected:
    // 重写鼠标事件(逻辑改为操作滚动区域)
    void mousePressEvent(QMouseEvent *event) override; // 
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;

private:
    QPropertyAnimation *m_anim;      // 动画对象(改为操作滚动条)
    QScrollArea *m_scrollArea;       // 列表的滚动容器
    bool m_isPress = false;          // 鼠标是否按下
    QPoint m_startMousePos;          // 鼠标按下时的全局位置
    int m_startScrollValue = 0;      // 按下时滚动条的初始值
};

#endif // SLIDEWIDGET_H

slidewidget.cpp(实现文件)

cpp 复制代码
#include "slidewidget.h"
#include <QMouseEvent>
#include <QVBoxLayout>
#include <QLabel>
#include <QScrollBar>
#include <QtMath>

SlideWidget::SlideWidget(QWidget *parent) : QWidget(parent) {
    // 1. 窗口基础设置(固定大小,模拟手机屏幕,位置固定)
    setFixedSize(300, 400);
    setWindowTitle("列表滑动示例");
    move(200, 200); // 固定窗口位置,不再随鼠标移动

    // 2. 创建滚动容器(核心:列表放在QScrollArea里,实现内容滚动)
    m_scrollArea = new QScrollArea(this);
    m_scrollArea->setWidgetResizable(true); // 自适应子控件大小
    m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // 隐藏水平滚动条

    // 3. 创建列表内容容器
    QWidget *contentWidget = new QWidget(m_scrollArea);
    QVBoxLayout *contentLayout = new QVBoxLayout(contentWidget);
    // 添加列表项(10个,高度50,总高度500,超出窗口400的高度,可滚动)
    for (int i = 0; i < 10; i++) {
        QLabel *label = new QLabel(QString("列表项 %1").arg(i+1), contentWidget);
        label->setFixedHeight(50);
        contentLayout->addWidget(label);
    }
    contentWidget->setLayout(contentLayout);
    m_scrollArea->setWidget(contentWidget);

    // 4. 将滚动容器铺满整个窗口
    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->setContentsMargins(0, 0, 0, 0); // 去掉边距
    mainLayout->addWidget(m_scrollArea);
    setLayout(mainLayout);

    // 5. 创建动画对象(改为操作滚动条的value属性)
    /**
     * @brief QPropertyAnimation构造函数:绑定滚动条的value属性
     * @param target 目标:滚动区域的垂直滚动条
     * @param propertyName 属性名:"value"(滚动条的数值)
     * @param parent 父对象,用于内存管理
     * @return 无返回值
     */
    m_anim = new QPropertyAnimation(m_scrollArea->verticalScrollBar(), "value", this);
    m_anim->setDuration(300); // 动画时长300ms,贴近原生滑动
}

SlideWidget::~SlideWidget() {
    // 释放动画对象(可选,父对象也会自动释放,但显式释放更规范)
    delete m_anim;
}

/**
 * @brief 鼠标按下事件:记录初始状态(滚动条位置+鼠标位置)
 * @param event 鼠标事件对象
 * @return void
 * @note 停止正在进行的动画,避免拖动和动画冲突
 */
void SlideWidget::mousePressEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {
        m_isPress = true;
        m_startMousePos = event->globalPos(); // 鼠标全局位置
        // 记录按下时滚动条的当前值(核心:替代原来的窗口位置)
        m_startScrollValue = m_scrollArea->verticalScrollBar()->value();
        m_anim->stop(); // 停止惯性动画,优先响应手动拖动
    }
    QWidget::mousePressEvent(event);
}

/**
 * @brief 鼠标移动事件:实时更新滚动条位置,实现列表跟随滑动
 * @param event 鼠标事件对象
 * @return void
 * @note 鼠标y轴偏移量反向映射到滚动条(鼠标上滑=列表下滑,符合直觉)
 */
void SlideWidget::mouseMoveEvent(QMouseEvent *event) {
    if (m_isPress) {
        // 计算鼠标y轴偏移量(全局坐标差)
        int deltaY = m_startMousePos.y() - event->globalPos().y();
        // 新的滚动条值 = 初始值 + 偏移量(反向映射,符合滑动直觉)
        int newScrollValue = m_startScrollValue + deltaY;
        
        // 限制滚动范围(0 ~ 滚动条最大值,避免滑出边界)
        int scrollMax = m_scrollArea->verticalScrollBar()->maximum();
        /**
				 * @brief qBound - Qt通用数值范围限制函数(模板函数)
				 * @headerfile <QtGlobal> (Qt核心头文件已间接包含,通常无需手动引入)
				 * @template T 支持所有数值类型(int/double/float/qint32等),要求min/val/max类型一致
				 * @param min 数值区间的最小值(下限)
				 * @param val 需要被限制的目标数值
				 * @param max 数值区间的最大值(上限)
				 * @return const T& 返回值规则:
				 *         1. 若val < min → 返回min(低于下限则取下限)
				 *         2. 若val > max → 返回max(高于上限则取上限)
				 *         3. 若min ≤ val ≤ max → 返回val(在区间内则保留原值)
				 * @note 核心优势:替代繁琐的if-else判断,一行代码实现数值边界限制,是Qt数值限制的最佳实践
				 */
        newScrollValue = qBound(0, newScrollValue, scrollMax);
        
        // 更新滚动条位置(列表滑动,窗口不动)
        m_scrollArea->verticalScrollBar()->setValue(newScrollValue);
    }
    QWidget::mouseMoveEvent(event);
}

/**
 * @brief 鼠标释放事件:实现列表惯性滑动动画
 * @param event 鼠标事件对象
 * @return void
 * @note 基于拖动速度计算惯性偏移,让滑动更自然
 */
void SlideWidget::mouseReleaseEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton && m_isPress) {
        m_isPress = false;
        // 计算鼠标释放时的偏移量(y轴)
        int deltaY = m_startMousePos.y() - event->globalPos().y();
        // 惯性偏移:放大偏移量(0.8倍,可调整),模拟惯性
        int inertiaDelta = deltaY * 0.8;
        // 目标滚动值 = 当前值 + 惯性偏移
        int currentValue = m_scrollArea->verticalScrollBar()->value();
        int endValue = currentValue + inertiaDelta;
        
        // 限制惯性滚动范围(0 ~ 滚动条最大值)
        int scrollMax = m_scrollArea->verticalScrollBar()->maximum();
        endValue = qBound(0, endValue, scrollMax);
        
        // 设置动画:滚动条从当前值滑到目标值(惯性效果)
        m_anim->setStartValue(currentValue);
        m_anim->setEndValue(endValue);
        /**
         * @brief start:启动惯性动画
         * @param mode 默认KeepWhenStopped,动画结束后保留最终值
         * @return void
         */
        m_anim->start();
    }
    QWidget::mouseReleaseEvent(event);
}

main.cpp(程序入口)

cpp 复制代码
#include <QApplication>
#include "slidewidget.h"

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    SlideWidget w;
    w.show();
    return a.exec();
}

场景:上滑关闭打开的软件窗口(仿手机上滑关闭App)

SlideCloseHelper.h

cpp 复制代码
#ifndef SLIDECLOSEHELPER_H
#define SLIDECLOSEHELPER_H

// 仅包含必要的前置声明/头文件(减少依赖)
#include <QObject>
#include <QPoint>
#include <QWidget>
#include <QPropertyAnimation>

// 前置声明(避免包含过多头文件)
class QElapsedTimer;
class QTouchEvent;
class QMouseEvent;
class QEasingCurve;

class SlideCloseHelper : public QObject
{
    Q_OBJECT // Qt元对象系统必需宏
public:
    // 构造函数声明
    explicit SlideCloseHelper(QWidget *target, QObject *parent = nullptr);

    // 公有配置接口声明
    void setSlideThreshold(int threshold);
    void setTimeThreshold(int threshold);
    void setFollowRatio(int ratio);

protected:
    // 事件过滤器声明
    bool eventFilter(QObject *watched, QEvent *event) override;

private:
    // 私有方法声明(内部复用逻辑)
    void moveTargetWithRatio(int dy);
    void checkSlide(const QPoint &endPos);

private:
    // 成员变量声明
    QWidget *m_target = nullptr;          // 目标窗口
    QPoint m_startPos;                    // 滑动起始点
    QPoint m_originPos;                   // 窗口初始位置(回弹用)
    QElapsedTimer *m_timer = nullptr;     // 滑动计时(改为指针,cpp中初始化)
    bool m_isPress = false;               // 是否按下/触摸
    int m_slideThreshold = 80;            // 滑动距离阈值
    int m_timeThreshold = 300;            // 滑动时间阈值
    int m_followRatio = 5;                // 滑动跟随比例
    QPropertyAnimation *m_anim = nullptr; // 回弹动画对象
};

#endif // SLIDECLOSEHELPER_H

SlideCloseHelper.cpp

cpp 复制代码
#include "SlideCloseHelper.h"
// 实现中需要的头文件(集中引入)
#include <QElapsedTimer>
#include <QTouchEvent>
#include <QMouseEvent>
#include <QEasingCurve>
#include <QtGlobal> // qBound需要

/**
 * @brief 构造函数实现:初始化目标窗口和动画对象
 * @param target 需添加上滑关闭的目标窗口
 * @param parent 父对象(内存管理)
 */
SlideCloseHelper::SlideCloseHelper(QWidget *target, QObject *parent)
    : QObject(parent), m_target(target) {
    if (m_target) {
        // 开启触摸事件支持(移动端)
        m_target->setAttribute(Qt::WA_AcceptTouchEvents, true);
        // 安装事件过滤器,拦截目标窗口事件
        m_target->installEventFilter(this);
        // 记录窗口初始位置(用于回弹)
        m_originPos = m_target->pos();
        // 初始化计时对象
        m_timer = new QElapsedTimer();
        // 初始化回弹动画
        m_anim = new QPropertyAnimation(m_target, "pos", this);
        m_anim->setDuration(200);
        m_anim->setEasingCurve(QEasingCurve::InOutCubic);
    }
}

/**
 * @brief 设置滑动距离阈值
 * @param threshold 阈值(像素)
 */
void SlideCloseHelper::setSlideThreshold(int threshold) {
    m_slideThreshold = threshold;
}

/**
 * @brief 设置滑动时间阈值
 * @param threshold 阈值(毫秒)
 */
void SlideCloseHelper::setTimeThreshold(int threshold) {
    m_timeThreshold = threshold;
}

/**
 * @brief 设置滑动跟随比例
 * @param ratio 比例(值越大,滑动跟随越慢)
 */
void SlideCloseHelper::setFollowRatio(int ratio) {
    m_followRatio = ratio;
}

/**
 * @brief 事件过滤器实现:拦截并处理目标窗口的触摸/鼠标事件
 * @param watched 被监控的对象
 * @param event 事件对象
 * @return bool 是否拦截事件(false=不拦截,交给目标窗口处理)
 */
bool SlideCloseHelper::eventFilter(QObject *watched, QEvent *event) {
    // 非目标窗口/目标窗口为空,直接返回false
    if (watched != m_target || !m_target) return false;

    // 处理触摸事件(移动端)
    if (event->type() == QEvent::TouchBegin) {
        QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
        m_startPos = touchEvent->touchPoints().first().pos().toPoint();
        m_timer->restart(); // 重启计时器
        m_isPress = true;
        if (m_anim) m_anim->stop(); // 停止正在进行的动画
    } else if (event->type() == QEvent::TouchUpdate) {
        if (m_isPress) {
            QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
            QPoint currentPos = touchEvent->touchPoints().first().pos().toPoint();
            int dy = currentPos.y() - m_startPos.y();
            moveTargetWithRatio(dy); // 触摸滑动跟随
        }
    } else if (event->type() == QEvent::TouchEnd) {
        if (!m_isPress) return false;
        QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
        QPoint endPos = touchEvent->touchPoints().first().pos().toPoint();
        checkSlide(endPos); // 判断是否触发关闭
        m_isPress = false;
    }

    // 处理鼠标事件(桌面端)
    else if (event->type() == QEvent::MouseButtonPress) {
        QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
        if (mouseEvent->button() == Qt::LeftButton) {
            m_startPos = mouseEvent->pos();
            m_timer->restart(); // 重启计时器
            m_isPress = true;
            if (m_anim) m_anim->stop(); // 停止正在进行的动画
        }
    } else if (event->type() == QEvent::MouseMove) {
        if (m_isPress) {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
            int dy = mouseEvent->pos().y() - m_startPos.y();
            moveTargetWithRatio(dy); // 鼠标滑动跟随
        }
    } else if (event->type() == QEvent::MouseButtonRelease) {
        QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
        if (mouseEvent->button() == Qt::LeftButton && m_isPress) {
            checkSlide(mouseEvent->pos()); // 判断是否触发关闭
            m_isPress = false;
        }
    }

    // 不拦截事件,交给目标窗口的默认处理逻辑
    return QObject::eventFilter(watched, event);
}

/**
 * @brief 滑动跟随逻辑:限制垂直滑动范围,更新窗口位置
 * @param dy y轴偏移量
 */
void SlideCloseHelper::moveTargetWithRatio(int dy) {
    if (!m_target) return;
    // 计算新的y坐标(按比例跟随,避免滑动过快)
    int newY = m_target->y() + dy / m_followRatio;
    // 限制y轴范围:避免滑出屏幕过多
    newY = qBound(m_originPos.y() - 50, newY, m_originPos.y() + 100);
    // 仅更新y轴(固定x轴,垂直滑动)
    m_target->move(m_target->x(), newY);
}

/**
 * @brief 滑动判断逻辑:满足阈值则关闭窗口,否则回弹
 * @param endPos 滑动结束位置
 */
void SlideCloseHelper::checkSlide(const QPoint &endPos) {
    if (!m_target || !m_timer) return;

    // 计算y轴偏移量(dy<0=上滑)和滑动耗时
    int dy = endPos.y() - m_startPos.y();
    int elapsed = m_timer->elapsed();

    // 满足上滑关闭条件:距离阈值 + 时间阈值
    if (dy < -m_slideThreshold && elapsed < m_timeThreshold) {
        // 创建透明度动画,关闭窗口(更顺滑)
        QPropertyAnimation *opacityAnim = new QPropertyAnimation(m_target, "windowOpacity");
        opacityAnim->setDuration(200);
        opacityAnim->setStartValue(1.0); // 动画开始时,窗口透明度为1.0(完全不透明)
        opacityAnim->setEndValue(0.0);  // 动画结束时,窗口透明度为0.0(完全透明)
        // 动画结束后关闭窗口+释放动画对象
        connect(opacityAnim, &QPropertyAnimation::finished, m_target, &QWidget::close);
        connect(opacityAnim, &QPropertyAnimation::finished, opacityAnim, &QPropertyAnimation::deleteLater);
        opacityAnim->start();
    } else {
        // 不满足条件:回弹到初始位置
        if (m_anim) {
            m_anim->setStartValue(m_target->pos());
            m_anim->setEndValue(m_originPos);
            m_anim->start();
        }
    }
}

main.cpp

cpp 复制代码
#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QVBoxLayout>
#include "SlideCloseHelper.h"

// 示例1:给普通QWidget添加上滑关闭
void createNormalWidget() {
    QWidget *w = new QWidget();
    w->setWindowTitle("普通窗口-上滑关闭");
    w->setFixedSize(300, 500);
    w->move(200, 100);
    // 一行代码添加上滑关闭功能(核心复用逻辑)
    new SlideCloseHelper(w);
    w->show();
}

// 示例2:给自定义内容窗口添加上滑关闭(自定义阈值)
void createCustomContentWidget() {
    QWidget *w = new QWidget();
    w->setWindowTitle("自定义内容窗口-上滑关闭");
    w->setFixedSize(300, 500);
    w->move(600, 100);

    // 自定义窗口内容
    QVBoxLayout *layout = new QVBoxLayout(w);
    for (int i = 0; i < 10; i++) {
        layout->addWidget(new QLabel(QString("列表项 %1").arg(i+1), w));
    }
    w->setLayout(layout);

    // 复用Helper,自定义阈值(更严格的关闭条件)
    SlideCloseHelper *helper = new SlideCloseHelper(w);
    helper->setSlideThreshold(100); // 距离阈值改为100px
    helper->setTimeThreshold(250);  // 时间阈值改为250ms

    w->show();
}

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    // 给两个不同窗口复用"上滑关闭"功能,无需修改窗口类代码
    createNormalWidget();
    createCustomContentWidget();

    return a.exec();
}

场景:滑动显示 / 隐藏顶部栏、侧边栏

slidebarwidget.h

cpp 复制代码
#ifndef SLIDEBARWIDGET_H
#define SLIDEBARWIDGET_H

#include <QWidget>
#include <QEvent>
#include <QPropertyAnimation>
#include <QMouseEvent>
#include <QTouchEvent>
#include <QShowEvent>
#include <QResizeEvent>

// 滑动阈值:滑动距离超过该值才触发显示/隐藏(避免误触)
#define SLIDE_THRESHOLD 50
// 动画时长(毫秒):控制过渡动画的顺滑度
#define ANIM_DURATION 300

/**
 * @class SlideBarWidget
 * @brief 滑动交互控件类
 * @details 支持触摸/鼠标滑动操作,实现全屏高度的顶部栏(隐藏在顶部外)、侧边栏(隐藏在左侧外)的动画显示/隐藏
 *          纯代码创建控件,无UI文件依赖,顶部栏高度与窗口高度保持一致
 */
class SlideBarWidget : public QWidget
{
    Q_OBJECT

public:
    /**
     * @brief 构造函数
     * @param parent 父窗口指针(用于内存管理)
     */
    explicit SlideBarWidget(QWidget *parent = nullptr);
    
    /**
     * @brief 析构函数
     * @details 释放动态创建的控件、动画对象,避免内存泄漏
     */
    ~SlideBarWidget() override;

protected:
    /**
     * @brief 事件分发函数(重写)
     * @details 处理触摸事件(触屏设备),包括触摸开始、更新、结束
     * @param event 事件对象指针
     * @return bool 事件是否被处理(true=已处理,false=交给父类处理)
     */
    bool event(QEvent *event) override;

    /**
     * @brief 鼠标按下事件(重写)
     * @details 电脑测试用,记录滑动起始位置,标记开始滑动状态
     * @param event 鼠标事件对象
     */
    void mousePressEvent(QMouseEvent *event) override;

    /**
     * @brief 鼠标移动事件(重写)
     * @details 电脑测试用,计算滑动距离,判断是否触发显示/隐藏逻辑
     * @param event 鼠标事件对象
     */
    void mouseMoveEvent(QMouseEvent *event) override;

    /**
     * @brief 鼠标释放事件(重写)
     * @details 电脑测试用,重置滑动状态
     * @param event 鼠标事件对象
     */
    void mouseReleaseEvent(QMouseEvent *event) override;

    /**
     * @brief 窗口大小变化事件(重写)
     * @details 保证顶部栏宽高、侧边栏高度跟随窗口大小自适应
     * @param event 大小变化事件对象
     */
    void resizeEvent(QResizeEvent *event) override;

    /**
     * @brief 窗口显示事件(重写)
     * @details 窗口首次显示时,同步顶部栏高度为窗口真实高度,并修正初始隐藏位置
     * @param event 显示事件对象
     */
    void showEvent(QShowEvent *event) override;

private slots:
    /**
     * @brief 显示顶部栏
     * @details 通过属性动画将顶部栏从顶部外(隐藏位置)移动到窗口顶部(显示位置)
     */
    void showTopBar();

    /**
     * @brief 隐藏顶部栏
     * @details 通过属性动画将顶部栏从窗口顶部(显示位置)移动到顶部外(隐藏位置)
     */
    void hideTopBar();

    /**
     * @brief 显示侧边栏
     * @details 通过属性动画将侧边栏从左侧外(隐藏位置)移动到窗口左侧(显示位置)
     */
    void showSideBar();

    /**
     * @brief 隐藏侧边栏
     * @details 通过属性动画将侧边栏从窗口左侧(显示位置)移动到左侧外(隐藏位置)
     */
    void hideSideBar();

private:
    // 控件对象(纯代码创建,替代UI文件)
    QWidget *m_topBar;    // 顶部栏控件(全屏高度)
    QWidget *m_sideBar;   // 侧边栏控件

    // 滑动状态变量
    QPoint m_startPos;    // 滑动起始位置(触摸/鼠标按下时的坐标)
    bool m_isSliding;     // 是否正在滑动(防止重复触发)

    // 控件尺寸配置
    int m_sideBarWidth;   // 侧边栏宽度(固定值)

    // 动画对象
    QPropertyAnimation *m_topBarAnim;  // 顶部栏动画
    QPropertyAnimation *m_sideBarAnim; // 侧边栏动画

    /**
     * @brief 初始化控件
     * @details 创建顶部栏、侧边栏,设置样式、初始位置、大小等属性
     */
    void initWidgets();

    /**
     * @brief 初始化动画
     * @details 创建属性动画,设置默认时长、缓动曲线(让动画更顺滑)
     */
    void initAnimations();
};

#endif // SLIDEBARWIDGET_H

slidebarwidget.cpp

cpp 复制代码
#include "slidebarwidget.h"

SlideBarWidget::SlideBarWidget(QWidget *parent)
    : QWidget(parent)
    , m_topBar(nullptr)          // 初始化控件指针
    , m_sideBar(nullptr)
    , m_isSliding(false)          // 初始无滑动
    , m_sideBarWidth(200)        // 侧边栏宽度200px
    , m_topBarAnim(nullptr)
    , m_sideBarAnim(nullptr)
{
    // 开启鼠标追踪(无需按下鼠标,也能捕获鼠标移动事件)
    this->setMouseTracking(true);

    // 初始化控件和动画
    initWidgets();
    initAnimations();
}

SlideBarWidget::~SlideBarWidget()
{
    // 释放动态创建的对象,避免内存泄漏
    delete m_topBarAnim;
    delete m_sideBarAnim;
    delete m_topBar;
    delete m_sideBar;
}

void SlideBarWidget::initWidgets()
{
    // ========== 创建顶部栏 ==========
    m_topBar = new QWidget(this); // 父对象设为当前窗口,自动管理内存
    // 初始化时仅设置宽度为窗口初始宽度,高度延迟到showEvent中同步
    m_topBar->setFixedWidth(this->width());
    // 初始位置:临时隐藏在窗口顶部外(后续在showEvent中修正为真实高度)
    m_topBar->move(0, -100);
    // 设置样式:蓝色背景、白色文字、20px字体
    m_topBar->setStyleSheet(R"(
        QWidget {
            background-color: #3498db;
            color: white;
            font-size: 20px;
        }
    )");

    // ========== 创建侧边栏 ==========
    m_sideBar = new QWidget(this);
    m_sideBar->setFixedWidth(m_sideBarWidth); // 固定宽度
    m_sideBar->setFixedHeight(this->height());// 高度跟随窗口
    // 初始位置:隐藏在窗口左侧外(x轴=-宽度)
    m_sideBar->move(-m_sideBarWidth, 0);
    // 设置样式:绿色背景、白色文字、20px字体
    m_sideBar->setStyleSheet(R"(
        QWidget {
            background-color: #2ecc71;
            color: white;
            font-size: 20px;
        }
    )");
    // 侧边栏隐藏时忽略鼠标事件(避免遮挡底层控件)
    m_sideBar->setAttribute(Qt::WA_TransparentForMouseEvents);
}

void SlideBarWidget::initAnimations()
{
    // ========== 顶部栏动画 ==========
    m_topBarAnim = new QPropertyAnimation(m_topBar, "pos", this);
    m_topBarAnim->setDuration(ANIM_DURATION); // 动画时长300ms
    // 缓动曲线:OutCubic(先快后慢,动画更自然)
    m_topBarAnim->setEasingCurve(QEasingCurve::OutCubic);

    // ========== 侧边栏动画 ==========
    m_sideBarAnim = new QPropertyAnimation(m_sideBar, "pos", this);
    m_sideBarAnim->setDuration(ANIM_DURATION);
    m_sideBarAnim->setEasingCurve(QEasingCurve::OutCubic);
}

bool SlideBarWidget::event(QEvent *event)
{
    // 处理触摸开始事件(触屏设备)
    if (event->type() == QEvent::TouchBegin) {
        QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
        // 记录第一个触摸点的起始位置
        m_startPos = touchEvent->touchPoints().first().pos().toPoint();
        m_isSliding = true; // 标记开始滑动
        return true; // 事件已处理,不再传递
    }
    // 处理触摸更新事件(触屏滑动中)
    else if (event->type() == QEvent::TouchUpdate && m_isSliding) {
        QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
        QPoint currentPos = touchEvent->touchPoints().first().pos().toPoint();
        int deltaY = currentPos.y() - m_startPos.y(); // 上下滑动距离
        int deltaX = currentPos.x() - m_startPos.x(); // 左右滑动距离

        // 优先处理左右滑动(侧边栏):滑动距离超过阈值、横向滑动>纵向、起始位置在窗口左半区
        if (qAbs(deltaX) > SLIDE_THRESHOLD && qAbs(deltaX) > qAbs(deltaY) && (m_startPos.x() < this->width()/2)) {
            if (deltaX > 0) { // 向右滑:显示侧边栏
                showSideBar();
            } else { // 向左滑:隐藏侧边栏
                hideSideBar();
            }
            m_isSliding = false; // 重置滑动状态,避免重复触发
        }
        // 处理上下滑动(顶部栏):滑动距离超过阈值
        else if (qAbs(deltaY) > SLIDE_THRESHOLD) {
            if (deltaY > 0) { // 向下滑:显示顶部栏
                showTopBar();
            } else { // 向上滑:隐藏顶部栏
                hideTopBar();
            }
            m_isSliding = false;
        }
        return true;
    }
    // 处理触摸结束事件
    else if (event->type() == QEvent::TouchEnd) {
        m_isSliding = false; // 重置滑动状态
        return true;
    }

    // 未处理的事件交给父类处理
    return QWidget::event(event);
}

void SlideBarWidget::mousePressEvent(QMouseEvent *event)
{
    // 记录鼠标按下的起始位置(电脑测试用)
    m_startPos = event->pos();
    m_isSliding = true;
    // 调用父类函数,保留默认行为(如焦点变化)
    QWidget::mousePressEvent(event);
}

void SlideBarWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (!m_isSliding) { // 未开始滑动,直接返回
        QWidget::mouseMoveEvent(event);
        return;
    }

    QPoint currentPos = event->pos();
    int deltaY = currentPos.y() - m_startPos.y();
    int deltaX = currentPos.x() - m_startPos.x();

    // 逻辑同触摸事件(电脑测试用)
    if (qAbs(deltaX) > SLIDE_THRESHOLD && qAbs(deltaX) > qAbs(deltaY) && (m_startPos.x() < this->width()/2)) {
        if (deltaX > 0) {
            showSideBar();
        } else {
            hideSideBar();
        }
        m_isSliding = false;
    }
    else if (qAbs(deltaY) > SLIDE_THRESHOLD) {
        if (deltaY > 0) {
            showTopBar();
        } else {
            hideTopBar();
        }
        m_isSliding = false;
    }

    QWidget::mouseMoveEvent(event);
}

void SlideBarWidget::mouseReleaseEvent(QMouseEvent *event)
{
    m_isSliding = false; // 重置滑动状态
    QWidget::mouseReleaseEvent(event);
}

void SlideBarWidget::showEvent(QShowEvent *event)
{
    if (m_topBar) {
        // 窗口首次显示时,同步顶部栏高度为窗口真实高度
        m_topBar->setFixedHeight(this->height());
        // 修正初始隐藏位置:y轴=-窗口高度(完全隐藏在顶部外)
        m_topBar->move(0, -this->height());
    }
    QWidget::showEvent(event);
}

void SlideBarWidget::resizeEvent(QResizeEvent *event)
{
    // 窗口大小变化时,同步顶部栏宽高为窗口尺寸
    if (m_topBar) {
        m_topBar->setFixedWidth(this->width());
        m_topBar->setFixedHeight(this->height());
        // 修正隐藏位置:保持当前显示/隐藏状态,适配新高度
        int currentY = m_topBar->y();
        if (currentY < 0) {
            m_topBar->move(0, -this->height());
        }
    }
    // 同步侧边栏高度为窗口高度
    if (m_sideBar) {
        m_sideBar->setFixedHeight(this->height());
    }
    QWidget::resizeEvent(event);
}

void SlideBarWidget::showTopBar()
{
    // 动画起始位置:顶部外(隐藏,使用实时窗口高度)
    m_topBarAnim->setStartValue(QPoint(0, -this->height()));
    // 动画结束位置:窗口顶部(显示)
    m_topBarAnim->setEndValue(QPoint(0, 0));
    m_topBarAnim->start(); // 启动动画
}

void SlideBarWidget::hideTopBar()
{
    // 动画起始位置:窗口顶部(显示)
    m_topBarAnim->setStartValue(QPoint(0, 0));
    // 动画结束位置:顶部外(隐藏,使用实时窗口高度)
    m_topBarAnim->setEndValue(QPoint(0, -this->height()));
    m_topBarAnim->start();
}

void SlideBarWidget::showSideBar()
{
    // 取消鼠标事件透明(显示后可交互)
    m_sideBar->setAttribute(Qt::WA_TransparentForMouseEvents, false);
    // 动画起始位置:左侧外(隐藏)
    m_sideBarAnim->setStartValue(QPoint(-m_sideBarWidth, 0));
    // 动画结束位置:窗口左侧(显示)
    m_sideBarAnim->setEndValue(QPoint(0, 0));
    m_sideBarAnim->start();
}

void SlideBarWidget::hideSideBar()
{
    // 动画起始位置:窗口左侧(显示)
    m_sideBarAnim->setStartValue(QPoint(0, 0));
    // 动画结束位置:左侧外(隐藏)
    m_sideBarAnim->setEndValue(QPoint(-m_sideBarWidth, 0));
    m_sideBarAnim->start();
    // 隐藏后设置鼠标事件透明(不遮挡底层控件)
    m_sideBar->setAttribute(Qt::WA_TransparentForMouseEvents, true);
}

main.cpp

cpp 复制代码
#include <QApplication>
#include "slidebarwidget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    
    // 创建滑动控件窗口
    SlideBarWidget w;
    w.resize(800, 600); // 设置窗口大小
    w.setWindowTitle("滑动显示/隐藏栏示例");
    w.show();

    return a.exec();
}

场景:窗口拖拽停靠(仿桌面软件窗口拖拽到边缘停靠)

DockWidget.h

cpp 复制代码
#ifndef DOCKWIDGET_H
#define DOCKWIDGET_H

// 必要头文件(仅包含类声明所需的核心头文件)
#include <QWidget>
#include <QPropertyAnimation>

// 前置声明(避免包含过多头文件,减少依赖)
class QMouseEvent;
class QRect;
class QPoint;
class QScreen;

/**
 * @brief DockWidget类:实现窗口拖拽停靠功能
 * @details 支持鼠标拖拽窗口到屏幕左/右/上/下边缘,触发自动停靠(占对应边缘一半屏幕区域);
 *          停靠时带顺滑动画,拖拽过程中实时检测边缘距离,释放鼠标后执行停靠逻辑
 * @inherits QWidget 继承自Qt窗口基类,具备窗口基础能力
 * @note 核心依赖QPropertyAnimation实现停靠动画,QMouseEvent处理拖拽交互
 */
class DockWidget : public QWidget {
    Q_OBJECT // Qt元对象系统必需宏,支持属性动画/信号槽

public:
    /**
     * @brief 构造函数:初始化窗口属性和停靠动画对象
     * @param parent 父窗口指针(默认nullptr,作为顶级窗口)
     * @return 无返回值
     */
    explicit DockWidget(QWidget *parent = nullptr);

protected:
    /**
     * @brief 重写鼠标按下事件:记录拖拽初始状态
     * @param event 鼠标事件对象,包含按下位置、按键类型等信息
     * @return void
     * @note 仅处理左键按下,记录鼠标相对窗口的偏移量,停止正在进行的停靠动画
     */
    void mousePressEvent(QMouseEvent *event) override;

    /**
     * @brief 重写鼠标移动事件:实现窗口拖拽+实时检测停靠边缘
     * @param event 鼠标事件对象,包含移动后的全局/相对位置信息
     * @return void
     * @note 左键按下时跟随鼠标拖拽窗口,同时调用checkDock检测是否靠近屏幕边缘
     */
    void mouseMoveEvent(QMouseEvent *event) override;

    /**
     * @brief 重写鼠标释放事件:执行停靠动画或保持当前位置
     * @param event 鼠标事件对象,包含释放位置、按键类型等信息
     * @return void
     * @note 左键释放时,若检测到停靠区域则执行动画停靠,否则保持当前位置
     */
    void mouseReleaseEvent(QMouseEvent *event) override;

private:
    /**
     * @brief 检测窗口是否靠近屏幕边缘,判断是否触发停靠
     * @param pos 窗口当前左上角的坐标(屏幕坐标系)
     * @return void
     * @note 检测左/右/上/下四个边缘,触发距离为20像素;
     *       停靠时窗口自动占对应边缘的一半屏幕区域(如左边缘占屏幕左半区)
     */
    void checkDock(const QPoint &pos);

private:
    QPropertyAnimation *m_anim;       // 停靠动画对象(绑定geometry属性)
    bool m_isPress = false;           // 鼠标是否左键按下的标记
    QPoint m_mouseOffset;             // 鼠标相对窗口左上角的偏移量(避免拖拽瞬移)
    bool m_isDock = false;            // 是否检测到停靠区域的标记
    QRect m_dockRect;                 // 停靠目标区域(屏幕坐标系下的矩形)
};

#endif // DOCKWIDGET_H

DockWidget.cpp

cpp 复制代码
#include "DockWidget.h"
// 实现中需要的头文件(集中引入,不污染头文件)
#include <QMouseEvent>
#include <QScreen>
#include <QApplication>
#include <QEasingCurve>

/**
 * @brief 构造函数实现:初始化窗口和动画对象
 */
DockWidget::DockWidget(QWidget *parent) : QWidget(parent) {
    // 设置窗口基础属性
    setWindowTitle("窗口拖拽停靠示例");
    resize(400, 300);          // 初始窗口大小:宽400px,高300px
    move(200, 200);            // 初始窗口位置:屏幕坐标(200,200)

    /**
     * @brief 创建停靠动画对象:绑定窗口的geometry属性
     * @param target 动画目标:当前窗口(this)
     * @param propertyName 动画属性:"geometry"(窗口位置+大小,对应setGeometry())
     * @param parent 动画父对象:当前窗口(Qt自动管理内存)
     */
    m_anim = new QPropertyAnimation(this, "geometry", this);
    m_anim->setDuration(250);  // 动画时长250ms,保证停靠顺滑不卡顿
    /**
     * @brief 设置缓动曲线:OutQuad(先快后慢)
     * @note OutQuad曲线让动画开头快、结尾慢,贴近原生系统的停靠动画体验
     */
    m_anim->setEasingCurve(QEasingCurve::OutQuad);
}

/**
 * @brief 鼠标按下事件实现:记录拖拽初始状态
 */
void DockWidget::mousePressEvent(QMouseEvent *event) {
    // 仅处理鼠标左键按下
    if (event->button() == Qt::LeftButton) {
        m_isPress = true;                  // 标记鼠标已按下
        /**
         * @note event->pos() 是鼠标相对窗口左上角的坐标(窗口坐标系)
         *       记录该偏移量,避免拖拽时窗口瞬移到鼠标位置
         */
        m_mouseOffset = event->pos();
        m_anim->stop();                    // 停止正在进行的停靠动画,优先响应手动拖拽
    }
    // 调用父类事件,保证基础鼠标逻辑不丢失
    QWidget::mousePressEvent(event);
}

/**
 * @brief 鼠标移动事件实现:拖拽窗口+实时检测停靠
 */
void DockWidget::mouseMoveEvent(QMouseEvent *event) {
    if (m_isPress) { // 仅处理左键按下后的移动
        /**
         * 计算窗口新位置:
         * event->globalPos() → 鼠标全局坐标(屏幕坐标系)
         * m_mouseOffset → 鼠标相对窗口的偏移量
         * 新位置 = 鼠标全局位置 - 偏移量 → 保证鼠标始终在拖拽时的相对位置
         */
        QPoint newPos = event->globalPos() - m_mouseOffset;
        move(newPos); // 更新窗口位置,实现跟随鼠标拖拽

        // 实时检测当前位置是否靠近屏幕边缘,判断是否触发停靠
        checkDock(newPos);
    }
    // 调用父类事件,保证基础鼠标逻辑不丢失
    QWidget::mouseMoveEvent(event);
}

/**
 * @brief 鼠标释放事件实现:执行停靠动画或保持当前位置
 */
void DockWidget::mouseReleaseEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton && m_isPress) {
        m_isPress = false; // 取消鼠标按下标记

        // 如果检测到停靠区域,执行停靠动画
        if (m_isDock) {
            m_anim->setStartValue(geometry()); // 动画起始值:窗口当前的位置+大小
            m_anim->setEndValue(m_dockRect);   // 动画结束值:停靠目标区域
            m_anim->start();                   // 启动停靠动画
        }
        m_isDock = false; // 重置停靠标记,准备下一次拖拽检测
    }
    // 调用父类事件,保证基础鼠标逻辑不丢失
    QWidget::mouseReleaseEvent(event);
}

/**
 * @brief 停靠检测逻辑实现:判断是否靠近屏幕边缘,设置停靠目标区域
 */
void DockWidget::checkDock(const QPoint &pos) {
    // 获取屏幕可用区域(排除任务栏等系统控件占用的区域)
    QRect screenRect = QApplication::primaryScreen()->availableGeometry();
    int dockMargin = 20; // 停靠触发距离:窗口边缘距离屏幕边缘<20像素则触发

    // 1. 左边缘停靠:窗口左边界(pos.x())距离屏幕左边界<20像素
    if (pos.x() < dockMargin) {
        m_isDock = true;
        // 停靠目标区域:屏幕左半区(x=0, y=0,宽=屏幕一半,高=屏幕全高)
        m_dockRect = QRect(0, 0, screenRect.width()/2, screenRect.height());
    }
    // 2. 右边缘停靠:窗口右边界(pos.x()+窗口宽度)距离屏幕右边界<20像素
    else if (pos.x() + width() > screenRect.width() - dockMargin) {
        m_isDock = true;
        // 停靠目标区域:屏幕右半区(x=屏幕一半,y=0,宽=屏幕一半,高=屏幕全高)
        m_dockRect = QRect(screenRect.width()/2, 0, screenRect.width()/2, screenRect.height());
    }
    // 3. 上边缘停靠:窗口上边界(pos.y())距离屏幕上边界<20像素
    else if (pos.y() < dockMargin) {
        m_isDock = true;
        // 停靠目标区域:屏幕上半区(x=0, y=0,宽=屏幕全宽,高=屏幕一半)
        m_dockRect = QRect(0, 0, screenRect.width(), screenRect.height()/2);
    }
    // 4. 下边缘停靠:窗口下边界(pos.y()+窗口高度)距离屏幕下边界<20像素
    else if (pos.y() + height() > screenRect.height() - dockMargin) {
        m_isDock = true;
        // 停靠目标区域:屏幕下半区(x=0, y=屏幕一半,宽=屏幕全宽,高=屏幕一半)
        m_dockRect = QRect(0, screenRect.height()/2, screenRect.width(), screenRect.height()/2);
    }
    // 未靠近任何边缘,取消停靠标记
    else {
        m_isDock = false;
    }
}

main.cpp

cpp 复制代码
#include <QApplication>
#include "DockWidget.h"

int main(int argc, char *argv[]) {
    // 创建Qt应用对象,管理应用生命周期和事件循环
    QApplication a(argc, argv);

    // 创建停靠窗口对象并显示
    DockWidget w;
    w.show(); // Qt窗口默认隐藏,需调用show()显示

    // 启动应用事件循环,阻塞直到应用退出
    return a.exec();
}

总结

  • QPoint 是Qt处理坐标的核心基础类,支持整数坐标的直接操作和向量运算,曼哈顿长度是快速判断位置偏移的高效方式;
  • QEvent 是所有事件的基类,Qt通过事件对象分发交互事件,可重写专用事件函数(如mousePressEvent)或自定义事件实现交互逻辑;
  • QPropertyAnimation 核心是动画QObject的属性(需有setter方法),结合鼠标事件可实现滑动、上滑关闭、窗口停靠等常见UI交互,缓动曲线能优化动画的原生体验。
相关推荐
郝学胜-神的一滴4 小时前
使用EBO绘制图形:解锁高效渲染与内存节省之道
c++·qt·游戏·设计模式·系统架构·图形渲染
枫叶丹45 小时前
【Qt开发】Qt事件(一)
c语言·开发语言·数据库·c++·qt·microsoft
刺客xs15 小时前
Qt------信号槽,属性,对象树
开发语言·qt·命令模式
zxb@hny17 小时前
配置beyondcompare合并git操作
qt
liangshanbo121518 小时前
深入理解 Model Context Protocol (MCP):从原理到实践
开发语言·qt·microsoft
27399202919 小时前
QT5使用QFtp
开发语言·qt
怪力左手19 小时前
qt qspinbox editingfinished事件问题
开发语言·qt
我喜欢就喜欢19 小时前
2025技术成长复盘:解决问题的365天
c++·qt
神仙别闹19 小时前
基于QT(C++)+MySQL实现(窗体)学生信息管理系统
c++·qt·mysql