From 452ff3a07b1e452a6fe91c241d6e11b22499d47b Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 24 Dec 2021 16:20:34 +0100 Subject: [PATCH] QskMenu improved --- skins/squiek/QskSquiekSkin.cpp | 7 +- src/controls/QskMenu.cpp | 62 ++++++++++++--- src/controls/QskMenu.h | 5 +- src/controls/QskMenuSkinlet.cpp | 136 ++++++++++++++------------------ 4 files changed, 120 insertions(+), 90 deletions(-) diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 7bb415fd..3f4745d3 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -317,12 +317,11 @@ void Editor::setupMenu() setVGradient( Q::Separator, c2, c1 ); #endif - // while fading out we are in Q::Closed state - for ( auto state : { QskAspect::NoState, Q::Closed } ) - setHGradient( Q::Cell | Q::Selected | state, c2, c2.lighter( 2 ) ); - setPadding( Q::Cell, QskMargins( 2, 10, 2, 10 ) ); setSpacing( Q::Cell, 5 ); + setGradient( Q::Cell, Qt::transparent ); + + setHGradient( Q::Cursor, c2, c2.lighter( 2 ) ); setColor( Q::Text, QColor( 255, 255, 255 ) ); setFontRole( Q::Text, QskSkin::SmallFont ); diff --git a/src/controls/QskMenu.cpp b/src/controls/QskMenu.cpp index 8db00e79..f224d982 100644 --- a/src/controls/QskMenu.cpp +++ b/src/controls/QskMenu.cpp @@ -5,13 +5,13 @@ #include #include -#include -#include +#include QSK_SUBCONTROL( QskMenu, Panel ) +QSK_SUBCONTROL( QskMenu, Cell ) +QSK_SUBCONTROL( QskMenu, Cursor ) QSK_SUBCONTROL( QskMenu, Text ) QSK_SUBCONTROL( QskMenu, Graphic ) -QSK_SUBCONTROL( QskMenu, Cell ) QSK_SYSTEM_STATE( QskMenu, Selected, QskAspect::FirstSystemState << 2 ) @@ -239,10 +239,12 @@ void QskMenu::traverse( int steps ) int QskMenu::indexAtPosition( const QPointF& pos ) const { - const auto r = contentsRect(); - - if( count() > 0 && r.contains( pos ) ) - return ( pos.y() - r.y() ) / ( r.height() / count() ); + for ( int i = 0; i < count(); i++ ) + { + // A menu never has many cells and we can simply iterate + if ( cellRect( i ).contains( pos ) ) + return i; + } return -1; } @@ -257,8 +259,12 @@ void QskMenu::mousePressEvent( QMouseEvent* event ) { if ( event->button() == Qt::LeftButton ) { - if ( contentsRect().contains( event->localPos() ) ) + const auto index = indexAtPosition( event->localPos() ); + if ( index >= 0 ) + { + setCurrentIndex( index ); m_data->isPressed = true; + } } } @@ -268,8 +274,11 @@ void QskMenu::mouseReleaseEvent( QMouseEvent* event ) { if( m_data->isPressed ) { - setSelectedIndex( indexAtPosition( event->localPos() ) ); m_data->isPressed = false; + + const auto index = indexAtPosition( event->localPos() ); + if ( index == m_data->currentIndex ) + setSelectedIndex( index ); } } } @@ -298,4 +307,39 @@ void QskMenu::setSelectedIndex( int index ) close(); } +QRectF QskMenu::cellRect( int index ) const +{ + /* + Calculations like this one should be done in the skinlet. + We need an API like subControlRect( int index, ... ) TODO ... + */ + + if ( index < 0 || index >= count() ) + return QRectF(); + + const auto r = subControlContentsRect( QskMenu::Panel ); + const auto h = cellHeight(); + + return QRectF( r.x(), r.y() + index * h, r.width(), h ); +} + +qreal QskMenu::cellHeight() const +{ + /* + Calculations like this one should be done in the skinlet. + We need an API like subControlRect( int index, ... ) TODO ... + */ + const auto graphicHeight = strutSizeHint( Graphic ).height(); + const auto textHeight = effectiveFontHeight( Text ); + const auto padding = paddingHint( Cell ); + + qreal h = qMax( graphicHeight, textHeight ); + h += padding.top() + padding.bottom(); + + const auto minHeight = strutSizeHint( Cell ).height(); + h = qMax( h, minHeight ); + + return h; +} + #include "moc_QskMenu.cpp" diff --git a/src/controls/QskMenu.h b/src/controls/QskMenu.h index 2c6638a6..e440af00 100644 --- a/src/controls/QskMenu.h +++ b/src/controls/QskMenu.h @@ -35,7 +35,7 @@ class QSK_EXPORT QskMenu : public QskPopup QString text; }; - QSK_SUBCONTROLS( Panel, Cell, Text, Graphic ) + QSK_SUBCONTROLS( Panel, Cell, Cursor, Text, Graphic ) QSK_STATES( Selected ) QskMenu( QQuickItem* parentItem = nullptr ); @@ -64,6 +64,9 @@ class QSK_EXPORT QskMenu : public QskPopup virtual QskColorFilter graphicFilterAt( int index ) const; QRectF focusIndicatorRect() const override; + QRectF cellRect( int index ) const; + qreal cellHeight() const; + Q_SIGNALS: void triggered( int index ); void currentIndexChanged( int index ); diff --git a/src/controls/QskMenuSkinlet.cpp b/src/controls/QskMenuSkinlet.cpp index bc9799e5..1e6f298b 100644 --- a/src/controls/QskMenuSkinlet.cpp +++ b/src/controls/QskMenuSkinlet.cpp @@ -47,11 +47,10 @@ static qreal qskMaxTextWidth( const QskMenu* menu ) return maxWidth; } -static QSizeF qskIconSize( const QskMenu* menu ) +static qreal qskGraphicWidth( const QskMenu* menu ) { const auto hint = menu->strutSizeHint( QskMenu::Graphic ); - const qreal textHeight = QFontMetrics( - menu->effectiveFont( QskMenu::Text ) ).height(); + const qreal textHeight = menu->effectiveFontHeight( QskMenu::Text ); const auto h = qMax( hint.height(), textHeight ); @@ -63,22 +62,23 @@ static QSizeF qskIconSize( const QskMenu* menu ) maxW = w; } - const auto w = qMax( hint.width(), maxW ); - - return QSizeF( w, h ); + return qMax( hint.width(), maxW ); } -static QSizeF qskItemSize( const QskMenu* menu ) +static qreal qskCellWidth( const QskMenu* menu ) { - const auto spacing = menu->spacingHint( QskMenu::Cell ); - const QskMargins padding = menu->paddingHint( QskMenu::Cell ); + using Q = QskMenu; - const auto sz = qskIconSize( menu ); + const auto spacing = menu->spacingHint( Q::Cell ); + const auto padding = menu->paddingHint( Q::Cell ); - const qreal h = sz.height() + padding.height(); - const qreal w = sz.width() + spacing + qskMaxTextWidth( menu ) + padding.width(); + auto w = qskGraphicWidth( menu ) + + spacing + qskMaxTextWidth( menu ); - return QSizeF( w, h ); + w += padding.left() + padding.right(); + + const auto minWidth = menu->strutSizeHint( Q::Cell ).width(); + return qMax( w, minWidth ); } static QSGNode* qskUpdateGraphicNode( const QskMenu* menu, @@ -101,41 +101,46 @@ static QSGNode* qskUpdateTextNode( const QskMenu* menu, text, menu->textOptions(), QskMenu::Text ); } -static QSGNode* qskUpdateBackgroundNode( const QskMenu*, QSGNode* ) +static QSGNode* qskUpdateBackgroundNode( const QskMenu* menu, QSGNode* rootNode ) { - return nullptr; // TODO -} + auto node = rootNode ? rootNode->firstChild() : nullptr; + QSGNode* lastNode = nullptr; -static QRectF qskCellRect( const QskMenu* menu, int index ) -{ - if ( index >= 0 ) + for( int i = 0; i < menu->count(); i++ ) { - auto r = menu->subControlRect( QskMenu::Panel ); - r = menu->innerBox( QskMenu::Panel, r ); + QSGNode* newNode = nullptr; - const auto sz = qskItemSize( menu ); + { + const StateChanger stateChanger( menu, menu->currentIndex() == i ); - const auto y = r.y() + index * sz.height(); - return QRectF( r.x(), y, r.width(), sz.height() ); + newNode = QskSkinlet::updateBoxNode( + menu, node, menu->cellRect( i ), QskMenu::Cell ); + } + + if ( newNode ) + { + if ( newNode == node ) + { + node = node->nextSibling(); + } + else + { + if ( rootNode == nullptr ) + rootNode = new QSGNode(); + + if ( node ) + rootNode->insertChildNodeBefore( newNode, node ); + else + rootNode->appendChildNode( newNode ); + } + + lastNode = newNode; + } } - return QRectF(); -} + QskSGNode::removeAllChildNodesAfter( rootNode, lastNode ); -static QSGNode* qskUpdateCursorNode( const QskMenu* menu, QSGNode* node ) -{ - const auto r = qskCellRect( menu, menu->currentIndex() ); - if ( !r.isEmpty() ) - { - const StateChanger stateChanger( menu, true ); - - auto cursorNode = QskSGNode::ensureNode< QskBoxNode >( node ); - cursorNode->setBoxData( r, menu->gradientHint( QskMenu::Cell ) ); - - return cursorNode; - } - - return nullptr; + return rootNode; } static void qskUpdateItemNode( @@ -161,32 +166,14 @@ static void qskUpdateItemNode( static QSGNode* qskUpdateItemsNode( const QskMenu* menu, QSGNode* rootNode ) { - const auto padding = menu->paddingHint( QskMenu::Cell ); const auto spacing = menu->spacingHint( QskMenu::Cell ); - const auto iconSize = qskIconSize( menu ); - - auto boundingRect = menu->subControlRect( QskMenu::Panel ); - boundingRect = menu->innerBox( QskMenu::Panel, boundingRect ); - - auto itemSize = iconSize; - itemSize.rwidth() += spacing + qskMaxTextWidth( menu ); - -#if QT_VERSION >= QT_VERSION_CHECK( 5, 14, 0 ) - itemSize = itemSize.grownBy( padding ); -#else - itemSize.rwidth() += padding.left() + padding.right(); - itemSize.rheight() += padding.top() + padding.bottom(); -#endif - - itemSize = itemSize.expandedTo( menu->strutSizeHint( QskMenu::Graphic ) ); + const auto graphicWidth = qskGraphicWidth( menu ); if ( rootNode == nullptr ) rootNode = new QSGNode(); QSGNode* node = nullptr; - qreal y = boundingRect.y(); - for( int i = 0; i < menu->count(); i++ ) { if ( node == nullptr ) @@ -203,13 +190,10 @@ static QSGNode* qskUpdateItemsNode( const QskMenu* menu, QSGNode* rootNode ) { const StateChanger stateChanger( menu, menu->currentIndex() == i ); - QRectF cellRect( boundingRect.x(), y, - boundingRect.width(), itemSize.height() ); - - cellRect = cellRect.marginsRemoved( padding ); + const auto cellRect = menu->cellRect( i ); auto graphicRect = cellRect; - graphicRect.setWidth( iconSize.width() ); + graphicRect.setWidth( graphicWidth ); auto textRect = cellRect; textRect.setX( graphicRect.right() + spacing ); @@ -217,12 +201,8 @@ static QSGNode* qskUpdateItemsNode( const QskMenu* menu, QSGNode* rootNode ) qskUpdateItemNode( menu, graphicRect, menu->graphicAt( i ), textRect, menu->entryAt( i ).text, node ); } - - y += itemSize.height(); - } - // Remove trailing nodes QskSGNode::removeAllChildNodesAfter( rootNode, node ); return rootNode; @@ -238,11 +218,18 @@ QRectF QskMenuSkinlet::subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const { + const auto menu = static_cast< const QskMenu* >( skinnable ); + if( subControl == QskMenu::Panel ) { return contentsRect; } + if( subControl == QskMenu::Cursor ) + { + return menu->cellRect( menu->currentIndex() ); + } + return Inherited::subControlRect( skinnable, contentsRect, subControl ); } @@ -261,7 +248,7 @@ QSGNode* QskMenuSkinlet::updateContentsNode( { auto oldNode = QskSGNode::findChildNode( contentsNode, role ); - QSGNode* newNode; + QSGNode* newNode = nullptr; switch( role ) { @@ -277,7 +264,7 @@ QSGNode* QskMenuSkinlet::updateContentsNode( } case Cursor: { - newNode = qskUpdateCursorNode( menu, oldNode ); + newNode = updateBoxNode( menu, oldNode, QskMenu::Cursor ); break; } case Items: @@ -296,18 +283,15 @@ QSGNode* QskMenuSkinlet::updateContentsNode( QSizeF QskMenuSkinlet::sizeHint( const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const { - using Q = QskMenu; - if ( which != Qt::PreferredSize ) return QSizeF(); const auto menu = static_cast< const QskMenu* >( skinnable ); - const auto itemSize = qskItemSize( menu ); - const auto count = menu->count(); + const qreal w = qskCellWidth( menu ); + const auto h = menu->count() * menu->cellHeight(); - const qreal h = count * itemSize.height(); - return menu->outerBoxSize( Q::Panel, QSizeF( itemSize.width(), h ) ); + return menu->outerBoxSize( QskMenu::Panel, QSizeF( w, h ) ); } #include "moc_QskMenuSkinlet.cpp"