some auto scrolling modes added

This commit is contained in:
Uwe Rathmann 2018-01-16 12:13:38 +01:00
parent b1c3acde8e
commit 4f6bf75426
8 changed files with 269 additions and 6 deletions

View File

@ -594,6 +594,9 @@ void QskMaterialSkin::initScrollViewHints()
setGradient( subControl, pal.accentColor );
setBoxBorderColors( subControl, pal.accentColor );
}
// when changing the position by QskScrollView::scrollTo
setAnimation( Q::Viewport | Metric, QskAnimationHint( 200, QEasingCurve::InCubic ) );
}
void QskMaterialSkin::initListViewHints()

View File

@ -634,6 +634,9 @@ void QskSquiekSkin::initScrollViewHints()
setAnimation( subControl | Color, qskDuration );
}
// when changing the position by QskScrollView::scrollTo
setAnimation( Q::Viewport | Metric, QskAnimationHint( 200, QEasingCurve::OutCubic ) );
}
void QskSquiekSkin::initListViewHints()

View File

@ -223,10 +223,12 @@ void QskListView::keyPressEvent( QKeyEvent* event )
if ( row != r )
{
auto pos = scrollPos();
const qreal rowPos = row * rowHeight();
if ( rowPos < scrollPos().y() )
{
setScrollPos( QPointF( scrollPos().x(), rowPos ) );
pos.setY( rowPos );
}
else
{
@ -236,9 +238,17 @@ void QskListView::keyPressEvent( QKeyEvent* event )
if ( rowPos + rowHeight() > scrolledBottom )
{
const double y = rowPos + rowHeight() - vr.height();
setScrollPos( QPointF( scrollPos().x(), y ) );
pos.setY( y );
}
}
if ( pos != scrollPos() )
{
if ( event->isAutoRepeat() )
setScrollPos( pos );
else
scrollTo( pos );
}
}
}

View File

@ -215,7 +215,7 @@ QskPopup::QskPopup( QQuickItem* parent ):
// we don't want to be resized by layout code
setTransparentForPositioner( true );
setFlag( ItemIsFocusScope );
setFlag( ItemIsFocusScope, true );
setTabFence( true );
setFocusPolicy( Qt::ClickFocus );
}

View File

@ -7,13 +7,28 @@
#include "QskScrollViewSkinlet.h"
#include "QskLayoutConstraint.h"
#include "QskBoxClipNode.h"
#include "QskEvent.h"
QSK_QT_PRIVATE_BEGIN
#include <private/qquickitem_p.h>
#include <private/qquickwindow_p.h>
#include <private/qquickclipnode_p.h>
#include <private/qquickitemchangelistener_p.h>
QSK_QT_PRIVATE_END
static inline bool qskIsAncestorOf( const QQuickItem* item, const QQuickItem* child )
{
while ( child )
{
if ( child == item )
return true;
child = child->parentItem();
}
return false;
}
static QSizeF qskAdjustedSize( const QQuickItem* item, const QSizeF& targetSize )
{
using namespace QskLayoutConstraint;
@ -171,6 +186,7 @@ public:
protected:
virtual bool event( QEvent* event ) override final;
virtual void windowChangeEvent( QskWindowChangeEvent* ) override final;
virtual void itemChange( ItemChange, const ItemChangeData& ) override final;
virtual void geometryChanged( const QRectF&, const QRectF& ) override final;
@ -195,6 +211,9 @@ protected:
virtual void updateNode( QSGNode* ) override final;
private:
void connectWindow( const QQuickWindow*, bool on );
void onFocusItemChanged();
inline QskScrollArea* scrollArea()
{
return static_cast< QskScrollArea* >( parentItem() );
@ -213,6 +232,8 @@ QskScrollAreaClipItem::QskScrollAreaClipItem( QskScrollArea* scrollArea ):
{
setObjectName( QStringLiteral( "QskScrollAreaClipItem" ) );
setClip( true );
connectWindow( window(), true );
}
QskScrollAreaClipItem::~QskScrollAreaClipItem()
@ -220,6 +241,23 @@ QskScrollAreaClipItem::~QskScrollAreaClipItem()
enableGeometryListener( false );
}
void QskScrollAreaClipItem::connectWindow( const QQuickWindow* window, bool on )
{
if ( window == nullptr )
return;
if ( on )
{
connect( window, &QQuickWindow::activeFocusItemChanged,
this, &QskScrollAreaClipItem::onFocusItemChanged );
}
else
{
disconnect( window, &QQuickWindow::activeFocusItemChanged,
this, &QskScrollAreaClipItem::onFocusItemChanged );
}
}
void QskScrollAreaClipItem::updateNode( QSGNode* )
{
auto* d = QQuickItemPrivate::get( this );
@ -337,6 +375,20 @@ void QskScrollAreaClipItem::enableGeometryListener( bool on )
}
}
void QskScrollAreaClipItem::onFocusItemChanged()
{
if ( window() == nullptr || !scrollArea()->autoScrollFocusItem() )
return;
const auto focusItem = window()->activeFocusItem();
if ( focusItem )
{
auto reason = QQuickWindowPrivate::get( window() )->lastFocusReason;
if ( reason == Qt::TabFocusReason || reason == Qt::BacktabFocusReason )
scrollArea()->ensureItemVisible( focusItem );
}
}
bool QskScrollAreaClipItem::event( QEvent* event )
{
if( event->type() == QEvent::LayoutRequest )
@ -348,11 +400,20 @@ bool QskScrollAreaClipItem::event( QEvent* event )
return Inherited::event( event );
}
void QskScrollAreaClipItem::windowChangeEvent( QskWindowChangeEvent* event )
{
Inherited::windowChangeEvent( event );
connectWindow( event->oldWindow(), false );
connectWindow( event->window(), true );
}
class QskScrollArea::PrivateData
{
public:
PrivateData():
isItemResizable( true )
isItemResizable( true ),
autoScrollFocusItem( true )
{
}
@ -371,7 +432,9 @@ public:
}
QskScrollAreaClipItem* clipItem;
bool isItemResizable : 1;
bool autoScrollFocusItem : 1;
};
/*
@ -424,6 +487,16 @@ QskScrollArea::~QskScrollArea()
delete m_data->clipItem;
}
void QskScrollArea::ensureItemVisible( const QQuickItem* item )
{
const QQuickItem* scrolledItem = this->scrolledItem();
if ( scrolledItem && qskIsAncestorOf( scrolledItem, item ) )
{
const auto pos = scrolledItem->mapFromItem( item, QPointF() );
ensureVisible( QRectF( pos.x(), pos.y(), item->width(), item->height() ) );
}
}
void QskScrollArea::updateLayout()
{
Inherited::updateLayout();
@ -469,6 +542,20 @@ void QskScrollArea::adjustItem()
}
}
void QskScrollArea::setAutoScrollFocusedItem( bool on )
{
if ( m_data->autoScrollFocusItem != on )
{
m_data->autoScrollFocusItem = on;
Q_EMIT autoScrollFocusedItemChanged();
}
}
bool QskScrollArea::autoScrollFocusItem() const
{
return m_data->autoScrollFocusItem;
}
void QskScrollArea::setItemResizable( bool on )
{
if ( on != m_data->isItemResizable )

View File

@ -18,6 +18,9 @@ class QSK_EXPORT QskScrollArea : public QskScrollView
Q_PROPERTY( bool itemResizable READ isItemResizable
WRITE setItemResizable NOTIFY itemResizableChanged FINAL )
Q_PROPERTY( bool autoScrollFocusedItem READ autoScrollFocusItem
WRITE setAutoScrollFocusedItem NOTIFY autoScrollFocusedItemChanged FINAL )
using Inherited = QskScrollView;
public:
@ -30,9 +33,15 @@ public:
void setItemResizable( bool on );
bool isItemResizable() const;
void setAutoScrollFocusedItem( bool on );
bool autoScrollFocusItem() const;
void ensureItemVisible( const QQuickItem * );
Q_SIGNALS:
void itemResizableChanged();
void scrolledItemChanged();
void autoScrollFocusedItemChanged();
protected:
virtual void updateLayout() override;

View File

@ -7,6 +7,7 @@
#include "QskPanGestureRecognizer.h"
#include "QskFlickAnimator.h"
#include "QskBoxBorderMetrics.h"
#include "QskAnimationHint.h"
#include "QskGesture.h"
#include "QskAspect.h"
#include "QskEvent.h"
@ -25,11 +26,12 @@ QSK_STATE( QskScrollView, HorizontalHandlePressed, QskAspect::FirstSystemState <
namespace
{
class FlickAnimator : public QskFlickAnimator
class FlickAnimator final : public QskFlickAnimator
{
public:
FlickAnimator()
{
// skin hints: TODO
setDuration( 1000 );
setEasingCurve( QEasingCurve::OutCubic );
}
@ -48,6 +50,61 @@ namespace
private:
QskScrollView* m_scrollView;
};
class ScrollAnimator final : public QskAnimator
{
public:
ScrollAnimator():
m_scrollView( nullptr )
{
}
void setScrollView( QskScrollView* scrollView )
{
m_scrollView = scrollView;
}
void scroll( const QPointF& from, const QPointF& to )
{
if ( isRunning() )
{
m_to = to;
return;
}
if ( from == to || m_scrollView == nullptr )
{
return;
}
m_from = from;
m_to = to;
const auto hint = m_scrollView->effectiveAnimation(
QskAspect::Metric, QskScrollView::Viewport );
setDuration( hint.duration );
setEasingCurve( hint.type );
setWindow( m_scrollView->window() );
start();
}
protected:
virtual void advance( qreal value ) override final
{
qreal x = m_from.x() + ( m_to.x() - m_from.x() ) * value;
qreal y = m_from.y() + ( m_to.y() - m_from.y() ) * value;
m_scrollView->setScrollPos( QPointF( x, y ) );
}
private:
QskScrollView* m_scrollView;
QPointF m_from;
QPointF m_to;
};
}
class QskScrollView::PrivateData
@ -58,6 +115,7 @@ public:
verticalScrollBarPolicy( Qt::ScrollBarAsNeeded ),
scrollableSize( 0.0, 0.0 ),
panRecognizerTimeout( 100 ), // value coming from the platform ???
viewportPadding( 10 ),
isScrolling( 0 )
{
}
@ -72,6 +130,9 @@ public:
int panRecognizerTimeout;
FlickAnimator flicker;
ScrollAnimator scroller;
qreal viewportPadding;
qreal scrollPressPos;
int isScrolling;
@ -82,6 +143,7 @@ QskScrollView::QskScrollView( QQuickItem* parent ):
m_data( new PrivateData() )
{
m_data->flicker.setScrollView( this );
m_data->scroller.setScrollView( this );
m_data->panRecognizer.setWatchedItem( this );
m_data->panRecognizer.setOrientations( Qt::Horizontal | Qt::Vertical );
@ -174,6 +236,11 @@ QPointF QskScrollView::scrollPos() const
return m_data->scrollPos;
}
void QskScrollView::scrollTo( const QPointF& pos )
{
m_data->scroller.scroll( scrollPos(), pos );
}
bool QskScrollView::isScrolling( Qt::Orientation orientation ) const
{
return m_data->isScrolling == orientation;
@ -198,6 +265,86 @@ QSizeF QskScrollView::scrollableSize() const
return m_data->scrollableSize;
}
void QskScrollView::ensureVisible( const QPointF& pos )
{
const qreal margin = m_data->viewportPadding;
QRectF r( scrollPos(), viewContentsRect().size() );
r.adjust( margin, margin, -margin, -margin );
qreal x = r.x();
qreal y = r.y();
if ( pos.x() < r.left() )
{
x = pos.x();
}
else if ( pos.x() > r.right() )
{
x = pos.x() - r.width();
}
if ( pos.y() < r.top() )
{
y = pos.y();
}
else if ( y > r.right() )
{
y = pos.y() - r.height();
}
const QPoint newPos( x - margin, y - margin );
if ( isInitiallyPainted() )
scrollTo( newPos );
else
setScrollPos( newPos );
}
void QskScrollView::ensureVisible( const QRectF& itemRect )
{
const qreal margin = m_data->viewportPadding;
QRectF r( scrollPos(), viewContentsRect().size() );
r.adjust( margin, margin, -margin, -margin );
qreal x = r.x();
qreal y = r.y();
if ( itemRect.width() > r.width() )
{
x = itemRect.left() + 0.5 * ( itemRect.width() - r.width() );
}
else if ( itemRect.right() > r.right() )
{
x = itemRect.right() - r.width();
}
else if ( itemRect.left() < r.left() )
{
x = itemRect.left();
}
if ( itemRect.height() > r.height() )
{
y = itemRect.top() + 0.5 * ( itemRect.height() - r.height() );
}
else if ( itemRect.bottom() > r.bottom() )
{
y = itemRect.bottom() - r.height();
}
else if ( itemRect.top() < r.top() )
{
y = itemRect.top();
}
const QPoint newPos( x - margin, y - margin );
if ( isInitiallyPainted() )
scrollTo( newPos );
else
setScrollPos( newPos );
}
QRectF QskScrollView::viewContentsRect() const
{
// This code should be done in the skinlet. TODO ...

View File

@ -67,6 +67,10 @@ Q_SIGNALS:
public Q_SLOTS:
void setScrollPos( const QPointF& );
void scrollTo( const QPointF& );
void ensureVisible( const QPointF& );
void ensureVisible( const QRectF& );
protected:
virtual void mouseMoveEvent( QMouseEvent* ) override;