diff --git a/examples/qvgviewer/MainWindow.cpp b/examples/qvgviewer/MainWindow.cpp index 5b9d8c55..e78a1a79 100644 --- a/examples/qvgviewer/MainWindow.cpp +++ b/examples/qvgviewer/MainWindow.cpp @@ -88,10 +88,11 @@ class GraphicLabel : public QskGraphicLabel QskMenu menu( this ); menu.setPopupFlag( QskPopup::DeleteOnClose, false ); - menu.addItem( "image://shapes/Rectangle/White", "Print" ); - menu.addItem( "image://shapes/Diamond/Yellow", "Save As" ); - menu.addItem( "image://shapes/Ellipse/Red", "Setup" ); - menu.addItem( "image://shapes/Hexagon/PapayaWhip", "Help" ); + menu.addOption( "image://shapes/Rectangle/White", "Print" ); + menu.addOption( "image://shapes/Diamond/Yellow", "Save As" ); + menu.addOption( "image://shapes/Ellipse/Red", "Setup" ); + menu.addSeparator(); + menu.addOption( "image://shapes/Hexagon/PapayaWhip", "Help" ); menu.setOrigin( qskMousePosition( event ) ); diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 64e59018..5fb16fe9 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -317,9 +317,9 @@ void Editor::setupMenu() #if 0 setPadding( Q::Separator, QMarginsF( 10, 0, 10, 0 ) ); - setMetric( Q::Separator | QskAspect::Size, 3 ); - setVGradient( Q::Separator, c2, c1 ); #endif + setMetric( Q::Separator | A::Size, 2 ); + setSeparator( Q::Separator | A::Horizontal ); setPadding( Q::Cell, QskMargins( 2, 10, 2, 10 ) ); setSpacing( Q::Cell, 5 ); diff --git a/src/controls/QskMenu.cpp b/src/controls/QskMenu.cpp index eeb75849..f2af02e0 100644 --- a/src/controls/QskMenu.cpp +++ b/src/controls/QskMenu.cpp @@ -16,23 +16,35 @@ QSK_SUBCONTROL( QskMenu, Cell ) QSK_SUBCONTROL( QskMenu, Cursor ) QSK_SUBCONTROL( QskMenu, Text ) QSK_SUBCONTROL( QskMenu, Graphic ) +QSK_SUBCONTROL( QskMenu, Separator ) QSK_SYSTEM_STATE( QskMenu, Selected, QskAspect::FirstSystemState << 2 ) namespace { - struct Entry + class Option { - QUrl graphicSource; + public: + Option( const QUrl& graphicSource, const QString& text ) + : graphicSource( graphicSource ) + , text( text ) + { + if( !graphicSource.isEmpty() ) + graphic = Qsk::loadGraphic( graphicSource ); + } + + const QUrl graphicSource; + const QString text; + QskGraphic graphic; - QString text; }; } class QskMenu::PrivateData { public: - QVector< Entry > entries; + QVector< Option > options; + QVector< int > separators; QskTextOptions textOptions; QPointF origin; @@ -94,13 +106,9 @@ QPointF QskMenu::origin() const return m_data->origin; } -void QskMenu::addItem( const QUrl& graphicSource, const QString& text ) +void QskMenu::addOption( const QUrl& graphicSource, const QString& text ) { - QskGraphic graphic; - if( !graphicSource.isEmpty() ) - graphic = Qsk::loadGraphic( graphicSource ); - - m_data->entries += { graphicSource, graphic, text }; + m_data->options += Option( graphicSource, text ); resetImplicitSize(); update(); @@ -109,38 +117,52 @@ void QskMenu::addItem( const QUrl& graphicSource, const QString& text ) countChanged( count() ); } -void QskMenu::addItem( const QString& graphicSource, const QString& text ) +void QskMenu::addOption( const QString& graphicSource, const QString& text ) { - addItem( QUrl( graphicSource ), text ); -} - -void QskMenu::addSeparator() -{ - // TODO ... -} - -void QskMenu::clear() -{ - m_data->entries.clear(); + addOption( QUrl( graphicSource ), text ); } int QskMenu::count() const { - return m_data->entries.count(); + return m_data->options.count(); } -QVariant QskMenu::itemAt( int index ) const +void QskMenu::addSeparator() { - const auto& entries = m_data->entries; + m_data->separators += m_data->options.count(); - if( index < 0 || index >= entries.count() ) - return QVariant(); + resetImplicitSize(); + update(); +} - const auto& entry = m_data->entries[ index ]; +int QskMenu::separatorPosition( int index ) const +{ + return m_data->separators.value( index, -1 ); +} + +int QskMenu::separatorCount() const +{ + return m_data->separators.count(); +} + +void QskMenu::clear() +{ + m_data->options.clear(); + m_data->separators.clear(); +} + +QVariantList QskMenu::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( entry.graphic ); - list += QVariant::fromValue( entry.text ); + list += QVariant::fromValue( option.graphic ); + list += QVariant::fromValue( option.text ); return list; } @@ -270,12 +292,6 @@ void QskMenu::traverse( int steps ) setCurrentIndex( newIndex ); } -QskColorFilter QskMenu::graphicFilterAt( int index ) const -{ - Q_UNUSED( index ) - return QskColorFilter(); -} - void QskMenu::mousePressEvent( QMouseEvent* event ) { // QGuiApplication::styleHints()->setFocusOnTouchRelease ?? diff --git a/src/controls/QskMenu.h b/src/controls/QskMenu.h index b9dbf598..3ab551e6 100644 --- a/src/controls/QskMenu.h +++ b/src/controls/QskMenu.h @@ -32,7 +32,7 @@ class QSK_EXPORT QskMenu : public QskPopup using Inherited = QskPopup; public: - QSK_SUBCONTROLS( Panel, Cell, Cursor, Text, Graphic ) + QSK_SUBCONTROLS( Panel, Cell, Cursor, Text, Graphic, Separator ) QSK_STATES( Selected ) QskMenu( QQuickItem* parentItem = nullptr ); @@ -45,22 +45,25 @@ class QSK_EXPORT QskMenu : public QskPopup void setOrigin( const QPointF& ); QPointF origin() const; - // insert, remove, functors, actions, QskGraphic ... - void addItem( const QUrl& graphicSource, const QString& text ); - void addItem( const QString& graphicSource, const QString& text ); - - void addSeparator(); - - virtual QVariant itemAt( int ) const; - virtual int count() const; - virtual void clear(); - void setTextOptions( const QskTextOptions& textOptions ); QskTextOptions textOptions() const; + // insert, remove, functors, actions, QskGraphic ... + void addOption( const QUrl& graphicSource, const QString& text ); + void addOption( const QString& graphicSource, const QString& text ); + + QVariantList optionAt( int ) const; + int count() const; + + void addSeparator(); + + int separatorPosition( int ) const; + int separatorCount() const; + + void clear(); + int currentIndex() const; - virtual QskColorFilter graphicFilterAt( int index ) const; QRectF focusIndicatorRect() const override; QRectF cellRect( int index ) const; diff --git a/src/controls/QskMenuSkinlet.cpp b/src/controls/QskMenuSkinlet.cpp index 931810e5..cd29b528 100644 --- a/src/controls/QskMenuSkinlet.cpp +++ b/src/controls/QskMenuSkinlet.cpp @@ -19,19 +19,11 @@ template< class T > static inline QVariant qskSampleAt( const QskMenu* menu, int index ) { - const auto item = menu->itemAt( index ); - - if ( item.canConvert< T >() ) - return item; - - if ( item.canConvert< QVariantList >() ) + const auto list = menu->optionAt( index ); + for ( const auto& value : list ) { - const auto list = item.value< QVariantList >(); - for ( const auto& value : list ) - { - if ( value.canConvert< T >() ) - return value; - } + if ( value.canConvert< T >() ) + return value; } return QVariant(); @@ -70,6 +62,18 @@ class QskMenuSkinlet::PrivateData m_cellHeight = m_cellWidth = m_graphicWidth = m_textWidth = -1.0; } + inline int separatorsBefore( const QskMenu* menu, int index ) const + { + int i = 0; + for ( ; i < menu->separatorCount(); i++ ) + { + if ( menu->separatorPosition( i ) > index ) + break; + } + + return i; + } + inline qreal graphicWidth( const QskMenu* menu ) const { if ( m_isCaching ) @@ -291,7 +295,16 @@ QRectF QskMenuSkinlet::sampleRect( if ( subControl == Q::Cell ) { const auto r = menu->subControlContentsRect( Q::Panel ); - const auto h = m_data->cellHeight( menu ); + + auto h = m_data->cellHeight( menu ); + + if ( int n = m_data->separatorsBefore( menu, index ) ) + { + // spacing ??? + + const qreal separatorH = menu->metric( Q::Separator | QskAspect::Size ); + h += n * separatorH; + } return QRectF( r.x(), r.y() + index * h, r.width(), h ); } @@ -322,6 +335,26 @@ QRectF QskMenuSkinlet::sampleRect( } } + if ( subControl == QskMenu::Separator ) + { + const int pos = menu->separatorPosition( index ); + if ( pos < 0 ) + return QRectF(); + + QRectF r = menu->subControlContentsRect( Q::Panel ); + + if ( pos < menu->count() ) + { + const auto cellRect = sampleRect( skinnable, contentsRect, Q::Cell, pos ); + r.setBottom( cellRect.top() ); // spacing ??? + } + + const qreal h = menu->metric( Q::Separator | QskAspect::Size ); + r.setTop( r.bottom() - h ); + + return r; + } + return Inherited::sampleRect( skinnable, contentsRect, subControl, index ); } @@ -345,6 +378,12 @@ int QskMenuSkinlet::sampleCount( return menu->count(); } + if ( subControl == Q::Separator ) + { + const auto menu = static_cast< const QskMenu* >( skinnable ); + return menu->separatorCount(); + } + return Inherited::sampleCount( skinnable, subControl ); } @@ -391,8 +430,8 @@ QSGNode* QskMenuSkinlet::updateContentsNode( QSGNode* QskMenuSkinlet::updateMenuNode( const QskSkinnable* skinnable, QSGNode* contentsNode ) const { - enum { Panel, Background, Cursor, Graphic, Text }; - static QVector< quint8 > roles = { Panel, Background, Cursor, Graphic, Text }; + enum { Panel, Cell, Cursor, Graphic, Text, Separator }; + static QVector< quint8 > roles = { Panel, Separator, Cell, Cursor, Graphic, Text }; if ( contentsNode == nullptr ) contentsNode = new QSGNode(); @@ -410,7 +449,7 @@ QSGNode* QskMenuSkinlet::updateMenuNode( newNode = updateBoxNode( skinnable, oldNode, QskMenu::Panel ); break; } - case Background: + case Cell: { newNode = updateSeriesNode( skinnable, QskMenu::Cell, oldNode ); break; @@ -430,6 +469,11 @@ QSGNode* QskMenuSkinlet::updateMenuNode( newNode = updateSeriesNode( skinnable, QskMenu::Text, oldNode ); break; } + case Separator: + { + newNode = updateSeriesNode( skinnable, QskMenu::Separator, oldNode ); + break; + } } QskSGNode::replaceChildNode( roles, role, contentsNode, oldNode, newNode ); @@ -475,34 +519,42 @@ QSGNode* QskMenuSkinlet::updateSampleNode( const QskSkinnable* skinnable, subControl, Qt::AlignVCenter | Qt::AlignLeft ); return QskSkinlet::updateTextNode( menu, node, rect, alignment, - text, menu->textOptions(), QskMenu::Text ); + text, QskTextOptions(), Q::Text ); + } + + if ( subControl == Q::Separator ) + { + return updateBoxNode( menu, node, rect, subControl ); } return nullptr; } - QSizeF QskMenuSkinlet::sizeHint( const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const { if ( which != Qt::PreferredSize ) return QSizeF(); - const PrivateData::CacheGuard guard( m_data.get() ); + using Q = QskMenu; + const auto menu = static_cast< const QskMenu* >( skinnable ); - const auto count = sampleCount( skinnable, QskMenu::Cell ); + const PrivateData::CacheGuard guard( m_data.get() ); qreal w = 0.0; qreal h = 0.0; - if ( count > 0 ) + if ( const auto count = sampleCount( skinnable, Q::Cell ) ) { - const auto menu = static_cast< const QskMenu* >( skinnable ); - w = m_data->cellWidth( menu ); h = count * m_data->cellHeight( menu ); } + if ( const auto count = sampleCount( skinnable, Q::Separator ) ) + { + h += count * menu->metric( Q::Separator | QskAspect::Size ); + } + auto hint = skinnable->outerBoxSize( QskMenu::Panel, QSizeF( w, h ) ); hint = hint.expandedTo( skinnable->strutSizeHint( QskMenu::Panel ) );