qskinny/src/controls/QskSlider.cpp

342 lines
8.0 KiB
C++
Raw Normal View History

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"
2024-11-27 15:27:57 +00:00
#include "QskFunctions.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 )
QSK_SYSTEM_STATE( QskSlider, Pressed, QskAspect::FirstSystemState << 2 )
2017-07-21 16:21:34 +00:00
2024-11-26 12:57:13 +00:00
static QRectF qskHandleSelectionRect( const QskSlider* slider )
{
return slider->subControlRect( QskSlider::Handle );
2024-11-26 12:57:13 +00:00
}
2024-11-26 12:59:42 +00:00
static QRectF qskSliderSelectionRect( const QskSlider* slider )
{
const qreal margin = 10.0;
const auto scaleRect = slider->subControlRect( QskSlider::Scale );
const auto handleRect = qskHandleSelectionRect( slider );
auto r = slider->subControlRect( QskSlider::Panel );
if ( slider->orientation() == Qt::Horizontal )
{
r.setTop( qMin( r.top(), handleRect.top() ) );
r.setBottom( qMax( r.bottom(), handleRect.bottom() ) );
r.setLeft( scaleRect.left() - margin );
r.setRight( scaleRect.right() + margin );
}
else
{
r.setLeft( qMin( r.left(), handleRect.left() ) );
r.setRight( qMax( r.right(), handleRect.right() ) );
r.setTop( scaleRect.top() - margin );
r.setBottom( scaleRect.bottom() + margin );
}
return r;
}
static inline int qskKeyOffset( Qt::Orientation orientation, int key )
{
if ( orientation == Qt::Horizontal )
{
if ( key == Qt::Key_Left )
return -1;
if ( key == Qt::Key_Right )
return 1;
}
else
{
if ( key == Qt::Key_Down )
return -1;
if ( key == Qt::Key_Up )
return 1;
}
return 0;
}
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 )
2024-11-27 15:27:57 +00:00
, hasOrigin( false )
2018-08-03 06:15:28 +00:00
, tracking( true )
2024-11-28 14:55:57 +00:00
, dragging( false )
2018-08-03 06:15:28 +00:00
, orientation( orientation )
2017-07-21 16:21:34 +00:00
{
}
QPointF pressedPos;
qreal pressedValue;
2024-11-27 15:27:57 +00:00
qreal origin = 0.0;
bool hasOrigin : 1;
2017-07-21 16:21:34 +00:00
bool tracking : 1;
2024-11-28 14:55:57 +00:00
bool dragging : 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
{
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 )
initSizePolicy( QskSizePolicy::Minimum, QskSizePolicy::Fixed );
2017-07-21 16:21:34 +00:00
else
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()
{
}
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
}
2024-11-27 15:27:57 +00:00
void QskSlider::setOrigin( qreal origin )
2024-11-28 14:55:57 +00:00
{
2024-11-27 15:27:57 +00:00
if ( isComponentComplete() )
origin = boundedValue( origin );
2024-11-28 14:55:57 +00:00
2024-11-27 15:27:57 +00:00
if( !m_data->hasOrigin || !qskFuzzyCompare( m_data->origin, origin ) )
2024-11-28 14:55:57 +00:00
{
2024-11-27 15:27:57 +00:00
m_data->hasOrigin = true;
m_data->origin = origin;
2024-11-28 14:55:57 +00:00
2024-11-27 15:27:57 +00:00
update();
Q_EMIT originChanged( origin );
2024-11-28 14:55:57 +00:00
}
}
2024-11-27 15:27:57 +00:00
void QskSlider::resetOrigin()
2024-11-28 14:55:57 +00:00
{
2024-11-27 15:27:57 +00:00
if ( m_data->hasOrigin )
{
m_data->hasOrigin = false;
2024-11-28 14:55:57 +00:00
2024-11-27 15:27:57 +00:00
update();
Q_EMIT originChanged( origin() );
}
2024-11-28 14:55:57 +00:00
}
2024-11-27 15:27:57 +00:00
qreal QskSlider::origin() const
{
if ( m_data->hasOrigin )
return boundedValue( m_data->origin );
return minimum();
}
QskAspect::Variation QskSlider::effectiveVariation() const
{
return static_cast< QskAspect::Variation >( m_data->orientation );
}
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;
}
2024-11-27 15:27:57 +00:00
void QskSlider::componentComplete()
{
Inherited::componentComplete();
if ( m_data->hasOrigin )
m_data->origin = boundedValue( m_data->origin );
}
void QskSlider::aboutToShow()
{
2021-12-29 16:05:29 +00:00
setPositionHint( Handle, valueAsRatio() );
Inherited::aboutToShow();
}
2017-07-21 16:21:34 +00:00
void QskSlider::mousePressEvent( QMouseEvent* event )
{
2024-11-26 12:59:42 +00:00
const auto pos = qskMousePosition( event );
if ( !qskHandleSelectionRect( this ).contains( pos ) )
2017-07-21 16:21:34 +00:00
{
2024-11-26 12:59:42 +00:00
const auto r = qskSliderSelectionRect( this );
if ( !r.contains( pos ) )
{
Inherited::mousePressEvent( event );
return;
}
2024-11-26 12:59:42 +00:00
qreal ratio;
const auto scaleRect = subControlRect( Scale );
if ( m_data->orientation == Qt::Horizontal )
ratio = ( pos.x() - scaleRect.left() ) / scaleRect.width();
else
ratio = ( scaleRect.bottom() - pos.y() ) / scaleRect.height();
setValue( valueFromRatio( ratio ) );
2017-07-21 16:21:34 +00:00
}
2024-11-26 12:59:42 +00:00
setSkinStateFlag( Pressed );
m_data->pressedPos = pos;
m_data->pressedValue = value();
2017-07-21 16:21:34 +00:00
}
void QskSlider::mouseMoveEvent( QMouseEvent* event )
{
2024-11-28 14:55:57 +00:00
if ( !hasSkinState( Pressed ) )
2017-07-21 16:21:34 +00:00
return;
2020-10-23 11:38:00 +00:00
const auto mousePos = qskMousePosition( event );
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();
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();
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 )
{
2024-11-28 14:55:57 +00:00
m_data->dragging = true;
2020-09-23 10:32:08 +00:00
setValue( newValue );
2024-11-28 14:55:57 +00:00
m_data->dragging = false;
2020-09-23 10:32:08 +00:00
}
else
{
2024-11-26 12:59:42 +00:00
// moving the handle without changing the value
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
}
2024-11-26 12:59:42 +00:00
void QskSlider::mouseReleaseEvent( QMouseEvent* )
2017-07-21 16:21:34 +00:00
{
2024-11-26 12:59:42 +00:00
if ( !m_data->tracking && ( m_data->pressedValue != value() ) )
Q_EMIT valueChanged( value() );
2017-07-21 16:21:34 +00:00
setSkinStateFlag( Pressed, false );
2020-09-23 10:43:46 +00:00
}
void QskSlider::keyPressEvent( QKeyEvent* event )
{
if ( const auto offset = qskKeyOffset( orientation(), event->key() ) )
{
increment( offset * stepSize() );
return;
}
if ( m_data->hasOrigin )
{
switch( event->key() )
{
case Qt::Key_Home:
{
setValue( origin() );
return;
}
case Qt::Key_End:
{
// we have 2 endpoints - better do nothing
return;
}
}
}
Inherited::keyPressEvent( event );
}
2020-09-23 10:43:46 +00:00
void QskSlider::moveHandle()
2017-07-21 16:21:34 +00:00
{
2024-11-26 12:59:42 +00:00
QskAnimationHint hint;
2024-11-28 14:55:57 +00:00
if ( !m_data->dragging )
2024-11-26 12:59:42 +00:00
{
const auto aspect = Handle | QskAspect::Metric | QskAspect::Position;
hint = animationHint( aspect | skinStates() );
}
moveHandleTo( value(), hint );
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;
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"