qskinny/src/controls/QskSpinBox.cpp

386 lines
9.4 KiB
C++
Raw Normal View History

2023-02-17 12:01:56 +01:00
/******************************************************************************
2023-02-17 14:05:05 +01:00
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
2023-02-17 12:01:56 +01:00
*****************************************************************************/
#include "QskSpinBox.h"
2023-02-19 11:29:13 +01:00
#include "QskEvent.h"
#include "QskFunctions.h"
2023-02-19 11:29:13 +01:00
2023-02-23 14:37:49 +01:00
#include <qbasictimer.h>
2023-02-24 08:24:32 +01:00
#include <qlocale.h>
#include <cfloat>
2023-02-23 14:37:49 +01:00
QSK_SUBCONTROL( QskSpinBox, Panel )
2023-02-17 14:05:05 +01:00
QSK_SUBCONTROL( QskSpinBox, TextPanel )
QSK_SUBCONTROL( QskSpinBox, Text )
2023-02-17 12:01:56 +01:00
2023-02-23 14:37:49 +01:00
QSK_SUBCONTROL( QskSpinBox, UpPanel )
QSK_SUBCONTROL( QskSpinBox, UpIndicator )
2023-02-23 14:37:49 +01:00
QSK_SUBCONTROL( QskSpinBox, DownPanel )
QSK_SUBCONTROL( QskSpinBox, DownIndicator )
2023-02-17 12:01:56 +01:00
2023-02-23 14:37:49 +01:00
QSK_SYSTEM_STATE( QskSpinBox, Increasing, ( QskAspect::FirstSystemState << 2 ) )
QSK_SYSTEM_STATE( QskSpinBox, Decreasing, ( QskAspect::FirstSystemState << 3 ) )
2023-02-17 12:01:56 +01:00
namespace
2023-02-17 13:30:39 +01:00
{
2023-02-23 14:37:49 +01:00
inline QskAspect::Subcontrol buttonAt( const QskSpinBox* spinBox, const QPointF& pos )
2023-02-17 15:22:40 +01:00
{
2023-02-23 14:37:49 +01:00
if ( spinBox->subControlRect( QskSpinBox::UpPanel ).contains( pos ) )
return QskSpinBox::UpPanel;
2023-02-17 15:22:40 +01:00
2023-02-23 14:37:49 +01:00
if ( spinBox->subControlRect( QskSpinBox::DownPanel ).contains( pos ) )
return QskSpinBox::DownPanel;
2023-02-17 15:22:40 +01:00
2023-02-23 14:37:49 +01:00
return QskAspect::NoSubcontrol;
2023-02-17 15:22:40 +01:00
}
2023-02-27 09:56:41 +01:00
inline QskAspect aspectDecoration()
{
return QskSpinBox::Panel | QskAspect::NoType | QskAspect::Style;
2023-02-27 09:56:41 +01:00
}
inline QskAspect aspectTextAlignment()
{
return QskSpinBox::TextPanel | QskAspect::NoType | QskAspect::Alignment;
2023-02-27 09:56:41 +01:00
}
}
2023-02-17 15:22:40 +01:00
class QskSpinBox::PrivateData
{
public:
2023-02-23 14:37:49 +01:00
PrivateData()
: wrapping( false )
2023-02-17 15:22:40 +01:00
{
}
2023-03-01 14:22:40 +01:00
inline bool isActivatedByMouse() const
{
return this->repeatTimer.isActive() && ( this->key == Qt::Key_unknown );
}
inline bool isTimerBlocked( QskSpinBox* spinBox, qreal offset ) const
{
if ( !this->wrapping )
{
if ( offset >= 0.0 )
return spinBox->value() >= spinBox->maximum();
else
return spinBox->value() <= spinBox->minimum();
}
return false;
}
2023-02-23 14:37:49 +01:00
inline void setAutoRepeat( QskSpinBox* spinBox, qreal offset )
2023-02-17 15:22:40 +01:00
{
2023-02-23 14:37:49 +01:00
this->autoRepeatIncrement = offset;
this->key = Qt::Key_unknown;
if( offset < 0.0 )
{
if ( spinBox->hasSkinState( QskSpinBox::Increasing ) )
spinBox->setSkinStateFlag( QskSpinBox::Increasing, false );
if ( !spinBox->hasSkinState( QskSpinBox::Decreasing ) )
{
spinBox->setSkinStateFlag( QskSpinBox::Decreasing, true );
this->repeatTimer.start( autoRepeatDelay, spinBox );
}
return;
}
if ( offset > 0.0 )
2023-02-17 15:22:40 +01:00
{
2023-02-23 14:37:49 +01:00
if ( spinBox->hasSkinState( QskSpinBox::Decreasing ) )
spinBox->setSkinStateFlag( QskSpinBox::Decreasing, false );
if ( !spinBox->hasSkinState( QskSpinBox::Increasing ) )
{
spinBox->setSkinStateFlag( QskSpinBox::Increasing, true );
this->repeatTimer.start( autoRepeatDelay, spinBox );
}
return;
2023-02-17 15:22:40 +01:00
}
2023-02-23 14:37:49 +01:00
spinBox->setSkinStateFlag( QskSpinBox::Decreasing, false );
spinBox->setSkinStateFlag( QskSpinBox::Increasing, false );
this->repeatTimer.stop();
2023-02-17 15:22:40 +01:00
}
2023-02-24 08:24:32 +01:00
int decimals = 2;
2023-02-23 14:37:49 +01:00
int autoRepeatDelay = 300;
int autoRepeatInterval = 100;
qreal autoRepeatIncrement = 0.0;
2023-02-17 15:22:40 +01:00
2023-02-23 14:37:49 +01:00
QBasicTimer repeatTimer;
int key = Qt::Key_unknown;
2023-02-27 09:56:41 +01:00
bool wrapping : 1;
#if 0
// not yet implemented: TODO ...
bool tracking : 1;
bool accelerating : 1;
#endif
2023-02-17 12:01:56 +01:00
};
2023-02-27 09:56:41 +01:00
QskSpinBox::QskSpinBox( qreal min, qreal max, qreal stepSize, QQuickItem* parent )
2023-02-17 15:22:40 +01:00
: Inherited( parent )
, m_data( new PrivateData )
2023-02-17 12:01:56 +01:00
{
2023-02-23 14:37:49 +01:00
initSizePolicy( QskSizePolicy::Minimum, QskSizePolicy::Fixed );
2023-02-27 09:56:41 +01:00
setBoundaries( min, max );
setStepSize( stepSize );
2023-02-17 15:22:40 +01:00
setAcceptedMouseButtons( Qt::LeftButton );
setFocusPolicy( Qt::StrongFocus );
2023-02-24 08:24:32 +01:00
connect( this, &QskSpinBox::valueChanged, this, &QskSpinBox::textChanged );
2023-02-17 12:01:56 +01:00
}
2023-02-27 09:56:41 +01:00
QskSpinBox::QskSpinBox( QQuickItem* parent )
: QskSpinBox( 0.0, 99.99, 0.1, parent )
{
}
QskSpinBox::~QskSpinBox()
2023-02-17 12:01:56 +01:00
{
}
2023-02-27 09:56:41 +01:00
void QskSpinBox::setDecoration( Decoration decoration )
2023-02-17 12:01:56 +01:00
{
2023-02-27 09:56:41 +01:00
if ( setFlagHint( aspectDecoration(), decoration ) )
Q_EMIT decorationChanged( decoration );
}
void QskSpinBox::resetDecoration()
{
if ( resetSkinHint( aspectDecoration() ) )
2023-02-27 09:56:41 +01:00
Q_EMIT decorationChanged( decoration() );
}
QskSpinBox::Decoration QskSpinBox::decoration() const
{
return flagHint< QskSpinBox::Decoration >( aspectDecoration(), Buttons );
}
void QskSpinBox::setTextAlignment( Qt::Alignment alignment )
{
alignment &= Qt::AlignHorizontal_Mask;
if ( setFlagHint( aspectTextAlignment(), alignment ) )
Q_EMIT textAlignmentChanged( alignment );
}
void QskSpinBox::resetTextAlignment()
{
if ( resetSkinHint( aspectTextAlignment() ) )
2023-02-27 09:56:41 +01:00
Q_EMIT textAlignmentChanged( textAlignment() );
2023-02-17 12:01:56 +01:00
}
2023-02-27 09:56:41 +01:00
Qt::Alignment QskSpinBox::textAlignment() const
2023-02-17 12:01:56 +01:00
{
2023-02-27 09:56:41 +01:00
return flagHint< Qt::Alignment >(
aspectTextAlignment(), Qt::AlignLeft ) & Qt::AlignHorizontal_Mask;
2023-02-17 12:01:56 +01:00
}
2023-02-23 14:37:49 +01:00
void QskSpinBox::setWrapping( bool on )
{
if ( on != m_data->wrapping )
{
m_data->wrapping = on;
2023-02-23 14:37:49 +01:00
Q_EMIT wrappingChanged( on );
2023-02-17 15:22:40 +01:00
}
2023-02-23 14:37:49 +01:00
}
2023-02-17 15:22:40 +01:00
2023-02-23 14:37:49 +01:00
bool QskSpinBox::isWrapping() const
{
return m_data->wrapping;
2023-02-17 12:01:56 +01:00
}
2023-02-24 08:24:32 +01:00
void QskSpinBox::setDecimals( int decimals )
{
decimals = qBound( 0, decimals, DBL_MAX_10_EXP + DBL_DIG );
if ( decimals != m_data->decimals )
{
m_data->decimals = decimals;
update();
resetImplicitSize();
}
}
int QskSpinBox::decimals() const
{
return m_data->decimals;
}
QString QskSpinBox::text() const
{
return textFromValue( value() );
}
QString QskSpinBox::textFromValue( qreal value ) const
{
return locale().toString( value, 'f', m_data->decimals );
}
void QskSpinBox::increment( qreal offset )
{
if ( m_data->wrapping )
{
const auto v = value();
if ( offset > 0.0 && qskFuzzyCompare( v, maximum() ) )
{
setValue( minimum() );
return;
}
if ( offset < 0.0 && qskFuzzyCompare( v, minimum() ) )
{
setValue( maximum() );
return;
}
}
Inherited::increment( offset );
}
2023-02-23 14:37:49 +01:00
void QskSpinBox::mousePressEvent( QMouseEvent* event )
{
if ( !isReadOnly() )
2023-02-17 12:01:56 +01:00
{
2023-02-23 14:37:49 +01:00
if ( auto subcontrol = buttonAt( this, qskMousePosition( event ) ) )
{
bool acceptPress = !m_data->repeatTimer.isActive();
if ( acceptPress && !m_data->wrapping )
{
if ( subcontrol == QskSpinBox::DownPanel )
acceptPress = value() > minimum();
else
acceptPress = value() < maximum();
}
if ( acceptPress )
2023-02-23 14:37:49 +01:00
{
auto offset = stepSize();
if ( event->modifiers() & ( Qt::ControlModifier | Qt::ShiftModifier ) )
offset *= pageSize();
2023-02-17 14:37:10 +01:00
2023-02-23 14:37:49 +01:00
if ( subcontrol == QskSpinBox::DownPanel )
offset = -offset;
2023-02-23 14:37:49 +01:00
increment( offset );
if ( !m_data->isTimerBlocked( this, offset ) )
m_data->setAutoRepeat( this, offset );
2023-02-23 14:37:49 +01:00
}
return;
2023-02-17 12:01:56 +01:00
}
2023-02-23 14:37:49 +01:00
}
2023-02-17 14:37:10 +01:00
2023-02-23 14:37:49 +01:00
Inherited::mousePressEvent( event );
}
2023-02-23 14:37:49 +01:00
void QskSpinBox::mouseReleaseEvent( QMouseEvent* )
{
2023-03-01 14:22:40 +01:00
if ( m_data->isActivatedByMouse() )
m_data->setAutoRepeat( this, 0.0 );
}
void QskSpinBox::mouseMoveEvent( QMouseEvent* event )
{
if ( m_data->isActivatedByMouse() )
{
const auto subcontrol = buttonAt( this, qskMousePosition( event ) );
if ( m_data->autoRepeatIncrement >= 0.0 )
{
if ( subcontrol != QskSpinBox::UpPanel )
m_data->setAutoRepeat( this, 0.0 );
}
else
{
if ( subcontrol != QskSpinBox::DownPanel )
m_data->setAutoRepeat( this, 0.0 );
}
}
}
void QskSpinBox::mouseUngrabEvent()
{
if ( m_data->isActivatedByMouse() )
m_data->setAutoRepeat( this, 0.0 );
2023-02-23 14:37:49 +01:00
}
2023-02-23 14:37:49 +01:00
void QskSpinBox::keyPressEvent( QKeyEvent* event )
{
if ( !isReadOnly() && !m_data->repeatTimer.isActive() )
{
const auto offset = incrementForKey( event );
if ( offset != 0.0 )
{
if ( !m_data->repeatTimer.isActive() )
{
increment( offset );
if ( !m_data->isTimerBlocked( this, offset ) )
{
m_data->setAutoRepeat( this, offset );
m_data->key = event->key();
}
2023-02-23 14:37:49 +01:00
}
return;
}
2023-02-17 12:01:56 +01:00
}
2023-02-17 15:22:40 +01:00
Inherited::keyPressEvent( event );
2023-02-17 12:01:56 +01:00
}
2023-02-23 14:37:49 +01:00
void QskSpinBox::keyReleaseEvent( QKeyEvent* event )
2023-02-17 12:01:56 +01:00
{
2023-02-23 14:37:49 +01:00
if ( m_data->key == event->key() )
{
m_data->setAutoRepeat( this, 0.0 );
return;
}
2023-02-17 15:22:40 +01:00
Inherited::keyReleaseEvent( event );
2023-02-17 12:01:56 +01:00
}
2023-02-23 14:37:49 +01:00
void QskSpinBox::timerEvent( QTimerEvent* event )
2023-02-17 12:01:56 +01:00
{
2023-02-23 14:37:49 +01:00
if ( event->timerId() == m_data->repeatTimer.timerId() )
{
if ( skinStates() & ( QskSpinBox::Increasing | QskSpinBox::Decreasing ) )
{
increment( m_data->autoRepeatIncrement );
if ( m_data->isTimerBlocked( this, m_data->autoRepeatIncrement ) )
m_data->setAutoRepeat( this, 0.0 );
else
m_data->repeatTimer.start( m_data->autoRepeatInterval, this );
2023-02-23 14:37:49 +01:00
}
2023-02-17 12:01:56 +01:00
2023-02-23 14:37:49 +01:00
return;
}
Inherited::timerEvent( event );
2023-02-17 13:30:39 +01:00
}
2023-02-23 14:37:49 +01:00
#include "moc_QskSpinBox.cpp"