qt-material-widgets/components/qtmaterialslider_internal.cpp

398 lines
14 KiB
C++

#include "qtmaterialslider_internal.h"
#include "lib/qtmaterialstatetransition.h"
#include "lib/qtmaterialstyle.h"
#include "qtmaterialslider.h"
#include <QAbstractTransition>
#include <QEventTransition>
#include <QPainter>
#include <QPropertyAnimation>
#include <QSignalTransition>
#include <QState>
/*!
* \class QtMaterialSliderStateMachine
* \internal
*/
QtMaterialSliderStateMachine::QtMaterialSliderStateMachine(QtMaterialSlider *slider,
QtMaterialSliderThumb *thumb,
QtMaterialSliderTrack *track)
: QStateMachine(slider)
, m_slider(slider)
, m_thumb(thumb)
, m_track(track)
, m_topState(new QState(QState::ParallelStates))
, m_fstState(new QState(m_topState))
, m_sndState(new QState(m_topState))
, m_inactiveState(new QState(m_fstState))
, m_focusState(new QState(m_fstState))
, m_slidingState(new QState(m_fstState))
, m_pulseOutState(new QState(m_focusState))
, m_pulseInState(new QState(m_focusState))
, m_minState(new QState(m_sndState))
, m_normalState(new QState(m_sndState))
{
addState(m_topState);
setInitialState(m_topState);
m_fstState->setInitialState(m_inactiveState);
m_focusState->setInitialState(m_pulseOutState);
m_inactiveState->assignProperty(thumb, "haloSize", 0);
m_slidingState->assignProperty(thumb, "haloSize", 0);
m_pulseOutState->assignProperty(thumb, "haloSize", 35);
m_pulseInState->assignProperty(thumb, "haloSize", 28);
m_inactiveState->assignProperty(thumb, "diameter", 11);
m_focusState->assignProperty(thumb, "diameter", 11);
m_slidingState->assignProperty(thumb, "diameter", 17);
QAbstractTransition *transition;
QtMaterialStateTransition *customTransition;
QPropertyAnimation *animation;
// Show halo on mouse enter
customTransition = new QtMaterialStateTransition(SliderNoFocusMouseEnter);
customTransition->setTargetState(m_focusState);
animation = new QPropertyAnimation(thumb, "haloSize", this);
animation->setEasingCurve(QEasingCurve::InOutSine);
customTransition->addAnimation(animation);
customTransition->addAnimation(new QPropertyAnimation(track, "fillColor", this));
m_inactiveState->addTransition(customTransition);
// Show halo on focus in
transition = new QEventTransition(slider, QEvent::FocusIn);
transition->setTargetState(m_focusState);
animation = new QPropertyAnimation(thumb, "haloSize", this);
animation->setEasingCurve(QEasingCurve::InOutSine);
transition->addAnimation(animation);
transition->addAnimation(new QPropertyAnimation(track, "fillColor", this));
m_inactiveState->addTransition(transition);
// Hide halo on focus out
transition = new QEventTransition(slider, QEvent::FocusOut);
transition->setTargetState(m_inactiveState);
animation = new QPropertyAnimation(thumb, "haloSize", this);
animation->setEasingCurve(QEasingCurve::InOutSine);
transition->addAnimation(animation);
transition->addAnimation(new QPropertyAnimation(track, "fillColor", this));
m_focusState->addTransition(transition);
// Hide halo on mouse leave, except if widget has focus
customTransition = new QtMaterialStateTransition(SliderNoFocusMouseLeave);
customTransition->setTargetState(m_inactiveState);
animation = new QPropertyAnimation(thumb, "haloSize", this);
animation->setEasingCurve(QEasingCurve::InOutSine);
customTransition->addAnimation(animation);
customTransition->addAnimation(new QPropertyAnimation(track, "fillColor", this));
m_focusState->addTransition(customTransition);
// Pulse in
transition = new QSignalTransition(m_pulseOutState, SIGNAL(propertiesAssigned()));
transition->setTargetState(m_pulseInState);
animation = new QPropertyAnimation(thumb, "haloSize", this);
animation->setEasingCurve(QEasingCurve::InOutSine);
animation->setDuration(1000);
transition->addAnimation(animation);
m_pulseOutState->addTransition(transition);
// Pulse out
transition = new QSignalTransition(m_pulseInState, SIGNAL(propertiesAssigned()));
transition->setTargetState(m_pulseOutState);
animation = new QPropertyAnimation(thumb, "haloSize", this);
animation->setEasingCurve(QEasingCurve::InOutSine);
animation->setDuration(1000);
transition->addAnimation(animation);
m_pulseInState->addTransition(transition);
// Slider pressed
transition = new QSignalTransition(slider, SIGNAL(sliderPressed()));
transition->setTargetState(m_slidingState);
animation = new QPropertyAnimation(thumb, "diameter", this);
animation->setDuration(70);
transition->addAnimation(animation);
animation = new QPropertyAnimation(thumb, "haloSize", this);
animation->setEasingCurve(QEasingCurve::InOutSine);
transition->addAnimation(animation);
m_focusState->addTransition(transition);
// Slider released
transition = new QSignalTransition(slider, SIGNAL(sliderReleased()));
transition->setTargetState(m_focusState);
animation = new QPropertyAnimation(thumb, "diameter", this);
animation->setDuration(70);
transition->addAnimation(animation);
animation = new QPropertyAnimation(thumb, "haloSize", this);
animation->setEasingCurve(QEasingCurve::InOutSine);
transition->addAnimation(animation);
m_slidingState->addTransition(transition);
// Min. value transitions
m_minState->assignProperty(thumb, "borderWidth", 2);
m_normalState->assignProperty(thumb, "borderWidth", 0);
m_sndState->setInitialState(m_minState);
customTransition = new QtMaterialStateTransition(SliderChangedFromMinimum);
customTransition->setTargetState(m_normalState);
animation = new QPropertyAnimation(thumb, "fillColor", this);
animation->setDuration(200);
customTransition->addAnimation(animation);
animation = new QPropertyAnimation(thumb, "haloColor", this);
animation->setDuration(300);
customTransition->addAnimation(animation);
animation = new QPropertyAnimation(thumb, "borderColor", this);
animation->setDuration(200);
customTransition->addAnimation(animation);
animation = new QPropertyAnimation(thumb, "borderWidth", this);
animation->setDuration(200);
customTransition->addAnimation(animation);
m_minState->addTransition(customTransition);
customTransition = new QtMaterialStateTransition(SliderChangedToMinimum);
customTransition->setTargetState(m_minState);
animation = new QPropertyAnimation(thumb, "fillColor", this);
animation->setDuration(200);
customTransition->addAnimation(animation);
animation = new QPropertyAnimation(thumb, "haloColor", this);
animation->setDuration(300);
customTransition->addAnimation(animation);
animation = new QPropertyAnimation(thumb, "borderColor", this);
animation->setDuration(200);
customTransition->addAnimation(animation);
animation = new QPropertyAnimation(thumb, "borderWidth", this);
animation->setDuration(200);
customTransition->addAnimation(animation);
m_normalState->addTransition(customTransition);
setupProperties();
}
QtMaterialSliderStateMachine::~QtMaterialSliderStateMachine() {}
void QtMaterialSliderStateMachine::setupProperties()
{
QColor trackColor = m_slider->trackColor();
QColor thumbColor = m_slider->thumbColor();
m_inactiveState->assignProperty(m_track, "fillColor", trackColor.lighter(130));
m_slidingState->assignProperty(m_track, "fillColor", trackColor);
m_focusState->assignProperty(m_track, "fillColor", trackColor);
QColor holeColor = m_slider->palette().color(QPalette::Base);
if (m_slider->parentWidget()) {
holeColor = m_slider->parentWidget()->palette().color(m_slider->backgroundRole());
}
m_minState->assignProperty(m_thumb, "fillColor", holeColor);
m_minState->assignProperty(m_thumb, "haloColor", trackColor);
m_minState->assignProperty(m_thumb, "borderColor", trackColor);
m_normalState->assignProperty(m_thumb, "fillColor", thumbColor);
m_normalState->assignProperty(m_thumb, "haloColor", thumbColor);
m_normalState->assignProperty(m_thumb, "borderColor", thumbColor);
m_slider->update();
}
/*!
* \class QtMaterialSliderThumb
* \internal
*/
QtMaterialSliderThumb::QtMaterialSliderThumb(QtMaterialSlider *slider)
: QtMaterialOverlayWidget(slider->parentWidget())
, m_slider(slider)
, m_diameter(11)
, m_borderWidth(2)
, m_haloSize(0)
, m_offset(0)
{
slider->installEventFilter(this);
setAttribute(Qt::WA_TransparentForMouseEvents, true);
}
QtMaterialSliderThumb::~QtMaterialSliderThumb() {}
bool QtMaterialSliderThumb::eventFilter(QObject *obj, QEvent *event)
{
if (QEvent::ParentChange == event->type()) {
setParent(m_slider->parentWidget());
}
return QtMaterialOverlayWidget::eventFilter(obj, event);
}
void QtMaterialSliderThumb::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
// Halo
QBrush brush;
brush.setStyle(Qt::SolidPattern);
brush.setColor(m_haloColor);
painter.setBrush(brush);
painter.setPen(Qt::NoPen);
QPointF disp = Qt::Horizontal == m_slider->orientation() ?
QPointF(QT_MATERIAL_SLIDER_MARGIN + m_offset, m_slider->height() / 2.0) :
QPointF(m_slider->width() / 2.0, QT_MATERIAL_SLIDER_MARGIN + m_offset);
QRectF halo((m_slider->pos() - QPointF(m_haloSize, m_haloSize) / 2) + disp, QSizeF(m_haloSize, m_haloSize));
painter.setOpacity(0.15);
painter.drawEllipse(halo);
// Knob
const bool isMin = m_slider->value() == m_slider->minimum();
brush.setColor(m_slider->isEnabled() ? m_fillColor : m_slider->disabledColor());
painter.setBrush(!m_slider->isEnabled() && isMin ? Qt::NoBrush : brush);
if (m_slider->isEnabled() || isMin) {
QPen pen;
pen.setColor(m_borderColor);
pen.setWidthF((isMin && !m_slider->isEnabled()) ? 1.7 : m_borderWidth);
painter.setPen(pen);
} else {
painter.setPen(Qt::NoPen);
}
QRectF geometry = Qt::Horizontal == m_slider->orientation() ? QRectF(m_offset,
m_slider->height() / 2.0 - QT_MATERIAL_SLIDER_MARGIN,
QT_MATERIAL_SLIDER_MARGIN * 2,
QT_MATERIAL_SLIDER_MARGIN * 2)
.translated(m_slider->pos()) :
QRectF(m_slider->width() / 2.0 - QT_MATERIAL_SLIDER_MARGIN,
m_offset,
QT_MATERIAL_SLIDER_MARGIN * 2,
QT_MATERIAL_SLIDER_MARGIN * 2)
.translated(m_slider->pos());
qreal s = m_slider->isEnabled() ? m_diameter : 7;
QRectF thumb(0, 0, s, s);
thumb.moveCenter(geometry.center());
painter.setOpacity(1);
painter.drawEllipse(thumb);
}
/*!
* \class QtMaterialSliderTrack
* \internal
*/
QtMaterialSliderTrack::QtMaterialSliderTrack(QtMaterialSliderThumb *thumb, QtMaterialSlider *slider)
: QtMaterialOverlayWidget(slider->parentWidget())
, m_slider(slider)
, m_thumb(thumb)
, m_trackWidth(2)
{
slider->installEventFilter(this);
setAttribute(Qt::WA_TransparentForMouseEvents, true);
connect(slider, SIGNAL(sliderMoved(int)), this, SLOT(update()));
}
QtMaterialSliderTrack::~QtMaterialSliderTrack() {}
bool QtMaterialSliderTrack::eventFilter(QObject *obj, QEvent *event)
{
if (QEvent::ParentChange == event->type()) {
setParent(m_slider->parentWidget());
}
return QtMaterialOverlayWidget::eventFilter(obj, event);
}
void QtMaterialSliderTrack::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QBrush fg;
fg.setStyle(Qt::SolidPattern);
fg.setColor(m_slider->isEnabled() ? m_slider->thumbColor() : m_slider->disabledColor());
QBrush bg;
bg.setStyle(Qt::SolidPattern);
bg.setColor(m_slider->isEnabled() ? m_fillColor : m_slider->disabledColor());
qreal offset = m_thumb->offset();
if (Qt::Horizontal == m_slider->orientation()) {
painter.translate(m_slider->x() + QT_MATERIAL_SLIDER_MARGIN,
m_slider->y() + m_slider->height() / 2.0 - static_cast<qreal>(m_trackWidth) / 2.0);
} else {
painter.translate(m_slider->x() + m_slider->width() / 2.0 - static_cast<qreal>(m_trackWidth) / 2.0,
m_slider->y() + QT_MATERIAL_SLIDER_MARGIN);
}
QRectF geometry = Qt::Horizontal == m_slider->orientation() ?
QRectF(0, 0, m_slider->width() - QT_MATERIAL_SLIDER_MARGIN * 2, m_trackWidth) :
QRectF(0, 0, m_trackWidth, m_slider->height() - QT_MATERIAL_SLIDER_MARGIN * 2);
QRectF bgRect;
QRectF fgRect;
if (Qt::Horizontal == m_slider->orientation()) {
fgRect = QRectF(0, 0, offset, m_trackWidth);
bgRect = QRectF(offset, 0, m_slider->width(), m_trackWidth).intersected(geometry);
} else {
fgRect = QRectF(0, 0, m_trackWidth, offset);
bgRect = QRectF(0, offset, m_trackWidth, m_slider->height()).intersected(geometry);
}
if (!m_slider->isEnabled()) {
fgRect = fgRect.width() < 9 ? QRectF() : fgRect.adjusted(0, 0, -6, 0);
bgRect = bgRect.width() < 9 ? QRectF() : bgRect.adjusted(6, 0, 0, 0);
}
if (m_slider->invertedAppearance()) {
qSwap(bgRect, fgRect);
}
painter.fillRect(bgRect, bg);
painter.fillRect(fgRect, fg);
}