Skip to content

Instantly share code, notes, and snippets.

@oclero
Last active November 17, 2021 13:26
Show Gist options
  • Save oclero/1b86893772eb2ced371e3927a7797636 to your computer and use it in GitHub Desktop.
Save oclero/1b86893772eb2ced371e3927a7797636 to your computer and use it in GitHub Desktop.
QPushButton shadow
#include "ButtonShadowBehavior.hpp"
#include <QAbstractButton>
#include <QtWidgets/QGraphicsDropShadowEffect>
#include <QState>
#include <QStateMachine>
#include <QSignalTransition>
#include <QParallelAnimationGroup>
#include <QVariantAnimation>
namespace abvent::noon {
ButtonShadowBehavior::ButtonShadowBehavior(QAbstractButton* parent)
: QObject(parent)
, _button(parent) {
install();
}
ButtonShadowBehavior::~ButtonShadowBehavior() {
uninstall();
}
void ButtonShadowBehavior::install() {
if (_installed)
return;
_installed = true;
// Setup shadow.
_shadow = new QGraphicsDropShadowEffect(_button);
_shadow->setBlurRadius(_blurNormal);
_shadow->setOffset(_offset);
_shadow->setColor(_colorNormal);
_button->setGraphicsEffect(_shadow);
// Setup state machine.
_stateMachine = new QStateMachine(this);
auto stateNormal = new QState();
auto statePressed = new QState();
// Normal->Pressed
auto normalToPressedTransition = stateNormal->addTransition(_button, &QAbstractButton::pressed, statePressed);
{
auto normalToPressedAnimation = new QParallelAnimationGroup(_stateMachine);
auto blurAnimation = new QVariantAnimation(normalToPressedAnimation);
blurAnimation->setDuration(_animationDuration);
blurAnimation->setEndValue(_blurPressed);
QObject::connect(blurAnimation, &QVariantAnimation::valueChanged, this, [this](const QVariant& value) {
if (_shadow) {
const auto newValue = value.toInt();
_shadow->setBlurRadius(newValue);
}
});
auto colorAnimation = new QVariantAnimation(normalToPressedAnimation);
colorAnimation->setDuration(_animationDuration);
colorAnimation->setEndValue(_colorPressed);
QObject::connect(colorAnimation, &QVariantAnimation::valueChanged, this, [this](const QVariant& value) {
if (_shadow) {
const auto newValue = value.value<QColor>();
_shadow->setColor(newValue);
}
});
normalToPressedAnimation->addAnimation(blurAnimation);
normalToPressedAnimation->addAnimation(colorAnimation);
normalToPressedTransition->addAnimation(normalToPressedAnimation);
QObject::connect(normalToPressedTransition, &QAbstractTransition::triggered, normalToPressedAnimation,
[this, normalToPressedAnimation, blurAnimation, colorAnimation]() {
blurAnimation->setStartValue(static_cast<int>(_shadow->blurRadius()));
colorAnimation->setStartValue(_shadow->color());
normalToPressedAnimation->start();
});
}
// Pressed->Normal
auto pressedToNormalTransition = statePressed->addTransition(_button, &QAbstractButton::released, stateNormal);
{
auto pressedToNormalAnimation = new QParallelAnimationGroup(_stateMachine);
auto blurAnimation = new QVariantAnimation(pressedToNormalAnimation);
blurAnimation->setDuration(_animationDuration);
blurAnimation->setEndValue(_blurNormal);
QObject::connect(blurAnimation, &QVariantAnimation::valueChanged, this, [this](const QVariant& value) {
if (_shadow) {
_shadow->setBlurRadius(value.toInt());
}
});
auto colorAnimation = new QVariantAnimation(pressedToNormalAnimation);
colorAnimation->setDuration(_animationDuration);
colorAnimation->setEndValue(_colorNormal);
QObject::connect(colorAnimation, &QVariantAnimation::valueChanged, this, [this](const QVariant& value) {
if (_shadow) {
_shadow->setColor(value.value<QColor>());
}
});
pressedToNormalTransition->addAnimation(blurAnimation);
pressedToNormalTransition->addAnimation(colorAnimation);
pressedToNormalTransition->addAnimation(pressedToNormalAnimation);
QObject::connect(pressedToNormalTransition, &QAbstractTransition::triggered, pressedToNormalAnimation,
[this, pressedToNormalAnimation, blurAnimation, colorAnimation]() {
if (_shadow) {
blurAnimation->setStartValue(static_cast<int>(_shadow->blurRadius()));
colorAnimation->setStartValue(_shadow->color());
pressedToNormalAnimation->start();
}
});
}
_stateMachine->addState(stateNormal); // Takes ownership.
_stateMachine->addState(statePressed); // Takes ownership.
_stateMachine->setInitialState(stateNormal);
_stateMachine->start();
}
void ButtonShadowBehavior::uninstall() {
if (!_installed)
return;
_installed = false;
_stateMachine.clear();
_shadow.clear();
_button->setGraphicsEffect(nullptr);
}
#pragma once
class ButtonShadowBehavior : public QObject {
Q_OBJECT
public:
explicit ButtonShadowBehavior(QAbstractButton* parent = nullptr);
~ButtonShadowBehavior();
private:
void install();
void uninstall();
bool _installed{ false };
QPointer<QAbstractButton> _button{ nullptr };
QPointer<QGraphicsDropShadowEffect> _shadow{ nullptr };
QPointer<QStateMachine> _stateMachine{ nullptr };
int _animationDuration{ 150 };
double _blurNormal{ 8. };
double _blurPressed{ 32. };
QColor _colorNormal{ 0, 0, 0, 80 };
QColor _colorPressed{ 0, 0, 0, 255 };
QPointF _offset{ 0., 1. };
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment