Material3 adjustements for progress bars

This commit is contained in:
Uwe Rathmann 2024-12-01 11:06:51 +01:00
parent d18017ca90
commit b0864965a8
7 changed files with 248 additions and 119 deletions

View File

@ -8,12 +8,14 @@ list(APPEND HEADERS
) )
list(APPEND PRIVATE_HEADERS list(APPEND PRIVATE_HEADERS
QskMaterial3ProgressBarSkinlet.h
QskMaterial3SliderSkinlet.h QskMaterial3SliderSkinlet.h
) )
list(APPEND SOURCES list(APPEND SOURCES
QskMaterial3Skin.cpp QskMaterial3Skin.cpp
QskMaterial3SkinFactory.cpp QskMaterial3SkinFactory.cpp
QskMaterial3ProgressBarSkinlet.cpp
QskMaterial3SliderSkinlet.cpp QskMaterial3SliderSkinlet.cpp
) )

View File

@ -0,0 +1,96 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskMaterial3ProgressBarSkinlet.h"
#include <QskProgressBar.h>
#include <QskBoxShapeMetrics.h>
#include <QskBoxBorderMetrics.h>
#include <QskBoxBorderColors.h>
#include <QskMargins.h>
#include <QskClipNode.h>
#include <QskSGNode.h>
using Q = QskProgressBar;
QskMaterial3ProgressBarSkinlet::QskMaterial3ProgressBarSkinlet( QskSkin* skin )
: Inherited( skin )
{
appendNodeRoles( { StopIndicatorRole } );
}
QSGNode* QskMaterial3ProgressBarSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
auto progressBar = static_cast< const QskProgressBar* >( skinnable );
switch( nodeRole )
{
case GrooveRole:
{
auto clippedNode = QskSGNode::findChildNode( node, GrooveRole );
clippedNode = Inherited::updateSubNode( skinnable, nodeRole, clippedNode );
if ( clippedNode == nullptr )
return nullptr;
auto clipNode = updateGrooveClipNode( progressBar, node );
QskSGNode::setNodeRole( clippedNode, nodeRole );
QskSGNode::setParentNode( clippedNode, clipNode );
return clipNode;
}
case StopIndicatorRole:
{
if ( !( progressBar->isIndeterminate() || progressBar->hasOrigin() ) )
return updateStopIndicatorNode( progressBar, node );
return nullptr;
}
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
QSGNode* QskMaterial3ProgressBarSkinlet::updateStopIndicatorNode(
const QskProgressBar* progressBar, QSGNode* node ) const
{
auto rect = progressBar->subControlRect( Q::Groove );
if ( rect.isEmpty() )
return nullptr;
if( progressBar->orientation() == Qt::Horizontal )
rect.setLeft( rect.right() - rect.height() );
else
rect.setBottom( rect.top() + rect.width() );
const auto color = progressBar->gradientHint( Q::Fill ).endColor();
const auto shape = progressBar->boxShapeHint( Q::Fill );
return updateBoxNode( progressBar, node, rect, shape,
QskBoxBorderMetrics(), QskBoxBorderColors(), color );
}
QSGNode* QskMaterial3ProgressBarSkinlet::updateGrooveClipNode(
const QskProgressBar* progressBar, QSGNode* node ) const
{
auto rect = progressBar->subControlRect( Q::Fill );
if ( rect.isEmpty() )
return nullptr;
QskMargins margins;
if ( progressBar->orientation() == Qt::Horizontal )
margins.setMargins( rect.height(), 0.0 );
else
margins.setMargins( 0.0, rect.width() );
rect = rect.marginsAdded( margins );
auto clipNode = QskSGNode::ensureNode< QskClipNode >( node );
clipNode->setRegion( progressBar->subControlRect( Q::Groove ), rect );
return clipNode;
}
#include "moc_QskMaterial3ProgressBarSkinlet.cpp"

View File

@ -0,0 +1,37 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_MATERIAL3_PROGRESSBAR_SKINLET_H
#define QSK_MATERIAL3_PROGRESSBAR_SKINLET_H
#include <QskProgressBarSkinlet.h>
class QskProgressBar;
class QskMaterial3ProgressBarSkinlet : QskProgressBarSkinlet
{
Q_GADGET
using Inherited = QskProgressBarSkinlet;
public:
enum NodeRole
{
StopIndicatorRole = Inherited::RoleCount,
RoleCount
};
Q_INVOKABLE QskMaterial3ProgressBarSkinlet( QskSkin* = nullptr );
protected:
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
private:
QSGNode* updateStopIndicatorNode( const QskProgressBar*, QSGNode* ) const;
QSGNode* updateGrooveClipNode( const QskProgressBar*, QSGNode* ) const;
};
#endif

View File

@ -9,6 +9,7 @@
*/ */
#include "QskMaterial3Skin.h" #include "QskMaterial3Skin.h"
#include "QskMaterial3ProgressBarSkinlet.h"
#include "QskMaterial3SliderSkinlet.h" #include "QskMaterial3SliderSkinlet.h"
#include <QskSkinHintTableEditor.h> #include <QskSkinHintTableEditor.h>
@ -487,20 +488,13 @@ void Editor::setupProgressBar()
using A = QskAspect; using A = QskAspect;
using Q = QskProgressBar; using Q = QskProgressBar;
auto size = 4_dp;
for ( auto subControl : { Q::Groove, Q::Fill } ) for ( auto subControl : { Q::Groove, Q::Fill } )
{ {
setMetric( subControl | A::Size, size ); setBoxShape( subControl, { 100, Qt::RelativeSize } );
setPadding( subControl, 0 ); setMetric( subControl | A::Size, 4_dp );
setBoxShape( subControl, 0 );
setBoxBorderMetrics( subControl, 0 );
} }
setMetric( Q::Groove | A::Size, size );
setGradient( Q::Groove, m_pal.surfaceContainerHighest ); setGradient( Q::Groove, m_pal.surfaceContainerHighest );
setGradient( Q::Groove | Q::Disabled, m_pal.onSurface12 ); setGradient( Q::Groove | Q::Disabled, m_pal.onSurface12 );
setGradient( Q::Fill, m_pal.primary ); setGradient( Q::Fill, m_pal.primary );
@ -1601,6 +1595,7 @@ qreal QskMaterial3Theme::stateOpacity( int state ) const
QskMaterial3Skin::QskMaterial3Skin( QObject* parent ) QskMaterial3Skin::QskMaterial3Skin( QObject* parent )
: Inherited( parent ) : Inherited( parent )
{ {
declareSkinlet< QskProgressBar, QskMaterial3ProgressBarSkinlet >();
declareSkinlet< QskSlider, QskMaterial3SliderSkinlet >(); declareSkinlet< QskSlider, QskMaterial3SliderSkinlet >();
} }

View File

@ -13,44 +13,76 @@
using Q = QskProgressBar; using Q = QskProgressBar;
namespace static QskIntervalF qskFillInterval( const QskProgressIndicator* indicator )
{ {
QskIntervalF qskFillInterval( const QskProgressIndicator* indicator ) qreal pos1, pos2;
if ( indicator->isIndeterminate() )
{ {
qreal pos1, pos2; const auto pos = indicator->positionHint( Q::Fill );
if ( indicator->isIndeterminate() ) static const QEasingCurve curve( QEasingCurve::InOutCubic );
{
const auto pos = indicator->positionHint( QskProgressIndicator::Fill );
static const QEasingCurve curve( QEasingCurve::InOutCubic ); const qreal off = 0.15;
const qreal off = 0.15; pos1 = curve.valueForProgress( qMax( pos - off, 0.0 ) );
pos2 = curve.valueForProgress( qMin( pos + off, 1.0 ) );
pos1 = curve.valueForProgress( qMax( pos - off, 0.0 ) );
pos2 = curve.valueForProgress( qMin( pos + off, 1.0 ) );
}
else
{
pos1 = indicator->valueAsRatio( indicator->origin() );
pos2 = indicator->valueAsRatio( indicator->value() );
}
auto bar = static_cast< const QskProgressBar* >( indicator );
if( bar->orientation() == Qt::Horizontal )
{
if ( bar->layoutMirroring() )
{
pos1 = 1.0 - pos1;
pos2 = 1.0 - pos2;
}
}
if ( pos1 > pos2 )
std::swap( pos1, pos2 );
return QskIntervalF( pos1, pos2 );
} }
else
{
pos1 = indicator->valueAsRatio( indicator->origin() );
pos2 = indicator->valueAsRatio( indicator->value() );
}
auto bar = static_cast< const QskProgressBar* >( indicator );
if( bar->orientation() == Qt::Horizontal )
{
if ( bar->layoutMirroring() )
{
pos1 = 1.0 - pos1;
pos2 = 1.0 - pos2;
}
}
if ( pos1 > pos2 )
std::swap( pos1, pos2 );
return QskIntervalF( pos1, pos2 );
}
static QskGradient qskFillGradient( const QskProgressBar* progressBar )
{
auto gradient = progressBar->gradientHint( Q::Fill );
if ( gradient.isVisible() && !gradient.isMonochrome()
&& ( gradient.type() == QskGradient::Stops ) )
{
/*
When having stops only we use a linear gradient,
where the colors are increasing in direction of the
progress value. We interprete the gradient as a
definition for the 100% situation and have to adjust
the stops for smaller bars.
For this situation it would be more convenient to
adjust the start/stop positions, but the box renderer is
not supporting this yet. TODO ...
*/
const auto intv = qskFillInterval( progressBar );
const auto stops = qskExtractedGradientStops(
gradient.stops(), intv.lowerBound(), intv.upperBound() );
gradient.setStops( stops );
gradient.setLinearDirection( progressBar->orientation() );
if ( progressBar->orientation() == Qt::Vertical || progressBar->layoutMirroring() )
gradient.reverse();
}
return gradient;
} }
QskProgressBarSkinlet::QskProgressBarSkinlet( QskSkin* skin ) QskProgressBarSkinlet::QskProgressBarSkinlet( QskSkin* skin )
@ -69,28 +101,10 @@ QRectF QskProgressBarSkinlet::subControlRect(
const auto bar = static_cast< const Q* >( skinnable ); const auto bar = static_cast< const Q* >( skinnable );
if( subControl == Q::Groove ) if( subControl == Q::Groove )
{ return grooveRect( bar, contentsRect );
const auto grooveSize = bar->metric( Q::Groove | QskAspect::Size );
auto rect = contentsRect;
if ( bar->orientation() == Qt::Horizontal )
{
rect.setY( rect.y() + 0.5 * ( rect.height() - grooveSize ) );
rect.setHeight( grooveSize );
}
else
{
rect.setX( rect.x() + 0.5 * ( rect.width() - grooveSize ) );
rect.setWidth( grooveSize );
}
return rect;
}
if( subControl == Q::Fill ) if( subControl == Q::Fill )
{ return fillRect( bar );
return barRect( bar );
}
return Inherited::subControlRect( skinnable, contentsRect, subControl ); return Inherited::subControlRect( skinnable, contentsRect, subControl );
} }
@ -104,76 +118,61 @@ QSGNode* QskProgressBarSkinlet::updateGrooveNode(
QSGNode* QskProgressBarSkinlet::updateFillNode( QSGNode* QskProgressBarSkinlet::updateFillNode(
const QskProgressIndicator* indicator, QSGNode* node ) const const QskProgressIndicator* indicator, QSGNode* node ) const
{ {
const auto bar = static_cast< const Q* >( indicator ); const auto rect = indicator->subControlRect( Q::Fill );
const auto subControl = Q::Fill;
const auto rect = indicator->subControlRect( subControl );
if ( rect.isEmpty() ) if ( rect.isEmpty() )
return nullptr; return nullptr;
auto gradient = indicator->gradientHint( subControl ); const auto progressBar = static_cast< const Q* >( indicator );
if ( !gradient.isVisible() ) return updateBoxNode( indicator, node, rect,
return nullptr; qskFillGradient( progressBar ), Q::Fill );
if ( ( gradient.type() == QskGradient::Stops ) && !gradient.isMonochrome() )
{
/*
When having stops only we use a linear gradient,
where the colors are increasing in direction of the
progress value. We interprete the gradient as a
definition for the 100% situation and have to adjust
the stops for smaller bars.
For this situation it would be more convenient to
adjust the start/stop positions, but the box renderer is
not supporting this yet. TODO ...
*/
const auto intv = qskFillInterval( bar );
const auto stops = qskExtractedGradientStops( gradient.stops(),
intv.lowerBound(), intv.upperBound() );
gradient.setStops( stops );
gradient.setLinearDirection( static_cast< Qt::Orientation >( bar->orientation() ) );
if ( bar->orientation() == Qt::Vertical || bar->layoutMirroring() )
gradient.reverse();
}
return updateBoxNode( indicator, node, rect, gradient, subControl );
} }
QRectF QskProgressBarSkinlet::barRect( const Q* bar ) const QRectF QskProgressBarSkinlet::grooveRect(
const QskProgressBar* progressBar, const QRectF& contentsRect ) const
{ {
const auto subControl = Q::Groove; const auto size = progressBar->metric( Q::Groove | QskAspect::Size );
const auto barSize = bar->metric( Q::Fill | QskAspect::Size ); auto rect = contentsRect;
auto rect = bar->subControlRect( subControl ); if ( progressBar->orientation() == Qt::Horizontal )
if ( bar->orientation() == Qt::Horizontal )
{ {
rect.setY( rect.y() + 0.5 * ( rect.height() - barSize ) ); rect.setY( rect.y() + 0.5 * ( rect.height() - size ) );
rect.setHeight( barSize ); rect.setHeight( size );
} }
else else
{ {
rect.setX( rect.x() + 0.5 * ( rect.width() - barSize ) ); rect.setX( rect.x() + 0.5 * ( rect.width() - size ) );
rect.setWidth( barSize ); rect.setWidth( size );
} }
const auto borderMetrics = bar->boxBorderMetricsHint( subControl ); return rect;
}
auto m = bar->paddingHint( subControl ); QRectF QskProgressBarSkinlet::fillRect( const QskProgressBar* progressBar ) const
{
const auto size = progressBar->metric( Q::Fill | QskAspect::Size );
auto rect = progressBar->subControlRect( Q::Groove );
if ( progressBar->orientation() == Qt::Horizontal )
{
rect.setY( rect.y() + 0.5 * ( rect.height() - size ) );
rect.setHeight( size );
}
else
{
rect.setX( rect.x() + 0.5 * ( rect.width() - size ) );
rect.setWidth( size );
}
const auto borderMetrics = progressBar->boxBorderMetricsHint( Q::Groove );
auto m = progressBar->paddingHint( Q::Groove );
m += 0.5 * borderMetrics.toAbsolute( rect.size() ).widths(); m += 0.5 * borderMetrics.toAbsolute( rect.size() ).widths();
rect = rect.marginsRemoved( m ); rect = rect.marginsRemoved( m );
const auto intv = qskFillInterval( bar ); const auto intv = qskFillInterval( progressBar );
if( bar->orientation() == Qt::Horizontal ) if( progressBar->orientation() == Qt::Horizontal )
{ {
const auto w = rect.width(); const auto w = rect.width();
@ -197,11 +196,12 @@ QSizeF QskProgressBarSkinlet::sizeHint( const QskSkinnable* skinnable,
if ( which != Qt::PreferredSize ) if ( which != Qt::PreferredSize )
return QSizeF(); return QSizeF();
const auto bar = static_cast< const Q* >( skinnable ); auto extent = skinnable->metric( Q::Groove | QskAspect::Size );
extent = qMax( extent, skinnable->metric( Q::Fill | QskAspect::Size ) );
const auto extent = bar->extent(); const auto progressBar = static_cast< const QskProgressBar* >( skinnable );
if ( bar->orientation() == Qt::Horizontal ) if ( progressBar->orientation() == Qt::Horizontal )
return QSizeF( -1, extent ); return QSizeF( -1, extent );
else else
return QSizeF( extent, -1 ); return QSizeF( extent, -1 );

View File

@ -31,7 +31,8 @@ class QSK_EXPORT QskProgressBarSkinlet : public QskProgressIndicatorSkinlet
QSGNode* updateFillNode( const QskProgressIndicator*, QSGNode* ) const override; QSGNode* updateFillNode( const QskProgressIndicator*, QSGNode* ) const override;
private: private:
QRectF barRect( const QskProgressBar* ) const; QRectF fillRect( const QskProgressBar* ) const;
QRectF grooveRect( const QskProgressBar*, const QRectF& ) const;
}; };
#endif #endif

View File

@ -148,9 +148,7 @@ void QskProgressIndicator::resetExtent()
qreal QskProgressIndicator::extent() const qreal QskProgressIndicator::extent() const
{ {
auto grooveSize = metric( Groove | QskAspect::Size ); return metric( Groove | QskAspect::Size );
auto fillSize = metric( Fill | QskAspect::Size );
return qMax( grooveSize, fillSize );
} }
void QskProgressIndicator::setOrigin( qreal origin ) void QskProgressIndicator::setOrigin( qreal origin )