QskMenu improved

This commit is contained in:
Uwe Rathmann 2021-12-24 16:20:34 +01:00
parent 1beec5e599
commit 452ff3a07b
4 changed files with 120 additions and 90 deletions

View File

@ -317,12 +317,11 @@ void Editor::setupMenu()
setVGradient( Q::Separator, c2, c1 ); setVGradient( Q::Separator, c2, c1 );
#endif #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 ) ); setPadding( Q::Cell, QskMargins( 2, 10, 2, 10 ) );
setSpacing( Q::Cell, 5 ); setSpacing( Q::Cell, 5 );
setGradient( Q::Cell, Qt::transparent );
setHGradient( Q::Cursor, c2, c2.lighter( 2 ) );
setColor( Q::Text, QColor( 255, 255, 255 ) ); setColor( Q::Text, QColor( 255, 255, 255 ) );
setFontRole( Q::Text, QskSkin::SmallFont ); setFontRole( Q::Text, QskSkin::SmallFont );

View File

@ -5,13 +5,13 @@
#include <QskGraphic.h> #include <QskGraphic.h>
#include <QskColorFilter.h> #include <QskColorFilter.h>
#include <QFontMetricsF> #include <qvector.h>
#include <QVector>
QSK_SUBCONTROL( QskMenu, Panel ) QSK_SUBCONTROL( QskMenu, Panel )
QSK_SUBCONTROL( QskMenu, Cell )
QSK_SUBCONTROL( QskMenu, Cursor )
QSK_SUBCONTROL( QskMenu, Text ) QSK_SUBCONTROL( QskMenu, Text )
QSK_SUBCONTROL( QskMenu, Graphic ) QSK_SUBCONTROL( QskMenu, Graphic )
QSK_SUBCONTROL( QskMenu, Cell )
QSK_SYSTEM_STATE( QskMenu, Selected, QskAspect::FirstSystemState << 2 ) QSK_SYSTEM_STATE( QskMenu, Selected, QskAspect::FirstSystemState << 2 )
@ -239,10 +239,12 @@ void QskMenu::traverse( int steps )
int QskMenu::indexAtPosition( const QPointF& pos ) const int QskMenu::indexAtPosition( const QPointF& pos ) const
{ {
const auto r = contentsRect(); for ( int i = 0; i < count(); i++ )
{
if( count() > 0 && r.contains( pos ) ) // A menu never has many cells and we can simply iterate
return ( pos.y() - r.y() ) / ( r.height() / count() ); if ( cellRect( i ).contains( pos ) )
return i;
}
return -1; return -1;
} }
@ -257,8 +259,12 @@ void QskMenu::mousePressEvent( QMouseEvent* event )
{ {
if ( event->button() == Qt::LeftButton ) 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; m_data->isPressed = true;
}
} }
} }
@ -268,8 +274,11 @@ void QskMenu::mouseReleaseEvent( QMouseEvent* event )
{ {
if( m_data->isPressed ) if( m_data->isPressed )
{ {
setSelectedIndex( indexAtPosition( event->localPos() ) );
m_data->isPressed = false; 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(); 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" #include "moc_QskMenu.cpp"

View File

@ -35,7 +35,7 @@ class QSK_EXPORT QskMenu : public QskPopup
QString text; QString text;
}; };
QSK_SUBCONTROLS( Panel, Cell, Text, Graphic ) QSK_SUBCONTROLS( Panel, Cell, Cursor, Text, Graphic )
QSK_STATES( Selected ) QSK_STATES( Selected )
QskMenu( QQuickItem* parentItem = nullptr ); QskMenu( QQuickItem* parentItem = nullptr );
@ -64,6 +64,9 @@ class QSK_EXPORT QskMenu : public QskPopup
virtual QskColorFilter graphicFilterAt( int index ) const; virtual QskColorFilter graphicFilterAt( int index ) const;
QRectF focusIndicatorRect() const override; QRectF focusIndicatorRect() const override;
QRectF cellRect( int index ) const;
qreal cellHeight() const;
Q_SIGNALS: Q_SIGNALS:
void triggered( int index ); void triggered( int index );
void currentIndexChanged( int index ); void currentIndexChanged( int index );

View File

@ -47,11 +47,10 @@ static qreal qskMaxTextWidth( const QskMenu* menu )
return maxWidth; return maxWidth;
} }
static QSizeF qskIconSize( const QskMenu* menu ) static qreal qskGraphicWidth( const QskMenu* menu )
{ {
const auto hint = menu->strutSizeHint( QskMenu::Graphic ); const auto hint = menu->strutSizeHint( QskMenu::Graphic );
const qreal textHeight = QFontMetrics( const qreal textHeight = menu->effectiveFontHeight( QskMenu::Text );
menu->effectiveFont( QskMenu::Text ) ).height();
const auto h = qMax( hint.height(), textHeight ); const auto h = qMax( hint.height(), textHeight );
@ -63,22 +62,23 @@ static QSizeF qskIconSize( const QskMenu* menu )
maxW = w; maxW = w;
} }
const auto w = qMax( hint.width(), maxW ); return qMax( hint.width(), maxW );
return QSizeF( w, h );
} }
static QSizeF qskItemSize( const QskMenu* menu ) static qreal qskCellWidth( const QskMenu* menu )
{ {
const auto spacing = menu->spacingHint( QskMenu::Cell ); using Q = QskMenu;
const QskMargins padding = menu->paddingHint( QskMenu::Cell );
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(); auto w = qskGraphicWidth( menu )
const qreal w = sz.width() + spacing + qskMaxTextWidth( menu ) + padding.width(); + 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, static QSGNode* qskUpdateGraphicNode( const QskMenu* menu,
@ -101,41 +101,46 @@ static QSGNode* qskUpdateTextNode( const QskMenu* menu,
text, menu->textOptions(), QskMenu::Text ); 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 ) for( int i = 0; i < menu->count(); i++ )
{
if ( index >= 0 )
{ {
auto r = menu->subControlRect( QskMenu::Panel ); QSGNode* newNode = nullptr;
r = menu->innerBox( QskMenu::Panel, r );
const auto sz = qskItemSize( menu ); {
const StateChanger stateChanger( menu, menu->currentIndex() == i );
const auto y = r.y() + index * sz.height(); newNode = QskSkinlet::updateBoxNode(
return QRectF( r.x(), y, r.width(), sz.height() ); 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 ) return rootNode;
{
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;
} }
static void qskUpdateItemNode( static void qskUpdateItemNode(
@ -161,32 +166,14 @@ static void qskUpdateItemNode(
static QSGNode* qskUpdateItemsNode( const QskMenu* menu, QSGNode* rootNode ) static QSGNode* qskUpdateItemsNode( const QskMenu* menu, QSGNode* rootNode )
{ {
const auto padding = menu->paddingHint( QskMenu::Cell );
const auto spacing = menu->spacingHint( QskMenu::Cell ); const auto spacing = menu->spacingHint( QskMenu::Cell );
const auto iconSize = qskIconSize( menu ); const auto graphicWidth = qskGraphicWidth( 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 ) );
if ( rootNode == nullptr ) if ( rootNode == nullptr )
rootNode = new QSGNode(); rootNode = new QSGNode();
QSGNode* node = nullptr; QSGNode* node = nullptr;
qreal y = boundingRect.y();
for( int i = 0; i < menu->count(); i++ ) for( int i = 0; i < menu->count(); i++ )
{ {
if ( node == nullptr ) if ( node == nullptr )
@ -203,13 +190,10 @@ static QSGNode* qskUpdateItemsNode( const QskMenu* menu, QSGNode* rootNode )
{ {
const StateChanger stateChanger( menu, menu->currentIndex() == i ); const StateChanger stateChanger( menu, menu->currentIndex() == i );
QRectF cellRect( boundingRect.x(), y, const auto cellRect = menu->cellRect( i );
boundingRect.width(), itemSize.height() );
cellRect = cellRect.marginsRemoved( padding );
auto graphicRect = cellRect; auto graphicRect = cellRect;
graphicRect.setWidth( iconSize.width() ); graphicRect.setWidth( graphicWidth );
auto textRect = cellRect; auto textRect = cellRect;
textRect.setX( graphicRect.right() + spacing ); textRect.setX( graphicRect.right() + spacing );
@ -217,12 +201,8 @@ static QSGNode* qskUpdateItemsNode( const QskMenu* menu, QSGNode* rootNode )
qskUpdateItemNode( menu, graphicRect, menu->graphicAt( i ), qskUpdateItemNode( menu, graphicRect, menu->graphicAt( i ),
textRect, menu->entryAt( i ).text, node ); textRect, menu->entryAt( i ).text, node );
} }
y += itemSize.height();
} }
// Remove trailing nodes
QskSGNode::removeAllChildNodesAfter( rootNode, node ); QskSGNode::removeAllChildNodesAfter( rootNode, node );
return rootNode; return rootNode;
@ -238,11 +218,18 @@ QRectF QskMenuSkinlet::subControlRect(
const QskSkinnable* skinnable, const QRectF& contentsRect, const QskSkinnable* skinnable, const QRectF& contentsRect,
QskAspect::Subcontrol subControl ) const QskAspect::Subcontrol subControl ) const
{ {
const auto menu = static_cast< const QskMenu* >( skinnable );
if( subControl == QskMenu::Panel ) if( subControl == QskMenu::Panel )
{ {
return contentsRect; return contentsRect;
} }
if( subControl == QskMenu::Cursor )
{
return menu->cellRect( menu->currentIndex() );
}
return Inherited::subControlRect( skinnable, contentsRect, subControl ); return Inherited::subControlRect( skinnable, contentsRect, subControl );
} }
@ -261,7 +248,7 @@ QSGNode* QskMenuSkinlet::updateContentsNode(
{ {
auto oldNode = QskSGNode::findChildNode( contentsNode, role ); auto oldNode = QskSGNode::findChildNode( contentsNode, role );
QSGNode* newNode; QSGNode* newNode = nullptr;
switch( role ) switch( role )
{ {
@ -277,7 +264,7 @@ QSGNode* QskMenuSkinlet::updateContentsNode(
} }
case Cursor: case Cursor:
{ {
newNode = qskUpdateCursorNode( menu, oldNode ); newNode = updateBoxNode( menu, oldNode, QskMenu::Cursor );
break; break;
} }
case Items: case Items:
@ -296,18 +283,15 @@ QSGNode* QskMenuSkinlet::updateContentsNode(
QSizeF QskMenuSkinlet::sizeHint( const QskSkinnable* skinnable, QSizeF QskMenuSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& ) const Qt::SizeHint which, const QSizeF& ) const
{ {
using Q = QskMenu;
if ( which != Qt::PreferredSize ) if ( which != Qt::PreferredSize )
return QSizeF(); return QSizeF();
const auto menu = static_cast< const QskMenu* >( skinnable ); const auto menu = static_cast< const QskMenu* >( skinnable );
const auto itemSize = qskItemSize( menu ); const qreal w = qskCellWidth( menu );
const auto count = menu->count(); const auto h = menu->count() * menu->cellHeight();
const qreal h = count * itemSize.height(); return menu->outerBoxSize( QskMenu::Panel, QSizeF( w, h ) );
return menu->outerBoxSize( Q::Panel, QSizeF( itemSize.width(), h ) );
} }
#include "moc_QskMenuSkinlet.cpp" #include "moc_QskMenuSkinlet.cpp"