qskinny/src/controls/QskProgressBarSkinlet.cpp

219 lines
5.9 KiB
C++
Raw Normal View History

2020-07-31 14:57:22 +00:00
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
2023-04-06 07:23:37 +00:00
* SPDX-License-Identifier: BSD-3-Clause
2020-07-31 14:57:22 +00:00
*****************************************************************************/
2020-08-01 15:51:45 +00:00
#include "QskProgressBarSkinlet.h"
#include "QskProgressBar.h"
2020-08-03 06:02:13 +00:00
#include "QskIntervalF.h"
2020-08-06 07:28:18 +00:00
#include "QskBoxBorderMetrics.h"
#include "QskGradientDirection.h"
2020-07-31 14:57:22 +00:00
2020-08-09 08:49:32 +00:00
#include <qeasingcurve.h>
2020-07-31 14:57:22 +00:00
#include <cmath>
2020-08-03 06:02:13 +00:00
static inline QskIntervalF qskBarInterval( const QskProgressBar* bar )
{
2020-08-09 08:49:32 +00:00
qreal pos1, pos2;
if ( bar->isIndeterminate() )
{
2021-12-29 16:05:29 +00:00
const auto pos = bar->positionHint( QskProgressBar::Bar );
2020-08-09 08:49:32 +00:00
static const QEasingCurve curve( QEasingCurve::InOutCubic );
const qreal off = 0.15;
pos1 = curve.valueForProgress( qMax( pos - off, 0.0 ) );
pos2 = curve.valueForProgress( qMin( pos + off, 1.0 ) );
}
else
{
pos1 = bar->valueAsRatio( bar->origin() );
pos2 = bar->valueAsRatio( bar->value() );
}
2020-08-03 06:02:13 +00:00
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 );
}
2020-08-01 15:51:45 +00:00
QskProgressBarSkinlet::QskProgressBarSkinlet( QskSkin* skin )
2020-07-31 14:57:22 +00:00
: QskSkinlet( skin )
{
2020-08-03 06:02:13 +00:00
setNodeRoles( { GrooveRole, BarRole } );
2020-07-31 14:57:22 +00:00
}
2020-08-01 15:51:45 +00:00
QskProgressBarSkinlet::~QskProgressBarSkinlet()
2020-07-31 14:57:22 +00:00
{
}
2020-08-01 15:51:45 +00:00
QRectF QskProgressBarSkinlet::subControlRect(
2020-07-31 14:57:22 +00:00
const QskSkinnable* skinnable, const QRectF& contentsRect,
QskAspect::Subcontrol subControl ) const
{
using Q = QskProgressBar;
const auto bar = static_cast< const Q* >( skinnable );
2020-07-31 14:57:22 +00:00
if( subControl == Q::Groove )
2020-07-31 14:57:22 +00:00
{
const auto grooveSize = bar->metric( Q::Groove | QskAspect::Size );
2020-08-09 09:50:34 +00:00
2020-07-31 14:57:22 +00:00
auto rect = contentsRect;
if ( bar->orientation() == Qt::Horizontal )
{
rect.setY( rect.y() + 0.5 * ( rect.height() - grooveSize ) );
rect.setHeight( grooveSize );
2020-07-31 14:57:22 +00:00
}
else
{
rect.setX( rect.x() + 0.5 * ( rect.width() - grooveSize ) );
rect.setWidth( grooveSize );
2020-07-31 14:57:22 +00:00
}
return rect;
}
if( subControl == Q::Bar )
2020-07-31 14:57:22 +00:00
{
2020-08-03 06:02:13 +00:00
return barRect( bar );
2020-07-31 14:57:22 +00:00
}
return Inherited::subControlRect( skinnable, contentsRect, subControl );
}
2020-08-01 15:51:45 +00:00
QSGNode* QskProgressBarSkinlet::updateSubNode(
2020-07-31 14:57:22 +00:00
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
switch( nodeRole )
{
case GrooveRole:
{
2020-08-01 15:51:45 +00:00
return updateBoxNode( skinnable, node, QskProgressBar::Groove );
2020-07-31 14:57:22 +00:00
}
2020-08-03 06:02:13 +00:00
case BarRole:
2020-07-31 14:57:22 +00:00
{
2020-08-01 15:51:45 +00:00
const auto bar = static_cast< const QskProgressBar* >( skinnable );
2020-08-03 06:02:13 +00:00
return updateBarNode( bar, node );
2020-07-31 14:57:22 +00:00
}
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
2020-08-03 06:02:13 +00:00
QSGNode* QskProgressBarSkinlet::updateBarNode(
2020-08-01 15:51:45 +00:00
const QskProgressBar* bar, QSGNode* node ) const
2020-07-31 14:57:22 +00:00
{
2020-08-03 06:02:13 +00:00
const auto subControl = QskProgressBar::Bar;
2020-07-31 14:57:22 +00:00
const auto rect = bar->subControlRect( subControl );
if ( rect.isEmpty() )
return nullptr;
2020-08-09 09:50:34 +00:00
2020-07-31 14:57:22 +00:00
auto gradient = bar->gradientHint( subControl );
if ( !gradient.isVisible() )
return nullptr;
2022-10-31 13:42:08 +00:00
if ( ( gradient.type() == QskGradient::Stops ) && !gradient.isMonochrome() )
2020-07-31 14:57:22 +00:00
{
2022-10-31 13:42:08 +00:00
/*
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
2022-10-31 13:42:08 +00:00
adjust the start/stop positions, but the box renderer is
not supporting this yet. TODO ...
*/
2020-08-03 06:02:13 +00:00
const auto intv = qskBarInterval( bar );
2020-07-31 14:57:22 +00:00
const auto stops = qskExtractedGradientStops( gradient.stops(),
2022-10-31 13:42:08 +00:00
intv.lowerBound(), intv.upperBound() );
gradient.setStops( stops );
gradient.setLinearDirection( bar->orientation() );
2020-08-03 06:02:13 +00:00
if ( bar->orientation() == Qt::Vertical || bar->layoutMirroring() )
2020-07-31 14:57:22 +00:00
gradient.reverse();
}
return updateBoxNode( bar, node, rect, gradient, subControl );
}
2020-08-03 06:02:13 +00:00
QRectF QskProgressBarSkinlet::barRect( const QskProgressBar* bar ) const
2020-07-31 14:57:22 +00:00
{
using Q = QskProgressBar;
const auto subControl = Q::Groove;
2020-08-06 07:28:18 +00:00
const auto barSize = bar->metric( Q::Bar | QskAspect::Size );
2020-08-06 07:28:18 +00:00
auto rect = bar->subControlRect( subControl );
if ( bar->orientation() == Qt::Horizontal )
{
rect.setY( rect.y() + 0.5 * ( rect.height() - barSize ) );
rect.setHeight( barSize );
}
else
{
rect.setX( rect.x() + 0.5 * ( rect.width() - barSize ) );
rect.setWidth( barSize );
}
2020-08-06 07:28:18 +00:00
const auto borderMetrics = bar->boxBorderMetricsHint( subControl );
auto m = bar->paddingHint( subControl );
2020-08-06 07:28:18 +00:00
m += 0.5 * borderMetrics.toAbsolute( rect.size() ).widths();
rect = rect.marginsRemoved( m );
2020-07-31 14:57:22 +00:00
2020-08-03 06:02:13 +00:00
const auto intv = qskBarInterval( bar );
2020-07-31 14:57:22 +00:00
if( bar->orientation() == Qt::Horizontal )
{
const auto w = rect.width();
2020-08-03 06:02:13 +00:00
rect.setRight( rect.left() + intv.upperBound() * w );
rect.setLeft( rect.left() + intv.lowerBound() * w );
2020-07-31 14:57:22 +00:00
}
else
{
const auto h = rect.height();
2020-08-03 06:02:13 +00:00
rect.setTop( rect.bottom() - h * intv.upperBound() );
rect.setBottom( rect.bottom() - h * intv.lowerBound() );
2020-07-31 14:57:22 +00:00
}
return rect;
}
QSizeF QskProgressBarSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& ) const
{
if ( which != Qt::PreferredSize )
return QSizeF();
const auto bar = static_cast< const QskProgressBar* >( skinnable );
const auto extent = bar->extent();
if ( bar->orientation() == Qt::Horizontal )
return QSizeF( -1, extent );
else
return QSizeF( extent, -1 );
}
2020-08-01 15:51:45 +00:00
#include "moc_QskProgressBarSkinlet.cpp"