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 ); setGradient( subControl, pal.accentColor );
setBoxBorderColors( 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() void QskMaterialSkin::initListViewHints()

View File

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

View File

@ -223,10 +223,12 @@ void QskListView::keyPressEvent( QKeyEvent* event )
if ( row != r ) if ( row != r )
{ {
auto pos = scrollPos();
const qreal rowPos = row * rowHeight(); const qreal rowPos = row * rowHeight();
if ( rowPos < scrollPos().y() ) if ( rowPos < scrollPos().y() )
{ {
setScrollPos( QPointF( scrollPos().x(), rowPos ) ); pos.setY( rowPos );
} }
else else
{ {
@ -236,9 +238,17 @@ void QskListView::keyPressEvent( QKeyEvent* event )
if ( rowPos + rowHeight() > scrolledBottom ) if ( rowPos + rowHeight() > scrolledBottom )
{ {
const double y = rowPos + rowHeight() - vr.height(); 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 // we don't want to be resized by layout code
setTransparentForPositioner( true ); setTransparentForPositioner( true );
setFlag( ItemIsFocusScope ); setFlag( ItemIsFocusScope, true );
setTabFence( true ); setTabFence( true );
setFocusPolicy( Qt::ClickFocus ); setFocusPolicy( Qt::ClickFocus );
} }

View File

@ -7,13 +7,28 @@
#include "QskScrollViewSkinlet.h" #include "QskScrollViewSkinlet.h"
#include "QskLayoutConstraint.h" #include "QskLayoutConstraint.h"
#include "QskBoxClipNode.h" #include "QskBoxClipNode.h"
#include "QskEvent.h"
QSK_QT_PRIVATE_BEGIN QSK_QT_PRIVATE_BEGIN
#include <private/qquickitem_p.h> #include <private/qquickitem_p.h>
#include <private/qquickwindow_p.h>
#include <private/qquickclipnode_p.h> #include <private/qquickclipnode_p.h>
#include <private/qquickitemchangelistener_p.h> #include <private/qquickitemchangelistener_p.h>
QSK_QT_PRIVATE_END 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 ) static QSizeF qskAdjustedSize( const QQuickItem* item, const QSizeF& targetSize )
{ {
using namespace QskLayoutConstraint; using namespace QskLayoutConstraint;
@ -171,6 +186,7 @@ public:
protected: protected:
virtual bool event( QEvent* event ) override final; virtual bool event( QEvent* event ) override final;
virtual void windowChangeEvent( QskWindowChangeEvent* ) override final;
virtual void itemChange( ItemChange, const ItemChangeData& ) override final; virtual void itemChange( ItemChange, const ItemChangeData& ) override final;
virtual void geometryChanged( const QRectF&, const QRectF& ) override final; virtual void geometryChanged( const QRectF&, const QRectF& ) override final;
@ -195,6 +211,9 @@ protected:
virtual void updateNode( QSGNode* ) override final; virtual void updateNode( QSGNode* ) override final;
private: private:
void connectWindow( const QQuickWindow*, bool on );
void onFocusItemChanged();
inline QskScrollArea* scrollArea() inline QskScrollArea* scrollArea()
{ {
return static_cast< QskScrollArea* >( parentItem() ); return static_cast< QskScrollArea* >( parentItem() );
@ -213,6 +232,8 @@ QskScrollAreaClipItem::QskScrollAreaClipItem( QskScrollArea* scrollArea ):
{ {
setObjectName( QStringLiteral( "QskScrollAreaClipItem" ) ); setObjectName( QStringLiteral( "QskScrollAreaClipItem" ) );
setClip( true ); setClip( true );
connectWindow( window(), true );
} }
QskScrollAreaClipItem::~QskScrollAreaClipItem() QskScrollAreaClipItem::~QskScrollAreaClipItem()
@ -220,6 +241,23 @@ QskScrollAreaClipItem::~QskScrollAreaClipItem()
enableGeometryListener( false ); 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* ) void QskScrollAreaClipItem::updateNode( QSGNode* )
{ {
auto* d = QQuickItemPrivate::get( this ); 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 ) bool QskScrollAreaClipItem::event( QEvent* event )
{ {
if( event->type() == QEvent::LayoutRequest ) if( event->type() == QEvent::LayoutRequest )
@ -348,11 +400,20 @@ bool QskScrollAreaClipItem::event( QEvent* event )
return Inherited::event( event ); return Inherited::event( event );
} }
void QskScrollAreaClipItem::windowChangeEvent( QskWindowChangeEvent* event )
{
Inherited::windowChangeEvent( event );
connectWindow( event->oldWindow(), false );
connectWindow( event->window(), true );
}
class QskScrollArea::PrivateData class QskScrollArea::PrivateData
{ {
public: public:
PrivateData(): PrivateData():
isItemResizable( true ) isItemResizable( true ),
autoScrollFocusItem( true )
{ {
} }
@ -371,7 +432,9 @@ public:
} }
QskScrollAreaClipItem* clipItem; QskScrollAreaClipItem* clipItem;
bool isItemResizable : 1; bool isItemResizable : 1;
bool autoScrollFocusItem : 1;
}; };
/* /*
@ -424,6 +487,16 @@ QskScrollArea::~QskScrollArea()
delete m_data->clipItem; 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() void QskScrollArea::updateLayout()
{ {
Inherited::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 ) void QskScrollArea::setItemResizable( bool on )
{ {
if ( on != m_data->isItemResizable ) if ( on != m_data->isItemResizable )

View File

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

View File

@ -7,6 +7,7 @@
#include "QskPanGestureRecognizer.h" #include "QskPanGestureRecognizer.h"
#include "QskFlickAnimator.h" #include "QskFlickAnimator.h"
#include "QskBoxBorderMetrics.h" #include "QskBoxBorderMetrics.h"
#include "QskAnimationHint.h"
#include "QskGesture.h" #include "QskGesture.h"
#include "QskAspect.h" #include "QskAspect.h"
#include "QskEvent.h" #include "QskEvent.h"
@ -25,11 +26,12 @@ QSK_STATE( QskScrollView, HorizontalHandlePressed, QskAspect::FirstSystemState <
namespace namespace
{ {
class FlickAnimator : public QskFlickAnimator class FlickAnimator final : public QskFlickAnimator
{ {
public: public:
FlickAnimator() FlickAnimator()
{ {
// skin hints: TODO
setDuration( 1000 ); setDuration( 1000 );
setEasingCurve( QEasingCurve::OutCubic ); setEasingCurve( QEasingCurve::OutCubic );
} }
@ -48,6 +50,61 @@ namespace
private: private:
QskScrollView* m_scrollView; 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 class QskScrollView::PrivateData
@ -58,6 +115,7 @@ public:
verticalScrollBarPolicy( Qt::ScrollBarAsNeeded ), verticalScrollBarPolicy( Qt::ScrollBarAsNeeded ),
scrollableSize( 0.0, 0.0 ), scrollableSize( 0.0, 0.0 ),
panRecognizerTimeout( 100 ), // value coming from the platform ??? panRecognizerTimeout( 100 ), // value coming from the platform ???
viewportPadding( 10 ),
isScrolling( 0 ) isScrolling( 0 )
{ {
} }
@ -72,6 +130,9 @@ public:
int panRecognizerTimeout; int panRecognizerTimeout;
FlickAnimator flicker; FlickAnimator flicker;
ScrollAnimator scroller;
qreal viewportPadding;
qreal scrollPressPos; qreal scrollPressPos;
int isScrolling; int isScrolling;
@ -82,6 +143,7 @@ QskScrollView::QskScrollView( QQuickItem* parent ):
m_data( new PrivateData() ) m_data( new PrivateData() )
{ {
m_data->flicker.setScrollView( this ); m_data->flicker.setScrollView( this );
m_data->scroller.setScrollView( this );
m_data->panRecognizer.setWatchedItem( this ); m_data->panRecognizer.setWatchedItem( this );
m_data->panRecognizer.setOrientations( Qt::Horizontal | Qt::Vertical ); m_data->panRecognizer.setOrientations( Qt::Horizontal | Qt::Vertical );
@ -174,6 +236,11 @@ QPointF QskScrollView::scrollPos() const
return m_data->scrollPos; return m_data->scrollPos;
} }
void QskScrollView::scrollTo( const QPointF& pos )
{
m_data->scroller.scroll( scrollPos(), pos );
}
bool QskScrollView::isScrolling( Qt::Orientation orientation ) const bool QskScrollView::isScrolling( Qt::Orientation orientation ) const
{ {
return m_data->isScrolling == orientation; return m_data->isScrolling == orientation;
@ -198,6 +265,86 @@ QSizeF QskScrollView::scrollableSize() const
return m_data->scrollableSize; 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 QRectF QskScrollView::viewContentsRect() const
{ {
// This code should be done in the skinlet. TODO ... // This code should be done in the skinlet. TODO ...

View File

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