自定义开关按钮

效果示例:

MSwitchButton.h

//********************************************************
/// @brief 开关按钮
/// @author y974183789@gmail.com
/// @date 2021/9/1
/// @note 状态切换时,支持动画效果
/// @version 1.0.0
//********************************************************

#pragma once

#include <QtCore/QPropertyAnimation>
#include <QtWidgets/QAbstractButton>
#include "MDefine.h"

class MSwitchButton : public QAbstractButton {
	Q_OBJECT
	Q_PROPERTY(int offset READ offset WRITE setOffset)
public:
    //开关布局方向
    typedef enum IndicatorLayoutDirection{
        LeftToRight,//左到右
        Horizontal = LeftToRight,//水平
        RightToLeft,//右到左
        TopToBottom,//上到下
        Vertical = TopToBottom,//垂直
        BottomToTop,//下到上
    }IndicatorLayoutDirection;
public:
	MSwitchButton(QWidget* parent = nullptr);

	void setChecked(bool bChecked);
    void setIndicatorLayout(IndicatorLayoutDirection layoutDirection);

protected:
	void paintEvent(QPaintEvent*) override;
	void mouseReleaseEvent(QMouseEvent*) override;
	//鼠标进入事件
    void enterEvent(MEnterEvent *pEvent) Q_DECL_OVERRIDE;
	void keyPressEvent(QKeyEvent *e) override;
    QSize sizeHint() const Q_DECL_OVERRIDE;

private:
    int offset() const;
    void setOffset(int nOffset) {
        m_nOffset = nOffset;
        update();
    }

private:
	int m_nOffset = 0;
	int m_nRadius = 7;
	int m_nMargin = 2;
	QPropertyAnimation *m_animation = nullptr;
    IndicatorLayoutDirection m_layoutDirection;
};

 MSwitchButton.cpp

//********************************************************
/// @brief 开关按钮
/// @author y974183789@gmail.com
/// @date 2021/9/1
/// @note 状态切换时,支持动画效果
/// @version 1.0.0
//********************************************************

#include "MSwitchButton.h"
#include <QtCore/QDebug>
#include <QtGui/QPainter>
#include <QtGui/QMouseEvent>
#include "Components/MDefine.h"
#include <cmath>

MSwitchButton::MSwitchButton(QWidget *parent) : QAbstractButton(parent)
{
    setIndicatorLayout(Horizontal);
	setOffset(m_nRadius + m_nMargin);
	setCheckable(true);
	setStyleSheet("/* /");

	m_animation = new QPropertyAnimation(this, "offset", this);
}

void MSwitchButton::paintEvent(QPaintEvent *e)
{
	QPainter p(this);
	p.setPen(Qt::NoPen);
    QRect rectBg(QRect(0, 0, width(), height()));
    QRectF rectIndicator;
    switch (m_layoutDirection) {
        case LeftToRight:
            rectIndicator = QRectF(offset() - m_nRadius, qMax(0, height() / 2 - m_nRadius), m_nRadius * 2, m_nRadius * 2);
            break;
        case RightToLeft:
            rectIndicator = QRectF(width() - offset() - m_nRadius, qMax(0, height() / 2 - m_nRadius), m_nRadius * 2, m_nRadius * 2);
            break;
        case TopToBottom:
            rectIndicator = QRectF(qMax(0, width() / 2 - m_nRadius), offset() - m_nRadius, m_nRadius * 2, m_nRadius * 2);
            break;
        case BottomToTop:
            rectIndicator = QRectF(qMax(0, width() / 2 - m_nRadius), height() - offset() - m_nRadius, m_nRadius * 2, m_nRadius * 2);
            break;
        default:
            break;
    }
	p.setBrush(isChecked() ? QBrush(M_COLOR_HIGHLIGHT_BACKGROUND) : QBrush(M_COLOR_BACKGROUND));
	p.setOpacity(1.0);
	p.setRenderHint(QPainter::Antialiasing, true);
	p.drawRoundedRect(rectBg, qMin(width(), height()) / 2.0, qMin(width(), height()) / 2.0);
	p.setBrush(isChecked() ? QBrush(M_COLOR_HIGHLIGHT_TEXT) : QBrush(M_COLOR_NORMAL_TEXT));
	p.setOpacity(1.0);
	p.drawEllipse(rectIndicator);
}

void MSwitchButton::mouseReleaseEvent(QMouseEvent *e)
{
	if (e->button() & Qt::LeftButton)
	{
		int nLeftOffset = m_nRadius + m_nMargin;
		int nRightOffset = 0;

        switch (m_layoutDirection) {
            case LeftToRight:
            case RightToLeft:
                nRightOffset = width() - (m_nRadius + m_nMargin);
                break;
            case TopToBottom:
            case BottomToTop:
                nRightOffset = height() - (m_nRadius + m_nMargin);
                break;
            default:
                break;
        }
        if(isChecked()) {
            std::swap(nLeftOffset, nRightOffset);
        }
        m_animation->setStartValue(nLeftOffset);
        m_animation->setEndValue(nRightOffset);
        m_animation->setDuration(120);
        m_animation->start();
	}
	QAbstractButton::mouseReleaseEvent(e);
}

void MSwitchButton::enterEvent(MEnterEvent *event)
{
	setCursor(Qt::PointingHandCursor);
	QAbstractButton::enterEvent(event);
}

void MSwitchButton::keyPressEvent(QKeyEvent *e)
{
	if (e->key() == Qt::Key_Space)
	{
		return;
	}
	return QAbstractButton::keyPressEvent(e);
}

void MSwitchButton::setChecked(bool bChecked)
{
    if(bChecked != isChecked()) {
        QAbstractButton::setChecked(bChecked);

        switch (m_layoutDirection) {
            case LeftToRight:
                setOffset(isChecked() ? width() - (m_nRadius + m_nMargin) : m_nRadius + m_nMargin);
                break;
            case RightToLeft:
                setOffset(isChecked() ? m_nRadius + m_nMargin : width() - (m_nRadius + m_nMargin));
                break;
            case TopToBottom:
                setOffset(isChecked() ? height() - (m_nRadius + m_nMargin) : m_nRadius + m_nMargin);
                break;
            case BottomToTop:
                setOffset(isChecked() ? m_nRadius + m_nMargin : height() - (m_nRadius + m_nMargin));
                break;
            default:
                break;
        }
        update();
    }
}

void MSwitchButton::setIndicatorLayout(IndicatorLayoutDirection layoutDirection) {
    m_layoutDirection = layoutDirection;
}

QSize MSwitchButton::sizeHint() const
{
    QSize size = QAbstractButton::sizeHint();
    if (TopToBottom == m_layoutDirection || BottomToTop == m_layoutDirection)
    {
        size.setHeight(qMax(30, size.width()));
        size.setWidth(20);
    } else {
        size.setHeight(qMax(20, size.height()));
        size.setWidth(qMax(40, size.width()));
    }

    return size;
}

int MSwitchButton::offset() const {
    switch (m_layoutDirection) {
        case LeftToRight:
        case RightToLeft:
            return qMax(m_nMargin + m_nRadius, qMin(m_nOffset, width() - m_nRadius - m_nMargin));
        case TopToBottom:
        case BottomToTop:
            return qMax(m_nMargin, qMin(m_nOffset, height() - m_nRadius - m_nMargin));
        default:
            break;
    }
    return m_nOffset;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值