Qt Writing Custom Control 67 - Universal Borderless

Keywords: Mobile Qt github Qt5

I. Preface

In a previous article, I wrote about a generic mobile control, which is used to pass in any widget control and move freely in the parent container. This article is about a general borderless class, which does not exactly mean that the control should be called a component. The control is something to see and has drawing requirements, which need to be attached to the control. In the projects we usually do, in order to make the U interface beautiful, many people will use the custom borderless form to deal with, beautify the title bar and so on. They will face the same problem. After setting the custom borderless form, they will handle the movement and stretching of the form themselves. If there are many borderless forms, many people will think of writing duplicate code everywhere. To achieve mobility or stretching, why not encapsulate a class to accomplish this function, just pass it directly into the form. In QDialog form, you can stretch the lower right corner by setting an attribute sizeGripEnabled, which is not enough to meet all the requirements. In many cases, we need to stretch around the four corners and upper and lower corners. This requires rewriting, installing event filters, recognizing that the mouse moves to a certain area, the shape of the mouse changes automatically, and then identifying whether it has been changed or not. After press, press the words to do the corresponding processing, the corresponding processing core is to reset the XY axis coordinates and size of the form.

Open source address: https://gitee.com/feiyangqingyun/QWidgetDemo https://github.com/feiyangqingyun/QWidgetDemo

II. Functions of Realization

  • 1: You can specify a widget that requires no borders
  • 2: All eight directions around the frame can be stretched freely.
  • 3: The margin of the corresponding position can be set to identify larger areas.
  • 4: Can be set to allow dragging
  • 5: Can be set to allow stretching

III. EFFECT CHARACTERISTICS

Header file code

#ifndef FRAMELESSWIDGET_H
#define FRAMELESSWIDGET_H

/**
 * Borderless Form Class Author: feiyangqingyun(QQ:517216493) 2019-10-03
 * 1:You can specify a widget that requires no borders
 * 2:All eight directions around the frame can be stretched freely.
 * 3:The margin of the corresponding position can be set to identify larger areas.
 * 4:Can be set to allow dragging
 * 5:Can be set to allow stretching
 */

#include <QWidget>

#ifdef quc
#if (QT_VERSION < QT_VERSION_CHECK(5,7,0))
#include <QtDesigner/QDesignerExportWidget>
#else
#include <QtUiPlugin/QDesignerExportWidget>
#endif

class QDESIGNER_WIDGET_EXPORT FramelessWidget : public QObject
#else
class FramelessWidget : public QObject
#endif

{
    Q_OBJECT
public:
    explicit FramelessWidget(QObject *parent = 0);

protected:
    bool eventFilter(QObject *watched, QEvent *event);

private:
    int padding;                    //Margin
    bool moveEnable;                //portability
    bool resizeEnable;              //Stretchable
    QWidget *widget;                //Borderless Form

    bool pressed;                   //Mouse down
    bool pressedLeft;               //Press the left mouse
    bool pressedRight;              //Press the mouse to the right
    bool pressedTop;                //Press the mouse down the upper side
    bool pressedBottom;             //Press the mouse down the lower side
    bool pressedLeftTop;            //Press the mouse on the upper left side
    bool pressedRightTop;           //Press the mouse on the upper right side
    bool pressedLeftBottom;         //Press the mouse on the lower left side
    bool pressedRightBottom;        //Lower right mouse down

    int rectX, rectY, rectW, rectH; //Form coordinates + width and height
    QPoint lastPos;                 //Click the coordinates below

    QRect rectLeft;                 //Left area
    QRect rectRight;                //Right region
    QRect rectTop;                  //Upper side area
    QRect rectBottom;               //Underside area
    QRect rectLeftTop;              //Left upper region
    QRect rectRightTop;             //Upper right area
    QRect rectLeftBottom;           //Left lower region
    QRect rectRightBottom;          //Right lower region

public Q_SLOTS:
    //Setting margins
    void setPadding(int padding);
    //Set drag + stretch
    void setMoveEnable(bool moveEnable);
    void setResizeEnable(bool resizeEnable);

    //Set the form to be borderless
    void setWidget(QWidget *widget);
};

#endif // FRAMELESSWIDGET_H


V. Core Code

#include "framelesswidget.h"
#include "qevent.h"
#include "qdebug.h"

FramelessWidget::FramelessWidget(QObject *parent) : QObject(parent)
{
    padding = 8;
    moveEnable = true;
    resizeEnable = true;
    widget = 0;

    pressed = false;
    pressedLeft = false;
    pressedRight = false;
    pressedTop = false;
    pressedBottom = false;
    pressedLeftTop = false;
    pressedRightTop = false;
    pressedLeftBottom = false;
    pressedRightBottom = false;

    //If the parent class is a form, set it directly
    if (parent->isWidgetType()) {
        setWidget((QWidget *)parent);
    }
}

bool FramelessWidget::eventFilter(QObject *watched, QEvent *event)
{
    if (widget != 0 && watched == widget) {
        if (event->type() == QEvent::Resize) {
            //To recalculate the area of eight tracing points, the function of the tracing point area is to calculate whether the mouse coordinates are in a certain area or not.
            int width = widget->width();
            int height = widget->height();

            //Left tracing area
            rectLeft = QRect(0, padding, padding, height - padding * 2);
            //Topside tracing area
            rectTop = QRect(padding, 0, width - padding * 2, padding);
            //Right tracing area
            rectRight = QRect(width - padding, padding, padding, height - padding * 2);
            //Lower outline area
            rectBottom = QRect(padding, height - padding, width - padding * 2, padding);

            //Top left corner tracing area
            rectLeftTop = QRect(0, 0, padding, padding);
            //Top right corner tracing area
            rectRightTop = QRect(width - padding, 0, padding, padding);
            //The lower left corner tracing area
            rectLeftBottom = QRect(0, height - padding, padding, padding);
            //The lower right corner tracing area
            rectRightBottom = QRect(width - padding, height - padding, padding, padding);
        } else if (event->type() == QEvent::HoverMove) {
            //Set the corresponding mouse shape, which must be placed here rather than below, because it can be recognized when the mouse is not pressed.
            QHoverEvent *hoverEvent = (QHoverEvent *)event;
            QPoint point = hoverEvent->pos();
            if (resizeEnable) {
                if (rectLeft.contains(point)) {
                    widget->setCursor(Qt::SizeHorCursor);
                } else if (rectRight.contains(point)) {
                    widget->setCursor(Qt::SizeHorCursor);
                } else if (rectTop.contains(point)) {
                    widget->setCursor(Qt::SizeVerCursor);
                } else if (rectBottom.contains(point)) {
                    widget->setCursor(Qt::SizeVerCursor);
                } else if (rectLeftTop.contains(point)) {
                    widget->setCursor(Qt::SizeFDiagCursor);
                } else if (rectRightTop.contains(point)) {
                    widget->setCursor(Qt::SizeBDiagCursor);
                } else if (rectLeftBottom.contains(point)) {
                    widget->setCursor(Qt::SizeBDiagCursor);
                } else if (rectRightBottom.contains(point)) {
                    widget->setCursor(Qt::SizeFDiagCursor);
                } else {
                    widget->setCursor(Qt::ArrowCursor);
                }
            }

            //Calculate how much the XY axis has moved based on the current mouse position
            int offsetX = point.x() - lastPos.x();
            int offsetY = point.y() - lastPos.y();

            //Determine whether a mobile control or a stretch control is based on the position at which it is pressed
            if (moveEnable) {
                if (pressed) {
                    widget->move(widget->x() + offsetX, widget->y() + offsetY);
                }
            }

            if (resizeEnable) {
                if (pressedLeft) {
                    int resizeW = widget->width() - offsetX;
                    if (widget->minimumWidth() <= resizeW) {
                        widget->setGeometry(widget->x() + offsetX, rectY, resizeW, rectH);
                    }
                } else if (pressedRight) {
                    widget->setGeometry(rectX, rectY, rectW + offsetX, rectH);
                } else if (pressedTop) {
                    int resizeH = widget->height() - offsetY;
                    if (widget->minimumHeight() <= resizeH) {
                        widget->setGeometry(rectX, widget->y() + offsetY, rectW, resizeH);
                    }
                } else if (pressedBottom) {
                    widget->setGeometry(rectX, rectY, rectW, rectH + offsetY);
                } else if (pressedLeftTop) {
                    int resizeW = widget->width() - offsetX;
                    int resizeH = widget->height() - offsetY;
                    if (widget->minimumWidth() <= resizeW) {
                        widget->setGeometry(widget->x() + offsetX, widget->y(), resizeW, resizeH);
                    }
                    if (widget->minimumHeight() <= resizeH) {
                        widget->setGeometry(widget->x(), widget->y() + offsetY, resizeW, resizeH);
                    }
                } else if (pressedRightTop) {
                    int resizeW = rectW + offsetX;
                    int resizeH = widget->height() - offsetY;
                    if (widget->minimumHeight() <= resizeH) {
                        widget->setGeometry(widget->x(), widget->y() + offsetY, resizeW, resizeH);
                    }
                } else if (pressedLeftBottom) {
                    int resizeW = widget->width() - offsetX;
                    int resizeH = rectH + offsetY;
                    if (widget->minimumWidth() <= resizeW) {
                        widget->setGeometry(widget->x() + offsetX, widget->y(), resizeW, resizeH);
                    }
                    if (widget->minimumHeight() <= resizeH) {
                        widget->setGeometry(widget->x(), widget->y(), resizeW, resizeH);
                    }
                } else if (pressedRightBottom) {
                    int resizeW = rectW + offsetX;
                    int resizeH = rectH + offsetY;
                    widget->setGeometry(widget->x(), widget->y(), resizeW, resizeH);
                }
            }
        } else if (event->type() == QEvent::MouseButtonPress) {
            //Keep in mind the coordinates and width of the current control and the coordinates pressed by the mouse
            QMouseEvent *mouseEvent = (QMouseEvent *)event;
            rectX = widget->x();
            rectY = widget->y();
            rectW = widget->width();
            rectH = widget->height();
            lastPos = mouseEvent->pos();

            //Judge the area position of the pressed handle
            if (rectLeft.contains(lastPos)) {
                pressedLeft = true;
            } else if (rectRight.contains(lastPos)) {
                pressedRight = true;
            } else if (rectTop.contains(lastPos)) {
                pressedTop = true;
            } else if (rectBottom.contains(lastPos)) {
                pressedBottom = true;
            } else if (rectLeftTop.contains(lastPos)) {
                pressedLeftTop = true;
            } else if (rectRightTop.contains(lastPos)) {
                pressedRightTop = true;
            } else if (rectLeftBottom.contains(lastPos)) {
                pressedLeftBottom = true;
            } else if (rectRightBottom.contains(lastPos)) {
                pressedRightBottom = true;
            } else {
                pressed = true;
            }
        } else if (event->type() == QEvent::MouseMove) {
            //Change to HoverMove Recognition
        } else if (event->type() == QEvent::MouseButtonRelease) {
            //Restore all
            pressed = false;
            pressedLeft = false;
            pressedRight = false;
            pressedTop = false;
            pressedBottom = false;
            pressedLeftTop = false;
            pressedRightTop = false;
            pressedLeftBottom = false;
            pressedRightBottom = false;
            widget->setCursor(Qt::ArrowCursor);
        }
    }

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

void FramelessWidget::setPadding(int padding)
{
    this->padding = padding;
}

void FramelessWidget::setMoveEnable(bool moveEnable)
{
    this->moveEnable = moveEnable;
}

void FramelessWidget::setResizeEnable(bool resizeEnable)
{
    this->resizeEnable = resizeEnable;
}

void FramelessWidget::setWidget(QWidget *widget)
{
    if (this->widget == 0) {
        this->widget = widget;
        //Set Mouse Tracking to True
        this->widget->setMouseTracking(true);
        //Binding Event Filter
        this->widget->installEventFilter(this);
        //To set hover to true, you must set this, otherwise when the parent form and the child form are all blocked from recognizing MouseMove, you need to recognize HoverMove.
        this->widget->setAttribute(Qt::WA_Hover, true);        
    }
}


Introduction of Control

  1. More than 160 exquisite controls, covering a variety of dashboards, progress bars, progress balls, compasses, curves, rulers, thermometers, navigation bars, navigation bars, flatui, highlighted buttons, sliding selectors, the lunar calendar and so on. Far more controls than qwt integrates.
  2. Each class can be separated into a separate control, zero-coupling, each control has a header file and an implementation file, independent of other files, to facilitate the integration of a single control in the form of source code into the project, less code. The control classes of qwt are interlinked and highly coupled. If you want to use one of the controls, you must include all the code.
  3. All pure Qt writing, QWidget+QPainter drawing, support any Qt version from Qt4.6 to Qt5.13, support mingw, msvc, gcc and other compilers, support any operating system such as windows+linux+mac + embedded linux, no scrambling, can be directly integrated into Qt Creator, as with its own controls, most of the effects can be set as few attributes, very convenient.
  4. Each control has a corresponding separate DEMO containing the source code of the control, which is convenient for reference. It also provides an integrated DEMO for all controls.
  5. The source code of each control has detailed Chinese annotations, which are compiled in accordance with the unified design specifications. It is convenient to learn how to compile custom controls.
  6. Each control's default color matching and demo's corresponding color matching are very beautiful.
  7. More than 130 visible controls and 6 invisible controls.
  8. Some controls provide a variety of style choices, a variety of indicator style choices.
  9. All controls adapt to form stretching changes.
  10. Integrate custom control property designer, support drag design, WYSIWYG, support import and export xml format.
  11. With activex control demo, all controls can run directly in ie browser.
  12. Integrate fontawesome graphic fonts + hundreds of graphic fonts collected by Alibaba iconfont to enjoy the fun of graphic fonts.
  13. All controls eventually generate a dynamic library file (dll or so, etc.) that can be directly integrated into the qtcreator to drag the design to use.
  14. At present, there is a version of qml, pyqt version will be considered later, if the user demand is large.
  15. Custom Control Plug-in Open Dynamic Library (permanently free), without any backdoor and restrictions, please feel free to use.
  16. At present, 32 versions of dll have been provided, of which qt_5_7_0_mingw530_32 will always guarantee the latest integrity.
  17. Increase control and improve control from time to time, update SDK from time to time. Welcome to make suggestions. Thank you!
  18. Qt introductory books recommend Hoyafei's Quick Start to Qt Creator, Qt5 Programming Introduction, and Qt advanced books recommend the official C++ GUI Qt4 Programming.
  19. I strongly recommend programmers'self-cultivation and planning series "Talking about Programmers", "Programmers' Growth Course" and "Solution Programmers". They benefit a lot and benefit a lifetime!
  20. SDK address: https://gitee.com/feiyangqingyun/QUCSDK https://github.com/feiyangqingyun/qucsdk

Posted by Soccerplayer1316 on Fri, 11 Oct 2019 18:52:13 -0700