diff --git a/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp index 87b53ccf..58114ea3 100644 --- a/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -127,6 +128,7 @@ namespace void setupProgressBar(); void setupPushButton(); void setupScrollView(); + void setupSegmentedBar(); void setupSeparator(); void setupSubWindow(); void setupSlider(); @@ -159,6 +161,7 @@ void Editor::setup() setupProgressBar(); setupPushButton(); setupScrollView(); + setupSegmentedBar(); setupSeparator(); setupSlider(); setupSubWindow(); @@ -326,6 +329,11 @@ void Editor::setupFocusIndicator() setGradient( Q::Panel, QskGradient() ); } +void Editor::setupSegmentedBar() +{ + // TODO +} + void Editor::setupSeparator() { using A = QskAspect; diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index afc1777f..2beb9c78 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -147,6 +147,7 @@ namespace void setupProgressBar(); void setupPushButton(); void setupScrollView(); + void setupSegmentedBar(); void setupSeparator(); void setupSlider(); void setupSubWindow(); @@ -267,6 +268,7 @@ void Editor::setup() setupProgressBar(); setupPushButton(); setupScrollView(); + setupSegmentedBar(); setupSeparator(); setupSlider(); setupSubWindow(); @@ -465,6 +467,11 @@ void Editor::setupSeparator() setSeparator( Q::Panel | A::Vertical ); } +void Editor::setupSegmentedBar() +{ + // TODO +} + void Editor::setupPageIndicator() { using Q = QskPageIndicator; diff --git a/src/controls/QskSegmentedBar.cpp b/src/controls/QskSegmentedBar.cpp new file mode 100644 index 00000000..a53301f2 --- /dev/null +++ b/src/controls/QskSegmentedBar.cpp @@ -0,0 +1,476 @@ +/****************************************************************************** + * 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 +#include +#include + +QSK_SUBCONTROL( QskSegmentedBar, Panel ) +QSK_SUBCONTROL( QskSegmentedBar, Segment ) +QSK_SUBCONTROL( QskSegmentedBar, Cursor ) +QSK_SUBCONTROL( QskSegmentedBar, Text ) +QSK_SUBCONTROL( QskSegmentedBar, Graphic ) + +QSK_SYSTEM_STATE( QskSegmentedBar, Selected, QskAspect::FirstSystemState << 2 ) + +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 ) + { + textOptions.setElideMode( Qt::ElideMiddle ); + } + + 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; + + QskTextOptions textOptions; + + 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::Preferred, QskSizePolicy::Fixed ); + else + initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Preferred ); + + 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 ) +{ + if( textOptions != m_data->textOptions ) + { + m_data->textOptions = textOptions; + update(); + } +} + +QskTextOptions QskSegmentedBar::textOptions() const +{ + return m_data->textOptions; +} + +int QskSegmentedBar::addText( const QString& text ) +{ + m_data->addOption( this, Option( QUrl(), text ) ); + return count() - 1; +} + +int QskSegmentedBar::addGraphic( const QUrl& graphicSource ) +{ + m_data->addOption( this, Option( graphicSource, QString() ) ); + return count() - 1; +} + +QVariant QskSegmentedBar::optionAt( int index ) const +{ + const auto& options = m_data->options; + + if( index < 0 || index >= options.count() ) + return QVariantList(); + + const auto& option = options[ index ]; + + QVariant value; + + if ( option.graphicSource.isValid() ) + value = QVariant::fromValue( option.graphic ); + else + value = QVariant::fromValue( option.text ); + + return value; +} + +QskAspect::Placement QskSegmentedBar::effectivePlacement() const +{ + return static_cast< QskAspect::Placement >( m_data->orientation ); +} + +void QskSegmentedBar::mousePressEvent( QMouseEvent* event ) +{ + const int index = indexAtPosition( event->localPos() ); + + 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( event->localPos() ); + } + + 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 ) + { + m_data->selectedIndex = index; + + movePositionHint( Cursor, index ); + update(); + + Q_EMIT selectedIndexChanged( 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" diff --git a/src/controls/QskSegmentedBar.h b/src/controls/QskSegmentedBar.h new file mode 100644 index 00000000..e01b4669 --- /dev/null +++ b/src/controls/QskSegmentedBar.h @@ -0,0 +1,95 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_SEGMENTED_BAR_H +#define QSK_SEGMENTED_BAR_H + +#include "QskControl.h" + +#include +#include + +class QskTextOptions; +class QskGraphic; + +class QSK_EXPORT QskSegmentedBar : public QskControl +{ + Q_OBJECT + + Q_PROPERTY( Qt::Orientation orientation READ orientation + WRITE setOrientation NOTIFY orientationChanged ) + + Q_PROPERTY( int selectedIndex READ selectedIndex + WRITE setSelectedIndex NOTIFY selectedIndexChanged USER true ) + + Q_PROPERTY( int currentIndex READ currentIndex + WRITE setCurrentIndex NOTIFY currentIndexChanged ) + + Q_PROPERTY( int count READ count NOTIFY countChanged ) + + using Inherited = QskControl; + + public: + QSK_SUBCONTROLS( Panel, Segment, Cursor, Text, Graphic ) + QSK_STATES( Selected ) + + QskSegmentedBar( QQuickItem* parent = nullptr ); + QskSegmentedBar( Qt::Orientation, QQuickItem* parent = nullptr ); + + ~QskSegmentedBar() override; + + void setOrientation( Qt::Orientation ); + Qt::Orientation orientation() const; + + void setTextOptions( const QskTextOptions& ); + QskTextOptions textOptions() const; + + int addText( const QString& ); + int addGraphic( const QUrl& ); + + void clear(); + + int selectedIndex() const; + int currentIndex() const; + + int count() const; + + QVariant optionAt( int ) const; + + void setSegmentEnabled( int, bool ); + bool isSegmentEnabled( int ) const; + + int indexAtPosition( const QPointF& ) const; + + QRectF focusIndicatorRect() const override final; + QskAspect::Placement effectivePlacement() const override; + + public Q_SLOTS: + void setSelectedIndex( int index ); + void setCurrentIndex( int index ); + + Q_SIGNALS: + void selectedIndexChanged( int ); + void currentIndexChanged( int ); + void countChanged(); + void orientationChanged(); + + protected: + void mouseReleaseEvent( QMouseEvent* ) override; + void mousePressEvent( QMouseEvent* ) override; + + void keyPressEvent( QKeyEvent* ) override; + void keyReleaseEvent( QKeyEvent* ) override; + + void focusInEvent( QFocusEvent* ) override; + + private: + int nextIndex( int index, bool forward ) const; + + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/controls/QskSegmentedBarSkinlet.cpp b/src/controls/QskSegmentedBarSkinlet.cpp new file mode 100644 index 00000000..fc7c9c83 --- /dev/null +++ b/src/controls/QskSegmentedBarSkinlet.cpp @@ -0,0 +1,322 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskSegmentedBarSkinlet.h" +#include "QskSegmentedBar.h" + +#include "QskGraphicNode.h" +#include "QskGraphic.h" +#include "QskColorFilter.h" +#include "QskTextNode.h" +#include "QskTextOptions.h" +#include "QskSGNode.h" +#include "QskFunctions.h" + +#include +#include + +QskSegmentedBarSkinlet::QskSegmentedBarSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ + setNodeRoles( { PanelRole, SegmentRole, CursorRole, TextRole, GraphicRole } ); +} + +QskSegmentedBarSkinlet::~QskSegmentedBarSkinlet() = default; + +QRectF QskSegmentedBarSkinlet::subControlRect( + const QskSkinnable* skinnable, const QRectF& contentsRect, + QskAspect::Subcontrol subControl ) const +{ + using Q = QskSegmentedBar; + + const auto bar = static_cast< const QskSegmentedBar* >( skinnable ); + + if( subControl == Q::Panel ) + return contentsRect; + + if( subControl == Q::Cursor ) + return cursorRect( bar, contentsRect ); + + return Inherited::subControlRect( skinnable, contentsRect, subControl ); +} + +QRectF QskSegmentedBarSkinlet::cursorRect( + const QskSegmentedBar* bar, const QRectF& contentsRect ) const +{ + using Q = QskSegmentedBar; + + if( bar->selectedIndex() < 0 || bar->count() <= 0 ) + return QRectF(); + + auto rect = subControlRect( bar, contentsRect, Q::Panel ); + rect = bar->innerBox( Q::Panel, rect ); + + if( rect.isEmpty() ) + return QRectF(); + + // position is related to the index: 2.5 means + // the cursor is between segment[2] and segment[3] + + const qreal position = bar->positionHint( Q::Cursor ); + + const int index1 = qFloor( position ); + const int index2 = qCeil( position ); + + auto cursorRect = segmentRect( bar, contentsRect, index1 ); + if ( index1 != index2 ) + { + const auto targetRect = segmentRect( bar, contentsRect, index2 ); + cursorRect = qskInterpolatedRect( cursorRect, targetRect, position - index1 ); + } + + return cursorRect; +} + +QRectF QskSegmentedBarSkinlet::segmentRect( + const QskSegmentedBar* bar, const QRectF& contentsRect, int index ) const +{ + using Q = QskSegmentedBar; + + const auto spacing = bar->spacingHint( Q::Panel ); + const auto count = bar->count(); + + auto rect = subControlRect( bar, contentsRect, Q::Panel ); + rect = bar->innerBox( Q::Panel, rect ); + + if( bar->orientation() == Qt::Horizontal ) + { + const qreal w = ( rect.width() - ( count - 1 ) * spacing ) / count; + + rect.setLeft( index * ( w + spacing ) ); + rect.setWidth( w ); + } + else + { + const qreal h = ( rect.height() - ( count - 1 ) * spacing ) / count; + + rect.setTop( index * ( h + spacing ) ); + rect.setHeight( h ); + } + + return rect; +} + +QSGNode* QskSegmentedBarSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + using Q = QskSegmentedBar; + + switch( nodeRole ) + { + case PanelRole: + return updateBoxNode( skinnable, node, Q::Panel ); + + case CursorRole: + return updateBoxNode( skinnable, node, Q::Cursor ); + + case SegmentRole: + return updateSeriesNode( skinnable, Q::Segment, node ); + + case TextRole: + return updateSeriesNode( skinnable, Q::Text, node ); + + case GraphicRole: + return updateSeriesNode( skinnable, Q::Graphic, node ); + } + + return nullptr; +} + +QSizeF QskSegmentedBarSkinlet::segmentSizeHint( const QskSegmentedBar* bar ) const +{ + qreal widthMax = 0; + qreal graphicRatioMax = 0; + + const QFontMetricsF fm( bar->effectiveFont( QskSegmentedBar::Text ) ); + + for ( int i = 0; i < bar->count(); i++ ) + { + const auto value = bar->optionAt( i ); + + if ( value.canConvert< QskGraphic >() ) + { + const auto graphic = value.value< QskGraphic >(); + + if ( !graphic.isNull() ) + { + const auto sz = graphic.defaultSize(); + + if( sz.isValid() ) + { + const qreal ratio = sz.width() / sz.height(); + + if( graphicRatioMax < ratio ) + graphicRatioMax = ratio; + } + } + } + else if ( value.canConvert< QString >() ) + { + const auto text = value.value< QString >(); + if ( !text.isEmpty() ) + { + const auto sz = fm.size( Qt::TextShowMnemonic, text ); + + if( sz.width() > widthMax ) + widthMax = sz.width(); + } + } + } + + if( graphicRatioMax > 0 ) + { + const qreal w = fm.height() * graphicRatioMax; + + if( w > widthMax ) + widthMax = w; + } + + return bar->outerBoxSize( QskSegmentedBar::Segment, QSizeF( widthMax, fm.height() ) ); +} + +QSizeF QskSegmentedBarSkinlet::sizeHint( const QskSkinnable* skinnable, + Qt::SizeHint which, const QSizeF& ) const +{ + using Q = QskSegmentedBar; + + if ( which != Qt::PreferredSize ) + return QSizeF(); + + const auto count = sampleCount( skinnable, Q::Segment ); + if( count == 0 ) + return QSizeF( 0, 0 ); + + qreal w = 0; + qreal h = 0; + + if ( count > 0 ) + { + const qreal spacing = skinnable->spacingHint( Q::Panel ); + + const auto bar = static_cast< const QskSegmentedBar* >( skinnable ); + const auto segmentSize = segmentSizeHint( bar ); + + if( bar->orientation() == Qt::Horizontal ) + { + w = count * segmentSize.width() + ( count - 1 ) * spacing; + h = segmentSize.height(); + } + else + { + w = segmentSize.width(); + h = count * segmentSize.height() + ( count - 1 ) * spacing; + } + } + + const auto hint = skinnable->outerBoxSize( Q::Panel, QSizeF( w, h ) ); + return hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) ); +} + +int QskSegmentedBarSkinlet::sampleCount( + const QskSkinnable* skinnable, QskAspect::Subcontrol ) const +{ + const auto bar = static_cast< const QskSegmentedBar* >( skinnable ); + return bar->count(); +} + +QRectF QskSegmentedBarSkinlet::sampleRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subControl, int index ) const +{ + using Q = QskSegmentedBar; + + if ( subControl == Q::Segment ) + { + const auto bar = static_cast< const QskSegmentedBar* >( skinnable ); + return segmentRect( bar, contentsRect, index ); + } + + if ( subControl == Q::Text || subControl == Q::Graphic ) + { + const auto rect = sampleRect( skinnable, contentsRect, Q::Segment, index ); + return skinnable->innerBox( Q::Segment, rect ); + } + + return Inherited::sampleRect( skinnable, contentsRect, subControl, index ); +} + +QskAspect::States QskSegmentedBarSkinlet::sampleStates( + const QskSkinnable* skinnable, QskAspect::Subcontrol subControl, int index ) const +{ + using Q = QskSegmentedBar; + + auto states = Inherited::sampleStates( skinnable, subControl, index ); + + if ( subControl == Q::Segment || subControl == Q::Graphic || subControl == Q::Text ) + { + const auto bar = static_cast< const QskSegmentedBar* >( skinnable ); + + if ( bar->isSegmentEnabled( index ) ) + { + if ( bar->selectedIndex() == index ) + states |= Q::Selected; + } + else + { + states |= Q::Disabled; + } + } + + return states; +} + +QSGNode* QskSegmentedBarSkinlet::updateSampleNode( const QskSkinnable* skinnable, + QskAspect::Subcontrol subControl, int index, QSGNode* node ) const +{ + using Q = QskSegmentedBar; + + auto bar = static_cast< const QskSegmentedBar* >( skinnable ); + + const auto rect = sampleRect( bar, bar->contentsRect(), subControl, index ); + + if ( subControl == Q::Segment ) + { + return updateBoxNode( skinnable, node, rect, subControl ); + } + + const auto alignment = bar->alignmentHint( subControl, Qt::AlignCenter ); + + if ( subControl == Q::Text ) + { + const auto value = bar->optionAt( index ); + if ( value.canConvert< QString >() ) + { + const auto text = value.value< QString >(); + + return QskSkinlet::updateTextNode( bar, node, + rect, alignment, text, bar->textOptions(), Q::Text ); + } + + return nullptr; + } + + if ( subControl == Q::Graphic ) + { + const auto value = bar->optionAt( index ); + if ( value.canConvert< QskGraphic >() ) + { + const auto graphic = value.value< QskGraphic >(); + const auto filter = bar->effectiveGraphicFilter( subControl ); + + return QskSkinlet::updateGraphicNode( + bar, node, graphic, filter, rect, alignment ); + } + + return nullptr; + } + + return Inherited::updateSampleNode( skinnable, subControl, index, node ); +} + +#include "moc_QskSegmentedBarSkinlet.cpp" diff --git a/src/controls/QskSegmentedBarSkinlet.h b/src/controls/QskSegmentedBarSkinlet.h new file mode 100644 index 00000000..5c878255 --- /dev/null +++ b/src/controls/QskSegmentedBarSkinlet.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_SEGMENTED_BAR_SKINLET_H +#define QSK_SEGMENTED_BAR_SKINLET_H + +#include "QskSkinlet.h" + +class QskSegmentedBar; + +class QSK_EXPORT QskSegmentedBarSkinlet : public QskSkinlet +{ + Q_GADGET + + using Inherited = QskSkinlet; + + public: + enum NodeRole + { + PanelRole, + SegmentRole, + CursorRole, + + TextRole, + GraphicRole, + + RoleCount + }; + + Q_INVOKABLE QskSegmentedBarSkinlet( QskSkin* = nullptr ); + ~QskSegmentedBarSkinlet() override; + + QRectF subControlRect( const QskSkinnable*, const QRectF&, + QskAspect::Subcontrol ) const override; + + QSizeF sizeHint( const QskSkinnable*, + Qt::SizeHint, const QSizeF& ) const override; + + int sampleCount( const QskSkinnable*, QskAspect::Subcontrol ) const override; + + QRectF sampleRect( const QskSkinnable*, + const QRectF&, QskAspect::Subcontrol, int index ) const override; + + QskAspect::States sampleStates( const QskSkinnable*, + QskAspect::Subcontrol, int index ) const override; + + protected: + QSGNode* updateSubNode( const QskSkinnable*, + quint8 nodeRole, QSGNode* ) const override; + + QSGNode* updateSampleNode( const QskSkinnable*, + QskAspect::Subcontrol, int index, QSGNode* ) const override; + + private: + QSizeF segmentSizeHint( const QskSegmentedBar* ) const; + + QRectF segmentRect( const QskSegmentedBar*, const QRectF&, int index ) const; + QRectF cursorRect( const QskSegmentedBar*, const QRectF& ) const; +}; + +#endif diff --git a/src/controls/QskSkin.cpp b/src/controls/QskSkin.cpp index b7ad051a..77f59bb7 100644 --- a/src/controls/QskSkin.cpp +++ b/src/controls/QskSkin.cpp @@ -44,18 +44,39 @@ QSK_QT_PRIVATE_END #include "QskMenu.h" #include "QskMenuSkinlet.h" +#include "QskPageIndicator.h" +#include "QskPageIndicatorSkinlet.h" + #include "QskPopup.h" #include "QskPopupSkinlet.h" +#include "QskProgressBar.h" +#include "QskProgressBarSkinlet.h" + #include "QskPushButton.h" #include "QskPushButtonSkinlet.h" #include "QskScrollView.h" #include "QskScrollViewSkinlet.h" +#include "QskSegmentedBar.h" +#include "QskSegmentedBarSkinlet.h" + +#include "QskSeparator.h" +#include "QskSeparatorSkinlet.h" + #include "QskSlider.h" #include "QskSliderSkinlet.h" +#include "QskSubWindow.h" +#include "QskSubWindowSkinlet.h" + +#include "QskSubWindowArea.h" +#include "QskSubWindowAreaSkinlet.h" + +#include "QskSwitchButton.h" +#include "QskSwitchButtonSkinlet.h" + #include "QskTabButton.h" #include "QskTabButtonSkinlet.h" @@ -68,24 +89,6 @@ QSK_QT_PRIVATE_END #include "QskTextInput.h" #include "QskTextInputSkinlet.h" -#include "QskSeparator.h" -#include "QskSeparatorSkinlet.h" - -#include "QskSubWindow.h" -#include "QskSubWindowSkinlet.h" - -#include "QskSubWindowArea.h" -#include "QskSubWindowAreaSkinlet.h" - -#include "QskSwitchButton.h" -#include "QskSwitchButtonSkinlet.h" - -#include "QskPageIndicator.h" -#include "QskPageIndicatorSkinlet.h" - -#include "QskProgressBar.h" -#include "QskProgressBarSkinlet.h" - #include "QskStatusIndicator.h" #include "QskStatusIndicatorSkinlet.h" @@ -155,6 +158,7 @@ QskSkin::QskSkin( QObject* parent ) declareSkinlet< QskMenu, QskMenuSkinlet >(); declareSkinlet< QskPushButton, QskPushButtonSkinlet >(); declareSkinlet< QskScrollView, QskScrollViewSkinlet >(); + declareSkinlet< QskSegmentedBar, QskSegmentedBarSkinlet >(); declareSkinlet< QskSeparator, QskSeparatorSkinlet >(); declareSkinlet< QskSlider, QskSliderSkinlet >(); declareSkinlet< QskStatusIndicator, QskStatusIndicatorSkinlet >(); diff --git a/src/src.pro b/src/src.pro index 9a345f05..366590c0 100644 --- a/src/src.pro +++ b/src/src.pro @@ -179,6 +179,8 @@ HEADERS += \ controls/QskScrollBox.h \ controls/QskScrollView.h \ controls/QskScrollViewSkinlet.h \ + controls/QskSegmentedBar.h \ + controls/QskSegmentedBarSkinlet.h \ controls/QskSeparator.h \ controls/QskSeparatorSkinlet.h \ controls/QskSetup.h \ @@ -261,6 +263,8 @@ SOURCES += \ controls/QskScrollBox.cpp \ controls/QskScrollView.cpp \ controls/QskScrollViewSkinlet.cpp \ + controls/QskSegmentedBar.cpp \ + controls/QskSegmentedBarSkinlet.cpp \ controls/QskSeparator.cpp \ controls/QskSeparatorSkinlet.cpp \ controls/QskSetup.cpp \