6#include "QskScrollBox.h"
7#include "QskAnimationHint.h"
9#include "QskFlickAnimator.h"
10#include "QskGesture.h"
11#include "QskPanGestureRecognizer.h"
15#include <private/qquickwindow_p.h>
18static inline constexpr qreal qskViewportPadding()
23static inline bool qskIsScrollable(
24 const QskScrollBox* scrollBox, Qt::Orientations orientations )
28 const QSizeF viewSize = scrollBox->viewContentsRect().size();
29 const QSizeF& scrollableSize = scrollBox->scrollableSize();
31 if ( orientations & Qt::Vertical )
33 if ( viewSize.height() < scrollableSize.height() )
37 if ( orientations & Qt::Horizontal )
39 if ( viewSize.width() < scrollableSize.width() )
56 setEasingCurve( QEasingCurve::OutCubic );
61 m_scrollBox = scrollBox;
64 void translate( qreal dx, qreal dy )
override
66 const QPointF pos = m_scrollBox->scrollPos();
67 m_scrollBox->setScrollPos( pos - QPointF( dx, -dy ) );
78 : m_scrollBox( nullptr )
84 m_scrollBox = scrollBox;
87 void scroll(
const QPointF& from,
const QPointF& to )
95 if ( from == to || m_scrollBox ==
nullptr )
103 const auto hint = m_scrollBox->flickHint();
105 setDuration( hint.duration );
106 setEasingCurve( hint.type );
107 setWindow( m_scrollBox->window() );
113 void advance( qreal value )
override
115 qreal x = m_from.x() + ( m_to.x() - m_from.x() ) * value;
116 qreal y = m_from.y() + ( m_to.y() - m_from.y() ) * value;
118 m_scrollBox->setScrollPos( QPointF( x, y ) );
133 PanRecognizer( QObject* parent =
nullptr )
136 setOrientations( Qt::Horizontal | Qt::Vertical );
139 bool isAcceptedPos(
const QPointF& pos )
const override
141 if (
auto scrollBox = qobject_cast< const QskScrollBox* >( watchedItem() ) )
143 if ( qskIsScrollable( scrollBox, orientations() ) )
144 return scrollBox->viewContentsRect().contains( pos );
152class QskScrollBox::PrivateData
156 QSizeF scrollableSize = QSize( 0.0, 0.0 );
158 PanRecognizer panRecognizer;
160 FlickAnimator flicker;
161 ScrollAnimator scroller;
163 bool autoScrollFocusItem =
true;
166QskScrollBox::QskScrollBox( QQuickItem* parent )
167 : Inherited( parent )
168 , m_data( new PrivateData() )
170 m_data->flicker.setScrollBox(
this );
171 m_data->scroller.setScrollBox(
this );
173 m_data->panRecognizer.setWatchedItem(
this );
175 setAcceptedMouseButtons( Qt::LeftButton );
176 setFiltersChildMouseEvents(
true );
178 setWheelEnabled(
true );
179 setFocusPolicy( Qt::StrongFocus );
181 connectWindow( window(),
true );
184QskScrollBox::~QskScrollBox()
188void QskScrollBox::setAutoScrollFocusedItem(
bool on )
190 if ( m_data->autoScrollFocusItem != on )
192 m_data->autoScrollFocusItem = on;
193 connectWindow( window(),
true );
194 Q_EMIT autoScrollFocusedItemChanged( on );
198bool QskScrollBox::autoScrollFocusItem()
const
200 return m_data->autoScrollFocusItem;
203void QskScrollBox::onFocusItemChanged()
207#if QT_VERSION >= QT_VERSION_CHECK( 6, 1, 0 )
208 auto wd = QQuickWindowPrivate::get( window() )->deliveryAgentPrivate();
210 auto wd = QQuickWindowPrivate::get( window() );
212 auto reason = wd->lastFocusReason;
213 if ( reason == Qt::TabFocusReason || reason == Qt::BacktabFocusReason )
214 ensureFocusItemVisible();
218void QskScrollBox::ensureFocusItemVisible()
220 if ( window() ==
nullptr )
223 if (
const auto focusItem = window()->activeFocusItem() )
224 ensureItemVisible( focusItem );
227void QskScrollBox::setFlickRecognizerTimeout(
int timeout )
232 m_data->panRecognizer.setTimeout( timeout );
235int QskScrollBox::flickRecognizerTimeout()
const
237 return m_data->panRecognizer.timeout();
240void QskScrollBox::setFlickableOrientations( Qt::Orientations orientations )
242 if ( m_data->panRecognizer.orientations() != orientations )
244 m_data->panRecognizer.setOrientations( orientations );
245 Q_EMIT flickableOrientationsChanged();
249Qt::Orientations QskScrollBox::flickableOrientations()
const
251 return m_data->panRecognizer.orientations();
254void QskScrollBox::setScrollPos(
const QPointF& pos )
256 const QPointF boundedPos = boundedScrollPos( pos );
257 if ( boundedPos != m_data->scrollPos )
259 m_data->scrollPos = boundedPos;
262 Q_EMIT scrollPosChanged();
263 Q_EMIT scrolledTo( boundedPos );
267QPointF QskScrollBox::scrollPos()
const
269 return m_data->scrollPos;
272void QskScrollBox::scrollTo(
const QPointF& pos )
274 m_data->scroller.scroll( scrollPos(), pos );
277void QskScrollBox::setScrollableSize(
const QSizeF& size )
279 const QSizeF boundedSize = size.expandedTo( QSizeF( 0, 0 ) );
281 if ( boundedSize != m_data->scrollableSize )
283 m_data->scrollableSize = boundedSize;
284 Q_EMIT scrollableSizeChanged( m_data->scrollableSize );
286 setScrollPos( m_data->scrollPos );
292QSizeF QskScrollBox::scrollableSize()
const
294 return m_data->scrollableSize;
297void QskScrollBox::ensureItemVisible(
const QQuickItem* item )
299 if ( qskIsAncestorOf(
this, item ) )
301 auto pos = scrollPos() - viewContentsRect().topLeft();
302 pos += mapFromItem( item, QPointF() );
304 ensureVisible( QRectF( pos.x(), pos.y(), item->width(), item->height() ) );
308void QskScrollBox::ensureVisible(
const QPointF& pos )
310 const qreal margin = qskViewportPadding();
312 QRectF r( scrollPos(), viewContentsRect().size() );
313 r.adjust( margin, margin, -margin, -margin );
318 if ( pos.x() < r.left() )
322 else if ( pos.x() > r.right() )
324 x = pos.x() - r.width();
327 if ( pos.y() < r.top() )
331 else if ( y > r.right() )
333 y = pos.y() - r.height();
336 const QPoint newPos( x - margin, y - margin );
341 setScrollPos( newPos );
344void QskScrollBox::ensureVisible(
const QRectF& itemRect )
346 const qreal margin = qskViewportPadding();
348 QRectF r( scrollPos(), viewContentsRect().size() );
349 r.adjust( margin, margin, -margin, -margin );
354 if ( itemRect.width() > r.width() )
356 x = itemRect.left() + 0.5 * ( itemRect.width() - r.width() );
358 else if ( itemRect.right() > r.right() )
360 x = itemRect.right() - r.width();
362 else if ( itemRect.left() < r.left() )
367 if ( itemRect.height() > r.height() )
369 y = itemRect.top() + 0.5 * ( itemRect.height() - r.height() );
371 else if ( itemRect.bottom() > r.bottom() )
373 y = itemRect.bottom() - r.height();
375 else if ( itemRect.top() < r.top() )
380 const QPoint newPos( x - margin, y - margin );
385 setScrollPos( newPos );
392 connectWindow( event->oldWindow(),
false );
393 connectWindow( event->window(),
true );
398 if ( event->isResized() )
399 setScrollPos( scrollPos() );
406 if ( event->gesture()->type() == QskGesture::Pan )
408 const auto gesture =
static_cast< const QskPanGesture*
>(
event->gesture().get() );
410 switch ( gesture->state() )
412 case QskGesture::Updated:
414 setScrollPos( scrollPos() - gesture->delta() );
417 case QskGesture::Finished:
419 m_data->flicker.setWindow( window() );
420 m_data->flicker.accelerate( gesture->angle(), gesture->velocity() );
423 case QskGesture::Canceled:
435 Inherited::gestureEvent( event );
438#ifndef QT_NO_WHEELEVENT
440QPointF QskScrollBox::scrollOffset(
const QWheelEvent* event )
const
444 const auto pos = qskWheelPosition( event );
445 const auto viewRect = viewContentsRect();
447 if ( viewRect.contains( pos ) )
449 offset =
event->pixelDelta();
450 if ( offset.isNull() )
451 offset =
event->angleDelta() / QWheelEvent::DefaultDeltasPerStep;
453 offset.rx() *= viewRect.width();
454 offset.ry() *= viewRect.height();
460void QskScrollBox::wheelEvent( QWheelEvent* event )
462 const auto offset = scrollOffset( event );
463 if ( !offset.isNull() )
464 setScrollPos( m_data->scrollPos - offset );
469QPointF QskScrollBox::boundedScrollPos(
const QPointF& pos )
const
471 const QRectF vr = viewContentsRect();
473 const qreal maxX = qMax( 0.0, scrollableSize().width() - vr.width() );
474 const qreal maxY = qMax( 0.0, scrollableSize().height() - vr.height() );
476 return QPointF( qBound( 0.0, pos.x(), maxX ), qBound( 0.0, pos.y(), maxY ) );
479void QskScrollBox::connectWindow(
const QQuickWindow* window,
bool on )
481 if ( ( window ==
nullptr ) || ( on && !autoScrollFocusItem() ) )
486 QObject::connect( window, &QQuickWindow::activeFocusItemChanged,
487 this, &QskScrollBox::onFocusItemChanged, Qt::UniqueConnection );
491 QObject::disconnect( window, &QQuickWindow::activeFocusItemChanged,
492 this, &QskScrollBox::onFocusItemChanged );
496#include "moc_QskScrollBox.cpp"
virtual void geometryChangeEvent(QskGeometryChangeEvent *)
bool isInitiallyPainted() const
virtual void windowChangeEvent(QskWindowChangeEvent *)