scrollbar hover events

This commit is contained in:
Uwe Rathmann 2023-07-20 16:46:21 +02:00
parent bdef864bf3
commit c6fb2f81c9
4 changed files with 154 additions and 51 deletions

View File

@ -1012,9 +1012,13 @@ void Editor::setupScrollView()
// scroll bars // scroll bars
for ( auto subControl : { Q::HorizontalScrollBar, Q::VerticalScrollBar } ) for ( auto subControl : { Q::HorizontalScrollBar, Q::VerticalScrollBar } )
{ {
setMetric( subControl | A::Size, 12_dp ); setMetric( subControl | A::Size, 14 );
setPadding( subControl, 0 ); setPadding( subControl, 2 );
setMargin( subControl, 0 ); setMargin( subControl, 0 );
setPanel( subControl, Sunken );
setBoxShape( subControl, 100, Qt::RelativeSize );
setBoxBorderMetrics( subControl, 1 );
} }
// scrollbar handles // scrollbar handles
@ -1024,6 +1028,8 @@ void Editor::setupScrollView()
setButton( subControl, Raised, bw ); setButton( subControl, Raised, bw );
setButton( subControl | Q::Pressed, Sunken, bw ); setButton( subControl | Q::Pressed, Sunken, bw );
setBoxShape( subControl, 100, Qt::RelativeSize );
setBoxShape( subControl | Q::Pressed, 100, Qt::RelativeSize );
const auto extent = 40_dp; const auto extent = 40_dp;

View File

@ -17,60 +17,132 @@ QSK_SUBCONTROL( QskScrollView, VerticalScrollHandle )
QSK_SYSTEM_STATE( QskScrollView, Pressed, QskAspect::FirstSystemState << 1 ) QSK_SYSTEM_STATE( QskScrollView, Pressed, QskAspect::FirstSystemState << 1 )
static inline QskAspect::Subcontrol qskSubControlAt(
const QskScrollView* scrollView, const QPointF& pos )
{
using Q = QskScrollView;
const auto rect = scrollView->contentsRect();
// order is important as the handles are inside the bars !
for ( auto subControl : { Q::VerticalScrollHandle, Q::VerticalScrollBar,
Q::HorizontalScrollHandle, Q::HorizontalScrollBar } )
{
if ( scrollView->subControlRect( rect, subControl ).contains( pos ) )
return subControl;
}
return QskAspect::NoSubcontrol;
}
class QskScrollView::PrivateData class QskScrollView::PrivateData
{ {
public: public:
inline void resetScrolling( QskScrollView* scrollView ) inline void resetScrolling( QskScrollView* scrollView )
{ {
setScrolling( scrollView, 0, 0.0 ); setScrolling( scrollView, QskAspect::NoSubcontrol, 0.0 );
} }
inline void setScrolling( QskScrollView* scrollView, int scrolling, qreal pos ) inline void setScrolling( QskScrollView* scrollView,
QskAspect::Subcontrol subControl, qreal pos )
{ {
if ( isScrolling == scrolling ) if ( subControl == pressedSubControl )
return; return;
Qt::Orientation orientation; QskAspect::Subcontrol subControls[2];
if ( subControl == VerticalScrollHandle || pressedSubControl == VerticalScrollHandle )
if ( ( isScrolling == Qt::Horizontal ) || ( scrolling == Qt::Horizontal ) ) {
orientation = Qt::Horizontal; subControls[0] = VerticalScrollHandle;
subControls[1] = VerticalScrollBar;
}
else else
orientation = Qt::Vertical; {
subControls[0] = HorizontalScrollHandle;;
subControls[1] = HorizontalScrollBar;
}
this->isScrolling = scrolling; pressedSubControl = subControl;
this->scrollPressPos = pos; scrollPressPos = pos;
scrollView->update(); scrollView->update();
auto oldStates = scrollView->skinStates(); auto oldStates = scrollView->skinStates() | scrollView->scrollBarStates( subControl );
auto newStates = oldStates | QskScrollView::Pressed; auto newStates = oldStates | QskScrollView::Pressed;
if ( scrolling == 0 ) if ( pressedSubControl == QskAspect::NoSubcontrol )
qSwap( oldStates, newStates ); qSwap( oldStates, newStates );
if ( orientation == Qt::Horizontal ) scrollView->startHintTransitions( { subControls[0] }, oldStates, newStates );
scrollView->startHintTransitions( { subControls[1] }, oldStates, newStates );
}
void setHovered( QskScrollView* scrollView, QskAspect::Subcontrol subControl )
{ {
scrollView->startHintTransitions( { HorizontalScrollHandle }, oldStates, newStates ); if ( subControl == this->hoveredSubControl )
scrollView->startHintTransitions( { HorizontalScrollBar }, oldStates, newStates ); return;
QskAspect::Subcontrol subControls[2];
if ( subControl == VerticalScrollHandle
|| hoveredSubControl == VerticalScrollHandle
|| subControl == VerticalScrollBar
|| hoveredSubControl == VerticalScrollBar )
{
subControls[0] = VerticalScrollHandle;
subControls[1] = VerticalScrollBar;
} }
else else
{ {
scrollView->startHintTransitions( { VerticalScrollHandle }, oldStates, newStates ); subControls[0] = HorizontalScrollHandle;;
scrollView->startHintTransitions( { VerticalScrollBar }, oldStates, newStates ); subControls[1] = HorizontalScrollBar;
} }
hoveredSubControl = subControl;
auto oldStates = scrollView->skinStates();
auto newStates = oldStates | QskScrollView::Hovered;
if ( hoveredSubControl == QskAspect::NoSubcontrol )
qSwap( oldStates, newStates );
scrollView->startHintTransitions( { subControls[0] }, oldStates, newStates );
scrollView->startHintTransitions( { subControls[1] }, oldStates, newStates );
}
bool hasState( QskAspect::Subcontrol subControl, QskAspect::State state ) const
{
if ( subControl == QskAspect::NoSubcontrol )
return false;
const auto stateSubcontrol =
( state == QskControl::Hovered ) ? hoveredSubControl : pressedSubControl;
if ( subControl == stateSubcontrol )
return true;
if ( subControl == VerticalScrollBar )
return stateSubcontrol == VerticalScrollHandle;
if ( subControl == HorizontalScrollBar )
return stateSubcontrol == HorizontalScrollHandle;
return false;
} }
Qt::ScrollBarPolicy horizontalScrollBarPolicy = Qt::ScrollBarAsNeeded; Qt::ScrollBarPolicy horizontalScrollBarPolicy = Qt::ScrollBarAsNeeded;
Qt::ScrollBarPolicy verticalScrollBarPolicy = Qt::ScrollBarAsNeeded; Qt::ScrollBarPolicy verticalScrollBarPolicy = Qt::ScrollBarAsNeeded;
qreal scrollPressPos; qreal scrollPressPos = 0.0;
int isScrolling = 0;
QskAspect::Subcontrol pressedSubControl = QskAspect::NoSubcontrol;
QskAspect::Subcontrol hoveredSubControl = QskAspect::NoSubcontrol;
}; };
QskScrollView::QskScrollView( QQuickItem* parent ) QskScrollView::QskScrollView( QQuickItem* parent )
: Inherited( parent ) : Inherited( parent )
, m_data( new PrivateData() ) , m_data( new PrivateData() )
{ {
setAcceptHoverEvents( true );
} }
QskScrollView::~QskScrollView() QskScrollView::~QskScrollView()
@ -117,15 +189,23 @@ Qt::ScrollBarPolicy QskScrollView::horizontalScrollBarPolicy() const
bool QskScrollView::isScrolling( Qt::Orientation orientation ) const bool QskScrollView::isScrolling( Qt::Orientation orientation ) const
{ {
return m_data->isScrolling == orientation; if ( orientation == Qt::Vertical )
return m_data->pressedSubControl == VerticalScrollHandle;
else
return m_data->pressedSubControl == HorizontalScrollHandle;
} }
QskAspect::States QskScrollView::scrollHandleStates( Qt::Orientation orientation ) const QskAspect::States QskScrollView::scrollBarStates(
QskAspect::Subcontrol subControl ) const
{ {
auto states = skinStates(); auto states = skinStates();
if ( m_data->isScrolling == orientation )
if ( m_data->hasState( subControl, Pressed ) )
states |= Pressed; states |= Pressed;
if ( m_data->hasState( subControl, Hovered ) )
states |= Hovered;
return states; return states;
} }
@ -143,11 +223,11 @@ void QskScrollView::mousePressEvent( QMouseEvent* event )
if ( subControlRect( VerticalScrollBar ).contains( mousePos ) ) if ( subControlRect( VerticalScrollBar ).contains( mousePos ) )
{ {
const QRectF handleRect = subControlRect( VerticalScrollHandle ); const auto handleRect = subControlRect( VerticalScrollHandle );
if ( handleRect.contains( mousePos ) ) if ( handleRect.contains( mousePos ) )
{ {
m_data->setScrolling( this, Qt::Vertical, mousePos.y() ); m_data->setScrolling( this, VerticalScrollHandle, mousePos.y() );
} }
else else
{ {
@ -172,7 +252,7 @@ void QskScrollView::mousePressEvent( QMouseEvent* event )
if ( handleRect.contains( mousePos ) ) if ( handleRect.contains( mousePos ) )
{ {
m_data->setScrolling( this, Qt::Horizontal, mousePos.x() ); m_data->setScrolling( this, HorizontalScrollHandle, mousePos.x() );
} }
else else
{ {
@ -196,7 +276,7 @@ void QskScrollView::mousePressEvent( QMouseEvent* event )
void QskScrollView::mouseMoveEvent( QMouseEvent* event ) void QskScrollView::mouseMoveEvent( QMouseEvent* event )
{ {
if ( !m_data->isScrolling ) if ( m_data->pressedSubControl == QskAspect::NoSubcontrol )
{ {
Inherited::mouseMoveEvent( event ); Inherited::mouseMoveEvent( event );
return; return;
@ -205,7 +285,7 @@ void QskScrollView::mouseMoveEvent( QMouseEvent* event )
const auto mousePos = qskMousePosition( event ); const auto mousePos = qskMousePosition( event );
QPointF pos = scrollPos(); QPointF pos = scrollPos();
if ( m_data->isScrolling == Qt::Horizontal ) if ( m_data->pressedSubControl == HorizontalScrollHandle )
{ {
const qreal dx = mousePos.x() - m_data->scrollPressPos; const qreal dx = mousePos.x() - m_data->scrollPressPos;
const qreal w = subControlRect( HorizontalScrollBar ).width(); const qreal w = subControlRect( HorizontalScrollBar ).width();
@ -213,7 +293,7 @@ void QskScrollView::mouseMoveEvent( QMouseEvent* event )
pos.rx() += dx / w * scrollableSize().width(); pos.rx() += dx / w * scrollableSize().width();
m_data->scrollPressPos = mousePos.x(); m_data->scrollPressPos = mousePos.x();
} }
else if ( m_data->isScrolling == Qt::Vertical ) else
{ {
const qreal dy = mousePos.y() - m_data->scrollPressPos; const qreal dy = mousePos.y() - m_data->scrollPressPos;
const qreal h = subControlRect( VerticalScrollBar ).height(); const qreal h = subControlRect( VerticalScrollBar ).height();
@ -228,7 +308,7 @@ void QskScrollView::mouseMoveEvent( QMouseEvent* event )
void QskScrollView::mouseReleaseEvent( QMouseEvent* event ) void QskScrollView::mouseReleaseEvent( QMouseEvent* event )
{ {
if ( !m_data->isScrolling ) if ( m_data->pressedSubControl == QskAspect::NoSubcontrol )
{ {
Inherited::mouseReleaseEvent( event ); Inherited::mouseReleaseEvent( event );
return; return;
@ -242,6 +322,23 @@ void QskScrollView::mouseUngrabEvent()
m_data->resetScrolling( this ); m_data->resetScrolling( this );
} }
void QskScrollView::hoverEnterEvent( QHoverEvent* event )
{
const auto subControl = qskSubControlAt( this, qskHoverPosition( event ) );
m_data->setHovered( this, subControl );
}
void QskScrollView::hoverMoveEvent( QHoverEvent* event )
{
const auto subControl = qskSubControlAt( this, qskHoverPosition( event ) );
m_data->setHovered( this, subControl );
}
void QskScrollView::hoverLeaveEvent( QHoverEvent* )
{
m_data->setHovered( this, QskAspect::NoSubcontrol );
}
#ifndef QT_NO_WHEELEVENT #ifndef QT_NO_WHEELEVENT
QPointF QskScrollView::scrollOffset( const QWheelEvent* event ) const QPointF QskScrollView::scrollOffset( const QWheelEvent* event ) const

View File

@ -39,7 +39,7 @@ class QSK_EXPORT QskScrollView : public QskScrollBox
Qt::Orientations scrollableOrientations() const; Qt::Orientations scrollableOrientations() const;
bool isScrolling( Qt::Orientation ) const; bool isScrolling( Qt::Orientation ) const;
QskAspect::States scrollHandleStates( Qt::Orientation ) const; QskAspect::States scrollBarStates( QskAspect::Subcontrol ) const;
QRectF viewContentsRect() const override; QRectF viewContentsRect() const override;
QskAnimationHint flickHint() const override; QskAnimationHint flickHint() const override;
@ -54,6 +54,10 @@ class QSK_EXPORT QskScrollView : public QskScrollBox
void mouseReleaseEvent( QMouseEvent* ) override; void mouseReleaseEvent( QMouseEvent* ) override;
void mouseUngrabEvent() override; void mouseUngrabEvent() override;
void hoverEnterEvent( QHoverEvent* ) override;
void hoverMoveEvent( QHoverEvent* ) override;
void hoverLeaveEvent( QHoverEvent* ) override;
#ifndef QT_NO_WHEELEVENT #ifndef QT_NO_WHEELEVENT
QPointF scrollOffset( const QWheelEvent* ) const override; QPointF scrollOffset( const QWheelEvent* ) const override;
#endif #endif

View File

@ -39,14 +39,16 @@ static void qskAlignedHandle( qreal start, qreal end,
} }
} }
static inline Qt::Orientation qskSubcontrolOrientation( QskAspect::Subcontrol subControl ) static qreal qskScrollBarExtent(
const QskScrollView* scrollView, Qt::Orientation orientation )
{ {
using Q = QskScrollView; const auto subControl = ( orientation == Qt::Horizontal )
? QskScrollView::HorizontalScrollBar : QskScrollView::VerticalScrollBar;
if ( subControl == Q::HorizontalScrollBar || subControl == Q::HorizontalScrollHandle ) QskSkinStateChanger stateChanger( scrollView );
return Qt::Horizontal; stateChanger.setStates( scrollView->scrollBarStates( subControl ) );
else
return Qt::Vertical; return scrollView->metric( subControl | QskAspect::Size );
} }
QskScrollViewSkinlet::QskScrollViewSkinlet( QskSkin* skin ) QskScrollViewSkinlet::QskScrollViewSkinlet( QskSkin* skin )
@ -128,10 +130,8 @@ QSGNode* QskScrollViewSkinlet::updateScrollBarNode( const QskScrollView* scrollV
const auto rect = subControlRect( scrollView, const auto rect = subControlRect( scrollView,
scrollView->contentsRect(), subControl ); scrollView->contentsRect(), subControl );
const auto orientation = qskSubcontrolOrientation( subControl );
QskSkinStateChanger stateChanger( scrollView ); QskSkinStateChanger stateChanger( scrollView );
stateChanger.setStates( scrollView->scrollHandleStates( orientation ) ); stateChanger.setStates( scrollView->scrollBarStates( subControl ) );
return updateBoxNode( scrollView, node, rect, subControl ); return updateBoxNode( scrollView, node, rect, subControl );
} }
@ -254,7 +254,7 @@ QRectF QskScrollViewSkinlet::scrollHandleRect( const QskScrollView* scrollView,
const auto sbRect = subControlRect( scrollView, contentsRect, Q::VerticalScrollBar ); const auto sbRect = subControlRect( scrollView, contentsRect, Q::VerticalScrollBar );
QskSkinStateChanger stateChanger( scrollView ); QskSkinStateChanger stateChanger( scrollView );
stateChanger.setStates( scrollView->scrollHandleStates( orientation ) ); stateChanger.setStates( scrollView->scrollBarStates( Q::VerticalScrollBar ) );
const auto padding = scrollView->paddingHint( Q::VerticalScrollBar ); const auto padding = scrollView->paddingHint( Q::VerticalScrollBar );
const auto strut = scrollView->strutSizeHint( Q::VerticalScrollHandle ); const auto strut = scrollView->strutSizeHint( Q::VerticalScrollHandle );
@ -275,7 +275,7 @@ QRectF QskScrollViewSkinlet::scrollHandleRect( const QskScrollView* scrollView,
const auto sbRect = subControlRect( scrollView, contentsRect, Q::HorizontalScrollBar ); const auto sbRect = subControlRect( scrollView, contentsRect, Q::HorizontalScrollBar );
QskSkinStateChanger stateChanger( scrollView ); QskSkinStateChanger stateChanger( scrollView );
stateChanger.setStates( scrollView->scrollHandleStates( orientation ) ); stateChanger.setStates( scrollView->scrollBarStates( Q::HorizontalScrollBar ) );
const auto padding = scrollView->paddingHint( Q::HorizontalScrollBar ); const auto padding = scrollView->paddingHint( Q::HorizontalScrollBar );
@ -299,7 +299,6 @@ QRectF QskScrollViewSkinlet::scrollHandleRect( const QskScrollView* scrollView,
QRectF QskScrollViewSkinlet::scrollBarRect( const QskScrollView* scrollView, QRectF QskScrollViewSkinlet::scrollBarRect( const QskScrollView* scrollView,
const QRectF& contentsRect, Qt::Orientation orientation ) const const QRectF& contentsRect, Qt::Orientation orientation ) const
{ {
using A = QskAspect;
using Q = QskScrollView; using Q = QskScrollView;
const auto scrollOrientations = scrollView->scrollableOrientations(); const auto scrollOrientations = scrollView->scrollableOrientations();
@ -308,28 +307,25 @@ QRectF QskScrollViewSkinlet::scrollBarRect( const QskScrollView* scrollView,
auto r = subControlRect( scrollView, contentsRect, Q::Panel ); auto r = subControlRect( scrollView, contentsRect, Q::Panel );
QskSkinStateChanger stateChanger( scrollView );
stateChanger.setStates( scrollView->scrollHandleStates( orientation ) );
if ( orientation == Qt::Horizontal ) if ( orientation == Qt::Horizontal )
{ {
const qreal h = scrollView->metric( Q::HorizontalScrollBar | A::Size ); const qreal h = qskScrollBarExtent( scrollView, Qt::Horizontal );
r.setTop( r.bottom() - h ); r.setTop( r.bottom() - h );
if ( scrollOrientations & Qt::Vertical ) if ( scrollOrientations & Qt::Vertical )
{ {
const qreal w = scrollView->metric( Q::VerticalScrollBar | A::Size ); const qreal w = qskScrollBarExtent( scrollView, Qt::Vertical );
r.setRight( r.right() - w ); r.setRight( r.right() - w );
} }
} }
else else
{ {
const qreal w = scrollView->metric( Q::VerticalScrollBar | A::Size ); const qreal w = qskScrollBarExtent( scrollView, Qt::Vertical );
r.setLeft( r.right() - w ); r.setLeft( r.right() - w );
if ( scrollOrientations & Qt::Horizontal ) if ( scrollOrientations & Qt::Horizontal )
{ {
const qreal h = scrollView->metric( Q::HorizontalScrollBar | A::Size ); const qreal h = qskScrollBarExtent( scrollView, Qt::Horizontal );
r.setBottom( r.bottom() - h ); r.setBottom( r.bottom() - h );
} }
} }