Qt无标题栏窗口的平移,缩放,拉伸

实现功能

在做Qt应用的时候,由于美观等问题经常会使用自定义的标题栏并将窗口默认的标题栏进行隐藏。随之带来的问题就是窗口失去了平移,缩放等功能。本文根据拦截自定义标题栏widget的事件,实现了通过移动标题栏进而移动整个窗口window,并且支持双击标题栏缩放整个窗口以及窗口边缘的拉伸功能。本文主要参考了Qt 之自定义界面(窗体缩放-跨平台终极版),在其基础上进行一些修改和整理。

在这里插入图片描述

UI结构如下

具体代码如下:

#ifndef WINDOWSTYLE_H
#define WINDOWSTYLE_H

#include <QObject>
#include <QMouseEvent>
/**
 * @brief 拉伸时候计算鼠标位置,更改鼠标样式
 */
class CursorStyle : public QObject
{
public:
    enum ShapeType
    {
        ShapeType_Origin,
        ShapeType_Left,
        ShapeType_Right,
        ShapeType_Top,
        ShapeType_Bottom,
        ShapeType_Topleft,
        ShapeType_Bottomleft,
        ShapeType_Topright,
        ShapeType_Bottomright,
    };

public:
    explicit CursorStyle(QObject *parent = 0);
    ~CursorStyle();

    /**
     * @brief 更新鼠标显示样式
     * @param 鼠标得全局坐标
     * @param 窗口
     */
    void updateCursorStyle(const QPoint &point, QWidget *widget);
    /**
     * @brief 获得鼠标当前样式
     * @return 返回鼠标样式
     */
    ShapeType CursorType(){return m_type;}

private:
    /**
     * @brief 计算鼠标的位置,更新m_type
     * @param 鼠标得全局坐标
     * @param 窗口的区域
     */
    void updateCursorPos(const QPoint &point, const QRect &rect);

private:
    ShapeType m_type;   //鼠标当前的样式
};


/**
 * @brief The WindowStyle class
 * @note 将窗口注册在此,进行拖拽移动,双击放大缩小,拉伸等功能
 */
class WindowStyle : public QObject
{
    Q_OBJECT
public:
    explicit WindowStyle(QObject *parent = 0);
    ~WindowStyle();
    /**
     * @brief 窗口注册
     * @param 注册窗口
     */
    void activateOn(QWidget *widget);
    /**
     * @brief 设置是否支持窗口移动
     * @param true or false
     */
    void setMovable(bool isMovable){m_isMovable = isMovable;}
    /**
     * @brief 设置是否支持窗口拉伸
     * @param true or false
     */
    void setResizable(bool isResizable){m_isResizable = isResizable;}

private:
    bool eventFilter(QObject *watched, QEvent *event);
    void mousePressHandle(QMouseEvent *event);
    void mouseReleaseHandle(QMouseEvent *event);
    void mouseMoveHandle(QMouseEvent *event);
    void mouseDBClickedHandle(QMouseEvent *event);
    void mouseHoverHandle(QHoverEvent *event);

    /**
     * @brief 拉伸窗口
     * @param 鼠标的全局坐标
     */
    void resizeWidget(const QPoint &point);

private:
    QPoint   m_distance;            //记录鼠标点击位置与m_wnd左上角距离
    QWidget *m_widget;              //鼠标可以操作的窗口,一般是标题栏
    QWidget *m_wnd;                 //进行拉伸,移动的窗口,一般为整个窗口

    bool     m_isLeftPressed;       //鼠标左键是否按下
    bool     m_isCursorOnWidget;    //鼠标按下时是否在m_widget上
    bool     m_isMovable;           //窗口是否可移动
    bool     m_isResizable;         //窗口是否可拉伸

    CursorStyle m_cursorStyle;
};

#endif // WINDOWSTYLE_H

#include "WindowStyle.h"

#include <QWidget>
#include <QDebug>

#define BORDER 10

CursorStyle::CursorStyle(QObject *parent)
    : QObject(parent)
    , m_type(ShapeType_Origin)
{

}

CursorStyle::~CursorStyle()
{

}

void CursorStyle::updateCursorPos(const QPoint &point, const QRect &rect)
{
    int cursorX = point.x();
    int cursorY = point.y();

    int widgetX = rect.x();
    int widgetY = rect.y();

    int widgetW = rect.width();
    int widgetH = rect.height();

    bool isLeftEdge = cursorX >= widgetX && cursorX <= widgetX + BORDER;
    bool isRightEdge = cursorX <= widgetX + widgetW && cursorX >= widgetX + widgetW - BORDER;
    bool isTopEdge = cursorY >= widgetY && cursorY <= widgetY + BORDER;
    bool isBottomEdge = cursorY <= widgetY + widgetH && cursorY >= widgetY + widgetH - BORDER;
    bool isTopLeftEdge = isTopEdge && isLeftEdge;
    bool isBottomLeftEdge = isBottomEdge && isLeftEdge;
    bool isTopRightEdge = isTopEdge && isRightEdge;
    bool isBottomRightEdge = isBottomEdge && isRightEdge;

    if (isTopLeftEdge) {
        m_type = ShapeType_Topleft;
    } else if (isBottomLeftEdge) {
        m_type = ShapeType_Bottomleft;
    } else if (isTopRightEdge) {
        m_type = ShapeType_Topright;
    } else if (isBottomRightEdge) {
        m_type = ShapeType_Bottomright;
    } else if (isLeftEdge) {
        m_type = ShapeType_Left;
    } else if (isRightEdge) {
        m_type = ShapeType_Right;
    } else if (isTopEdge) {
        m_type = ShapeType_Top;
    } else if (isBottomEdge) {
        m_type = ShapeType_Bottom;
    } else {
        m_type = ShapeType_Origin;
    }
}

void CursorStyle::updateCursorStyle(const QPoint &point, QWidget *widget)
{
    updateCursorPos(point, widget->geometry());

    if (m_type == ShapeType_Topleft || m_type == ShapeType_Bottomright) {
        widget->setCursor(Qt::SizeFDiagCursor);
    } else if (m_type == ShapeType_Topright || m_type == ShapeType_Bottomleft) {
        widget->setCursor(Qt::SizeBDiagCursor);
    } else if (m_type == ShapeType_Left || m_type == ShapeType_Right) {
        widget->setCursor(Qt::SizeHorCursor);
    } else if (m_type == ShapeType_Top || m_type == ShapeType_Bottom) {
        widget->setCursor(Qt::SizeVerCursor);
    } else {
         widget->unsetCursor();
    }
}

WindowStyle::WindowStyle(QObject *parent)
    : QObject(parent)
    , m_widget(nullptr)
    , m_wnd(nullptr)
    , m_isLeftPressed(false)
    , m_isCursorOnWidget(false)
    , m_isMovable(true)
    , m_isResizable(true)
{ 
}

WindowStyle::~WindowStyle()
{

}

void WindowStyle::activateOn(QWidget *widget)
{
    if (widget == nullptr) {
        return;
    }

    m_widget = widget;
    m_wnd = widget->window();
    
	m_wnd->setWindowFlags(Qt::FramelessWindowHint | m_wnd->windowFlags());
    m_wnd->setAttribute(Qt::WA_Hover);
    m_wnd->installEventFilter(this);
}

bool WindowStyle::eventFilter(QObject *watched, QEvent *event)
{
    if (watched == m_wnd) {
        QEvent::Type type = event->type();

        switch(type) {
        case QEvent::MouseButtonPress:
            mousePressHandle(dynamic_cast<QMouseEvent*>(event));
            break;
        case QEvent::MouseButtonRelease:
            mouseReleaseHandle(dynamic_cast<QMouseEvent*>(event));
            break;
        case QEvent::MouseMove:
            mouseMoveHandle(dynamic_cast<QMouseEvent*>(event));
            break;
        case QEvent::MouseButtonDblClick:
            mouseDBClickedHandle(dynamic_cast<QMouseEvent*>(event));
            break;
        case QEvent::HoverMove:
            mouseHoverHandle(dynamic_cast<QHoverEvent*>(event));
            break;
        default:
            break;
        }
    }

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

void WindowStyle::mousePressHandle(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        m_isLeftPressed = true;

        //若m_wnd等于m_widget使用全局坐标,否则使用相对坐标
        QPoint pos = m_widget == m_wnd ? event->globalPos() : event->pos();
        if (m_widget->geometry().contains(pos)) {
            m_isCursorOnWidget = true;
            m_distance = event->globalPos() - m_wnd->mapToGlobal(QPoint(0,0));
        }
    }
}

void WindowStyle::mouseReleaseHandle(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        m_isLeftPressed = false;
        m_isCursorOnWidget = false;
    }
}

void WindowStyle::mouseMoveHandle(QMouseEvent *event)
{
    if (m_isLeftPressed) {
        if (m_cursorStyle.CursorType() != CursorStyle::ShapeType_Origin) {
            resizeWidget(event->globalPos());
            return;
        }

        if (m_isMovable &&  m_isCursorOnWidget) {
            m_wnd->move(event->globalPos() - m_distance);
        }
    }
}

void WindowStyle::mouseDBClickedHandle(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton &&
        m_widget->geometry().contains(event->pos()))
    {
        if (m_wnd->isMaximized()) {
            m_wnd->showNormal();
        } else {
            m_wnd->showMaximized();
        }
    }
}

void WindowStyle::mouseHoverHandle(QHoverEvent *event)
{
    if (m_isResizable && !m_isLeftPressed) {
        m_cursorStyle.updateCursorStyle(m_wnd->mapToGlobal(event->pos()), m_wnd);
    }
}

void WindowStyle::resizeWidget(const QPoint &point)
{
    QRect originRect = m_wnd->geometry();

    //原始窗口四个点得坐标
    int left = originRect.left();
    int right = originRect.right();
    int top = originRect.top();
    int bottom = originRect.bottom();

    //窗口最小宽度和高度
    int minWidth = m_wnd->minimumWidth();
    int minHeight = m_wnd->minimumHeight();

    //根据鼠标得位置更新四个点得坐标
    switch (m_cursorStyle.CursorType()) {
    case CursorStyle::ShapeType_Topleft:
        top = point.y();
        left = point.x();
        break;
    case CursorStyle::ShapeType_Bottomleft:
        bottom = point.y();
        left = point.x();
        break;
    case CursorStyle::ShapeType_Topright:
        top = point.y();
        right = point.x();
        break;
    case CursorStyle::ShapeType_Bottomright:
        bottom = point.y();
        right = point.x();
        break;
    case CursorStyle::ShapeType_Left:
        left = point.x();
        break;
    case CursorStyle::ShapeType_Right:
        right = point.x();
        break;
    case CursorStyle::ShapeType_Top:
        top = point.y();
        break;
    case CursorStyle::ShapeType_Bottom:
        bottom = point.y();
        break;
    default:
        break;
    }

    //窗口得拉伸后得区域
    QRect newRect(QPoint(left, top), QPoint(right, bottom));

    //防止窗口到达最小宽度和高度之后,开始平移
    if ( newRect.isValid() )
    {
      if ( minWidth > newRect.width() )
      {
        if( left != originRect.left() )
          newRect.setLeft( originRect.left() );
        else
          newRect.setRight( originRect.right() );
      }
      if ( minHeight > newRect.height() )
      {
        if ( top != originRect.top() )
          newRect.setTop( originRect.top() );
        else
          newRect.setBottom( originRect.bottom() );
      }

      m_wnd->setGeometry(newRect);
    }
}

使用方法

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , m_windowStyle(NULL)
    , m_hostInfo(NULL)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    setWindowFlags(windowFlags() | Qt::FramelessWindowHint);

    m_windowStyle = new WindowStyle(this);
    m_windowStyle->activateOn(ui->widgetTitle);
}

下载地址:https://download.csdn.net/download/qq_35905572/11245578

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值