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 );
#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 );

View File

@ -5,13 +5,13 @@
#include <QskGraphic.h>
#include <QskColorFilter.h>
#include <QFontMetricsF>
#include <QVector>
#include <qvector.h>
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"

View File

@ -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 );

View File

@ -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"