diff --git a/skins/fluent2/QskFluent2Skin.cpp b/skins/fluent2/QskFluent2Skin.cpp index 3a47da77..2c438d03 100644 --- a/skins/fluent2/QskFluent2Skin.cpp +++ b/skins/fluent2/QskFluent2Skin.cpp @@ -612,8 +612,10 @@ void Editor::setupFocusIndicatorColors( void Editor::setupListViewMetrics() { using Q = QskListView; + using A = QskAspect; - setBoxBorderMetrics( Q::Cell | Q::Selected, { 3, 0, 0, 0 } ); + for ( auto state : { A::NoState, Q::Hovered, Q::Pressed } ) + setBoxBorderMetrics( Q::Cell | state | Q::Selected, { 3, 0, 0, 0 } ); #if 1 // taken from M3 - what are the actual values, TODO ... setPadding( Q::Cell, { 16, 12, 16, 12 } ); @@ -693,6 +695,9 @@ void Editor::setupListViewColors( setColor( text, textColor ); } } + + setAnimation( Q::Cell | A::Color, 100 ); + setAnimation( Q::Text | A::Color, 100 ); } void Editor::setupMenuMetrics() diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index ccdb2e21..c13187e4 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -1150,6 +1150,7 @@ void Editor::setupScrollView() void Editor::setupListView() { + using A = QskAspect; using Q = QskListView; setStrutSize( Q::Cell, { -1, 56 } ); @@ -1157,7 +1158,9 @@ void Editor::setupListView() setBoxBorderColors( Q::Cell, m_pal.outline ); setGradient( Q::Cell, m_pal.surface ); - setGradient( Q::Cell | Q::Selected, m_pal.primary12 ); + + for ( auto state : { A::NoState, Q::Hovered, Q::Pressed } ) + setGradient( Q::Cell | state | Q::Selected, m_pal.primary12 ); setColor( Q::Text, m_pal.onSurface ); } diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 65fa848d..bdd40e47 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -1057,12 +1057,17 @@ void Editor::setupListView() // alternating row colors setColor( Q::Cell | A::Lower, Qt::white ); - setColor( Q::Cell | Q::Selected | A::Lower, m_pal.highlighted ); - setColor( Q::Cell | A::Upper, m_pal.contrasted ); - setColor( Q::Cell | Q::Selected | A::Upper, m_pal.highlighted ); - setColor( Q::Text | Q::Selected, m_pal.highlightedText ); + for ( auto state : { A::NoState, Q::Hovered, Q::Pressed } ) + { + const auto aspect = Q::Cell | state | Q::Selected; + + setColor( aspect | A::Lower, m_pal.highlighted ); + setColor( aspect | A::Upper, m_pal.highlighted ); + + setColor( Q::Text | state | Q::Selected, m_pal.highlightedText ); + } } void Editor::setupSubWindow() diff --git a/src/controls/QskListView.cpp b/src/controls/QskListView.cpp index 08e5cf44..dd97c20a 100644 --- a/src/controls/QskListView.cpp +++ b/src/controls/QskListView.cpp @@ -46,6 +46,48 @@ class QskListView::PrivateData { } + void setRowState( QskListView* listView, int row, QskAspect::State state ) + { + using Q = QskListView; + + auto& storedRow = ( state == Q::Hovered ) + ? hoveredRow : ( ( state == Q::Pressed ) ? pressedRow : selectedRow ); + + if ( row == storedRow ) + return; + + if ( storedRow >= 0 ) + { + const auto states = listView->rowStates( storedRow ); + startTransitions( listView, storedRow, states, states & ~state ); + } + + if ( row >= 0 ) + { + const auto states = listView->rowStates( row ); + startTransitions( listView, row, states, states | state ); + } + + storedRow = row; + } + + private: + inline void startTransitions( QskListView* listView, int row, + QskAspect::States oldStates, QskAspect::States newStates ) + { + /* + working implementation can be found in + https://github.com/uwerat/qskinny/tree/features/listview + */ + + Q_UNUSED( row ); + Q_UNUSED( oldStates ); + Q_UNUSED( newStates ); + + listView->update(); + } + + public: /* Currently we only support single selection. We can't navigate the current item ( = focus ) without changing the selection. @@ -55,6 +97,8 @@ class QskListView::PrivateData bool preferredWidthFromColumns : 1; SelectionMode selectionMode : 4; + int hoveredRow = -1; + int pressedRow = -1; int selectedRow = -1; }; @@ -131,7 +175,8 @@ void QskListView::setSelectedRow( int row ) if ( row != m_data->selectedRow ) { - m_data->selectedRow = row; + m_data->setRowState( this, row, Selected ); + Q_EMIT selectedRowChanged( row ); Q_EMIT focusIndicatorRectChanged(); @@ -280,6 +325,7 @@ void QskListView::mousePressEvent( QMouseEvent* event ) const int row = qskRowAt( this, qskMousePosition( event ) ); if ( row >= 0 ) { + m_data->setRowState( this, row, Pressed ); setSelectedRow( row ); return; } @@ -290,9 +336,44 @@ void QskListView::mousePressEvent( QMouseEvent* event ) void QskListView::mouseReleaseEvent( QMouseEvent* event ) { + m_data->setRowState( this, -1, Pressed ); Inherited::mouseReleaseEvent( event ); } +void QskListView::mouseUngrabEvent() +{ + m_data->setRowState( this, -1, Pressed ); + Inherited::mouseUngrabEvent(); +} + +void QskListView::hoverEnterEvent( QHoverEvent* event ) +{ + if ( m_data->selectionMode != NoSelection ) + { + const int row = qskRowAt( this, qskHoverPosition( event ) ); + m_data->setRowState( this, row, Hovered ); + } + + Inherited::hoverEnterEvent( event ); +} + +void QskListView::hoverMoveEvent( QHoverEvent* event ) +{ + if ( m_data->selectionMode != NoSelection ) + { + const int row = qskRowAt( this, qskHoverPosition( event ) ); + m_data->setRowState( this, row, Hovered ); + } + + Inherited::hoverMoveEvent( event ); +} + +void QskListView::hoverLeaveEvent( QHoverEvent* event ) +{ + m_data->setRowState( this, -1, Hovered ); + Inherited::hoverLeaveEvent( event ); +} + void QskListView::changeEvent( QEvent* event ) { if ( event->type() == QEvent::StyleChange ) @@ -301,6 +382,25 @@ void QskListView::changeEvent( QEvent* event ) Inherited::changeEvent( event ); } +QskAspect::States QskListView::rowStates( int row ) const +{ + auto states = skinStates(); + + if ( row >= 0 ) + { + if ( row == m_data->selectedRow ) + states |= Selected; + + if ( row == m_data->hoveredRow ) + states |= Hovered; + + if ( row == m_data->pressedRow ) + states |= Pressed; + } + + return states; +} + #ifndef QT_NO_WHEELEVENT static qreal qskAlignedToRows( const qreal y0, qreal dy, diff --git a/src/controls/QskListView.h b/src/controls/QskListView.h index 13fe8055..8023cfcf 100644 --- a/src/controls/QskListView.h +++ b/src/controls/QskListView.h @@ -54,6 +54,7 @@ class QSK_EXPORT QskListView : public QskScrollView QskTextOptions textOptions() const; Q_INVOKABLE int selectedRow() const; + QskAspect::States rowStates( int ) const; virtual int rowCount() const = 0; virtual int columnCount() const = 0; @@ -83,6 +84,11 @@ class QSK_EXPORT QskListView : public QskScrollView void mousePressEvent( QMouseEvent* ) override; void mouseReleaseEvent( QMouseEvent* ) override; + void mouseUngrabEvent() override; + + void hoverEnterEvent( QHoverEvent* ) override; + void hoverMoveEvent( QHoverEvent* ) override; + void hoverLeaveEvent( QHoverEvent* ) override; #ifndef QT_NO_WHEELEVENT virtual QPointF scrollOffset( const QWheelEvent* ) const override; diff --git a/src/controls/QskListViewSkinlet.cpp b/src/controls/QskListViewSkinlet.cpp index 91d8f226..5dc2fa5c 100644 --- a/src/controls/QskListViewSkinlet.cpp +++ b/src/controls/QskListViewSkinlet.cpp @@ -470,12 +470,7 @@ QskAspect::States QskListViewSkinlet::sampleStates( const QskSkinnable* skinnabl if ( subControl == Q::Cell || subControl == Q::Text || subControl == Q::Graphic ) { const auto listView = static_cast< const QskListView* >( skinnable ); - - auto states = listView->skinStates(); - if ( index == listView->selectedRow() ) - states |= Q::Selected; - - return states; + return listView->rowStates( index ); } return Inherited::sampleStates( skinnable, subControl, index );