slider ticks - not only for M3

This commit is contained in:
Uwe Rathmann 2024-11-21 17:59:54 +01:00
parent d04c030924
commit 9a5dd886ed
8 changed files with 231 additions and 89 deletions

View File

@ -927,15 +927,15 @@ void Editor::setupSlider()
setBoxShape( Q::Groove, 100, Qt::RelativeSize );
setBoxShape( Q::Fill, 100, Qt::RelativeSize );
setStrutSize( Q::Ticks, { 4_dp, 4_dp } );
setBoxShape( Q::Ticks, 100, Qt::RelativeSize );
setStrutSize( Q::Tick, { 4_dp, 4_dp } );
setBoxShape( Q::Tick, 100, Qt::RelativeSize );
setGradient( Q::Ticks, m_pal.primary );
setGradient( Q::Ticks | Q::Disabled, m_pal.onSurface );
setGradient( Q::Tick, m_pal.primary );
setGradient( Q::Tick | Q::Disabled, m_pal.onSurface );
setGradient( Q::Ticks | SK::Filled, m_pal.secondaryContainer,
setGradient( Q::Tick | SK::Filled, m_pal.secondaryContainer,
{ QskStateCombination::CombinationNoState, Q::Focused | Q::Pressed } );
setGradient( Q::Ticks | SK::Filled | Q::Disabled, m_pal.inverseOnSurface );
setGradient( Q::Tick | SK::Filled | Q::Disabled, m_pal.inverseOnSurface );
for ( const auto variation : { A::Horizontal, A::Vertical } )
{

View File

@ -16,31 +16,9 @@ QSK_SYSTEM_STATE( QskMaterial3SliderSkinlet, Filled, QskAspect::FirstUserState >
using Q = QskSlider;
static inline bool qskHasOrigin( const QskSlider* )
static inline bool qskHasBoundaryTicks( const QskSlider* slider )
{
return false; // TODO
}
static inline qreal qskTickValue( const QskSlider* slider, int index )
{
if( slider->isSnapping() && slider->stepSize() )
return slider->minimum() + index * slider->stepSize();
if ( qskHasOrigin( slider ) )
{
switch( index )
{
case 0:
return slider->minimum();
#if 0
case 1:
return slider->origin();
#endif
}
}
return slider->maximum();
return ( slider->graduationPolicy() == Qsk::Maybe ) && !slider->isSnapping();
}
QskMaterial3SliderSkinlet::QskMaterial3SliderSkinlet( QskSkin* skin )
@ -82,7 +60,7 @@ QSGNode* QskMaterial3SliderSkinlet::updateSubNode(
case GrooveRole:
case FillRole:
{
auto clippedNode = QskSGNode::findChildNode( node, nodeRole );
auto clippedNode = QskSGNode::findChildNode( node, nodeRole );
clippedNode = Inherited::updateSubNode( skinnable, nodeRole, clippedNode );
if ( clippedNode )
@ -102,11 +80,6 @@ QSGNode* QskMaterial3SliderSkinlet::updateSubNode(
return nullptr;
}
case TicksRole:
{
return updateSeriesNode( skinnable, Q::Ticks, node );
}
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
@ -115,48 +88,50 @@ QSGNode* QskMaterial3SliderSkinlet::updateSubNode(
int QskMaterial3SliderSkinlet::sampleCount( const QskSkinnable* skinnable,
QskAspect::Subcontrol subControl ) const
{
if ( subControl == Q::Ticks )
if ( subControl == Q::Tick )
{
const auto slider = static_cast< const QskSlider* >( skinnable );
if( slider->isSnapping() && slider->stepSize() )
return qCeil( slider->boundaryLength() / slider->stepSize() ) + 1;
if ( qskHasBoundaryTicks( slider ) )
{
const bool hasOrigin = false;
// min/origin/max or max
return qskHasOrigin( slider ) ? 3 : 1;
// min/origin/max or max
return hasOrigin ? 3 : 1;
}
}
return Inherited::sampleCount( skinnable, subControl );
}
QRectF QskMaterial3SliderSkinlet::sampleRect(
const QskSkinnable* skinnable, const QRectF& contentsRect,
QVariant QskMaterial3SliderSkinlet::sampleAt( const QskSkinnable* skinnable,
QskAspect::Subcontrol subControl, int index ) const
{
if ( subControl != Q::Ticks )
return Inherited::sampleRect( skinnable, contentsRect, subControl, index );
const auto slider = static_cast< const QskSlider* >( skinnable );
const auto tickPos = slider->valueAsRatio( qskTickValue( slider, index ) );
const auto size = skinnable->strutSizeHint( Q::Ticks );
const auto r = subControlRect( skinnable, contentsRect, Q::Scale );
qreal x, y;
if( slider->orientation() == Qt::Horizontal )
if ( subControl == Q::Tick )
{
x = tickPos * r.width() - 0.5 * size.width();
y = 0.5 * ( r.height() - size.height() );
}
else
{
y = r.height() - ( tickPos * r.height() ) - 0.5 * size.height();
x = 0.5 * ( r.width() - size.width() );
const auto slider = static_cast< const QskSlider* >( skinnable );
if ( qskHasBoundaryTicks( slider ) )
{
switch( index )
{
case 1:
return slider->minimum();
#if 0
case 2:
return slider->origin();
#endif
default:
return slider->maximum();
}
return QVariant();
}
}
return QRectF( r.x() + x, r.y() + y, size.width(), size.height() );
return Inherited::sampleAt( skinnable, subControl, index );
}
QskAspect::States QskMaterial3SliderSkinlet::sampleStates(
@ -164,28 +139,19 @@ QskAspect::States QskMaterial3SliderSkinlet::sampleStates(
{
auto states = Inherited::sampleStates( skinnable, subControl, index );
if ( subControl == Q::Ticks )
if ( subControl == Q::Tick )
{
const auto slider = static_cast< const QskSlider* >( skinnable );
if ( qskTickValue( slider, index ) <= slider->value() )
states |= QskMaterial3SliderSkinlet::Filled;
const auto tickValue = sampleAt( skinnable, subControl, index );
if ( tickValue.canConvert< qreal >() )
{
const auto slider = static_cast< const QskSlider* >( skinnable );
if ( tickValue.value< qreal >() <= slider->value() )
states |= QskMaterial3SliderSkinlet::Filled;
}
}
return states;
}
QSGNode* QskMaterial3SliderSkinlet::updateSampleNode( const QskSkinnable* skinnable,
QskAspect::Subcontrol subControl, int index, QSGNode* node ) const
{
if ( subControl == Q::Ticks )
{
const auto slider = static_cast< const QskSlider* >( skinnable );
const auto rect = sampleRect( slider, slider->contentsRect(), subControl, index );
return updateBoxNode( skinnable, node, rect, subControl );
}
return Inherited::updateSampleNode( skinnable, subControl, index, node );
}
#include "moc_QskMaterial3SliderSkinlet.cpp"

View File

@ -24,8 +24,8 @@ class QskMaterial3SliderSkinlet : QskSliderSkinlet
int sampleCount( const QskSkinnable*, QskAspect::Subcontrol ) const override;
QRectF sampleRect( const QskSkinnable*,
const QRectF&, QskAspect::Subcontrol, int index ) const override;
QVariant sampleAt( const QskSkinnable*,
QskAspect::Subcontrol, int index ) const override;
QskAspect::States sampleStates( const QskSkinnable*,
QskAspect::Subcontrol, int ) const override;
@ -33,9 +33,6 @@ class QskMaterial3SliderSkinlet : QskSliderSkinlet
protected:
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
QSGNode* updateSampleNode( const QskSkinnable*,
QskAspect::Subcontrol, int index, QSGNode* ) const override;
};
#endif

View File

@ -13,6 +13,14 @@ namespace Qsk
{
Q_NAMESPACE_EXPORT( QSK_EXPORT )
enum Policy
{
Maybe,
Always,
Never
};
Q_ENUM_NS( Policy )
enum Direction
{
LeftToRight,

View File

@ -9,15 +9,42 @@
#include "QskIntervalF.h"
#include "QskEvent.h"
#include <qmath.h>
QSK_SUBCONTROL( QskSlider, Panel )
QSK_SUBCONTROL( QskSlider, Groove )
QSK_SUBCONTROL( QskSlider, Fill )
QSK_SUBCONTROL( QskSlider, Scale )
QSK_SUBCONTROL( QskSlider, Ticks )
QSK_SUBCONTROL( QskSlider, Tick )
QSK_SUBCONTROL( QskSlider, Handle )
QSK_SYSTEM_STATE( QskSlider, Pressed, QskAspect::FirstSystemState << 2 )
static inline constexpr QskAspect qskAspectGraduationPolicy()
{
return QskSlider::Tick | QskAspect::Option;
}
static inline bool qskHasGraduation( const QskSlider* slider )
{
if ( slider->stepSize() )
{
switch( slider->graduationPolicy() )
{
case Qsk::Always:
return true;
case Qsk::Maybe:
return slider->isSnapping();
case Qsk::Never:
return false;
}
}
return false;
}
class QskSlider::PrivateData
{
public:
@ -90,6 +117,42 @@ QskAspect::Variation QskSlider::effectiveVariation() const
return static_cast< QskAspect::Variation >( m_data->orientation );
}
void QskSlider::setGraduationPolicy( Qsk::Policy policy )
{
if ( setFlagHint( qskAspectGraduationPolicy(), policy ) )
Q_EMIT graduationPolicyChanged( graduationPolicy() );
}
void QskSlider::resetGraduationPolicy()
{
if ( resetSkinHint( qskAspectGraduationPolicy() ) )
Q_EMIT graduationPolicyChanged( graduationPolicy() );
}
Qsk::Policy QskSlider::graduationPolicy() const
{
return flagHint< Qsk::Policy >( qskAspectGraduationPolicy(), Qsk::Maybe );
}
QVector< qreal > QskSlider::visualGraduation() const
{
QVector< qreal > graduation;
if ( qskHasGraduation( this ) )
{
const auto n = qCeil( boundaryLength() / stepSize() ) + 1;
graduation.reserve( n );
for ( int i = 0; i < n - 1; i++ )
graduation += minimum() + i * stepSize();
graduation += maximum();
}
return graduation;
}
void QskSlider::setTracking( bool on )
{
if ( on != m_data->tracking )

View File

@ -7,6 +7,9 @@
#define QSK_SLIDER_H
#include "QskBoundedValueInput.h"
#include "QskNamespace.h"
#include <qvector.h>
class QSK_EXPORT QskSlider : public QskBoundedValueInput
{
@ -14,6 +17,10 @@ class QSK_EXPORT QskSlider : public QskBoundedValueInput
Q_PROPERTY( bool isPressed READ isPressed NOTIFY pressedChanged )
Q_PROPERTY( Qsk::Policy graduationPolicy READ graduationPolicy
WRITE setGraduationPolicy RESET resetGraduationPolicy
NOTIFY graduationPolicyChanged )
Q_PROPERTY( Qt::Orientation orientation READ orientation
WRITE setOrientation NOTIFY orientationChanged )
@ -25,7 +32,7 @@ class QSK_EXPORT QskSlider : public QskBoundedValueInput
using Inherited = QskBoundedValueInput;
public:
QSK_SUBCONTROLS( Panel, Groove, Fill, Scale, Ticks, Handle )
QSK_SUBCONTROLS( Panel, Groove, Fill, Scale, Tick, Handle )
QSK_STATES( Pressed )
explicit QskSlider( QQuickItem* parent = nullptr );
@ -38,6 +45,12 @@ class QSK_EXPORT QskSlider : public QskBoundedValueInput
void setOrientation( Qt::Orientation );
Qt::Orientation orientation() const;
void setGraduationPolicy( Qsk::Policy );
void resetGraduationPolicy();
Qsk::Policy graduationPolicy() const;
virtual QVector< qreal > visualGraduation() const;
void setTracking( bool );
bool isTracking() const;
@ -49,6 +62,7 @@ class QSK_EXPORT QskSlider : public QskBoundedValueInput
void pressedChanged( bool );
void orientationChanged( Qt::Orientation );
void trackingChanged( bool );
void graduationPolicyChanged( Qsk::Policy );
protected:
void mousePressEvent( QMouseEvent* ) override;

View File

@ -89,11 +89,65 @@ QSGNode* QskSliderSkinlet::updateSubNode(
case HandleRole:
return updateBoxNode( slider, node, Q::Handle );
case TicksRole:
return updateSeriesNode( slider, Q::Tick, node );
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
int QskSliderSkinlet::sampleCount( const QskSkinnable* skinnable,
QskAspect::Subcontrol subControl ) const
{
if ( subControl == Q::Tick )
{
const auto slider = static_cast< const QskSlider* >( skinnable );
return slider->visualGraduation().count();
}
return Inherited::sampleCount( skinnable, subControl );
}
QVariant QskSliderSkinlet::sampleAt( const QskSkinnable* skinnable,
QskAspect::Subcontrol subControl, int index ) const
{
if ( subControl == Q::Tick )
{
const auto slider = static_cast< const QskSlider* >( skinnable );
return slider->visualGraduation().value( index );
}
return Inherited::sampleAt( skinnable, subControl, index );
}
QRectF QskSliderSkinlet::sampleRect(
const QskSkinnable* skinnable, const QRectF& contentsRect,
QskAspect::Subcontrol subControl, int index ) const
{
if ( subControl == Q::Tick )
{
const auto slider = static_cast< const QskSlider* >( skinnable );
return tickRect( slider, contentsRect, index );
}
return Inherited::sampleRect( skinnable, contentsRect, subControl, index );
}
QSGNode* QskSliderSkinlet::updateSampleNode( const QskSkinnable* skinnable,
QskAspect::Subcontrol subControl, int index, QSGNode* node ) const
{
if ( subControl == Q::Tick )
{
const auto slider = static_cast< const QskSlider* >( skinnable );
const auto rect = sampleRect( slider, slider->contentsRect(), subControl, index );
return updateBoxNode( skinnable, node, rect, subControl );
}
return Inherited::updateSampleNode( skinnable, subControl, index, node );
}
QRectF QskSliderSkinlet::panelRect(
const QskSlider* slider, const QRectF& contentsRect ) const
{
@ -166,6 +220,34 @@ QRectF QskSliderSkinlet::handleRect(
return handleRect;
}
QRectF QskSliderSkinlet::tickRect( const QskSlider* slider,
const QRectF& contentsRect, int index ) const
{
const auto tickValue = sampleAt( slider, Q::Tick, index );
if ( !tickValue.canConvert< qreal >() )
return QRectF();
const auto tickPos = slider->valueAsRatio( tickValue.value< qreal >() );
const auto size = slider->strutSizeHint( Q::Tick );
const auto r = subControlRect( slider, contentsRect, Q::Scale );
qreal x, y;
if( slider->orientation() == Qt::Horizontal )
{
x = tickPos * r.width() - 0.5 * size.width();
y = 0.5 * ( r.height() - size.height() );
}
else
{
y = r.height() - ( tickPos * r.height() ) - 0.5 * size.height();
x = 0.5 * ( r.width() - size.width() );
}
return QRectF( r.x() + x, r.y() + y, size.width(), size.height() );
}
QSizeF QskSliderSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& ) const
{

View File

@ -37,14 +37,26 @@ class QSK_EXPORT QskSliderSkinlet : public QskSkinlet
QSizeF sizeHint( const QskSkinnable*,
Qt::SizeHint, const QSizeF& ) const override;
int sampleCount( const QskSkinnable*, QskAspect::Subcontrol ) const override;
QRectF sampleRect( const QskSkinnable*,
const QRectF&, QskAspect::Subcontrol, int index ) const override;
QVariant sampleAt( const QskSkinnable*,
QskAspect::Subcontrol, int index ) const override;
protected:
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
QSGNode* updateSampleNode( const QskSkinnable*,
QskAspect::Subcontrol, int index, QSGNode* ) const override;
private:
QRectF panelRect( const QskSlider*, const QRectF& ) const;
QRectF fillRect( const QskSlider*, const QRectF& ) const;
QRectF handleRect( const QskSlider*, const QRectF& ) const;
QRectF tickRect( const QskSlider*, const QRectF&, int index ) const;
};
#endif