qskinny/src/controls/QskSpinBox.cpp

461 lines
11 KiB
C++
Raw Normal View History

2023-02-17 11:01:56 +00:00
/******************************************************************************
2023-02-17 13:05:05 +00: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 11:01:56 +00:00
*****************************************************************************/
#include "QskSpinBox.h"
2023-02-19 10:29:13 +00:00
#include "QskIntervalF.h"
#include "QskEvent.h"
#include <qguiapplication.h>
#include <qstylehints.h>
#include <qmath.h>
2023-02-17 13:05:05 +00:00
2023-02-17 11:01:56 +00:00
#include <array>
2023-02-17 14:22:40 +00:00
QSK_SUBCONTROL( QskSpinBox, IncrementPanel )
QSK_SUBCONTROL( QskSpinBox, DecrementPanel )
QSK_SUBCONTROL( QskSpinBox, IncrementText )
QSK_SUBCONTROL( QskSpinBox, DecrementText )
QSK_SUBCONTROL( QskSpinBox, Text )
QSK_SUBCONTROL( QskSpinBox, TextPanel )
QSK_SUBCONTROL( QskSpinBox, Layout )
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
QSK_SYSTEM_STATE( QskSpinBox, Pressed, ( QskAspect::QskAspect::FirstSystemState << 0 ) )
2023-02-17 11:01:56 +00:00
2023-02-17 12:30:39 +00:00
namespace aliased_enum
{
2023-02-17 14:22:40 +00:00
constexpr auto D = QskSpinBox::Decrement;
constexpr auto T = QskSpinBox::Textbox;
constexpr auto I = QskSpinBox::Increment;
constexpr auto N = QskSpinBox::None;
2023-02-17 12:30:39 +00:00
}
2023-02-17 11:01:56 +00:00
class QskSpinBox::PrivateData
{
2023-02-17 14:22:40 +00:00
public:
explicit PrivateData( QskSpinBox* const parent )
: q( parent )
2023-02-17 11:01:56 +00:00
{
}
2023-02-17 14:22:40 +00:00
FocusIndeces defaultFocusIndex() const
{
const auto layout = q->alignmentHint( QskSpinBox::Layout );
if ( layout == Qt::AlignLeft )
{
return QskSpinBox::Textbox;
}
if ( layout == Qt::AlignRight )
{
return QskSpinBox::Decrement;
}
if ( layout == Qt::AlignHCenter )
{
return QskSpinBox::Decrement;
}
if ( layout == Qt::AlignTop )
{
return QskSpinBox::Textbox;
}
if ( layout == Qt::AlignBottom )
{
return QskSpinBox::Increment;
}
if ( layout == Qt::AlignVCenter )
{
return QskSpinBox::Increment;
}
if ( layout == ( Qt::AlignLeft | Qt::AlignVCenter ) )
{
return QskSpinBox::Textbox;
}
if ( layout == ( Qt::AlignRight | Qt::AlignVCenter ) )
{
return QskSpinBox::Increment;
}
if ( layout == ( Qt::AlignTop | Qt::AlignHCenter ) )
{
return QskSpinBox::Textbox;
}
if ( layout == ( Qt::AlignBottom | Qt::AlignHCenter ) )
{
return QskSpinBox::Decrement;
}
return None;
}
FocusIndeces nextFocusIndex() const
{
const auto layout = q->alignmentHint( QskSpinBox::Layout );
using namespace aliased_enum;
// [0][1][2][3] := index
// [D][T][I][N] := control
using LUT = std::array< QskSpinBox::FocusIndeces, 4 >;
if ( layout == Qt::AlignLeft )
{
return LUT{ I, D, N, T }[ m_focusIndex ];
}
if ( layout == Qt::AlignRight )
{
return LUT{ I, N, T, D }[ m_focusIndex ];
}
if ( layout == Qt::AlignHCenter )
{
return LUT{ T, I, N, D }[ m_focusIndex ];
}
if ( layout == Qt::AlignTop )
{
return LUT{ N, I, D, T }[ m_focusIndex ];
}
if ( layout == Qt::AlignBottom )
{
return LUT{ T, N, D, I }[ m_focusIndex ];
}
if ( layout == Qt::AlignVCenter )
{
return LUT{ N, D, T, I }[ m_focusIndex ];
}
if ( layout == ( Qt::AlignLeft | Qt::AlignVCenter ) )
{
return LUT{ N, I, D, T }[ m_focusIndex ];
}
if ( layout == ( Qt::AlignRight | Qt::AlignVCenter ) )
{
return LUT{ T, N, D, I }[ m_focusIndex ];
}
if ( layout == ( Qt::AlignTop | Qt::AlignHCenter ) )
{
return LUT{ I, D, N, T }[ m_focusIndex ];
}
if ( layout == ( Qt::AlignBottom | Qt::AlignHCenter ) )
{
return LUT{ I, N, T, D }[ m_focusIndex ];
}
return None;
}
FocusIndeces previousFocusIndex() const
{
const auto layout = q->alignmentHint( QskSpinBox::Layout );
using namespace aliased_enum;
// [0][1][2][3] := index
// [D][T][I][N] := control
using LUT = std::array< FocusIndeces, 4 >;
if ( layout == Qt::AlignLeft )
{
return LUT{ T, N, D, I }[ m_focusIndex ];
}
if ( layout == Qt::AlignRight )
{
return LUT{ N, I, D, T }[ m_focusIndex ];
}
if ( layout == Qt::AlignHCenter )
{
return LUT{ N, D, T, I }[ m_focusIndex ];
}
if ( layout == Qt::AlignTop )
{
return LUT{ I, N, T, D }[ m_focusIndex ];
}
if ( layout == Qt::AlignBottom )
{
return LUT{ I, D, N, T }[ m_focusIndex ];
}
if ( layout == Qt::AlignVCenter )
{
return LUT{ T, I, N, D }[ m_focusIndex ];
}
if ( layout == ( Qt::AlignLeft | Qt::AlignVCenter ) )
{
return LUT{ I, N, T, D }[ m_focusIndex ];
}
if ( layout == ( Qt::AlignRight | Qt::AlignVCenter ) )
{
return LUT{ I, D, N, T }[ m_focusIndex ];
}
if ( layout == ( Qt::AlignTop | Qt::AlignHCenter ) )
{
return LUT{ T, N, D, I }[ m_focusIndex ];
}
if ( layout == ( Qt::AlignBottom | Qt::AlignHCenter ) )
{
return LUT{ N, I, D, T }[ m_focusIndex ];
}
return None;
}
FocusIndeces focusIndex() const
{
return m_focusIndex;
}
void focusNext()
{
const auto index = nextFocusIndex();
setFocus( index );
}
void focusPrevious()
{
const auto index = previousFocusIndex();
setFocus( index );
}
void focusDefault()
{
const auto index = defaultFocusIndex();
setFocus( index );
}
void setFocus( const FocusIndeces index )
{
using namespace aliased_enum;
Q_ASSERT( index == D || index == T || index == I || index == N );
if ( index == D || index == T || index == I || index == N )
{
m_focusIndex = index;
Q_EMIT q->focusIndexChanged( m_focusIndex );
q->update();
}
}
QRectF focusIndicatorRect() const
{
if ( m_focusIndex == QskSpinBox::Decrement )
{
return q->subControlRect( QskSpinBox::DecrementPanel );
}
if ( m_focusIndex == QskSpinBox::Increment )
{
return q->subControlRect( QskSpinBox::IncrementPanel );
}
if ( m_focusIndex == QskSpinBox::Textbox )
{
return q->subControlRect( QskSpinBox::TextPanel );
}
return {};
}
void saveMousePosition( const QPointF& pos )
{
q->setSkinHint( QskSpinBox::Layout | QskAspect::Metric | QskAspect::Position, pos );
}
bool focusNow() const
{
const auto focusOnClick = ( q->focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus;
const auto focusOnTouchRelease = QGuiApplication::styleHints()->setFocusOnTouchRelease();
return focusOnClick && !focusOnTouchRelease;
}
private:
QskSpinBox* const q;
FocusIndeces m_focusIndex = FocusIndeces::None;
2023-02-17 11:01:56 +00:00
};
using S = QskSpinBox;
2023-02-17 14:22:40 +00:00
QskSpinBox::QskSpinBox( QQuickItem* const parent )
: Inherited( parent )
, m_data( std::make_unique< PrivateData >( this ) )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
setBoundaries( 0.0, 1.0 );
setAcceptHoverEvents( true );
setAcceptedMouseButtons( Qt::LeftButton );
setFocusPolicy( Qt::StrongFocus );
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
connect( this, &S::focusIndexChanged, this, &S::focusIndicatorRectChanged );
2023-02-17 11:01:56 +00:00
}
QskSpinBox::~QskSpinBox() = default;
2023-02-17 14:22:40 +00:00
void QskSpinBox::hoverEnterEvent( QHoverEvent* const event )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
m_data->saveMousePosition( qskHoverPosition( event ) );
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
void QskSpinBox::hoverLeaveEvent( QHoverEvent* /*const event */ )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
m_data->saveMousePosition( {} );
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
void QskSpinBox::hoverMoveEvent( QHoverEvent* const event )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
m_data->saveMousePosition( qskHoverPosition( event ) );
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
void QskSpinBox::mouseReleaseEvent( QMouseEvent* const event )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
m_data->saveMousePosition( qskMousePosition( event ) );
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
const auto focus = m_data->focusNow();
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
if ( subControlRect( QskSpinBox::IncrementPanel ).contains( event->pos() ) )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
increment( +stepSize() );
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
if ( focus )
{
m_data->setFocus( Increment );
}
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
return;
}
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
if ( subControlRect( QskSpinBox::DecrementPanel ).contains( event->pos() ) )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
increment( -stepSize() );
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
if ( focus )
{
m_data->setFocus( Decrement );
}
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
return;
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
if ( subControlRect( QskSpinBox::TextPanel ).contains( event->pos() ) )
{
if ( focus )
{
m_data->setFocus( Textbox );
}
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
return;
}
event->ignore();
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
void QskSpinBox::mousePressEvent( QMouseEvent* const event )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
m_data->saveMousePosition( -1 * qskMousePosition( event ) );
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
const auto focus = m_data->focusNow();
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
if ( subControlRect( QskSpinBox::IncrementPanel ).contains( event->pos() ) )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
if ( focus )
{
m_data->setFocus( QskSpinBox::Increment );
}
return;
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
if ( subControlRect( QskSpinBox::DecrementPanel ).contains( event->pos() ) )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
if ( focus )
{
m_data->setFocus( QskSpinBox::Decrement );
}
return;
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
event->ignore();
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
void QskSpinBox::keyPressEvent( QKeyEvent* const event )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
switch ( event->key() )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
case Qt::Key_Plus:
case Qt::Key_Up:
case Qt::Key_Right:
increment( +stepSize() );
return;
case Qt::Key_Minus:
case Qt::Key_Down:
case Qt::Key_Left:
increment( -stepSize() );
return;
case Qt::Key_Select:
case Qt::Key_Space:
if ( focusIndex() == Increment )
{
increment( +stepSize() );
}
if ( focusIndex() == Decrement )
{
increment( -stepSize() );
}
return;
default:
break;
}
2023-02-17 13:37:10 +00:00
2023-02-17 14:22:40 +00:00
const int steps = qskFocusChainIncrement( event );
if ( steps < 0 )
{
for ( int i = 0; i < qAbs( steps ); ++i )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
m_data->focusPrevious();
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
}
2023-02-17 13:37:10 +00:00
2023-02-17 14:22:40 +00:00
if ( steps > 0 )
{
for ( int i = 0; i < steps; ++i )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
m_data->focusNext();
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
}
2023-02-17 13:37:10 +00:00
2023-02-17 14:22:40 +00:00
if ( steps != 0 && m_data->focusIndex() != None )
{
2023-02-17 13:37:10 +00:00
return;
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
Inherited::keyPressEvent( event );
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
void QskSpinBox::keyReleaseEvent( QKeyEvent* const event )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
if ( event->key() == Qt::Key_Select || event->key() == Qt::Key_Space )
{
return;
}
2023-02-17 11:01:56 +00:00
2023-02-17 14:22:40 +00:00
Inherited::keyReleaseEvent( event );
2023-02-17 11:01:56 +00:00
}
2023-02-17 14:22:40 +00:00
void QskSpinBox::focusInEvent( QFocusEvent* const event )
2023-02-17 11:01:56 +00:00
{
2023-02-17 14:22:40 +00:00
if ( event->reason() == Qt::TabFocusReason )
{
m_data->focusNext();
return;
}
if ( event->reason() == Qt::BacktabFocusReason )
{
m_data->focusPrevious();
return;
}
if ( m_data->focusIndex() == QskSpinBox::None )
{
2023-02-17 11:01:56 +00:00
m_data->focusDefault();
return;
2023-02-17 14:22:40 +00:00
}
Inherited::focusInEvent( event );
2023-02-17 11:01:56 +00:00
}
QRectF QskSpinBox::focusIndicatorRect() const
{
2023-02-17 14:22:40 +00:00
return m_data->focusIndicatorRect();
2023-02-17 11:01:56 +00:00
}
2023-02-17 12:30:39 +00:00
QskSpinBox::FocusIndeces QskSpinBox::focusIndex() const
{
2023-02-17 14:22:40 +00:00
return m_data->focusIndex();
2023-02-17 12:30:39 +00:00
}