Qt无边框界面添加鼠标事件

在Qt中实现无边框窗口的鼠标事件处理,主要涉及窗口拖动和调整大小功能。以下是分步实现的代码示例:

1. 创建无边框窗口

首先,创建一个继承自QWidget的自定义窗口类,并设置无边框标志:

#include <QWidget>
#include <QMouseEvent>

class FramelessWindow : public QWidget {
    Q_OBJECT

public:
    FramelessWindow(QWidget *parent = nullptr) : QWidget(parent) {
        setWindowFlags(Qt::FramelessWindowHint);
        setMouseTracking(true); // 启用鼠标追踪
    }
};

2. 处理窗口拖动

在标题栏区域按下鼠标左键时拖动窗口:

protected:
    void mousePressEvent(QMouseEvent *event) override {
        if (event->button() == Qt::LeftButton) {
            if (isInDragRegion(event->pos())) {
                m_dragging = true;
                m_dragStartPos = event->globalPos();
                m_windowPos = pos();
            }
        }
        QWidget::mousePressEvent(event);
    }

    void mouseMoveEvent(QMouseEvent *event) override {
        if (m_dragging) {
            QPoint delta = event->globalPos() - m_dragStartPos;
            move(m_windowPos + delta);
        }
        QWidget::mouseMoveEvent(event);
    }

    void mouseReleaseEvent(QMouseEvent *event) override {
        if (event->button() == Qt::LeftButton) {
            m_dragging = false;
        }
        QWidget::mouseReleaseEvent(event);
    }

private:
    bool m_dragging = false;
    QPoint m_dragStartPos;
    QPoint m_windowPos;

    bool isInDragRegion(const QPoint &pos) const {
        // 假设标题栏高度为30像素
        return pos.y() < 30;
    }

3. 实现调整窗口大小

检测边缘并调整窗口大小:

protected:
    enum ResizeDirection { None, Left, Right, Top, Bottom, TopLeft, TopRight, BottomLeft, BottomRight };
    ResizeDirection m_resizeDir = None;

    void checkResizeRegion(const QPoint &pos) {
        const int borderWidth = 5;
        QRect rect = this->rect();
        bool left = pos.x() <= borderWidth;
        bool right = pos.x() >= rect.width() - borderWidth;
        bool top = pos.y() <= borderWidth;
        bool bottom = pos.y() >= rect.height() - borderWidth;

        if (left && top) m_resizeDir = TopLeft;
        else if (right && top) m_resizeDir = TopRight;
        else if (left && bottom) m_resizeDir = BottomLeft;
        else if (right && bottom) m_resizeDir = BottomRight;
        else if (left) m_resizeDir = Left;
        else if (right) m_resizeDir = Right;
        else if (top) m_resizeDir = Top;
        else if (bottom) m_resizeDir = Bottom;
        else m_resizeDir = None;
    }

    void mousePressEvent(QMouseEvent *event) override {
        // ... 原有拖动处理 ...
        checkResizeRegion(event->pos());
        if (m_resizeDir != None) {
            m_resizing = true;
            m_resizeStartPos = event->globalPos();
            m_initialGeometry = geometry();
        }
    }

    void handleResize(const QPoint &globalPos) {
        QRect newGeometry = m_initialGeometry;
        QPoint delta = globalPos - m_resizeStartPos;

        switch (m_resizeDir) {
        case Left:
            newGeometry.setLeft(newGeometry.left() + delta.x());
            break;
        case Right:
            newGeometry.setRight(newGeometry.right() + delta.x());
            break;
        case Top:
            newGeometry.setTop(newGeometry.top() + delta.y());
            break;
        case Bottom:
            newGeometry.setBottom(newGeometry.bottom() + delta.y());
            break;
        case TopLeft:
            newGeometry.setTopLeft(newGeometry.topLeft() + delta);
            break;
        case TopRight:
            newGeometry.setTopRight(newGeometry.topRight() + delta);
            break;
        case BottomLeft:
            newGeometry.setBottomLeft(newGeometry.bottomLeft() + delta);
            break;
        case BottomRight:
            newGeometry.setBottomRight(newGeometry.bottomRight() + delta);
            break;
        }

        // 限制最小尺寸
        newGeometry = newGeometry.normalized().adjusted(0, 0, 1, 1);
        if (newGeometry.width() < minimumWidth() || newGeometry.height() < minimumHeight())
            return;

        setGeometry(newGeometry);
    }

4. 更新光标形状

根据鼠标位置调整光标:

    void updateCursorShape(const QPoint &pos) {
        checkResizeRegion(pos);
        switch (m_resizeDir) {
        case Left:
        case Right:
            setCursor(Qt::SizeHorCursor);
            break;
        case Top:
        case Bottom:
            setCursor(Qt::SizeVerCursor);
            break;
        case TopLeft:
        case BottomRight:
            setCursor(Qt::SizeFDiagCursor);
            break;
        case TopRight:
        case BottomLeft:
            setCursor(Qt::SizeBDiagCursor);
            break;
        default:
            unsetCursor();
        }
    }

5. 双击最大化

双击标题栏切换最大化:

    void mouseDoubleClickEvent(QMouseEvent *event) override {
        if (event->button() == Qt::LeftButton && isInDragRegion(event->pos())) {
            toggleMaximize();
        }
        QWidget::mouseDoubleClickEvent(event);
    }

    void toggleMaximize() {
        isMaximized() ? showNormal() : showMaximized();
    }

完整示例代码

整合以上代码,完整的无边框窗口类如下:

#include <QWidget>
#include <QMouseEvent>
#include <QCursor>

class FramelessWindow : public QWidget {
    Q_OBJECT

public:
    FramelessWindow(QWidget *parent = nullptr) : QWidget(parent) {
        setWindowFlags(Qt::FramelessWindowHint);
        setMouseTracking(true);
    }

protected:
    void mousePressEvent(QMouseEvent *event) override {
        if (event->button() == Qt::LeftButton) {
            if (isInDragRegion(event->pos())) {
                m_dragging = true;
                m_dragStartPos = event->globalPos();
                m_windowPos = pos();
            } else {
                checkResizeRegion(event->pos());
                if (m_resizeDir != None) {
                    m_resizing = true;
                    m_resizeStartPos = event->globalPos();
                    m_initialGeometry = geometry();
                }
            }
        }
        QWidget::mousePressEvent(event);
    }

    void mouseMoveEvent(QMouseEvent *event) override {
        if (!m_resizing && !m_dragging)
            updateCursorShape(event->pos());

        if (m_dragging) {
            QPoint delta = event->globalPos() - m_dragStartPos;
            move(m_windowPos + delta);
        } else if (m_resizing) {
            handleResize(event->globalPos());
        }
        QWidget::mouseMoveEvent(event);
    }

    void mouseReleaseEvent(QMouseEvent *event) override {
        if (event->button() == Qt::LeftButton) {
            m_dragging = false;
            m_resizing = false;
            m_resizeDir = None;
            unsetCursor();
        }
        QWidget::mouseReleaseEvent(event);
    }

    void mouseDoubleClickEvent(QMouseEvent *event) override {
        if (event->button() == Qt::LeftButton && isInDragRegion(event->pos())) {
            toggleMaximize();
        }
        QWidget::mouseDoubleClickEvent(event);
    }

private:
    bool m_dragging = false;
    QPoint m_dragStartPos;
    QPoint m_windowPos;

    bool m_resizing = false;
    QPoint m_resizeStartPos;
    QRect m_initialGeometry;

    enum ResizeDirection {
        None, Left, Right, Top, Bottom,
        TopLeft, TopRight, BottomLeft, BottomRight
    };
    ResizeDirection m_resizeDir = None;

    bool isInDragRegion(const QPoint &pos) const {
        return pos.y() < 30; // 标题栏区域高度
    }

    void checkResizeRegion(const QPoint &pos) {
        const int border = 5;
        bool left = pos.x() <= border;
        bool right = pos.x() >= width() - border;
        bool top = pos.y() <= border;
        bool bottom = pos.y() >= height() - border;

        if (left && top) m_resizeDir = TopLeft;
        else if (right && top) m_resizeDir = TopRight;
        else if (left && bottom) m_resizeDir = BottomLeft;
        else if (right && bottom) m_resizeDir = BottomRight;
        else if (left) m_resizeDir = Left;
        else if (right) m_resizeDir = Right;
        else if (top) m_resizeDir = Top;
        else if (bottom) m_resizeDir = Bottom;
        else m_resizeDir = None;
    }

    void updateCursorShape(const QPoint &pos) {
        checkResizeRegion(pos);
        switch (m_resizeDir) {
        case Left: case Right:
            setCursor(Qt::SizeHorCursor); break;
        case Top: case Bottom:
            setCursor(Qt::SizeVerCursor); break;
        case TopLeft: case BottomRight:
            setCursor(Qt::SizeFDiagCursor); break;
        case TopRight: case BottomLeft:
            setCursor(Qt::SizeBDiagCursor); break;
        default:
            unsetCursor();
        }
    }

    void handleResize(const QPoint &globalPos) {
        QRect newGeo = m_initialGeometry;
        int deltaX = globalPos.x() - m_resizeStartPos.x();
        int deltaY = globalPos.y() - m_resizeStartPos.y();

        switch (m_resizeDir) {
        case Left:
            newGeo.setLeft(newGeo.left() + deltaX);
            break;
        case Right:
            newGeo.setRight(newGeo.right() + deltaX);
            break;
        case Top:
            newGeo.setTop(newGeo.top() + deltaY);
            break;
        case Bottom:
            newGeo.setBottom(newGeo.bottom() + deltaY);
            break;
        case TopLeft:
            newGeo.setTopLeft(newGeo.topLeft() + QPoint(deltaX, deltaY));
            break;
        case TopRight:
            newGeo.setTopRight(newGeo.topRight() + QPoint(deltaX, deltaY));
            break;
        case BottomLeft:
            newGeo.setBottomLeft(newGeo.bottomLeft() + QPoint(deltaX, deltaY));
            break;
        case BottomRight:
            newGeo.setBottomRight(newGeo.bottomRight() + QPoint(deltaX, deltaY));
            break;
        }

        if (newGeo.width() < minimumWidth() || newGeo.height() < minimumHeight())
            return;

        setGeometry(newGeo);
    }

    void toggleMaximize() {
        isMaximized() ? showNormal() : showMaximized();
    }
};

注意事项

  • 边缘检测:调整边缘检测的阈值(如border = 5)以适应不同需求。
  • 最小尺寸:确保窗口不会调整到小于minimumWidthminimumHeight
  • 最大化处理:在最大化状态下可能需要禁用调整大小或特殊处理拖动行为。
  • 性能优化:避免频繁的几何计算影响性能。

通过以上步骤,即可在Qt的无边框窗口中实现完整的鼠标交互功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水瓶丫头站住

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值