Qt 之自定义界面(窗体缩放-跨平台终极版)-转,修改原版不支持有父窗口的情况

本文介绍了一个经过修改的无边框窗口管理器,使其能够同时支持父窗口和子窗口。通过代码实现,当鼠标悬停或点击时,可以调整窗口大小或移动窗口位置,特别适用于那些希望去除窗口边框但仍保持窗口可操作性的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Qt 之自定义界面(窗体缩放-跨平台终极版)原版只支持父窗口(灰色),不支持子窗口(淡蓝色),现在改成两个都支持。

代码下载地址:https://download.youkuaiyun.com/download/bubbleyang/10906204

下面是代码部分:
头文件"framelesshelper.h",这部分未改动:

#ifndef FRAMELESSHELPER_H
#define FRAMELESSHELPER_H

#include <QObject>

class QWidget;
class FramelessHelperPrivate;

class FramelessHelper : public QObject
{
    Q_OBJECT
public:
    explicit FramelessHelper(QObject *parent = nullptr);
    ~FramelessHelper();

    void activateOn(QWidget *topLevelWidget);

    void removeFrom(QWidget *topLevelWidget);
    
    void setWidgetMovable(bool movable);

    void setWidgetResizable(bool resizable);

    void setRubberBandOnMove(bool movable);

    void setRubberBandOnResize(bool resizable);

    void setBorderWidth(uint width);

    void setTitleHeight(uint height);

    bool widgetResizable();

    bool widgetMovable();

    bool rubberBandOnMove();

    bool rubberBandOnResize();

    uint borderWidth();

    uint titleHeight();

signals:

public slots:

protected:

    virtual bool eventFilter(QObject *obj,QEvent *event);

private:
    FramelessHelperPrivate *d;

};

#endif // FRAMELESSHELPER_H

源文件:改为支持父子窗口部件,凡是添加注释的部分都是改动的部分:

#include "framelesshelper.h"

#include <QHash>
#include <QPoint>
#include <QRect>
#include <QMouseEvent>
#include <QRubberBand>
#include <QDebug>

class WidgetData;
class FramelessHelperPrivate
{
public:
    QHash<QWidget*,WidgetData*> m_widgetDataHash;
    bool m_bWidgetMovable       : true;
    bool m_bWidgetResizable     : true;
    bool m_bRubberBandOnResize  : true;
    bool m_bRubberBandOnMove    : true;
};

class CursorPosCalculator
{
public:
    explicit CursorPosCalculator();

    void reset();

    void recalculate(const QPoint &globalMousePos,const QRect &frameRect);

public:
    bool m_bOnEdges             : true;
    bool m_bOnLeftEdge          : true;
    bool m_bOnRightEdge         : true;
    bool m_bOnTopEdge           : true;
    bool m_bOnBottomEdge        : true;
    bool m_bOnTopLeftEdge       : true;
    bool m_bOnBottomLeftEdge    : true;
    bool m_bOnTopRightEdge      : true;
    bool m_bOnBottomRightEdge   : true;

    static int m_nBorderWidth;
    static int m_nTitleHeight;
};

int CursorPosCalculator::m_nBorderWidth = 5;
int CursorPosCalculator::m_nTitleHeight = 30;

CursorPosCalculator::CursorPosCalculator()
{
    reset();
}

void CursorPosCalculator::reset()
{
    m_bOnEdges = false;
    m_bOnLeftEdge = false;
    m_bOnRightEdge = false;
    m_bOnTopEdge = false;
    m_bOnBottomEdge = false;
    m_bOnTopLeftEdge = false;
    m_bOnBottomLeftEdge = false;
    m_bOnTopRightEdge = false;
    m_bOnBottomRightEdge = false;
}

void CursorPosCalculator::recalculate(const QPoint &globalMousePos, const QRect &frameRect)
{
    int globalMouseX = globalMousePos.x();
    int globalMouseY = globalMousePos.y();

    int frameX = frameRect.x();
    int frameY = frameRect.y();

    int frameWidth = frameRect.width();
    int frameHeight = frameRect.height();

    m_bOnLeftEdge = (globalMouseX >= frameX &&
                     globalMouseX <= frameX + m_nBorderWidth);

    m_bOnRightEdge = (globalMouseX >= frameX + frameWidth - m_nBorderWidth &&
                      globalMouseX <= frameX + frameWidth);

    m_bOnTopEdge = (globalMouseY >= frameY &&
                    globalMouseY <= frameY + m_nBorderWidth);

    m_bOnBottomEdge = (globalMouseY >= frameY + frameHeight - m_nBorderWidth &&
                    globalMouseY <= frameY + frameHeight);

    m_bOnTopLeftEdge = m_bOnTopEdge && m_bOnLeftEdge;
    m_bOnBottomLeftEdge = m_bOnBottomEdge && m_bOnLeftEdge;
    m_bOnTopRightEdge = m_bOnTopEdge && m_bOnRightEdge;
    m_bOnBottomRightEdge = m_bOnBottomEdge && m_bOnRightEdge;

    m_bOnEdges = m_bOnLeftEdge || m_bOnRightEdge || m_bOnTopEdge || m_bOnBottomEdge;
}

class WidgetData
{
public:
    explicit WidgetData(FramelessHelperPrivate *d,QWidget *pTopLevelWidget);

    ~WidgetData();

    QWidget *widget();

    void handleWidgetEvent(QEvent *event);

    void updateRubberBandStatus(QWidget *w = nullptr);

private:
    void updateCursorShape(const QPoint &gMousePos);

    void resizeWidget(const QPoint &gMousePos);

    void moveWidget(const QPoint &gMousePos);

    void handleMousePressEvent(QMouseEvent *event);

    void handleMouseReleaseEvent(QMouseEvent *event);

    void handleMouseMoveEvent(QMouseEvent *event);

    void handleLeaveEvent(QEvent *event);

    void handleHoverMoveEvent(QHoverEvent *event);

private:
    FramelessHelperPrivate *d;
    QRubberBand *m_pRubberBand;
    QWidget *m_pWidget;
    QPoint m_ptDragPos;
    CursorPosCalculator m_pressedMousePos;
    CursorPosCalculator m_moveMousePos;
    bool m_bLeftButtonPressed;
    bool m_bCursorShapeChanged;
    bool m_bLeftButtonTitlePressed;
    Qt::WindowFlags m_windowFlags;
};

WidgetData::WidgetData(FramelessHelperPrivate *_d,QWidget *pTopLevelWidget)
    : d(_d)
    , m_pRubberBand(nullptr)
    , m_pWidget(pTopLevelWidget)
    , m_bLeftButtonPressed(false)
    , m_bCursorShapeChanged(false)
    , m_bLeftButtonTitlePressed(false)
    , m_windowFlags(m_pWidget->windowFlags())
{
    m_pWidget->setMouseTracking(true);
    m_pWidget->setAttribute(Qt::WA_Hover,true);
    updateRubberBandStatus(m_pWidget->parentWidget());//支持父窗口
}

WidgetData::~WidgetData()
{
    m_pWidget->setMouseTracking(false);
    m_pWidget->setWindowFlags(m_windowFlags);
    m_pWidget->setAttribute(Qt::WA_Hover,false);

    delete m_pRubberBand;
    m_pRubberBand = nullptr;
}

QWidget* WidgetData::widget()
{
    return m_pWidget;
}

void WidgetData::handleWidgetEvent(QEvent *event)
{
    switch (event->type())
    {
    case QEvent::MouseButtonPress:
        handleMousePressEvent(static_cast<QMouseEvent*>(event));
        break;
    case QEvent::MouseButtonRelease:
        handleMouseReleaseEvent(static_cast<QMouseEvent*>(event));
        break;
    case QEvent::MouseMove:
        handleMouseMoveEvent(static_cast<QMouseEvent*>(event));
        break;
    case QEvent::Leave:
        handleLeaveEvent(static_cast<QMouseEvent*>(event));
        break;
    case QEvent::HoverMove:
        handleHoverMoveEvent(static_cast<QHoverEvent*>(event));
        break;
    default:
        break;
    }
}

 /******************************这个函数有改动,橡皮筋指定父窗口********************************/

void WidgetData::updateRubberBandStatus(QWidget *w)
{
    if (d->m_bRubberBandOnMove || d->m_bRubberBandOnResize)
    {
        if (nullptr == m_pRubberBand)
        {
            if (w != nullptr)
            {
                m_pRubberBand = new QRubberBand(QRubberBand::Rectangle,w);
            }
            else
            {
                m_pRubberBand = new QRubberBand(QRubberBand::Rectangle);
            }
        }
    }
    else
    {
        delete m_pRubberBand;
        m_pRubberBand = nullptr;
    }
}


void WidgetData::updateCursorShape(const QPoint &gMousePos)
{
    if (m_pWidget->isFullScreen() || m_pWidget->isMaximized())
    {
        if (m_bCursorShapeChanged)
        {
            m_pWidget->unsetCursor();
        }

        return;
    }

    m_moveMousePos.recalculate(gMousePos,m_pWidget->frameGeometry());

    if (m_moveMousePos.m_bOnTopLeftEdge || m_moveMousePos.m_bOnBottomRightEdge)
    {
        m_pWidget->setCursor(Qt::SizeFDiagCursor);
        m_bCursorShapeChanged = true;
    }
    else if (m_moveMousePos.m_bOnTopRightEdge || m_moveMousePos.m_bOnBottomLeftEdge)
    {
        m_pWidget->setCursor(Qt::SizeBDiagCursor);
        m_bCursorShapeChanged = true;
    }
    else if (m_moveMousePos.m_bOnLeftEdge || m_moveMousePos.m_bOnRightEdge)
    {
        m_pWidget->setCursor(Qt::SizeHorCursor);
        m_bCursorShapeChanged = true;
    }
    else if (m_moveMousePos.m_bOnTopEdge || m_moveMousePos.m_bOnBottomEdge)
    {
        m_pWidget->setCursor(Qt::SizeVerCursor);
        m_bCursorShapeChanged = true;
    }
    else
    {
        if (m_bCursorShapeChanged)
        {
            m_pWidget->unsetCursor();
            m_bCursorShapeChanged = false;
        }
    }
}

void WidgetData::resizeWidget(const QPoint &gMousePos)
{
    QRect origRect;

    if (d->m_bRubberBandOnResize)
    {
        origRect = m_pRubberBand->frameGeometry();
    }
    else
    {
        origRect = m_pWidget->frameGeometry();
    }

    int left = origRect.left();
    int top = origRect.top();
    int right = origRect.right();
    int bottom = origRect.bottom();
    origRect.getCoords(&left,&top,&right,&bottom);

    int minWidth = m_pWidget->minimumWidth();
    int minHeight = m_pWidget->minimumHeight();

    if (m_pressedMousePos.m_bOnTopLeftEdge)
    {
        left = gMousePos.x();
        top = gMousePos.y();
    }
    else if (m_pressedMousePos.m_bOnBottomLeftEdge)
    {
        left = gMousePos.x();
        bottom = gMousePos.y();
    }
    else if (m_pressedMousePos.m_bOnTopRightEdge)
    {
        right = gMousePos.x();
        top = gMousePos.y();
    }
    else if (m_pressedMousePos.m_bOnBottomRightEdge)
    {
        right = gMousePos.x();
        bottom = gMousePos.y();
    }
    else if (m_pressedMousePos.m_bOnLeftEdge)
    {
        left = gMousePos.x();
    }
    else if (m_pressedMousePos.m_bOnRightEdge)
    {
        right = gMousePos.x();
    }
    else if (m_pressedMousePos.m_bOnTopEdge)
    {
        top = gMousePos.y();
    }
    else if (m_pressedMousePos.m_bOnBottomEdge)
    {
        bottom = gMousePos.y();
    }

    QRect newRect(QPoint(left,top),QPoint(right,bottom));
    if (newRect.isValid())
    {
        if (minWidth > newRect.width())
        {
            if (left != origRect.left())
            {
                newRect.setLeft(origRect.left());
            }
            else
            {
                newRect.setRight(origRect.right());
            }
        }

        if (minHeight > newRect.height())
        {
            if (top != origRect.top())
            {
                newRect.setTop(origRect.top());
            }
            else
            {
                newRect.setBottom(origRect.bottom());
            }
        }

        if (d->m_bRubberBandOnResize)
        {
            m_pRubberBand->setGeometry(newRect);
        }
        else
        {
            m_pWidget->setGeometry(newRect);
        }
    }
}

void WidgetData::moveWidget(const QPoint &gMousePos)
{
    if (d->m_bRubberBandOnMove)
    {
        m_pRubberBand->move(gMousePos - m_ptDragPos);
    }
    else
    {
        m_pWidget->move(gMousePos - m_ptDragPos);
    }
}

void WidgetData::handleMousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        m_bLeftButtonPressed = true;
        m_bLeftButtonTitlePressed = event->pos().y() < m_moveMousePos.m_nTitleHeight;

        QRect frameRect = m_pWidget->frameGeometry();
        m_pressedMousePos.recalculate(m_pWidget->mapToParent(event->pos()),frameRect);//将event->globalPos()改为m_pWidget->mapToParent(event->pos())

        m_ptDragPos = m_pWidget->mapToParent(event->pos()) - frameRect.topLeft();//将event->globalPos()改为m_pWidget->mapToParent(event->pos())

        if (m_pressedMousePos.m_bOnEdges)
        {
            if (d->m_bRubberBandOnResize)
            {
                m_pRubberBand->setGeometry(frameRect);
                m_pRubberBand->show();
            }
        }
        else if (d->m_bRubberBandOnMove)
        {
            m_pRubberBand->setGeometry(frameRect);
            m_pRubberBand->show();
        }
    }
}

void WidgetData::handleMouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        m_bLeftButtonPressed = false;
        m_bLeftButtonTitlePressed = false;
        m_pressedMousePos.reset();
        if (m_pRubberBand && m_pRubberBand->isVisible())
        {
            m_pRubberBand->hide();
            m_pWidget->setGeometry(m_pRubberBand->geometry());
        }
    }
}

void WidgetData::handleMouseMoveEvent(QMouseEvent *event)
{
    if (m_bLeftButtonPressed)
    {
        if (d->m_bWidgetResizable && m_pressedMousePos.m_bOnEdges)
        {
            resizeWidget(m_pWidget->mapToParent(event->pos()));//将event->globalPos()改为m_pWidget->mapToParent(event->pos())
        }
        else if (d->m_bWidgetMovable && m_bLeftButtonTitlePressed)
        {
            moveWidget(m_pWidget->mapToParent(event->pos()));//将event->globalPos()改为m_pWidget->mapToParent(event->pos())
        }
    }
    else if (d->m_bWidgetResizable)
    {
        updateCursorShape(m_pWidget->mapToParent(event->pos()));//将event->globalPos()改为m_pWidget->mapToParent(event->pos())
    }
}

void WidgetData::handleLeaveEvent(QEvent *event)
{
    Q_UNUSED(event);
    if (!m_bLeftButtonPressed)
    {
        m_pWidget->unsetCursor();
    }
}

void WidgetData::handleHoverMoveEvent(QHoverEvent *event)
{
    if (d->m_bWidgetResizable)
    {
        updateCursorShape(m_pWidget->mapToParent(event->pos()));//将m_pWidget->mapToGlobal(event->pos())改为m_pWidget->mapToParent(event->pos())
    }
}

FramelessHelper::FramelessHelper(QObject *parent)
    : QObject(parent)
    , d(new FramelessHelperPrivate)
{
    d->m_bWidgetMovable = true;
    d->m_bWidgetResizable = true;
    d->m_bRubberBandOnResize = true;
    d->m_bRubberBandOnMove = true;
}

FramelessHelper::~FramelessHelper()
{
    QList<QWidget*> keys = d->m_widgetDataHash.keys();
    int size = keys.size();
    for (int i = 0;i < size; ++i)
    {
        delete d->m_widgetDataHash.take(keys[i]);
    }

    delete d;
}

bool FramelessHelper::eventFilter(QObject *obj, QEvent *event)
{
    switch (event->type())
    {
    case QEvent::MouseMove:
    case QEvent::HoverMove:
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease:
    case QEvent::Leave:
    {
        WidgetData *data = d->m_widgetDataHash.value(static_cast<QWidget*>(obj));
        if (data != nullptr)
        {
            data->handleWidgetEvent(event);
//            return true;//这个注释,为了使被监听的窗口也能收到事件,并对事件作出处理,根据需要,有无都可以。return false 效果和注释掉这一行的效果相同。
        }
    }

        break;
    default:
        break;
    }

    return QObject::eventFilter(obj,event);
}

void FramelessHelper::activateOn(QWidget *topLevelWidget)
{
    if (!d->m_widgetDataHash.contains(topLevelWidget))
    {
        WidgetData *data = new WidgetData(d,topLevelWidget);
        d->m_widgetDataHash.insert(topLevelWidget,data);

        topLevelWidget->installEventFilter(this);
    }
}

void FramelessHelper::removeFrom(QWidget *topLevelWidget)
{
    WidgetData *data = d->m_widgetDataHash.take(topLevelWidget);
    if (data != nullptr)
    {
        topLevelWidget->removeEventFilter(this);
        delete data;
    }
}


void FramelessHelper::setRubberBandOnMove(bool movable)
{
    d->m_bRubberBandOnMove = movable;
    QList<WidgetData*> list = d->m_widgetDataHash.values();
    foreach (WidgetData *data,list)
    {
        data->updateRubberBandStatus(data->widget()->parentWidget());//支持父窗口
    }
}

void FramelessHelper::setWidgetMovable(bool movable)
{
    d->m_bWidgetMovable = movable;
}

void FramelessHelper::setWidgetResizable(bool resizable)
{
    d->m_bRubberBandOnResize = resizable;
}


void FramelessHelper::setRubberBandOnResize(bool resizable)
{
    d->m_bRubberBandOnResize = resizable;
    QList<WidgetData*> list = d->m_widgetDataHash.values();
    foreach (WidgetData *data, list)
    {
        data->updateRubberBandStatus(data->widget()->parentWidget());//支持父窗口
    }
}

void FramelessHelper::setBorderWidth(uint width)
{
    if (width > 0)
    {
        CursorPosCalculator::m_nBorderWidth = width;
    }
}


void FramelessHelper::setTitleHeight(uint height)
{
    if (height > 0)
    {
        CursorPosCalculator::m_nTitleHeight = height;
    }
}

bool FramelessHelper::widgetMovable()
{
    return d->m_bWidgetMovable;
}

bool FramelessHelper::widgetResizable()
{
    return d->m_bWidgetResizable;
}

bool FramelessHelper::rubberBandOnMove()
{
    return d->m_bRubberBandOnMove;
}

bool FramelessHelper::rubberBandOnResize()
{
    return d->m_bWidgetResizable;
}

uint FramelessHelper::borderWidth()
{
    return CursorPosCalculator::m_nBorderWidth;
}

uint FramelessHelper::titleHeight()
{
    return CursorPosCalculator::m_nTitleHeight;
}

以上源代码转载自 “一去丶二三里 ”,博客地址:https://blog.youkuaiyun.com/liang19890820/article/details/50557240
对上述代码做了修改,使之支持有父窗口组件的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值