2022-04-20 12:14:37 +00:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
2023-04-06 07:23:37 +00:00
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
2022-04-20 12:14:37 +00:00
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskSegmentedBar.h"
|
|
|
|
|
2023-03-09 14:30:40 +00:00
|
|
|
#include "QskLabelData.h"
|
2022-04-20 12:14:37 +00:00
|
|
|
#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 )
|
2022-06-17 08:01:10 +00:00
|
|
|
QSK_SUBCONTROL( QskSegmentedBar, Separator )
|
2022-04-20 12:14:37 +00:00
|
|
|
QSK_SUBCONTROL( QskSegmentedBar, Cursor )
|
|
|
|
QSK_SUBCONTROL( QskSegmentedBar, Text )
|
2023-03-08 13:11:27 +00:00
|
|
|
QSK_SUBCONTROL( QskSegmentedBar, Icon )
|
2022-04-20 12:14:37 +00:00
|
|
|
|
2022-06-16 07:53:12 +00:00
|
|
|
QSK_SYSTEM_STATE( QskSegmentedBar, Selected, QskAspect::FirstSystemState << 1 )
|
|
|
|
QSK_SYSTEM_STATE( QskSegmentedBar, Minimum, QskAspect::FirstSystemState << 2 )
|
|
|
|
QSK_SYSTEM_STATE( QskSegmentedBar, Maximum, QskAspect::FirstSystemState << 3 )
|
2022-04-20 12:14:37 +00:00
|
|
|
|
|
|
|
class QskSegmentedBar::PrivateData
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PrivateData( Qt::Orientation orientation )
|
|
|
|
: orientation( orientation )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-03-09 14:30:40 +00:00
|
|
|
QVector< QskLabelData > options;
|
|
|
|
QVector< bool > enabled;
|
2022-04-20 12:14:37 +00:00
|
|
|
|
|
|
|
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 )
|
2022-04-20 13:25:15 +00:00
|
|
|
initSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::Fixed );
|
2022-04-20 12:14:37 +00:00
|
|
|
else
|
2022-04-20 13:25:15 +00:00
|
|
|
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::MinimumExpanding );
|
2022-04-20 12:14:37 +00:00
|
|
|
|
|
|
|
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 );
|
2022-04-20 12:14:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QskTextOptions QskSegmentedBar::textOptions() const
|
|
|
|
{
|
2022-08-25 07:39:33 +00:00
|
|
|
return textOptionsHint( Text );
|
2022-04-20 12:14:37 +00:00
|
|
|
}
|
|
|
|
|
2023-02-01 14:47:13 +00:00
|
|
|
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 )
|
2022-04-20 12:14:37 +00:00
|
|
|
{
|
2023-03-09 14:30:40 +00:00
|
|
|
m_data->options += option;
|
|
|
|
m_data->enabled += true;
|
|
|
|
|
|
|
|
resetImplicitSize();
|
|
|
|
update();
|
2022-04-20 12:14:37 +00:00
|
|
|
|
2023-03-10 08:18:52 +00:00
|
|
|
if ( isComponentComplete() )
|
|
|
|
Q_EMIT optionsChanged();
|
2022-04-20 12:14:37 +00:00
|
|
|
|
2023-03-09 14:30:40 +00:00
|
|
|
if ( count() == 1 )
|
|
|
|
setSelectedIndex( 0 );
|
2022-04-20 12:14:37 +00:00
|
|
|
|
2023-03-09 14:30:40 +00:00
|
|
|
return count() - 1;
|
|
|
|
}
|
2022-04-20 12:14:37 +00:00
|
|
|
|
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();
|
2022-04-20 12:14:37 +00:00
|
|
|
}
|
|
|
|
|
2023-02-15 15:37:54 +00:00
|
|
|
QskAspect::Variation QskSegmentedBar::effectiveVariation() const
|
2022-04-20 12:14:37 +00:00
|
|
|
{
|
2023-02-15 15:37:54 +00:00
|
|
|
return static_cast< QskAspect::Variation >( m_data->orientation );
|
2022-04-20 12:14:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskSegmentedBar::mousePressEvent( QMouseEvent* event )
|
|
|
|
{
|
2022-05-02 14:51:24 +00:00
|
|
|
const int index = indexAtPosition( qskMousePosition( event ) );
|
2022-04-20 12:14:37 +00:00
|
|
|
|
2023-03-09 14:30:40 +00:00
|
|
|
if( isSegmentEnabled( index ) )
|
2022-04-20 12:14:37 +00:00
|
|
|
{
|
2023-03-09 14:30:40 +00:00
|
|
|
m_data->isPressed = true;
|
|
|
|
|
|
|
|
if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
|
2022-04-20 12:14:37 +00:00
|
|
|
{
|
2023-03-09 14:30:40 +00:00
|
|
|
if( !QGuiApplication::styleHints()->setFocusOnTouchRelease() )
|
|
|
|
{
|
|
|
|
if( index != m_data->currentIndex )
|
|
|
|
setCurrentIndex( index );
|
|
|
|
}
|
2022-04-20 12:14:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-09 14:30:40 +00:00
|
|
|
void QskSegmentedBar::mouseUngrabEvent()
|
|
|
|
{
|
|
|
|
m_data->isPressed = false;
|
|
|
|
}
|
|
|
|
|
2022-04-20 12:14:37 +00:00
|
|
|
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 ) );
|
2022-04-20 12:14:37 +00:00
|
|
|
}
|
|
|
|
|
2023-03-09 14:30:40 +00:00
|
|
|
if( isSegmentEnabled( index ) )
|
2022-04-20 12:14:37 +00:00
|
|
|
{
|
2023-03-09 14:30:40 +00:00
|
|
|
if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
|
2022-04-20 12:14:37 +00:00
|
|
|
{
|
2023-03-09 14:30:40 +00:00
|
|
|
if( QGuiApplication::styleHints()->setFocusOnTouchRelease() )
|
|
|
|
{
|
|
|
|
if( index != m_data->currentIndex )
|
|
|
|
setCurrentIndex( index );
|
|
|
|
}
|
2022-04-20 12:14:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2022-04-20 12:14:37 +00:00
|
|
|
|
|
|
|
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 ) )
|
2022-04-20 12:14:37 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2022-04-20 12:14:37 +00:00
|
|
|
void QskSegmentedBar::setSelectedIndex( int index )
|
|
|
|
{
|
2023-03-09 14:30:40 +00:00
|
|
|
if ( !isSegmentEnabled( index ) )
|
2022-04-20 12:14:37 +00:00
|
|
|
index = -1;
|
|
|
|
|
|
|
|
if( index != m_data->selectedIndex )
|
|
|
|
{
|
2022-09-13 10:40:44 +00:00
|
|
|
const auto oldIndex = m_data->selectedIndex;
|
2022-04-20 12:14:37 +00:00
|
|
|
m_data->selectedIndex = index;
|
|
|
|
|
|
|
|
movePositionHint( Cursor, index );
|
|
|
|
update();
|
|
|
|
|
|
|
|
Q_EMIT selectedIndexChanged( index );
|
2022-06-16 07:53:12 +00:00
|
|
|
|
|
|
|
setSkinStateFlag( Minimum, ( m_data->selectedIndex == 0 ) );
|
|
|
|
setSkinStateFlag( Maximum, ( m_data->selectedIndex == count() - 1 ) );
|
2022-09-13 10:40:44 +00:00
|
|
|
|
|
|
|
const auto states = skinStates();
|
|
|
|
|
|
|
|
if ( oldIndex >= 0 )
|
|
|
|
startHintTransitions( states | Selected, states, oldIndex );
|
|
|
|
|
|
|
|
if ( index >= 0 )
|
|
|
|
startHintTransitions( states, states | Selected, index );
|
2022-04-20 12:14:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 ) )
|
2022-04-20 12:14:37 +00:00
|
|
|
return index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( index < 0 || index >= count )
|
|
|
|
index = count;
|
|
|
|
|
|
|
|
while( --index >= 0 )
|
|
|
|
{
|
2023-03-09 14:30:40 +00:00
|
|
|
if( isSegmentEnabled( index ) )
|
2022-04-20 12:14:37 +00:00
|
|
|
return index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2023-03-09 14:30:40 +00:00
|
|
|
QVector< QskLabelData > QskSegmentedBar::options() const
|
|
|
|
{
|
|
|
|
return m_data->options;
|
|
|
|
}
|
|
|
|
|
2022-04-20 12:14:37 +00:00
|
|
|
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;
|
2022-04-20 12:14:37 +00:00
|
|
|
|
2023-03-09 14:30:40 +00:00
|
|
|
if( ( index < 0 ) || ( index >= bitVector.count() )
|
|
|
|
|| ( bitVector[ index ] == enabled ) )
|
2022-04-20 12:14:37 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-03-09 14:30:40 +00:00
|
|
|
bitVector[ index ] = enabled;
|
2022-04-20 12:14:37 +00:00
|
|
|
|
|
|
|
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 );
|
2022-04-20 12:14:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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"
|