Qt custom control ------ SwitchButton

brief introduction

SwitchButton is a very common and practical button. I was surprised that there was no Switch in Qt, so I had to implement it myself.

effect

The renderings are shown below

Control split

1, The control is generally composed of two parts, one is the background and the other is the white slider. The original background was to use QWidget and set the fillet directly, and then found that the effect was very poor. Therefore, the background is drawn by QPainter, which is divided into two 180 ° arcs and a rectangle.

2, Background = left arc + rectangle + right arc. There are two methods. One is the literal method. Draw two arcs through drawArc, draw up and down two lines through drawLine, and then fill. Another simple and crude method is to draw ellipses directly at the left and right ends, draw rectangles in the middle, and then fill the background, similar to the method in the figure below.

3, The personal method of slider animation is to divide the width of the whole button into N equal parts, that is, width / N. the value obtained is the distance each time the slider moves. Here I directly call it step. Then use the timer to redraw the button regularly. Each time you redraw, the position of the slider is the last distance + step, which forms a simple animation. If you don't want the animation to directly define the position of start and end, just redraw it directly.

4, For the arc width and rectangle width of the button, the individual takes the minimum value of width and height as the radius of the arc, and then the rectangle width is the maximum value of width and height minus the minimum value of width and height. Of course, there is a problem. When the width and height are the same, the button is a circle, so when setting the width and height, it is best to have a difference of about 10px between the width and height.

5, In order to be beautiful, it is best to have a spacing of about 2~3px between the slider and the background.

realization

Because it is a module to be used for a long time, some common interfaces still have to be exposed.
When the module is implemented, it is just an ordinary QWidget project, so just build a QWidget.

#ifndef SWITCHBUTTON_H
#define SWITCHBUTTON_H

#include <QWidget>
#include <QTimer>

namespace Ui {
class SwitchButton;
}

class SwitchButton : public QWidget
{
    Q_OBJECT

public:
    explicit SwitchButton(QWidget *parent = nullptr);
    ~SwitchButton();
    /**
     * @brief SetSize Sets the size of the button
     * @param nWidth New width of button
     * @param nHeight New height of button
     */
    void SetSize(int nWidth, int nHeight);

    /**
     * @brief SetActiveColor Set the color when the button is activated
     * @param color Activate color
     */
    void SetActiveColor(QColor color);

    /**
     * @brief SetInactiveColor Set the color when the button is not activated
     * @param color Inactive color
     */
    void SetInactiveColor(QColor color);

    /**
     * @brief SetSliderColor Set slider color
     * @param color Color of slider
     */
    void SetSliderColor(QColor color);

    /**
     * @brief SetStatus Set button status
     * @param bActive true: Activated, false: not activated
     */
    void SetStatus(bool bActive);

    /**
     * @brief GetStatus Get the current status of the button
     * @return  true: Activated, false: not activated
     */
    inline bool GetStatus();

protected:
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void DrawSlider(QPainter* p);

private:
    Ui::SwitchButton *ui;
    bool m_bActive; // Activate
    float m_fStep; // The step value of the slider at each redraw
    float m_fCurrent; // Current slider position (left position)
    int m_nArcRadius; // Radius of arc
    int m_nRectWidth; // Width of rectangle
    QColor m_colorActive; // Color when activated
    QColor m_colorInactive;
    QColor m_colorSlider;
    QTimer m_timer;
private slots:
    void SwitchAnimation(); // Toggle animation
#include "SwitchButton.h"
#include "ui_SwitchButton.h"
#include <QPainter>
#include <QPainterPath>

SwitchButton::SwitchButton(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SwitchButton)
{
    ui->setupUi(this);
    resize(100, 60); // Default 100, 60 width and height
    m_bActive = false; // Default inactive
    m_fStep = width() / 15.0;
    m_fCurrent = 2.0f;
    m_nArcRadius = std::min(width(), height()); // Default radius
    m_nRectWidth = width() - m_nArcRadius;
    m_colorActive = Qt::green;
    m_colorInactive = Qt::red;
    m_colorSlider = Qt::white;
    setCursor(QCursor(Qt::PointingHandCursor));
    connect(&m_timer, &QTimer::timeout, this, &SwitchButton::SwitchAnimation);
}

SwitchButton::~SwitchButton()
{
    delete ui;
}

void SwitchButton::SetSize(int nWidth, int nHeight)
{
    resize(nWidth, nHeight);
    m_nArcRadius = std::min(width(), height());
    m_nRectWidth = width() - m_nArcRadius;
    m_fStep = width() / 15.0;
}

void SwitchButton::SetActiveColor(QColor color)
{
    m_colorActive = color;
}

void SwitchButton::SetInactiveColor(QColor color)
{
    m_colorInactive = color;
}

void SwitchButton::SetSliderColor(QColor color)
{
    m_colorSlider = color;
}

void SwitchButton::SetStatus(bool bActive)
{
    if(m_bActive == bActive) return;
    m_bActive = bActive;
    if(m_bActive) m_fCurrent = width() - m_nArcRadius;
    else m_fCurrent = 2.0f;
    update();
}

bool SwitchButton::GetStatus()
{
    return m_bActive;
}

void SwitchButton::paintEvent(QPaintEvent *)
{
    QPainter p;
    p.begin(this);
    p.setRenderHint(QPainter::Antialiasing, true);
    p.setPen(Qt::NoPen);
    if(m_bActive) p.setBrush(QBrush(m_colorActive));
    else p.setBrush(QBrush(m_colorInactive));

    QPainterPath leftPath;
    leftPath.addEllipse(0, 0, m_nArcRadius, m_nArcRadius);

    QPainterPath middlePath;
    middlePath.addRect(m_nArcRadius / 2, 0, m_nRectWidth, m_nArcRadius);

    QPainterPath rightPath;
    rightPath.addEllipse(m_nRectWidth, 0, m_nArcRadius, m_nArcRadius);

    QPainterPath path = leftPath + middlePath + rightPath;

    p.drawPath(path);

    DrawSlider(&p);

    p.end();
}

void SwitchButton::mousePressEvent(QMouseEvent *event)
{
    QWidget::mousePressEvent(event);
    m_bActive = !m_bActive;
    m_timer.start(15);
}

void SwitchButton::DrawSlider(QPainter *p)
{
    p->setBrush(QBrush(m_colorSlider));
    p->drawEllipse(m_fCurrent, 1, m_nArcRadius - 2, m_nArcRadius - 2);
}

void SwitchButton::SwitchAnimation()
{
    if(!m_bActive)
    {
        m_fCurrent -= m_fStep;
        if(m_fCurrent <= 2.0f)
        {
            m_fCurrent = 2.0f;
            m_timer.stop();
        }
    }
    else
    {
        m_fCurrent += m_fStep;
        if(m_fCurrent >= width() - m_nArcRadius)
        {
            m_fCurrent = width() - m_nArcRadius;
            m_timer.stop();
        }
    }
    update();
}

Such a simple SwitchButton is completed.
If there is something wrong or there is a better plan, please correct it in the comment area.

Keywords: C++ Qt

Added by markc1 on Sun, 30 Jan 2022 08:17:31 +0200