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

View File

@ -13,44 +13,76 @@
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() )
{
const auto pos = indicator->positionHint( QskProgressIndicator::Fill );
static const QEasingCurve curve( QEasingCurve::InOutCubic );
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 ) );
}
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 );
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 );
}
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 )
@ -69,28 +101,10 @@ QRectF QskProgressBarSkinlet::subControlRect(
const auto bar = static_cast< const Q* >( skinnable );
if( subControl == Q::Groove )
{
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;
}
return grooveRect( bar, contentsRect );
if( subControl == Q::Fill )
{
return barRect( bar );
}
return fillRect( bar );
return Inherited::subControlRect( skinnable, contentsRect, subControl );
}
@ -104,76 +118,61 @@ QSGNode* QskProgressBarSkinlet::updateGrooveNode(
QSGNode* QskProgressBarSkinlet::updateFillNode(
const QskProgressIndicator* indicator, QSGNode* node ) const
{
const auto bar = static_cast< const Q* >( indicator );
const auto subControl = Q::Fill;
const auto rect = indicator->subControlRect( subControl );
const auto rect = indicator->subControlRect( Q::Fill );
if ( rect.isEmpty() )
return nullptr;
auto gradient = indicator->gradientHint( subControl );
if ( !gradient.isVisible() )
return nullptr;
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 );
const auto progressBar = static_cast< const Q* >( indicator );
return updateBoxNode( indicator, node, rect,
qskFillGradient( progressBar ), Q::Fill );
}
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 = bar->subControlRect( subControl );
if ( bar->orientation() == Qt::Horizontal )
auto rect = contentsRect;
if ( progressBar->orientation() == Qt::Horizontal )
{
rect.setY( rect.y() + 0.5 * ( rect.height() - barSize ) );
rect.setHeight( barSize );
rect.setY( rect.y() + 0.5 * ( rect.height() - size ) );
rect.setHeight( size );
}
else
{
rect.setX( rect.x() + 0.5 * ( rect.width() - barSize ) );
rect.setWidth( barSize );
rect.setX( rect.x() + 0.5 * ( rect.width() - size ) );
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();
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();
@ -197,11 +196,12 @@ QSizeF QskProgressBarSkinlet::sizeHint( const QskSkinnable* skinnable,
if ( which != Qt::PreferredSize )
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 );
else
return QSizeF( extent, -1 );

View File

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

View File

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