qskinny/src/controls/QskSegmentedBar.cpp

476 lines
11 KiB
C++
Raw Normal View History

/******************************************************************************
* 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 )
{
2022-08-25 09:39:33 +02:00
setTextOptionsHint( Text, textOptions );
}
QskTextOptions QskSegmentedBar::textOptions() const
{
2022-08-25 09:39:33 +02:00
return textOptionsHint( Text );
}
int QskSegmentedBar::addOption( const QUrl& graphicSource, const QString& text )
2023-01-21 16:53:58 +01:00
{
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 )
{
2022-05-02 16:51:24 +02:00
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;
2022-05-02 16:51:24 +02:00
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"