2017-07-21 16:21:34 +00:00
|
|
|
/******************************************************************************
|
2024-01-17 13:31:45 +00:00
|
|
|
* QSkinny - Copyright (C) The authors
|
2023-04-06 07:23:37 +00:00
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
2017-07-21 16:21:34 +00:00
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskSliderSkinlet.h"
|
2018-08-03 06:30:23 +00:00
|
|
|
#include "QskSlider.h"
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
#include "QskAspect.h"
|
2017-10-17 15:34:00 +00:00
|
|
|
#include "QskBoxBorderMetrics.h"
|
2017-07-21 16:21:34 +00:00
|
|
|
#include "QskFunctions.h"
|
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
#include <QFontMetricsF>
|
|
|
|
#include <QtMath>
|
|
|
|
|
|
|
|
using Q = QskSlider;
|
|
|
|
|
|
|
|
namespace
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2024-11-07 16:31:14 +00:00
|
|
|
inline QRectF qskInnerPanelRect(
|
|
|
|
const QskSlider* slider, const QRectF& contentsRect )
|
|
|
|
{
|
|
|
|
#if 1
|
|
|
|
auto padding = slider->paddingHint( Q::Panel );
|
|
|
|
padding += slider->boxBorderMetricsHint( Q::Panel ).widths();
|
2020-12-17 15:14:56 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
auto r = slider->subControlRect( contentsRect, Q::Panel );
|
|
|
|
r = r.marginsRemoved( padding );
|
|
|
|
#else
|
|
|
|
r = slider->subControlContentsRect( contentsRect, Q::Panel );
|
|
|
|
#endif
|
2020-12-17 15:14:56 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
return r;
|
|
|
|
}
|
2020-12-17 15:14:56 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
QRectF qskInnerValueRect( const QskSlider* slider, const QRectF& contentsRect )
|
|
|
|
{
|
|
|
|
// For M3 the stop indicators have some padding related to the groove (and fill),
|
|
|
|
// so we use the rect between first and last stop indicator as authoritative for
|
|
|
|
// indicators, handle etc.
|
|
|
|
const auto grooveIndicatorMargins = slider->paddingHint( Q::GrooveStopIndicators );
|
|
|
|
const auto r = qskInnerPanelRect( slider, contentsRect ).marginsRemoved( grooveIndicatorMargins );
|
|
|
|
return r;
|
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
2018-08-03 06:15:28 +00:00
|
|
|
QskSliderSkinlet::QskSliderSkinlet( QskSkin* skin )
|
|
|
|
: Inherited( skin )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2024-11-07 16:31:14 +00:00
|
|
|
setNodeRoles( {
|
|
|
|
PanelRole,
|
|
|
|
GrooveRole,
|
|
|
|
FillRole,
|
|
|
|
FillStopIndicatorsRole,
|
|
|
|
GrooveStopIndicatorsRole,
|
|
|
|
HandleRole
|
|
|
|
} );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
2020-12-16 11:49:24 +00:00
|
|
|
QskSliderSkinlet::~QskSliderSkinlet()
|
|
|
|
{
|
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2019-04-25 12:23:39 +00:00
|
|
|
QRectF QskSliderSkinlet::subControlRect( const QskSkinnable* skinnable,
|
|
|
|
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
const auto slider = static_cast< const QskSlider* >( skinnable );
|
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
if ( subControl == Q::Panel )
|
2017-09-01 09:55:55 +00:00
|
|
|
{
|
2019-04-25 12:23:39 +00:00
|
|
|
return panelRect( slider, contentsRect );
|
2017-09-01 09:55:55 +00:00
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
if ( subControl == Q::Groove )
|
2017-09-01 09:55:55 +00:00
|
|
|
{
|
2019-04-25 12:23:39 +00:00
|
|
|
return grooveRect( slider, contentsRect );
|
2017-09-01 09:55:55 +00:00
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
if ( subControl == Q::Fill )
|
2017-09-01 09:55:55 +00:00
|
|
|
{
|
2019-04-25 12:23:39 +00:00
|
|
|
return fillRect( slider, contentsRect );
|
2017-09-01 09:55:55 +00:00
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
if ( subControl == Q::Handle )
|
2017-09-01 09:55:55 +00:00
|
|
|
{
|
2019-04-25 12:23:39 +00:00
|
|
|
return handleRect( slider, contentsRect );
|
2017-09-01 09:55:55 +00:00
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
if ( subControl == Q::Scale )
|
2017-09-01 09:55:55 +00:00
|
|
|
{
|
2019-04-25 12:23:39 +00:00
|
|
|
return scaleRect( slider, contentsRect );
|
2017-09-01 09:55:55 +00:00
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
return Inherited::subControlRect( skinnable, contentsRect, subControl );
|
|
|
|
}
|
|
|
|
|
|
|
|
int QskSliderSkinlet::sampleCount( const QskSkinnable* skinnable,
|
|
|
|
QskAspect::Subcontrol subControl ) const
|
|
|
|
{
|
|
|
|
const auto slider = static_cast< const QskSlider* >( skinnable );
|
|
|
|
|
|
|
|
if( slider->snap() )
|
2022-07-05 11:27:35 +00:00
|
|
|
{
|
2024-11-07 16:31:14 +00:00
|
|
|
const auto num = qCeil( slider->boundaryLength() / slider->stepSize() ) + 1;
|
|
|
|
return num;
|
2022-07-05 11:27:35 +00:00
|
|
|
}
|
2024-11-07 16:31:14 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return ( subControl == Q::GrooveStopIndicators ) ? 1 : 0;
|
|
|
|
}
|
|
|
|
}
|
2022-07-05 11:27:35 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
QRectF QskSliderSkinlet::sampleRect(
|
|
|
|
const QskSkinnable* skinnable, const QRectF& contentsRect,
|
|
|
|
QskAspect::Subcontrol subControl, int index ) const
|
|
|
|
{
|
|
|
|
const auto slider = static_cast< const QskSlider* >( skinnable );
|
|
|
|
|
|
|
|
auto r = qskInnerValueRect( slider, contentsRect );
|
|
|
|
|
|
|
|
const auto size = slider->strutSizeHint( subControl );
|
|
|
|
|
|
|
|
const auto filledPoints = qFloor( ( slider->value() - slider->minimum() ) / slider->stepSize() );
|
|
|
|
|
|
|
|
if( slider->snap())
|
|
|
|
{
|
|
|
|
if( slider->snap()
|
|
|
|
&& ( ( index >= filledPoints && subControl == Q::FillStopIndicators )
|
|
|
|
|| ( index < filledPoints && subControl == Q::GrooveStopIndicators ) ) )
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto pos = slider->snap() ? slider->minimum() + index * slider->stepSize() : slider->maximum();
|
|
|
|
|
|
|
|
if( slider->orientation() == Qt::Horizontal )
|
|
|
|
{
|
|
|
|
r.setTop( r.center().y() - size.height() / 2 );
|
|
|
|
const auto x = r.left() + slider->valueAsRatio( pos ) * r.width() - size.width() / 2;
|
|
|
|
r.setLeft( x );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
r.setLeft( r.center().x() - size.width() / 2 );
|
|
|
|
const auto y = r.bottom() - slider->valueAsRatio( pos ) * r.height() - size.height() / 2;
|
|
|
|
r.setTop( y );
|
|
|
|
}
|
|
|
|
|
|
|
|
r.setHeight( size.height() );
|
|
|
|
r.setWidth( size.width() );
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
QskAspect::States QskSliderSkinlet::sampleStates( const QskSkinnable*, QskAspect::Subcontrol, int ) const
|
|
|
|
{
|
|
|
|
return {};
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
2017-09-01 09:55:55 +00:00
|
|
|
QSGNode* QskSliderSkinlet::updateSubNode(
|
|
|
|
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
|
|
|
|
{
|
|
|
|
const auto slider = static_cast< const QskSlider* >( skinnable );
|
|
|
|
|
2018-08-03 06:15:28 +00:00
|
|
|
switch ( nodeRole )
|
2017-09-01 09:55:55 +00:00
|
|
|
{
|
|
|
|
case PanelRole:
|
|
|
|
{
|
2024-11-07 16:31:14 +00:00
|
|
|
return updateBoxNode( slider, node, Q::Panel );
|
2017-09-01 09:55:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case GrooveRole:
|
|
|
|
{
|
2024-11-07 16:31:14 +00:00
|
|
|
return updateBoxNode( slider, node, Q::Groove );
|
2017-09-01 09:55:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case FillRole:
|
|
|
|
{
|
2024-11-07 16:31:14 +00:00
|
|
|
return updateBoxNode( slider, node, Q::Fill );
|
2017-09-01 09:55:55 +00:00
|
|
|
}
|
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
case GrooveStopIndicatorsRole:
|
2017-09-01 09:55:55 +00:00
|
|
|
{
|
2024-11-07 16:31:14 +00:00
|
|
|
return updateSeriesNode( slider, Q::GrooveStopIndicators, node );
|
2017-09-01 09:55:55 +00:00
|
|
|
}
|
2022-07-05 11:27:35 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
case FillStopIndicatorsRole:
|
2022-07-05 11:27:35 +00:00
|
|
|
{
|
2024-11-07 16:31:14 +00:00
|
|
|
return updateSeriesNode( slider, Q::FillStopIndicators, node );
|
|
|
|
}
|
|
|
|
|
|
|
|
case HandleRole:
|
|
|
|
{
|
|
|
|
return updateBoxNode( slider, node, Q::Handle );
|
2022-07-05 11:27:35 +00:00
|
|
|
}
|
2017-09-01 09:55:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Inherited::updateSubNode( skinnable, nodeRole, node );
|
|
|
|
}
|
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
QSGNode* QskSliderSkinlet::updateSampleNode( const QskSkinnable* skinnable,
|
|
|
|
QskAspect::Subcontrol subControl, int index, QSGNode* node ) const
|
|
|
|
{
|
|
|
|
const auto slider = static_cast< const QskSlider* >( skinnable );
|
|
|
|
const auto rect = sampleRect( slider, slider->contentsRect(), subControl, index );
|
|
|
|
|
|
|
|
return updateBoxNode( skinnable, node, rect, subControl );
|
|
|
|
}
|
|
|
|
|
2019-04-25 12:23:39 +00:00
|
|
|
QRectF QskSliderSkinlet::panelRect(
|
|
|
|
const QskSlider* slider, const QRectF& contentsRect ) const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2020-12-16 11:49:24 +00:00
|
|
|
auto r = contentsRect;
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
const qreal size = slider->metric( Q::Panel | QskAspect::Size ); // 0: no hint
|
2017-07-21 16:21:34 +00:00
|
|
|
if ( size > 0 && size < r.height() )
|
|
|
|
{
|
2024-11-07 16:31:14 +00:00
|
|
|
const auto alignment = slider->alignmentHint( Q::Panel );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
if ( slider->orientation() == Qt::Horizontal )
|
|
|
|
r = qskAlignedRectF( r, r.width(), size, alignment & Qt::AlignVertical_Mask );
|
|
|
|
else
|
|
|
|
r = qskAlignedRectF( r, size, r.height(), alignment & Qt::AlignHorizontal_Mask );
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2019-04-25 12:23:39 +00:00
|
|
|
QRectF QskSliderSkinlet::innerRect( const QskSlider* slider,
|
|
|
|
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2020-12-17 15:14:56 +00:00
|
|
|
auto r = qskInnerPanelRect( slider, contentsRect );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
QskSkinHintStatus status;
|
|
|
|
|
2020-12-15 06:21:12 +00:00
|
|
|
const qreal extent = slider->metric( subControl | QskAspect::Size, &status );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
if ( slider->orientation() == Qt::Horizontal )
|
|
|
|
{
|
2020-12-15 06:21:12 +00:00
|
|
|
if ( status.isValid() && ( extent < r.height() ) )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2020-12-15 06:21:12 +00:00
|
|
|
r.setTop( r.center().y() - 0.5 * extent );
|
|
|
|
r.setHeight( extent );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-12-15 06:21:12 +00:00
|
|
|
if ( status.isValid() && ( extent < r.width() ) )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2020-12-15 06:21:12 +00:00
|
|
|
r.setLeft( r.center().x() - 0.5 * extent );
|
|
|
|
r.setWidth( extent );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2019-04-25 12:23:39 +00:00
|
|
|
QRectF QskSliderSkinlet::grooveRect(
|
2024-11-07 16:31:14 +00:00
|
|
|
const QskSlider* slider, const QRectF& contentsRect ) const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2024-11-07 16:31:14 +00:00
|
|
|
const auto r = qskInnerPanelRect( slider, contentsRect );
|
|
|
|
auto grooveRect = innerRect( slider, contentsRect, Q::Groove );
|
|
|
|
const auto handleRect = slider->subControlRect( Q::Handle );
|
|
|
|
|
|
|
|
if ( slider->orientation() == Qt::Horizontal )
|
|
|
|
{
|
|
|
|
grooveRect.setLeft( handleRect.right() );
|
|
|
|
grooveRect.setRight( r.right() );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
grooveRect.setBottom( handleRect.top() );
|
|
|
|
grooveRect.setTop( r.top() );
|
|
|
|
}
|
|
|
|
|
|
|
|
return grooveRect;
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
2019-04-25 12:23:39 +00:00
|
|
|
QRectF QskSliderSkinlet::scaleRect(
|
|
|
|
const QskSlider* slider, const QRectF& rect ) const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2024-11-07 16:31:14 +00:00
|
|
|
return innerRect( slider, rect, Q::Groove );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
2019-04-25 12:23:39 +00:00
|
|
|
QRectF QskSliderSkinlet::fillRect(
|
|
|
|
const QskSlider* slider, const QRectF& contentsRect ) const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2020-12-17 15:14:56 +00:00
|
|
|
const auto r = qskInnerPanelRect( slider, contentsRect );
|
2024-11-07 16:31:14 +00:00
|
|
|
const auto handleRect = slider->subControlRect( Q::Handle );
|
2020-12-16 11:49:24 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
auto fillRect = innerRect( slider, contentsRect, Q::Fill );
|
2017-07-21 16:21:34 +00:00
|
|
|
if ( slider->orientation() == Qt::Horizontal )
|
|
|
|
{
|
|
|
|
fillRect.setLeft( r.left() );
|
2024-11-07 16:31:14 +00:00
|
|
|
fillRect.setRight( handleRect.left() );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fillRect.setBottom( r.bottom() );
|
2024-11-07 16:31:14 +00:00
|
|
|
fillRect.setTop( handleRect.bottom() );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return fillRect;
|
|
|
|
}
|
|
|
|
|
2019-04-25 12:23:39 +00:00
|
|
|
QRectF QskSliderSkinlet::handleRect(
|
|
|
|
const QskSlider* slider, const QRectF& contentsRect ) const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2020-12-17 15:14:56 +00:00
|
|
|
auto handleSize = slider->strutSizeHint( Q::Handle );
|
|
|
|
const auto pos = qBound( 0.0, slider->handlePosition(), 1.0 );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
const auto r = qskInnerValueRect( slider, contentsRect );
|
2020-12-17 15:14:56 +00:00
|
|
|
auto center = r.center();
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2020-12-17 15:14:56 +00:00
|
|
|
if ( slider->orientation() == Qt::Horizontal )
|
2017-10-17 15:34:00 +00:00
|
|
|
{
|
2020-12-17 15:14:56 +00:00
|
|
|
if ( handleSize.height() < 0.0 )
|
|
|
|
handleSize.setHeight( r.height() );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2020-12-17 15:14:56 +00:00
|
|
|
if ( handleSize.width() < 0.0 )
|
|
|
|
handleSize.setWidth( handleSize.height() );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2020-12-17 15:14:56 +00:00
|
|
|
center.setX( r.left() + pos * r.width() );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-12-17 15:14:56 +00:00
|
|
|
if ( handleSize.width() < 0.0 )
|
|
|
|
handleSize.setWidth( r.width() );
|
|
|
|
|
|
|
|
if ( handleSize.height() < 0.0 )
|
|
|
|
handleSize.setHeight( handleSize.width() );
|
|
|
|
|
|
|
|
center.setY( r.bottom() - pos * r.height() );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
2020-12-17 15:14:56 +00:00
|
|
|
QRectF handleRect( 0, 0, handleSize.width(), handleSize.height() );
|
|
|
|
handleRect.moveCenter( center );
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
return handleRect;
|
|
|
|
}
|
|
|
|
|
2020-12-29 08:45:00 +00:00
|
|
|
QSizeF QskSliderSkinlet::sizeHint( const QskSkinnable* skinnable,
|
|
|
|
Qt::SizeHint which, const QSizeF& ) const
|
|
|
|
{
|
|
|
|
if ( which != Qt::PreferredSize )
|
|
|
|
return QSizeF();
|
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
const auto panelHint = skinnable->strutSizeHint( Q::Panel );
|
|
|
|
const auto grooveHint = skinnable->strutSizeHint( Q::Groove );
|
|
|
|
const auto fillHint = skinnable->strutSizeHint( Q::Fill );
|
|
|
|
const auto handleHint = skinnable->strutSizeHint( Q::Handle );
|
2020-12-29 08:45:00 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
auto hint = panelHint;
|
|
|
|
hint = hint.expandedTo( grooveHint );
|
|
|
|
hint = hint.expandedTo( fillHint );
|
|
|
|
hint = hint.expandedTo( handleHint );
|
2020-12-29 08:45:00 +00:00
|
|
|
|
2024-11-07 16:31:14 +00:00
|
|
|
return hint;
|
2020-12-29 08:45:00 +00:00
|
|
|
}
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
#include "moc_QskSliderSkinlet.cpp"
|