Qt::QWidget has no default method for modifying the size by dragging the title bar border

Keywords: Windows Qt

  This article is reproduced from: Qt::QWidget has no default title bar border. How to modify the size - childe Kaiming - blog Garden

Development environment: win10+vs2015+qt5.9.1

Background: in the development process, the title bar and border provided by the system are rarely used; They often customize a scheme designed by themselves. At this time, flag: QT:: frameleswindowhint needs to be added to the QWidget (the implementation method is easy, and Baidu will not repeat it). However, the problem caused by this is that the functions of dragging and moving and dragging to modify the window size provided by the title bar border of the system are abandoned. In this way, you need to implement a scheme to provide this function.

Implementation: drag and drop should be introduced in many articles. The code is also very simple. I won't repeat it. I mainly talk about the way to modify the window size by dragging; There are two schemes. One is completed by borrowing the processing logic of the system platform. The scheme of windows platform is pasted here (Linux has not tried, but there should be a corresponding scheme to adapt to it)

/*
* Description: No border to drag window size
* Author: Childe KaiMing Prince
* Detail: The drag size of the border is the effect implemented by windows by default. In order to beautify the window, you often need to remove the default border, customize the title bar and border, so the drag effect needs to be implemented by yourself
* Class: FrameLessDragResizeWidget
* Implement: This class implements the drag size effect of QWidget without border
*/
#ifndef _FRAMELESS_DRAG_RESIZE_WIDGET_H__
#define _FRAMELESS_DRAG_RESIZE_WIDGET_H__
#include <QWidget>

class FrameLessDragResizeWidget : public QWidget
{
public:
    FrameLessDragResizeWidget(QWidget *parent=Q_NULLPTR);
    ~FrameLessDragResizeWidget();

protected:
    //The first scheme is to use windows's own message mechanism
    virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result);
};
#endif //_FRAMELESS_DRAG_RESIZE_WIDGET_H__

We can see that qt itself provides a nativeEvent event to handle system events, and the windows platform is the message mechanism of the window, so we can implement it as long as we feed back the drag and modify messages to the system itself where we think it is appropriate

#include "FrameLessDragResizeWidget.h"#include <QMouseEvent>

#include <qt_windows.h>

const int g_nBorder = 4;

FrameLessDragResizeWidget::FrameLessDragResizeWidget(QWidget *parent)
    : QWidget(parent, Qt::FramelessWindowHint)
{
    setMouseTracking(true);
}

FrameLessDragResizeWidget::~FrameLessDragResizeWidget()
{
}

bool FrameLessDragResizeWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
    MSG* pMsg = (MSG*)message;
    switch (pMsg->message)
    {
        case WM_NCHITTEST:
        {
            QPoint pos = mapFromGlobal(QPoint(LOWORD(pMsg->lParam), HIWORD(pMsg->lParam)));
            bool bHorLeft = pos.x() < g_nBorder;
            bool bHorRight = pos.x() > width() - g_nBorder;
            bool bVertTop = pos.y() < g_nBorder;
            bool bVertBottom = pos.y() > height() - g_nBorder;
            if (bHorLeft && bVertTop)
            {
                *result = HTTOPLEFT;
            }
            else if (bHorLeft && bVertBottom)
            {
                *result = HTBOTTOMLEFT;
            }
            else if (bHorRight && bVertTop)
            {
                *result = HTTOPRIGHT;
            }
            else if (bHorRight && bVertBottom)
            {
                *result = HTBOTTOMRIGHT;
            }
            else if (bHorLeft)
            {
                *result = HTLEFT;
            }
            else if (bHorRight)
            {
                *result = HTRIGHT;
            }
            else if (bVertTop)
            {
                *result = HTTOP;
            }
            else if (bVertBottom)
            {
                *result = HTBOTTOM;
            }
            else
            {
                return false;
            }
            return true;
        }
        break;
    default:
        break;
    }
    return QWidget::nativeEvent(eventType, message, result);
}

Customize a border with a width of 4, which is regarded as a drag area within this range; Of course, you can also customize other values; The main processing is to look at the internal processing of nativeEvent, and return HTXX to the system under appropriate judgment, so that the system can handle the work of modifying the window size

Scheme 2: the problem of scheme 1 is that it is only applicable to windows platform; If you modify the platform, you need another copy of code. Therefore, we should consider using qt's own processing method to avoid redundant implementation; Since it is a qt implementation, it is necessary to consider the handling of various events of the mouse, such as changing the cursor state when moving to the edge, pressing and dragging within the dragging range, etc.. Re analyze the specific paste code (scheme 1 is still reserved in the code, and those unnecessary can be eliminated by themselves)

/*
* Description: No border to drag window size
* Author: Childe KaiMing Prince
* Detail: The drag size of the border is the effect implemented by windows by default. In order to beautify the window, you often need to remove the default border, customize the title bar and border, so the drag effect needs to be implemented by yourself
* Class: FrameLessDragResizeWidget
* Implement: This class implements the drag size effect of QWidget without border
*/
#ifndef _FRAMELESS_DRAG_RESIZE_WIDGET_H__
#define _FRAMELESS_DRAG_RESIZE_WIDGET_H__
#include <QWidget>

enum DRAG_ABSOLUTE_POSITION
{
    DRAG_KEEP=0,        //Hold still
    DRAG_LOCK_LEFT,     //Lock left coordinates
    DRAG_LOCK_TOP=1,    //Lock upper coordinates
    DRAG_LOCK_RIGHT,    //Lock right coordinates
    DRAG_LOCK_BOTTOM=2  //Lock lower coordinates
};

class FrameLessDragResizeWidget : public QWidget
{
public:
    FrameLessDragResizeWidget(QWidget *parent=Q_NULLPTR);
    ~FrameLessDragResizeWidget();

protected:
    //The first scheme is to use windows's own message mechanism
    virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result);

    //The second scheme is to judge and handle the mouse events by yourself
    virtual void mousePressEvent(QMouseEvent *event);
    virtual void mouseMoveEvent(QMouseEvent *event);
    virtual void mouseReleaseEvent(QMouseEvent *event);

private:
    bool                         m_bCanDragResize;         //Whether the current window is in the drag window size state
    QPoint                        m_start_drag_point;        //Start position of drag start
    DRAG_ABSOLUTE_POSITION        m_emHorizontal;            //Lateral movement range
    DRAG_ABSOLUTE_POSITION        m_emVertical;             //Longitudinal movement range
};
#endif //_FRAMELESS_DRAG_RESIZE_WIDGET_H__

Enumerating DRAG_ABSOLUTE_POSITION indicates a state of dragging. There must be a state of holding still when dragging left, right, up and down. We can use this state to confirm how the window changes. For example, when we drag in the upper left corner, the point in the lower right corner must keep the original position unchanged, while the position in the upper left corner is modified. In this way, you can judge how to modify the geometry when dragging

#include "FrameLessDragResizeWidget.h"

#include <QMouseEvent>

//#define _NATIVE_EVENT_

#ifdef _NATIVE_EVENT_
#include <qt_windows.h>
#endif

const int g_nBorder = 4;

FrameLessDragResizeWidget::FrameLessDragResizeWidget(QWidget *parent)
    : QWidget(parent, Qt::FramelessWindowHint),
    m_bCanDragResize(false)
{
    setMouseTracking(true);
}

FrameLessDragResizeWidget::~FrameLessDragResizeWidget()
{
}

bool FrameLessDragResizeWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
#ifdef _NATIVE_EVENT_
    MSG* pMsg = (MSG*)message;
    switch (pMsg->message)
    {
        case WM_NCHITTEST:
        {
            QPoint pos = mapFromGlobal(QPoint(LOWORD(pMsg->lParam), HIWORD(pMsg->lParam)));
            bool bHorLeft = pos.x() < g_nBorder;
            bool bHorRight = pos.x() > width() - g_nBorder;
            bool bVertTop = pos.y() < g_nBorder;
            bool bVertBottom = pos.y() > height() - g_nBorder;
            if (bHorLeft && bVertTop)
            {
                *result = HTTOPLEFT;
            }
            else if (bHorLeft && bVertBottom)
            {
                *result = HTBOTTOMLEFT;
            }
            else if (bHorRight && bVertTop)
            {
                *result = HTTOPRIGHT;
            }
            else if (bHorRight && bVertBottom)
            {
                *result = HTBOTTOMRIGHT;
            }
            else if (bHorLeft)
            {
                *result = HTLEFT;
            }
            else if (bHorRight)
            {
                *result = HTRIGHT;
            }
            else if (bVertTop)
            {
                *result = HTTOP;
            }
            else if (bVertBottom)
            {
                *result = HTBOTTOM;
            }
            else
            {
                return false;
            }
            return true;
        }
        break;
    default:
        break;
    }
#endif
    return QWidget::nativeEvent(eventType, message, result);
}

void FrameLessDragResizeWidget::mousePressEvent(QMouseEvent *event)
{
#ifndef _NATIVE_EVENT_
    if (Qt::LeftButton == event->button() && m_bCanDragResize)
    {
        m_start_drag_point = event->globalPos();
    }
#endif
    QWidget::mousePressEvent(event);
}

void FrameLessDragResizeWidget::mouseMoveEvent(QMouseEvent *event)
{
#ifndef _NATIVE_EVENT_
    if (Qt::LeftButton & event->buttons())
    {
        if (m_bCanDragResize)                    //If the left key is pressed and moved and is in the drag state
        {
            const QPoint point_offset = event->globalPos() - m_start_drag_point;
            m_start_drag_point = event->globalPos();
            int nOffsetX1 = DRAG_LOCK_RIGHT == m_emHorizontal ? point_offset.x() : 0;
            int nOffsetY1 = DRAG_LOCK_BOTTOM == m_emVertical ? point_offset.y() : 0;
            int nOffsetX2 = DRAG_LOCK_LEFT == m_emHorizontal ? point_offset.x() : 0;
            int nOffsetY2 = DRAG_LOCK_TOP == m_emVertical ? point_offset.y() : 0;
            setGeometry(geometry().adjusted(nOffsetX1, nOffsetY1, nOffsetX2, nOffsetY2));
        }
    }
    else if (Qt::NoButton == event->button())
    {
        //First, determine whether the cursor is in the window position that can be dragged
        const QPoint& pos = event->pos();
        bool bHorLeft = pos.x() < g_nBorder;
        bool bHorRight = pos.x() > rect().right() - g_nBorder;
        bool bVertTop = pos.y() < g_nBorder;
        bool bVertBottom = pos.y() > rect().bottom() - g_nBorder;
        if (bHorLeft || bHorRight || bVertTop || bVertBottom)
        {
            m_bCanDragResize = true;
            if (bHorLeft && bVertTop)        //Drag in the upper left corner
            {
                setCursor(Qt::SizeFDiagCursor);
                m_emHorizontal = DRAG_LOCK_RIGHT;
                m_emVertical = DRAG_LOCK_BOTTOM;
            }
            else if (bHorLeft && bVertBottom)    //Drag in the lower left corner
            {
                setCursor(Qt::SizeBDiagCursor);
                m_emHorizontal = DRAG_LOCK_RIGHT;
                m_emVertical = DRAG_LOCK_TOP;
            }
            else if (bHorRight && bVertTop)        //Drag in the upper right corner
            {
                setCursor(Qt::SizeBDiagCursor);
                m_emHorizontal = DRAG_LOCK_LEFT;
                m_emVertical = DRAG_LOCK_BOTTOM;
            }
            else if (bHorRight && bVertBottom)    //Drag in the lower right corner
            {
                setCursor(Qt::SizeFDiagCursor);
                m_emHorizontal = DRAG_LOCK_LEFT;
                m_emVertical = DRAG_LOCK_TOP;
            }
            else if (bHorLeft)                    //Left border drag
            {
                setCursor(Qt::SizeHorCursor);
                m_emHorizontal = DRAG_LOCK_RIGHT;
                m_emVertical = DRAG_KEEP;
            }
            else if (bHorRight)                    //Right border drag
            {
                setCursor(Qt::SizeHorCursor);
                m_emHorizontal = DRAG_LOCK_LEFT;
                m_emVertical = DRAG_KEEP;
            }
            else if (bVertTop)                    //Top border drag
            {
                setCursor(Qt::SizeVerCursor);
                m_emHorizontal = DRAG_KEEP;
                m_emVertical = DRAG_LOCK_BOTTOM;
            }
            else if (bVertBottom)                //Bottom border drag
            {
                setCursor(Qt::SizeVerCursor);
                m_emHorizontal = DRAG_KEEP;
                m_emVertical = DRAG_LOCK_TOP;
            }
        }
        else if (m_bCanDragResize)
        {
            m_bCanDragResize = false;
            setCursor(Qt::ArrowCursor);            //If the cursor was modified in the last judgment, it is no longer in the drag state. Change the cursor back
        }
    }
#endif
    QWidget::mouseMoveEvent(event);
}

void FrameLessDragResizeWidget::mouseReleaseEvent(QMouseEvent *event)
{
    QWidget::mouseReleaseEvent(event);
}

First, look at the no button of mousemove, modify the cursor state in the edge range and record the edge position state; Then judge whether to modify the window size according to the drag identification, determine the change size value according to the mouse position offset, confirm the adjusted parameter filling according to the status, and constantly update the starting coordinate position.

The renderings will not be recorded and uploaded. If you are interested, you can directly try the copy code; The next article talks about the merging of drag movement and drag size.

Posted by bkbragg on Thu, 30 Sep 2021 14:51:31 -0700