push button: Allow for animations when clicking

Which e.g. the Material 3 style does
This commit is contained in:
Peter Hartmann 2022-07-13 07:25:24 +02:00 committed by uwerat
parent 4ef3fe3164
commit 7eed06659b
5 changed files with 89 additions and 2 deletions

View File

@ -476,11 +476,16 @@ void Editor::setupPushButton()
setGradient( Q::Panel | Q::Pressed, focusColor ); setGradient( Q::Panel | Q::Pressed, focusColor );
// we cannot use relative size here because the rect changes size during the animation:
setBoxShape( Q::Ripple, 15.5 );
setGradient( Q::Ripple, stateLayerColor( m_pal.onPrimary, m_pal.hoverOpacity ) );
setColor( Q::Text, m_pal.onPrimary ); setColor( Q::Text, m_pal.onPrimary );
setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); setColor( Q::Text | Q::Disabled, m_pal.onSurface38 );
setAnimation( Q::Panel | A::Color, qskDuration ); setAnimation( Q::Panel | A::Color, qskDuration );
setAnimation( Q::Panel | A::Metric, qskDuration ); setAnimation( Q::Panel | A::Metric, qskDuration );
setAnimation( Q::Ripple | A::Color, qskDuration );
setAnimation( Q::Text | A::Color, qskDuration ); setAnimation( Q::Text | A::Color, qskDuration );
} }

View File

@ -4,6 +4,8 @@
*****************************************************************************/ *****************************************************************************/
#include "QskPushButton.h" #include "QskPushButton.h"
#include "QskAnimationHint.h"
#include "QskAnimator.h"
#include "QskAspect.h" #include "QskAspect.h"
#include "QskBoxShapeMetrics.h" #include "QskBoxShapeMetrics.h"
#include "QskGraphic.h" #include "QskGraphic.h"
@ -16,9 +18,41 @@
#include <qfontmetrics.h> #include <qfontmetrics.h>
QSK_SUBCONTROL( QskPushButton, Panel ) QSK_SUBCONTROL( QskPushButton, Panel )
QSK_SUBCONTROL( QskPushButton, Ripple )
QSK_SUBCONTROL( QskPushButton, Text ) QSK_SUBCONTROL( QskPushButton, Text )
QSK_SUBCONTROL( QskPushButton, Graphic ) QSK_SUBCONTROL( QskPushButton, Graphic )
namespace
{
class ClickAnimator : public QskAnimator
{
public:
ClickAnimator()
: QskAnimator()
{
}
void setButton( QskPushButton* button )
{
m_button = button;
}
protected:
void advance( qreal value ) override
{
m_button->setMetric( QskPushButton::Ripple | QskAspect::Size, value );
}
void done() override
{
m_button->setMetric( QskPushButton::Ripple | QskAspect::Size, 0.0 );
}
private:
QskPushButton* m_button;
};
}
class QskPushButton::PrivateData class QskPushButton::PrivateData
{ {
public: public:
@ -49,6 +83,9 @@ class QskPushButton::PrivateData
QSizeF graphicSourceSize; QSizeF graphicSourceSize;
ClickAnimator clickAnimator;
qreal rippleSize = 0.0;
bool isCheckable : 1; bool isCheckable : 1;
bool isGraphicSourceDirty : 1; bool isGraphicSourceDirty : 1;
}; };
@ -273,6 +310,24 @@ void QskPushButton::changeEvent( QEvent* event )
Inherited::changeEvent( event ); Inherited::changeEvent( event );
} }
void QskPushButton::mousePressEvent( QMouseEvent* event )
{
Inherited::mousePressEvent( event );
const auto hint = animationHint( Ripple | QskAspect::Color );
if( hint.isValid() )
{
setSkinHint( Ripple | QskAspect::Position, event->pos() );
m_data->clickAnimator.setWindow( window() );
m_data->clickAnimator.setButton( this );
m_data->clickAnimator.setDuration( hint.duration );
m_data->clickAnimator.setEasingCurve( hint.type );
m_data->clickAnimator.start();
}
}
QskGraphic QskPushButton::loadGraphic( const QUrl& url ) const QskGraphic QskPushButton::loadGraphic( const QUrl& url ) const
{ {
return Qsk::loadGraphic( url ); return Qsk::loadGraphic( url );

View File

@ -42,7 +42,7 @@ class QSK_EXPORT QskPushButton : public QskAbstractButton
using Inherited = QskAbstractButton; using Inherited = QskAbstractButton;
public: public:
QSK_SUBCONTROLS( Panel, Text, Graphic ) QSK_SUBCONTROLS( Panel, Ripple, Text, Graphic )
QskPushButton( QQuickItem* parent = nullptr ); QskPushButton( QQuickItem* parent = nullptr );
QskPushButton( const QString& text, QQuickItem* parent = nullptr ); QskPushButton( const QString& text, QQuickItem* parent = nullptr );
@ -90,6 +90,7 @@ class QSK_EXPORT QskPushButton : public QskAbstractButton
protected: protected:
void changeEvent( QEvent* ) override; void changeEvent( QEvent* ) override;
void mousePressEvent( QMouseEvent* ) override;
void updateResources() override; void updateResources() override;
virtual QskGraphic loadGraphic( const QUrl& ) const; virtual QskGraphic loadGraphic( const QUrl& ) const;

View File

@ -6,6 +6,7 @@
#include "QskPushButtonSkinlet.h" #include "QskPushButtonSkinlet.h"
#include "QskPushButton.h" #include "QskPushButton.h"
#include "QskAnimationHint.h"
#include "QskGraphic.h" #include "QskGraphic.h"
#include "QskTextOptions.h" #include "QskTextOptions.h"
#include "QskFunctions.h" #include "QskFunctions.h"
@ -29,7 +30,7 @@ static inline Qt::Orientation qskOrientation( const QskPushButton* button )
QskPushButtonSkinlet::QskPushButtonSkinlet( QskSkin* skin ) QskPushButtonSkinlet::QskPushButtonSkinlet( QskSkin* skin )
: Inherited( skin ) : Inherited( skin )
{ {
setNodeRoles( { PanelRole, GraphicRole, TextRole } ); setNodeRoles( { PanelRole, RippleRole, GraphicRole, TextRole } );
} }
QskPushButtonSkinlet::~QskPushButtonSkinlet() = default; QskPushButtonSkinlet::~QskPushButtonSkinlet() = default;
@ -51,6 +52,17 @@ QRectF QskPushButtonSkinlet::subControlRect( const QskSkinnable* skinnable,
{ {
return contentsRect; return contentsRect;
} }
else if ( subControl == QskPushButton::Ripple )
{
const auto clickPos = button->effectiveSkinHint( QskPushButton::Ripple | QskAspect::Position ).toPointF();
const auto ratio = button->metric( QskPushButton::Ripple | QskAspect::Size );
const auto w = contentsRect.width() * ratio;
const auto h = contentsRect.height() * ratio;
const auto x = clickPos.x() - w;
const auto y = clickPos.y() - h;
const QRectF r( x, y, w * 2, h * 2 );
return r.intersected( contentsRect );
}
return Inherited::subControlRect( skinnable, contentsRect, subControl ); return Inherited::subControlRect( skinnable, contentsRect, subControl );
} }
@ -67,6 +79,19 @@ QSGNode* QskPushButtonSkinlet::updateSubNode(
return updateBoxNode( button, node, QskPushButton::Panel ); return updateBoxNode( button, node, QskPushButton::Panel );
} }
case RippleRole:
{
if( button->hasAnimationHint( QskPushButton::Ripple | QskAspect::Color )
&& button->metric( QskPushButton::Ripple | QskAspect::Size ) > 0.0 )
{
return updateBoxNode( button, node, QskPushButton::Ripple );
}
else
{
return nullptr;
}
}
case TextRole: case TextRole:
{ {
return updateTextNode( button, node ); return updateTextNode( button, node );

View File

@ -20,6 +20,7 @@ class QSK_EXPORT QskPushButtonSkinlet : public QskSkinlet
enum NodeRole enum NodeRole
{ {
PanelRole, PanelRole,
RippleRole,
TextRole, TextRole,
GraphicRole, GraphicRole,