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 "QskSlider.h"
|
|
|
|
#include "QskAnimationHint.h"
|
2018-08-03 06:15:28 +00:00
|
|
|
#include "QskAspect.h"
|
2020-11-09 13:18:20 +00:00
|
|
|
#include "QskEvent.h"
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
QSK_SUBCONTROL( QskSlider, Panel )
|
|
|
|
QSK_SUBCONTROL( QskSlider, Groove )
|
|
|
|
QSK_SUBCONTROL( QskSlider, Fill )
|
|
|
|
QSK_SUBCONTROL( QskSlider, Scale )
|
2024-11-21 16:59:54 +00:00
|
|
|
QSK_SUBCONTROL( QskSlider, Tick )
|
2017-07-21 16:21:34 +00:00
|
|
|
QSK_SUBCONTROL( QskSlider, Handle )
|
|
|
|
|
2020-09-21 10:53:25 +00:00
|
|
|
QSK_SYSTEM_STATE( QskSlider, Pressed, QskAspect::FirstSystemState << 2 )
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2024-11-21 18:19:53 +00:00
|
|
|
static inline QskAspect qskAspectGraduationPolicy()
|
2024-11-21 16:59:54 +00:00
|
|
|
{
|
|
|
|
return QskSlider::Tick | QskAspect::Option;
|
|
|
|
}
|
|
|
|
|
2024-11-26 12:57:13 +00:00
|
|
|
static QRectF qskHandleSelectionRect( const QskSlider* slider )
|
|
|
|
{
|
|
|
|
auto rect = slider->subControlRect( QskSlider::Handle );
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
// minimum handle strut size TODO ...
|
|
|
|
const QSizeF strutSize( 60, 60 );
|
|
|
|
const auto w = qMax( ( strutSize.width() - rect.width() ) / 2, 0.0 );
|
|
|
|
const auto h = qMax( ( strutSize.height() - rect.height() ) / 2, 0.0 );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return rect.marginsAdded( { w, h, w, h } );
|
|
|
|
}
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
class QskSlider::PrivateData
|
|
|
|
{
|
2018-08-03 06:15:28 +00:00
|
|
|
public:
|
|
|
|
PrivateData( Qt::Orientation orientation )
|
|
|
|
: pressedValue( 0 )
|
|
|
|
, tracking( true )
|
|
|
|
, orientation( orientation )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QPointF pressedPos;
|
|
|
|
qreal pressedValue;
|
|
|
|
bool tracking : 1;
|
2024-11-14 06:43:34 +00:00
|
|
|
uint orientation : 2;
|
2017-07-21 16:21:34 +00:00
|
|
|
};
|
|
|
|
|
2018-08-03 06:15:28 +00:00
|
|
|
QskSlider::QskSlider( QQuickItem* parent )
|
|
|
|
: QskSlider( Qt::Horizontal, parent )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-08-03 06:15:28 +00:00
|
|
|
QskSlider::QskSlider( Qt::Orientation orientation, QQuickItem* parent )
|
|
|
|
: Inherited( parent )
|
|
|
|
, m_data( new PrivateData( orientation ) )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-10-17 15:34:00 +00:00
|
|
|
setAcceptHoverEvents( true );
|
2018-08-03 06:15:28 +00:00
|
|
|
setFocusPolicy( Qt::StrongFocus );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
if ( orientation == Qt::Horizontal )
|
2017-08-31 07:09:05 +00:00
|
|
|
initSizePolicy( QskSizePolicy::Minimum, QskSizePolicy::Fixed );
|
2017-07-21 16:21:34 +00:00
|
|
|
else
|
2017-08-31 07:09:05 +00:00
|
|
|
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Minimum );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2020-09-23 10:43:46 +00:00
|
|
|
connect( this, &QskSlider::boundariesChanged, this, &QskSlider::moveHandle );
|
|
|
|
connect( this, &QskSlider::valueChanged, this, &QskSlider::moveHandle );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QskSlider::~QskSlider()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskSlider::isPressed() const
|
|
|
|
{
|
2021-08-30 13:30:41 +00:00
|
|
|
return hasSkinState( Pressed );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskSlider::setOrientation( Qt::Orientation orientation )
|
|
|
|
{
|
|
|
|
if ( orientation != m_data->orientation )
|
|
|
|
{
|
|
|
|
m_data->orientation = orientation;
|
|
|
|
#if 1
|
|
|
|
// swapping the size policy: guess this is what a user expects
|
|
|
|
setSizePolicy( sizePolicy( Qt::Vertical ), sizePolicy( Qt::Horizontal ) );
|
|
|
|
#endif
|
|
|
|
resetImplicitSize();
|
|
|
|
update();
|
|
|
|
|
2024-11-14 06:43:34 +00:00
|
|
|
Q_EMIT orientationChanged( this->orientation() );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt::Orientation QskSlider::orientation() const
|
|
|
|
{
|
2024-11-14 06:43:34 +00:00
|
|
|
return static_cast< Qt::Orientation >( m_data->orientation );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
2023-02-15 15:37:54 +00:00
|
|
|
QskAspect::Variation QskSlider::effectiveVariation() const
|
2017-10-17 15:34:00 +00:00
|
|
|
{
|
2023-02-15 15:37:54 +00:00
|
|
|
return static_cast< QskAspect::Variation >( m_data->orientation );
|
2017-10-17 15:34:00 +00:00
|
|
|
}
|
|
|
|
|
2024-11-21 16:59:54 +00:00
|
|
|
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
|
|
|
|
{
|
2024-11-22 16:38:58 +00:00
|
|
|
return flagHint< Qsk::Policy >( qskAspectGraduationPolicy(), Qsk::Never );
|
2024-11-21 16:59:54 +00:00
|
|
|
}
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
void QskSlider::setTracking( bool on )
|
|
|
|
{
|
|
|
|
if ( on != m_data->tracking )
|
|
|
|
{
|
|
|
|
m_data->tracking = on;
|
|
|
|
Q_EMIT trackingChanged( on );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskSlider::isTracking() const
|
|
|
|
{
|
|
|
|
return m_data->tracking;
|
|
|
|
}
|
|
|
|
|
2020-09-21 09:31:37 +00:00
|
|
|
void QskSlider::aboutToShow()
|
|
|
|
{
|
2021-12-29 16:05:29 +00:00
|
|
|
setPositionHint( Handle, valueAsRatio() );
|
2020-09-21 09:31:37 +00:00
|
|
|
Inherited::aboutToShow();
|
|
|
|
}
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
void QskSlider::mousePressEvent( QMouseEvent* event )
|
|
|
|
{
|
2024-11-26 12:57:13 +00:00
|
|
|
if ( qskHandleSelectionRect( this ).contains( event->pos() ) )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-10-24 17:32:54 +00:00
|
|
|
// Case 1: press started in the handle, start sliding
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
m_data->pressedPos = event->pos();
|
|
|
|
m_data->pressedValue = value();
|
|
|
|
setSkinStateFlag( Pressed );
|
|
|
|
Q_EMIT pressedChanged( true );
|
|
|
|
}
|
2024-10-22 09:55:22 +00:00
|
|
|
else if ( pageSteps() == 0 )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2017-10-24 17:32:54 +00:00
|
|
|
// Case 2: pageSize is not used, we're done here
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Case 3: pressed outside of the handle, page the scroller in
|
|
|
|
// the direction of the press requires an auto-repeat behavior
|
|
|
|
// until the slider reaches the destination, or it simply jumps
|
|
|
|
// there (configurable)
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskSlider::mouseMoveEvent( QMouseEvent* event )
|
|
|
|
{
|
|
|
|
if ( !isPressed() )
|
|
|
|
return;
|
|
|
|
|
2020-10-23 11:38:00 +00:00
|
|
|
const auto mousePos = qskMousePosition( event );
|
2017-08-28 08:15:47 +00:00
|
|
|
const auto r = subControlRect( Scale );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
qreal newValue;
|
|
|
|
|
|
|
|
if ( m_data->orientation == Qt::Horizontal )
|
|
|
|
{
|
2020-10-23 11:38:00 +00:00
|
|
|
const auto distance = mousePos.x() - m_data->pressedPos.x();
|
2020-07-25 10:50:26 +00:00
|
|
|
newValue = m_data->pressedValue + distance / r.width() * boundaryLength();
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-10-23 11:38:00 +00:00
|
|
|
const auto distance = mousePos.y() - m_data->pressedPos.y();
|
2020-07-25 10:50:26 +00:00
|
|
|
newValue = m_data->pressedValue - distance / r.height() * boundaryLength();
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
2020-09-23 10:32:08 +00:00
|
|
|
if ( m_data->tracking )
|
|
|
|
{
|
|
|
|
setValue( newValue );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-09-23 10:43:46 +00:00
|
|
|
moveHandleTo( newValue, QskAnimationHint() );
|
2020-09-23 10:32:08 +00:00
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskSlider::mouseReleaseEvent( QMouseEvent* event )
|
|
|
|
{
|
|
|
|
if ( !isPressed() ) // Page event
|
|
|
|
{
|
2020-10-23 11:38:00 +00:00
|
|
|
const auto mousePos = qskMousePosition( event );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2024-11-26 12:57:13 +00:00
|
|
|
const auto szHandle = qskHandleSelectionRect( this ).size();
|
2017-07-21 16:21:34 +00:00
|
|
|
const auto rect = contentsRect();
|
|
|
|
|
|
|
|
bool up;
|
|
|
|
if ( m_data->orientation == Qt::Horizontal )
|
|
|
|
{
|
|
|
|
const qreal w = szHandle.width();
|
|
|
|
|
2020-10-23 11:38:00 +00:00
|
|
|
const qreal x = ( mousePos.x() - rect.x() - w * 0.5 ) / ( rect.width() - w );
|
2020-07-25 10:50:26 +00:00
|
|
|
up = x > valueAsRatio();
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const qreal h = szHandle.height();
|
|
|
|
|
2020-10-23 11:38:00 +00:00
|
|
|
const qreal y = ( mousePos.y() - rect.y() - h * 0.5 ) / ( rect.height() - h );
|
2020-07-25 10:50:26 +00:00
|
|
|
up = y < 1.0 - valueAsRatio();
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( up )
|
|
|
|
pageUp();
|
|
|
|
else
|
|
|
|
pageDown();
|
|
|
|
}
|
2020-09-23 10:32:08 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( !m_data->tracking )
|
|
|
|
{
|
2020-09-23 10:43:46 +00:00
|
|
|
const auto pos = handlePosition();
|
2020-09-23 10:32:08 +00:00
|
|
|
setValue( valueFromRatio( pos ) );
|
|
|
|
}
|
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
setSkinStateFlag( Pressed, false );
|
|
|
|
Q_EMIT pressedChanged( false );
|
|
|
|
}
|
|
|
|
|
2020-09-23 10:43:46 +00:00
|
|
|
qreal QskSlider::handlePosition() const
|
|
|
|
{
|
2021-12-29 16:05:29 +00:00
|
|
|
return positionHint( Handle );
|
2020-09-23 10:43:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskSlider::moveHandle()
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2020-12-21 08:57:57 +00:00
|
|
|
const auto aspect = Handle | QskAspect::Metric | QskAspect::Position;
|
2021-09-14 11:05:56 +00:00
|
|
|
moveHandleTo( value(), animationHint( aspect | skinStates() ) );
|
2020-09-23 10:32:08 +00:00
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2020-09-23 10:43:46 +00:00
|
|
|
void QskSlider::moveHandleTo( qreal value, const QskAnimationHint& hint )
|
2020-09-23 10:32:08 +00:00
|
|
|
{
|
|
|
|
const qreal pos = valueAsRatio( value );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2021-12-29 16:05:29 +00:00
|
|
|
if ( hint.isValid() )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2021-12-29 16:05:29 +00:00
|
|
|
const qreal oldPos = positionHint( Handle );
|
|
|
|
setPositionHint( Handle, pos );
|
2017-08-22 17:47:06 +00:00
|
|
|
|
2021-12-29 16:05:29 +00:00
|
|
|
const auto aspect = Handle | QskAspect::Metric | QskAspect::Position;
|
2017-10-17 15:34:00 +00:00
|
|
|
startTransition( aspect, hint, oldPos, pos );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-12-29 16:05:29 +00:00
|
|
|
setPositionHint( Handle, pos );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "moc_QskSlider.cpp"
|