qskinny/src/controls/QskSegmentedBar.cpp

460 lines
10 KiB
C++
Raw Normal View History

/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
2023-04-06 07:23:37 +00:00
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskSegmentedBar.h"
2023-03-09 14:30:40 +00:00
#include "QskLabelData.h"
#include "QskTextOptions.h"
#include "QskEvent.h"
#include "QskSkinlet.h"
#include "QskAspect.h"
#include <QGuiApplication>
#include <QStyleHints>
#include <QFontMetricsF>
QSK_SUBCONTROL( QskSegmentedBar, Panel )
QSK_SUBCONTROL( QskSegmentedBar, Segment )
QSK_SUBCONTROL( QskSegmentedBar, Separator )
QSK_SUBCONTROL( QskSegmentedBar, Cursor )
QSK_SUBCONTROL( QskSegmentedBar, Text )
QSK_SUBCONTROL( QskSegmentedBar, Icon )
QSK_SYSTEM_STATE( QskSegmentedBar, Selected, QskAspect::FirstSystemState << 1 )
QSK_SYSTEM_STATE( QskSegmentedBar, Minimum, QskAspect::FirstSystemState << 2 )
QSK_SYSTEM_STATE( QskSegmentedBar, Maximum, QskAspect::FirstSystemState << 3 )
class QskSegmentedBar::PrivateData
{
public:
PrivateData( Qt::Orientation orientation )
: orientation( orientation )
{
}
2023-03-09 14:30:40 +00:00
QVector< QskLabelData > options;
QVector< bool > enabled;
int selectedIndex = -1;
int currentIndex = -1;
Qt::Orientation orientation;
bool isPressed = false;
};
QskSegmentedBar::QskSegmentedBar( QQuickItem* parent )
: QskSegmentedBar( Qt::Horizontal, parent )
{
}
QskSegmentedBar::QskSegmentedBar( Qt::Orientation orientation, QQuickItem* parent )
: Inherited( parent )
, m_data( new PrivateData( orientation ) )
{
if( orientation == Qt::Horizontal )
initSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::Fixed );
else
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::MinimumExpanding );
setAcceptedMouseButtons( Qt::LeftButton );
setWheelEnabled( true );
setFocusPolicy( Qt::StrongFocus );
connect( this, &QskSegmentedBar::currentIndexChanged,
this, &QskControl::focusIndicatorRectChanged );
}
QskSegmentedBar::~QskSegmentedBar()
{
}
void QskSegmentedBar::setOrientation( Qt::Orientation orientation )
{
if( orientation != m_data->orientation )
{
setSizePolicy( sizePolicy( Qt::Vertical ), sizePolicy( Qt::Horizontal ) );
m_data->orientation = orientation;
resetImplicitSize();
update();
}
}
Qt::Orientation QskSegmentedBar::orientation() const
{
return m_data->orientation;
}
void QskSegmentedBar::setTextOptions( const QskTextOptions& textOptions )
{
2022-08-25 07:39:33 +00:00
setTextOptionsHint( Text, textOptions );
}
QskTextOptions QskSegmentedBar::textOptions() const
{
2022-08-25 07:39:33 +00:00
return textOptionsHint( Text );
}
int QskSegmentedBar::addOption( const QUrl& graphicSource, const QString& text )
2023-01-21 15:53:58 +00:00
{
2023-03-09 14:30:40 +00:00
return addOption( QskLabelData( text, graphicSource ) );
2023-01-21 15:53:58 +00:00
}
2023-03-09 14:30:40 +00:00
int QskSegmentedBar::addOption( const QskLabelData& option )
{
2023-03-09 14:30:40 +00:00
m_data->options += option;
m_data->enabled += true;
resetImplicitSize();
update();
2023-03-10 08:18:52 +00:00
if ( isComponentComplete() )
Q_EMIT optionsChanged();
2023-03-09 14:30:40 +00:00
if ( count() == 1 )
setSelectedIndex( 0 );
2023-03-09 14:30:40 +00:00
return count() - 1;
}
2023-03-09 14:30:40 +00:00
QskLabelData QskSegmentedBar::optionAt( int index ) const
{
return m_data->options.value( index );
}
2023-03-10 11:46:19 +00:00
void QskSegmentedBar::setOptions( const QStringList& options )
{
setOptions( qskCreateLabelData( options ) );
}
2023-03-09 14:30:40 +00:00
void QskSegmentedBar::setOptions( const QVector< QskLabelData >& options )
{
m_data->options = options;
m_data->enabled.fill( true, options.count() );
resetImplicitSize();
update();
2023-03-09 16:59:54 +00:00
// selectedIndex ???
2023-03-09 14:30:40 +00:00
Q_EMIT optionsChanged();
}
QskAspect::Variation QskSegmentedBar::effectiveVariation() const
{
return static_cast< QskAspect::Variation >( m_data->orientation );
}
void QskSegmentedBar::mousePressEvent( QMouseEvent* event )
{
2022-05-02 14:51:24 +00:00
const int index = indexAtPosition( qskMousePosition( event ) );
2023-03-09 14:30:40 +00:00
if( isSegmentEnabled( index ) )
{
2023-03-09 14:30:40 +00:00
m_data->isPressed = true;
if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
{
2023-03-09 14:30:40 +00:00
if( !QGuiApplication::styleHints()->setFocusOnTouchRelease() )
{
if( index != m_data->currentIndex )
setCurrentIndex( index );
}
}
}
}
2023-03-09 14:30:40 +00:00
void QskSegmentedBar::mouseUngrabEvent()
{
m_data->isPressed = false;
}
void QskSegmentedBar::mouseReleaseEvent( QMouseEvent* event )
{
int index = -1;
if( m_data->isPressed )
{
m_data->isPressed = false;
2022-05-02 14:51:24 +00:00
index = indexAtPosition( qskMousePosition( event ) );
}
2023-03-09 14:30:40 +00:00
if( isSegmentEnabled( index ) )
{
2023-03-09 14:30:40 +00:00
if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
{
2023-03-09 14:30:40 +00:00
if( QGuiApplication::styleHints()->setFocusOnTouchRelease() )
{
if( index != m_data->currentIndex )
setCurrentIndex( index );
}
}
}
setSelectedIndex( index );
}
void QskSegmentedBar::keyPressEvent( QKeyEvent* event )
{
switch( event->key() )
{
case Qt::Key_Up:
case Qt::Key_Down:
case Qt::Key_Left:
case Qt::Key_Right:
{
bool forwards;
if ( m_data->orientation == Qt::Vertical )
forwards = ( event->key() == Qt::Key_Down );
else
forwards = ( event->key() == Qt::Key_Right );
const int index = nextIndex( m_data->selectedIndex, forwards );
if ( index != m_data->selectedIndex )
{
if ( index >= 0 && index < count() )
setSelectedIndex( index );
}
return;
}
case Qt::Key_Select:
case Qt::Key_Space:
// stop further processing
return;
default:
{
const int steps = qskFocusChainIncrement( event );
if( steps != 0 )
{
const int index = nextIndex( m_data->currentIndex, steps > 0 );
if( index != m_data->currentIndex )
setCurrentIndex( index );
if( index >= 0 )
return;
}
}
}
Inherited::keyPressEvent( event );
}
void QskSegmentedBar::keyReleaseEvent( QKeyEvent* event )
{
if( event->key() == Qt::Key_Select || event->key() == Qt::Key_Space )
{
if( m_data->currentIndex >= 0 )
setSelectedIndex( m_data->currentIndex );
return;
}
Inherited::keyReleaseEvent( event );
}
void QskSegmentedBar::focusInEvent( QFocusEvent* event )
{
int index = m_data->currentIndex;
switch( event->reason() )
{
case Qt::TabFocusReason:
{
index = nextIndex( -1, true );
break;
}
case Qt::BacktabFocusReason:
{
index = nextIndex( -1, false );
break;
}
default:
{
if( index < 0 || index >= count() )
index = nextIndex( -1, true );
}
}
if( index != m_data->currentIndex )
setCurrentIndex( index );
Inherited::focusInEvent( event );
}
void QskSegmentedBar::clear()
{
if( count() == 0 )
return;
m_data->options.clear();
2023-03-09 14:30:40 +00:00
Q_EMIT optionsChanged();
if( m_data->selectedIndex >= 0 )
{
m_data->selectedIndex = -1;
Q_EMIT selectedIndexChanged( m_data->selectedIndex );
}
if( m_data->currentIndex >= 0 )
{
m_data->currentIndex = -1;
Q_EMIT currentIndexChanged( m_data->currentIndex );
}
update();
}
void QskSegmentedBar::setCurrentIndex( int index )
{
2023-03-09 14:30:40 +00:00
if ( !isSegmentEnabled( index ) )
index = -1;
if( index != m_data->currentIndex )
{
m_data->currentIndex = index;
Q_EMIT currentIndexChanged( index );
}
}
int QskSegmentedBar::currentIndex() const
{
return m_data->currentIndex;
}
2023-03-10 08:18:52 +00:00
QString QskSegmentedBar::currentText() const
{
if ( m_data->currentIndex >= 0 )
return optionAt( m_data->currentIndex ).text();
return QString();
}
void QskSegmentedBar::setSelectedIndex( int index )
{
2023-03-09 14:30:40 +00:00
if ( !isSegmentEnabled( index ) )
index = -1;
if( index != m_data->selectedIndex )
{
const auto oldIndex = m_data->selectedIndex;
m_data->selectedIndex = index;
movePositionHint( Cursor, index );
update();
Q_EMIT selectedIndexChanged( index );
setSkinStateFlag( Minimum, ( m_data->selectedIndex == 0 ) );
setSkinStateFlag( Maximum, ( m_data->selectedIndex == count() - 1 ) );
const auto states = skinStates();
if ( oldIndex >= 0 )
startHintTransitions( states | Selected, states, oldIndex );
if ( index >= 0 )
startHintTransitions( states, states | Selected, index );
}
}
int QskSegmentedBar::selectedIndex() const
{
return m_data->selectedIndex;
}
int QskSegmentedBar::nextIndex( int index, bool forwards ) const
{
const auto& options = m_data->options;
const int count = options.count();
if( forwards )
{
if( index < 0 || index >= count )
index = -1;
while( ++index < count )
{
2023-03-09 14:30:40 +00:00
if( isSegmentEnabled( index ) )
return index;
}
}
else
{
if( index < 0 || index >= count )
index = count;
while( --index >= 0 )
{
2023-03-09 14:30:40 +00:00
if( isSegmentEnabled( index ) )
return index;
}
}
return -1;
}
2023-03-09 14:30:40 +00:00
QVector< QskLabelData > QskSegmentedBar::options() const
{
return m_data->options;
}
int QskSegmentedBar::count() const
{
return m_data->options.count();
}
void QskSegmentedBar::setSegmentEnabled( int index, bool enabled )
{
2023-03-09 14:30:40 +00:00
auto& bitVector = m_data->enabled;
2023-03-09 14:30:40 +00:00
if( ( index < 0 ) || ( index >= bitVector.count() )
|| ( bitVector[ index ] == enabled ) )
{
return;
}
2023-03-09 14:30:40 +00:00
bitVector[ index ] = enabled;
if( !enabled )
{
if( m_data->currentIndex == index )
setCurrentIndex( -1 );
}
update();
}
bool QskSegmentedBar::isSegmentEnabled( int index ) const
{
2023-03-09 14:30:40 +00:00
return m_data->enabled.value( index, false );
}
int QskSegmentedBar::indexAtPosition( const QPointF& pos ) const
{
return effectiveSkinlet()->sampleIndexAt( this,
contentsRect(), QskSegmentedBar::Segment, pos );
}
QRectF QskSegmentedBar::focusIndicatorRect() const
{
if( m_data->currentIndex >= 0 )
{
return effectiveSkinlet()->sampleRect( this,
contentsRect(), QskSegmentedBar::Segment, m_data->currentIndex );
}
return Inherited::focusIndicatorRect();
}
#include "moc_QskSegmentedBar.cpp"