From c4711537706d6fd6ffa45ad701511a388b930ee0 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 28 Jul 2017 18:59:59 +0100 Subject: [PATCH] Add styled progress bars, including animations for the 'infinite' case --- qrenderdoc/Styles/RDStyle/RDStyle.cpp | 182 +++++++++++++++++++++++++- qrenderdoc/Styles/RDStyle/RDStyle.h | 23 ++++ 2 files changed, 204 insertions(+), 1 deletion(-) diff --git a/qrenderdoc/Styles/RDStyle/RDStyle.cpp b/qrenderdoc/Styles/RDStyle/RDStyle.cpp index d12a50863..37b7c511d 100644 --- a/qrenderdoc/Styles/RDStyle/RDStyle.cpp +++ b/qrenderdoc/Styles/RDStyle/RDStyle.cpp @@ -59,6 +59,65 @@ static const int ComboArrowDim = 12; static const int SpinButtonDim = 12; static const int SpinMargin = 1; + +static const int ProgressMargin = 2; +static const qreal ProgressRadius = 4.0; +}; + +namespace Animation +{ +QHash animations; + +bool has(QObject *target) +{ + return animations.contains(target); +} + +QAbstractAnimation *get(QObject *target) +{ + return animations.value(target); +} + +template +AnimationType *get(QObject *target) +{ + return (AnimationType *)get(target); +} + +void stop(QObject *target) +{ + if(has(target)) + { + QAbstractAnimation *existing = get(target); + existing->stop(); + delete existing; + animations.remove(target); + } +} + +void removeOnDelete(QObject *target) +{ + if(has(target)) + animations.remove(target); +} + +void start(QAbstractAnimation *anim) +{ + QObject *target = anim->parent(); + + stop(target); + if(has(target)) + { + QAbstractAnimation *existing = get(target); + existing->stop(); + delete existing; + animations.remove(target); + } + + animations.insert(target, anim); + QObject::connect(target, &QObject::destroyed, &removeOnDelete); + anim->start(); +} }; RDStyle::RDStyle(ColorScheme scheme) : RDTweakedNativeStyle(new QCommonStyle()) @@ -175,6 +234,11 @@ void RDStyle::polish(QWidget *widget) widget->setAttribute(Qt::WA_Hover); } +void RDStyle::unpolish(QWidget *widget) +{ + Animation::stop(widget); +} + QRect RDStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, const QWidget *widget) const { @@ -458,7 +522,7 @@ QSize RDStyle::sizeFromContents(ContentsType type, const QStyleOption *opt, cons return ret; } - else if(type == CT_GroupBox || type == CT_ScrollBar) + else if(type == CT_GroupBox || type == CT_ScrollBar || type == CT_ProgressBar) { return size; } @@ -497,6 +561,9 @@ int RDStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QWid if(metric == PM_ScrollBarExtent) return Constants::ScrollButtonDim + 2; + // not used for rendering but just as an estimate of how small a progress bar can get + if(metric == PM_ProgressBarChunkWidth) + return 10; return RDTweakedNativeStyle::pixelMetric(metric, opt, widget); } @@ -519,6 +586,12 @@ int RDStyle::styleHint(StyleHint stylehint, const QStyleOption *opt, const QWidg if(stylehint == SH_UnderlineShortcut) return 0; + if(stylehint == SH_ProgressDialog_CenterCancelButton) + return 1; + + if(stylehint == SH_ProgressDialog_TextLabelAlignment) + return Qt::AlignCenter; + return RDTweakedNativeStyle::styleHint(stylehint, opt, widget, returnData); } @@ -1141,6 +1214,68 @@ void RDStyle::drawControl(ControlElement control, const QStyleOption *opt, QPain return; } + else if(control == QStyle::CE_ProgressBar) + { + QRect rect = opt->rect; + + rect.adjust(Constants::ProgressMargin, Constants::ProgressMargin, -Constants::ProgressMargin, + -Constants::ProgressMargin); + + QPainterPath path; + path.addRoundedRect(rect, Constants::ProgressRadius, Constants::ProgressRadius); + + const QStyleOptionProgressBar *progress = qstyleoption_cast(opt); + + p->save(); + p->setRenderHint(QPainter::Antialiasing); + + p->setPen(QPen(outlineBrush(opt->palette), 1.0)); + p->drawPath(path); + + if(progress->minimum >= progress->maximum && opt->styleObject) + { + // animate an 'infinite' progress bar by adding animated clip regions + if(!Animation::has(opt->styleObject)) + Animation::start(new RDProgressAnimation(2, 30, opt->styleObject)); + + RDProgressAnimation *anim = Animation::get(opt->styleObject); + + QRegion region; + + rect.setWidth(anim->chunkSize()); + + rect.moveLeft(rect.left() + anim->offset()); + + while(rect.intersects(opt->rect)) + { + region += rect; + + // step two chunks, to skip over the chunk we're excluding from the region + rect.moveLeft(rect.left() + anim->chunkSize() * 2); + } + + p->setClipRegion(region); + } + + // if we're rendering a normal progress bar, set the clip rect + if(progress->minimum < progress->maximum) + { + qreal delta = qreal(progress->progress) / qreal(progress->maximum - progress->minimum); + rect.setRight(rect.left() + rect.width() * delta); + + p->setClipRect(rect); + } + + p->fillPath(path, opt->palette.brush(QPalette::Highlight)); + + p->restore(); + + return; + } + else if(control == QStyle::CE_ProgressBarGroove) + { + return; + } RDTweakedNativeStyle::drawControl(control, opt, p, widget); } @@ -1200,3 +1335,48 @@ void RDStyle::drawRoundedRectBorder(const QStyleOption *opt, QPainter *p, const p->restore(); } + +RDProgressAnimation::RDProgressAnimation(int stepSize, int chunkSize, QObject *parent) + : QAbstractAnimation(parent) +{ + m_stepSize = stepSize; + m_offset = 0; + m_chunkSize = chunkSize; + m_prevTime = 0; +} + +void RDProgressAnimation::updateCurrentTime(int currentTime) +{ + // update every 33ms, for a 30Hz animation + const int rate = 33; + + // how many steps to take + int steps = 0; + + int delta = currentTime - m_prevTime; + + // depending on how fast we're updated, we might have to process multiple frames together. + while(delta > rate) + { + m_prevTime += rate; + delta = currentTime - m_prevTime; + + steps++; + } + + if(steps > 0) + { + m_offset += steps * m_stepSize; + + // the animation loops after two chunks, but to visualise a smooth animation with a new chunk + // coming in from the left, we wrap to negative. + // Consider the graph y = (x+1) % 2 - 1 + + if(m_offset > m_chunkSize) + m_offset -= m_chunkSize * 2; + + QEvent event(QEvent::StyleAnimationUpdate); + event.setAccepted(false); + QCoreApplication::sendEvent(parent(), &event); + } +} diff --git a/qrenderdoc/Styles/RDStyle/RDStyle.h b/qrenderdoc/Styles/RDStyle/RDStyle.h index 42387cf25..96ddd7af0 100644 --- a/qrenderdoc/Styles/RDStyle/RDStyle.h +++ b/qrenderdoc/Styles/RDStyle/RDStyle.h @@ -24,10 +24,32 @@ #pragma once +#include #include #include #include "Styles/RDTweakedNativeStyle/RDTweakedNativeStyle.h" +class RDProgressAnimation : public QAbstractAnimation +{ +private: + Q_OBJECT + +public: + RDProgressAnimation(int stepSize, int chunkSize, QObject *parent); + + int duration() const override { return -1; } + int offset() const { return m_offset; } + int chunkSize() const { return m_chunkSize; } + int stepSize() const { return m_stepSize; } +protected: + void updateCurrentTime(int currentTime) override; + + int m_prevTime; + int m_offset; + int m_chunkSize; + int m_stepSize; +}; + class RDStyle : public RDTweakedNativeStyle { private: @@ -43,6 +65,7 @@ public: void polish(QPalette &pal) override; void polish(QWidget *widget) override; + void unpolish(QWidget *widget) override; QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, const QWidget *widget = Q_NULLPTR) const override;