476 lines
11 KiB
C++
476 lines
11 KiB
C++
/******************************************************************************
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
*****************************************************************************/
|
|
|
|
#include "QskSegmentedBar.h"
|
|
|
|
#include "QskGraphic.h"
|
|
#include "QskGraphicProvider.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, Graphic )
|
|
|
|
QSK_SYSTEM_STATE( QskSegmentedBar, Selected, QskAspect::FirstSystemState << 1 )
|
|
QSK_SYSTEM_STATE( QskSegmentedBar, Minimum, QskAspect::FirstSystemState << 2 )
|
|
QSK_SYSTEM_STATE( QskSegmentedBar, Maximum, QskAspect::FirstSystemState << 3 )
|
|
|
|
namespace
|
|
{
|
|
class Option
|
|
{
|
|
public:
|
|
Option() = default;
|
|
|
|
Option( const QUrl& graphicSource, const QString& text )
|
|
: graphicSource( graphicSource )
|
|
, text( text )
|
|
{
|
|
#if 1
|
|
// lazy loading TODO ...
|
|
if ( !graphicSource.isEmpty() )
|
|
graphic = Qsk::loadGraphic( graphicSource );
|
|
#endif
|
|
}
|
|
|
|
QUrl graphicSource;
|
|
QString text;
|
|
|
|
QskGraphic graphic;
|
|
|
|
bool isEnabled = true;
|
|
};
|
|
}
|
|
|
|
class QskSegmentedBar::PrivateData
|
|
{
|
|
public:
|
|
PrivateData( Qt::Orientation orientation )
|
|
: orientation( orientation )
|
|
{
|
|
}
|
|
|
|
void addOption( QskSegmentedBar* bar, const Option& option )
|
|
{
|
|
this->options += option;
|
|
|
|
bar->resetImplicitSize();
|
|
bar->update();
|
|
|
|
Q_EMIT bar->countChanged();
|
|
|
|
if ( this->options.count() == 1 )
|
|
bar->setSelectedIndex( 0 );
|
|
}
|
|
|
|
QVector< Option > options;
|
|
|
|
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 )
|
|
{
|
|
setTextOptionsHint( Text, textOptions );
|
|
}
|
|
|
|
QskTextOptions QskSegmentedBar::textOptions() const
|
|
{
|
|
return textOptionsHint( Text );
|
|
}
|
|
|
|
int QskSegmentedBar::addOption( const QUrl& graphicSource, const QString& text )
|
|
{
|
|
m_data->addOption( this, Option( graphicSource, text ) );
|
|
return count() - 1;
|
|
}
|
|
|
|
QVariantList QskSegmentedBar::optionAt( int index ) const
|
|
{
|
|
const auto& options = m_data->options;
|
|
|
|
if( index < 0 || index >= options.count() )
|
|
return QVariantList();
|
|
|
|
const auto& option = options[ index ];
|
|
|
|
QVariantList list;
|
|
list += QVariant::fromValue( option.graphic );
|
|
list += QVariant::fromValue( option.text );
|
|
|
|
return list;
|
|
}
|
|
|
|
QskAspect::Variation QskSegmentedBar::effectiveVariation() const
|
|
{
|
|
return static_cast< QskAspect::Variation >( m_data->orientation );
|
|
}
|
|
|
|
void QskSegmentedBar::mousePressEvent( QMouseEvent* event )
|
|
{
|
|
const int index = indexAtPosition( qskMousePosition( event ) );
|
|
|
|
if( index < 0 || index >= count() || !m_data->options[ index ].isEnabled )
|
|
return;
|
|
|
|
m_data->isPressed = true;
|
|
|
|
if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
|
|
{
|
|
if( !QGuiApplication::styleHints()->setFocusOnTouchRelease() )
|
|
{
|
|
if( index != m_data->currentIndex )
|
|
setCurrentIndex( index );
|
|
}
|
|
}
|
|
}
|
|
|
|
void QskSegmentedBar::mouseReleaseEvent( QMouseEvent* event )
|
|
{
|
|
int index = -1;
|
|
|
|
if( m_data->isPressed )
|
|
{
|
|
m_data->isPressed = false;
|
|
index = indexAtPosition( qskMousePosition( event ) );
|
|
}
|
|
|
|
if( index < 0 || !m_data->options[ index ].isEnabled )
|
|
return;
|
|
|
|
if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
|
|
{
|
|
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();
|
|
Q_EMIT countChanged();
|
|
|
|
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 )
|
|
{
|
|
const auto& options = m_data->options;
|
|
|
|
if( ( index < 0 ) || ( index >= options.count() )
|
|
|| !options[ index ].isEnabled )
|
|
{
|
|
index = -1;
|
|
}
|
|
|
|
if( index != m_data->currentIndex )
|
|
{
|
|
m_data->currentIndex = index;
|
|
Q_EMIT currentIndexChanged( index );
|
|
}
|
|
}
|
|
|
|
int QskSegmentedBar::currentIndex() const
|
|
{
|
|
return m_data->currentIndex;
|
|
}
|
|
|
|
void QskSegmentedBar::setSelectedIndex( int index )
|
|
{
|
|
if( index < 0 || index >= m_data->options.count() )
|
|
{
|
|
index = -1;
|
|
}
|
|
else if ( !m_data->options[ index ].isEnabled )
|
|
{
|
|
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 )
|
|
{
|
|
if( options[ index ].isEnabled )
|
|
return index;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( index < 0 || index >= count )
|
|
index = count;
|
|
|
|
while( --index >= 0 )
|
|
{
|
|
if( options[ index ].isEnabled )
|
|
return index;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int QskSegmentedBar::count() const
|
|
{
|
|
return m_data->options.count();
|
|
}
|
|
|
|
void QskSegmentedBar::setSegmentEnabled( int index, bool enabled )
|
|
{
|
|
auto& options = m_data->options;
|
|
|
|
if( ( index < 0 ) || ( index >= options.count() )
|
|
|| ( options[ index ].isEnabled == enabled ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
options[ index ].isEnabled = enabled;
|
|
|
|
if( !enabled )
|
|
{
|
|
if( m_data->currentIndex == index )
|
|
setCurrentIndex( -1 );
|
|
}
|
|
|
|
update();
|
|
}
|
|
|
|
bool QskSegmentedBar::isSegmentEnabled( int index ) const
|
|
{
|
|
const auto& options = m_data->options;
|
|
|
|
if( index < 0 || index >= options.count() )
|
|
return false;
|
|
|
|
return options[ index ].isEnabled;
|
|
}
|
|
|
|
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"
|