flickable tabbars

This commit is contained in:
Uwe Rathmann 2020-03-13 07:39:31 +01:00
parent 3c7308e23f
commit 53e924a999
14 changed files with 333 additions and 88 deletions

View File

@ -39,7 +39,7 @@ class TabView : public QskTabView
TabView( QQuickItem* parent = nullptr ) TabView( QQuickItem* parent = nullptr )
: QskTabView( parent ) : QskTabView( parent )
{ {
for ( int i = 0; i < 6; i++ ) for ( int i = 0; i < 10; i++ )
{ {
QString text; QString text;
if ( i == 4 ) if ( i == 4 )

View File

@ -569,6 +569,9 @@ void QskMaterialSkin::initTabBarHints()
setBoxShape( Q::Panel, 0 ); setBoxShape( Q::Panel, 0 );
setBoxBorderMetrics( Q::Panel, 0 ); setBoxBorderMetrics( Q::Panel, 0 );
setGradient( Q::Panel, QskGradient() ); setGradient( Q::Panel, QskGradient() );
// when flicking
setAnimation( Q::Panel | Metric, QskAnimationHint( 200, QEasingCurve::InCubic ) );
} }
void QskMaterialSkin::initTabViewHints() void QskMaterialSkin::initTabViewHints()

View File

@ -641,6 +641,9 @@ void QskSquiekSkin::initTabBarHints()
setMargins( Q::Panel | Padding, 0 ); setMargins( Q::Panel | Padding, 0 );
setMargins( Q::Panel | Margin, 0 ); setMargins( Q::Panel | Margin, 0 );
setPanel( Q::Panel, NoPanel ); setPanel( Q::Panel, NoPanel );
// when flicking
setAnimation( Q::Panel | Metric, QskAnimationHint( 200, QEasingCurve::OutCubic ) );
} }
void QskSquiekSkin::initTabViewHints() void QskSquiekSkin::initTabViewHints()

View File

@ -959,6 +959,15 @@ QRectF QskControl::focusIndicatorRect() const
return contentsRect(); return contentsRect();
} }
bool QskControl::hasFocusIndicatorClip() const
{
/*
Often we want to clip the focus indicator,
when the control is clipped.
*/
return clip();
}
void QskControl::updateLayout() void QskControl::updateLayout()
{ {
} }

View File

@ -91,7 +91,9 @@ class QSK_EXPORT QskControl : public QskQuickItem, public QskSkinnable
virtual QRectF layoutRectForSize( const QSizeF& ) const; virtual QRectF layoutRectForSize( const QSizeF& ) const;
virtual QRectF gestureRect() const; virtual QRectF gestureRect() const;
virtual QRectF focusIndicatorRect() const; virtual QRectF focusIndicatorRect() const;
virtual bool hasFocusIndicatorClip() const;
QRectF subControlRect( QskAspect::Subcontrol ) const; QRectF subControlRect( QskAspect::Subcontrol ) const;
QRectF subControlRect( const QSizeF&, QskAspect::Subcontrol ) const; QRectF subControlRect( const QSizeF&, QskAspect::Subcontrol ) const;

View File

@ -103,17 +103,26 @@ void QskFocusIndicator::onFocusItemChanged()
{ {
auto itemParent = item->parentItem(); auto itemParent = item->parentItem();
if ( itemParent == window()->contentItem() || itemParent->clip() ) bool doReparent = ( itemParent == window()->contentItem() );
if ( !doReparent )
{ {
/* /*
When the focus item is clipped - maybe because of being at the When the focus item is clipped - maybe because of being at the
border of a scrollarea - the focus indicator needs to be border of a scrollarea - the focus indicator might need to be
clipped as well. The easiest way to have this is to put us clipped as well. The easiest way to have this is to put us
below the item having the clip. below the item having the clip.
*/ */
setParentItem( itemParent );
if ( auto control = qskControlCast( itemParent ) )
doReparent = control->hasFocusIndicatorClip();
else
doReparent = itemParent->clip();
} }
if ( doReparent )
setParentItem( itemParent );
if ( itemParent == parentItem() ) if ( itemParent == parentItem() )
{ {
// We want to be on top, but do we cover all corner cases ??? // We want to be on top, but do we cover all corner cases ???

View File

@ -124,9 +124,13 @@ class QskScrollAreaClipItem final : public QskControl, public QQuickItemChangeLi
return scrollArea()->subControlRect( QskScrollView::Viewport ); return scrollArea()->subControlRect( QskScrollView::Viewport );
} }
bool hasFocusIndicatorClip() const override
{
return scrollArea()->hasFocusIndicatorClip();
}
protected: protected:
bool event( QEvent* event ) override; bool event( QEvent* event ) override;
void windowChangeEvent( QskWindowChangeEvent* ) override;
void itemChange( ItemChange, const ItemChangeData& ) override; void itemChange( ItemChange, const ItemChangeData& ) override;
void geometryChanged( const QRectF&, const QRectF& ) override; void geometryChanged( const QRectF&, const QRectF& ) override;
@ -151,9 +155,6 @@ class QskScrollAreaClipItem final : public QskControl, public QQuickItemChangeLi
void updateNode( QSGNode* ) override; void updateNode( QSGNode* ) override;
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() );
@ -172,8 +173,6 @@ QskScrollAreaClipItem::QskScrollAreaClipItem( QskScrollArea* scrollArea )
{ {
setObjectName( QStringLiteral( "QskScrollAreaClipItem" ) ); setObjectName( QStringLiteral( "QskScrollAreaClipItem" ) );
setClip( true ); setClip( true );
connectWindow( window(), true );
} }
QskScrollAreaClipItem::~QskScrollAreaClipItem() QskScrollAreaClipItem::~QskScrollAreaClipItem()
@ -181,23 +180,6 @@ 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 );
@ -310,20 +292,6 @@ 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 )
@ -335,20 +303,11 @@ 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 )
{ {
} }
@ -369,7 +328,6 @@ class QskScrollArea::PrivateData
QskScrollAreaClipItem* clipItem = nullptr; QskScrollAreaClipItem* clipItem = nullptr;
bool isItemResizable : 1; bool isItemResizable : 1;
bool autoScrollFocusItem : 1;
}; };
/* /*
@ -401,16 +359,6 @@ 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();
@ -457,18 +405,9 @@ void QskScrollArea::adjustItem()
} }
} }
void QskScrollArea::setAutoScrollFocusedItem( bool on ) bool QskScrollArea::hasFocusIndicatorClip() const
{ {
if ( m_data->autoScrollFocusItem != on ) return true;
{
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 )
@ -476,7 +415,7 @@ void QskScrollArea::setItemResizable( bool on )
if ( on != m_data->isItemResizable ) if ( on != m_data->isItemResizable )
{ {
m_data->isItemResizable = on; m_data->isItemResizable = on;
Q_EMIT itemResizableChanged(); Q_EMIT itemResizableChanged( on );
if ( m_data->isItemResizable ) if ( m_data->isItemResizable )
polish(); polish();
@ -520,8 +459,7 @@ QQuickItem* QskScrollArea::scrolledItem() const
void QskScrollArea::translateItem() void QskScrollArea::translateItem()
{ {
auto item = m_data->clipItem->scrolledItem(); if ( auto item = m_data->clipItem->scrolledItem() )
if ( item )
{ {
const QPointF pos = viewContentsRect().topLeft() - scrollPos(); const QPointF pos = viewContentsRect().topLeft() - scrollPos();
item->setPosition( pos ); item->setPosition( pos );

View File

@ -18,9 +18,6 @@ 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:
@ -33,15 +30,11 @@ class QSK_EXPORT QskScrollArea : public QskScrollView
void setItemResizable( bool on ); void setItemResizable( bool on );
bool isItemResizable() const; bool isItemResizable() const;
void setAutoScrollFocusedItem( bool on ); bool hasFocusIndicatorClip() const override;
bool autoScrollFocusItem() const;
void ensureItemVisible( const QQuickItem* );
Q_SIGNALS: Q_SIGNALS:
void itemResizableChanged();
void scrolledItemChanged(); void scrolledItemChanged();
void autoScrollFocusedItemChanged(); void itemResizableChanged( bool );
protected: protected:
void updateLayout() override; void updateLayout() override;

View File

@ -9,6 +9,11 @@
#include "QskFlickAnimator.h" #include "QskFlickAnimator.h"
#include "QskGesture.h" #include "QskGesture.h"
#include "QskPanGestureRecognizer.h" #include "QskPanGestureRecognizer.h"
#include "QskQuick.h"
QSK_QT_PRIVATE_BEGIN
#include <private/qquickwindow_p.h>
QSK_QT_PRIVATE_END
namespace namespace
{ {
@ -95,6 +100,11 @@ namespace
class QskScrollBox::PrivateData class QskScrollBox::PrivateData
{ {
public: public:
PrivateData()
: autoScrollFocusItem( true )
{
}
QPointF scrollPos; QPointF scrollPos;
QSizeF scrollableSize = QSize( 0.0, 0.0 ); QSizeF scrollableSize = QSize( 0.0, 0.0 );
@ -105,6 +115,8 @@ class QskScrollBox::PrivateData
ScrollAnimator scroller; ScrollAnimator scroller;
const qreal viewportPadding = 10; const qreal viewportPadding = 10;
bool autoScrollFocusItem : 1;
}; };
QskScrollBox::QskScrollBox( QQuickItem* parent ) QskScrollBox::QskScrollBox( QQuickItem* parent )
@ -119,14 +131,52 @@ QskScrollBox::QskScrollBox( QQuickItem* parent )
setFiltersChildMouseEvents( true ); setFiltersChildMouseEvents( true );
setAcceptedMouseButtons( Qt::LeftButton );
setWheelEnabled( true ); setWheelEnabled( true );
setFocusPolicy( Qt::StrongFocus ); setFocusPolicy( Qt::StrongFocus );
connectWindow( window(), true );
} }
QskScrollBox::~QskScrollBox() QskScrollBox::~QskScrollBox()
{ {
} }
void QskScrollBox::setAutoScrollFocusedItem( bool on )
{
if ( m_data->autoScrollFocusItem != on )
{
m_data->autoScrollFocusItem = on;
connectWindow( window(), true );
Q_EMIT autoScrollFocusedItemChanged( on );
}
}
bool QskScrollBox::autoScrollFocusItem() const
{
return m_data->autoScrollFocusItem;
}
void QskScrollBox::onFocusItemChanged()
{
if ( window() )
{
auto reason = QQuickWindowPrivate::get( window() )->lastFocusReason;
if ( reason == Qt::TabFocusReason || reason == Qt::BacktabFocusReason )
ensureFocusItemVisible();
}
}
void QskScrollBox::ensureFocusItemVisible()
{
if ( window() == nullptr )
return;
if ( const auto focusItem = window()->activeFocusItem() )
ensureItemVisible( focusItem );
}
void QskScrollBox::setFlickRecognizerTimeout( int timeout ) void QskScrollBox::setFlickRecognizerTimeout( int timeout )
{ {
if ( timeout < 0 ) if ( timeout < 0 )
@ -202,6 +252,17 @@ QRectF QskScrollBox::gestureRect() const
return viewContentsRect(); return viewContentsRect();
} }
void QskScrollBox::ensureItemVisible( const QQuickItem* item )
{
if ( qskIsAncestorOf( this, item ) )
{
auto pos = scrollPos() - viewContentsRect().topLeft();
pos += mapFromItem( item, QPointF() );
ensureVisible( QRectF( pos.x(), pos.y(), item->width(), item->height() ) );
}
}
void QskScrollBox::ensureVisible( const QPointF& pos ) void QskScrollBox::ensureVisible( const QPointF& pos )
{ {
const qreal margin = m_data->viewportPadding; const qreal margin = m_data->viewportPadding;
@ -282,6 +343,14 @@ void QskScrollBox::ensureVisible( const QRectF& itemRect )
setScrollPos( newPos ); setScrollPos( newPos );
} }
void QskScrollBox::windowChangeEvent( QskWindowChangeEvent* event )
{
Inherited::windowChangeEvent( event );
connectWindow( event->oldWindow(), false );
connectWindow( event->window(), true );
}
void QskScrollBox::geometryChangeEvent( QskGeometryChangeEvent* event ) void QskScrollBox::geometryChangeEvent( QskGeometryChangeEvent* event )
{ {
if ( event->isResized() ) if ( event->isResized() )
@ -443,4 +512,21 @@ QPointF QskScrollBox::boundedScrollPos( const QPointF& pos ) const
return QPointF( qBound( 0.0, pos.x(), maxX ), qBound( 0.0, pos.y(), maxY ) ); return QPointF( qBound( 0.0, pos.x(), maxX ), qBound( 0.0, pos.y(), maxY ) );
} }
void QskScrollBox::connectWindow( const QQuickWindow* window, bool on )
{
if ( ( window == nullptr ) || ( on && !autoScrollFocusItem() ) )
return;
if ( on )
{
QObject::connect( window, &QQuickWindow::activeFocusItemChanged,
this, &QskScrollBox::onFocusItemChanged, Qt::UniqueConnection );
}
else
{
QObject::disconnect( window, &QQuickWindow::activeFocusItemChanged,
this, &QskScrollBox::onFocusItemChanged );
}
}
#include "moc_QskScrollBox.cpp" #include "moc_QskScrollBox.cpp"

View File

@ -18,12 +18,18 @@ class QSK_EXPORT QskScrollBox : public QskControl
Q_PROPERTY( Qt::Orientations flickableOrientations READ flickableOrientations Q_PROPERTY( Qt::Orientations flickableOrientations READ flickableOrientations
WRITE setFlickableOrientations NOTIFY flickableOrientationsChanged FINAL ) WRITE setFlickableOrientations NOTIFY flickableOrientationsChanged FINAL )
Q_PROPERTY( bool autoScrollFocusedItem READ autoScrollFocusItem
WRITE setAutoScrollFocusedItem NOTIFY autoScrollFocusedItemChanged FINAL )
using Inherited = QskControl; using Inherited = QskControl;
public: public:
QskScrollBox( QQuickItem* parent = nullptr ); QskScrollBox( QQuickItem* parent = nullptr );
~QskScrollBox() override; ~QskScrollBox() override;
void setAutoScrollFocusedItem( bool on );
bool autoScrollFocusItem() const;
void setFlickableOrientations( Qt::Orientations ); void setFlickableOrientations( Qt::Orientations );
Qt::Orientations flickableOrientations() const; Qt::Orientations flickableOrientations() const;
@ -43,17 +49,21 @@ class QSK_EXPORT QskScrollBox : public QskControl
void scrollPosChanged(); void scrollPosChanged();
void scrollableSizeChanged( const QSizeF& ); void scrollableSizeChanged( const QSizeF& );
void autoScrollFocusedItemChanged( bool );
void flickableOrientationsChanged(); void flickableOrientationsChanged();
public Q_SLOTS: public Q_SLOTS:
void setScrollPos( const QPointF& ); void setScrollPos( const QPointF& );
void scrollTo( const QPointF& ); void scrollTo( const QPointF& );
void ensureItemVisible( const QQuickItem* );
void ensureFocusItemVisible();
void ensureVisible( const QPointF& ); void ensureVisible( const QPointF& );
void ensureVisible( const QRectF& ); void ensureVisible( const QRectF& );
protected: protected:
void geometryChangeEvent( QskGeometryChangeEvent* ) override; void geometryChangeEvent( QskGeometryChangeEvent* ) override;
void windowChangeEvent( QskWindowChangeEvent* ) override;
void gestureEvent( QskGestureEvent* ) override; void gestureEvent( QskGestureEvent* ) override;
#ifndef QT_NO_WHEELEVENT #ifndef QT_NO_WHEELEVENT
@ -66,6 +76,8 @@ class QSK_EXPORT QskScrollBox : public QskControl
private: private:
QPointF boundedScrollPos( const QPointF& ) const; QPointF boundedScrollPos( const QPointF& ) const;
void onFocusItemChanged();
void connectWindow( const QQuickWindow*, bool on );
class PrivateData; class PrivateData;
std::unique_ptr< PrivateData > m_data; std::unique_ptr< PrivateData > m_data;

View File

@ -38,7 +38,6 @@ QskScrollView::QskScrollView( QQuickItem* parent )
: Inherited( parent ) : Inherited( parent )
, m_data( new PrivateData() ) , m_data( new PrivateData() )
{ {
setAcceptedMouseButtons( Qt::LeftButton );
} }
QskScrollView::~QskScrollView() QskScrollView::~QskScrollView()

View File

@ -5,9 +5,12 @@
#include "QskTabBar.h" #include "QskTabBar.h"
#include "QskAspect.h" #include "QskAspect.h"
#include "QskScrollBox.h"
#include "QskLinearBox.h" #include "QskLinearBox.h"
#include "QskTabButton.h" #include "QskTabButton.h"
#include "QskTextOptions.h" #include "QskTextOptions.h"
#include "QskAnimationHint.h"
#include "QskQuick.h"
QSK_SUBCONTROL( QskTabBar, Panel ) QSK_SUBCONTROL( QskTabBar, Panel )
@ -21,8 +24,10 @@ static inline Qt::Orientation qskOrientation( int position )
namespace namespace
{ {
class ButtonBox : public QskLinearBox class ButtonBox final : public QskLinearBox
{ {
using Inherited = QskLinearBox;
public: public:
ButtonBox( Qt::Orientation orientation, QQuickItem* parent ) ButtonBox( Qt::Orientation orientation, QQuickItem* parent )
: QskLinearBox( orientation, parent ) : QskLinearBox( orientation, parent )
@ -48,6 +53,142 @@ namespace
} }
} }
}; };
class ScrollBox final : public QskScrollBox
{
using Inherited = QskScrollBox;
public:
ScrollBox( QQuickItem* parent )
: QskScrollBox( parent )
{
setPolishOnResize( true );
enableAutoTranslation( true );
setFocusPolicy( Qt::NoFocus );
}
bool hasFocusIndicatorClip() const override
{
return false;
}
QskAnimationHint flickHint() const override
{
if ( auto tabBar = qobject_cast< const QskTabBar* >( parentItem() ) )
{
return tabBar->effectiveAnimation( QskAspect::Metric,
QskTabBar::Panel, QskAspect::NoState );
}
return QskAnimationHint( 200, QEasingCurve::OutCubic );
}
QRectF viewContentsRect() const override
{
return layoutRect();
}
void setOrientation( Qt::Orientation orientation )
{
setFlickableOrientations( orientation );
if ( orientation == Qt::Horizontal )
setSizePolicy( QskSizePolicy::Ignored, QskSizePolicy::MinimumExpanding );
else
setSizePolicy( QskSizePolicy::MinimumExpanding, QskSizePolicy::Ignored );
}
void ensureItemVisible( const QQuickItem* item )
{
if ( qskIsAncestorOf( this, item ) )
{
const auto pos = mapFromItem( item, QPointF() );
ensureVisible( QRectF( pos.x(), pos.y(), item->width(), item->height() ) );
}
}
protected:
bool event( QEvent* event ) override
{
if ( event->type() == QEvent::LayoutRequest )
{
resetImplicitSize();
polish();
}
return Inherited::event( event );
}
void updateLayout() override
{
auto scrolledItem = this->scrolledItem();
auto itemSize = viewContentsRect().size();
itemSize = qskConstrainedItemSize( scrolledItem, itemSize );
scrolledItem->setSize( itemSize );
enableAutoTranslation( false );
setScrollableSize( itemSize );
setScrollPos( scrollPos() );
enableAutoTranslation( true );
translateItem();
setClip( size().width() < itemSize.width()
|| size().height() < itemSize.height() );
}
QSizeF layoutSizeHint( Qt::SizeHint which, const QSizeF& constraint ) const override
{
auto hint = scrolledItem()->sizeConstraint( which, constraint );
if ( which == Qt::MinimumSize )
{
if ( sizePolicy().horizontalPolicy() & QskSizePolicy::ShrinkFlag )
hint.setWidth( -1 );
if ( sizePolicy().verticalPolicy() & QskSizePolicy::ShrinkFlag )
hint.setHeight( -1 );
}
return hint;
}
private:
inline QskControl* scrolledItem() const
{
return qskControlCast( childItems().first() );
}
void enableAutoTranslation( bool on )
{
if ( on )
{
connect( this, &QskScrollBox::scrollPosChanged,
this, &ScrollBox::translateItem );
}
else
{
disconnect( this, &QskScrollBox::scrollPosChanged,
this, &ScrollBox::translateItem );
}
}
void translateItem()
{
if ( auto item = this->scrolledItem() )
{
const QPointF pos = viewContentsRect().topLeft() - scrollPos();
item->setPosition( pos );
}
}
};
} }
class QskTabBar::PrivateData class QskTabBar::PrivateData
@ -72,6 +213,7 @@ class QskTabBar::PrivateData
} }
} }
ScrollBox* scrollBox = nullptr;
ButtonBox* buttonBox = nullptr; ButtonBox* buttonBox = nullptr;
int currentIndex = -1; int currentIndex = -1;
@ -97,9 +239,17 @@ QskTabBar::QskTabBar( Qsk::Position position, QQuickItem* parent )
else else
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Preferred ); initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Preferred );
m_data->buttonBox = new ButtonBox( orientation, this ); m_data->scrollBox = new ScrollBox( this );
m_data->scrollBox->setOrientation( orientation );
m_data->buttonBox = new ButtonBox( orientation, m_data->scrollBox );
m_data->buttonBox->setSpacing( metric( QskTabBar::Panel | QskAspect::Spacing ) ); m_data->buttonBox->setSpacing( metric( QskTabBar::Panel | QskAspect::Spacing ) );
#if 1
// We might want to have a mode, where the buttons are stretched: TODO ...
m_data->buttonBox->setSizePolicy( QskSizePolicy::Maximum, QskSizePolicy::Maximum );
#endif
connect( this, &QskTabBar::currentIndexChanged, connect( this, &QskTabBar::currentIndexChanged,
m_data->buttonBox, &ButtonBox::restack, Qt::QueuedConnection ); m_data->buttonBox, &ButtonBox::restack, Qt::QueuedConnection );
} }
@ -120,6 +270,7 @@ void QskTabBar::setPosition( Qsk::Position position )
{ {
setSizePolicy( sizePolicy( Qt::Vertical ), sizePolicy( Qt::Horizontal ) ); setSizePolicy( sizePolicy( Qt::Vertical ), sizePolicy( Qt::Horizontal ) );
m_data->buttonBox->setOrientation( orientation ); m_data->buttonBox->setOrientation( orientation );
m_data->scrollBox->setOrientation( orientation );
} }
resetImplicitSize(); resetImplicitSize();
@ -140,6 +291,20 @@ Qt::Orientation QskTabBar::orientation() const
return qskOrientation( m_data->position ); return qskOrientation( m_data->position );
} }
void QskTabBar::setAutoScrollFocusedButton( bool on )
{
if ( m_data->scrollBox->autoScrollFocusItem() != on )
{
m_data->scrollBox->setAutoScrollFocusedItem( on );
Q_EMIT autoScrollFocusedButtonChanged( on );
}
}
bool QskTabBar::autoScrollFocusButton() const
{
return m_data->scrollBox->autoScrollFocusItem();
}
void QskTabBar::setTextOptions( const QskTextOptions& options ) void QskTabBar::setTextOptions( const QskTextOptions& options )
{ {
if ( options != m_data->textOptions ) if ( options != m_data->textOptions )
@ -366,6 +531,11 @@ int QskTabBar::indexOf( QskTabButton* button ) const
return m_data->buttonBox->indexOf( button ); return m_data->buttonBox->indexOf( button );
} }
void QskTabBar::ensureButtonVisible( const QskTabButton* button )
{
m_data->scrollBox->ensureItemVisible( button );
}
void QskTabBar::componentComplete() void QskTabBar::componentComplete()
{ {
Inherited::componentComplete(); Inherited::componentComplete();

View File

@ -20,6 +20,9 @@ class QSK_EXPORT QskTabBar : public QskBox
Q_PROPERTY( Qt::Orientation orientation READ orientation ) Q_PROPERTY( Qt::Orientation orientation READ orientation )
Q_PROPERTY( bool autoScrollFocusButton READ autoScrollFocusButton
WRITE setAutoScrollFocusedButton NOTIFY autoScrollFocusedButtonChanged FINAL )
Q_PROPERTY( int count READ count NOTIFY countChanged FINAL ) Q_PROPERTY( int count READ count NOTIFY countChanged FINAL )
Q_PROPERTY( int currentIndex READ currentIndex Q_PROPERTY( int currentIndex READ currentIndex
@ -43,6 +46,11 @@ class QSK_EXPORT QskTabBar : public QskBox
Qt::Orientation orientation() const; Qt::Orientation orientation() const;
void setAutoScrollFocusedButton( bool on );
bool autoScrollFocusButton() const;
void ensureButtonVisible( const QskTabButton* );
void setTextOptions( const QskTextOptions& ); void setTextOptions( const QskTextOptions& );
QskTextOptions textOptions() const; QskTextOptions textOptions() const;
@ -86,6 +94,7 @@ class QSK_EXPORT QskTabBar : public QskBox
void countChanged( int ); void countChanged( int );
void textOptionsChanged( const QskTextOptions& ); void textOptionsChanged( const QskTextOptions& );
void positionChanged( Qsk::Position ); void positionChanged( Qsk::Position );
void autoScrollFocusedButtonChanged( bool );
protected: protected:
void componentComplete() override; void componentComplete() override;

View File

@ -210,12 +210,22 @@ QSizeF QskTabView::layoutSizeHint(
const auto& tabBar = m_data->tabBar; const auto& tabBar = m_data->tabBar;
const auto& stackBox = m_data->stackBox; const auto& stackBox = m_data->stackBox;
const auto barHint = tabBar->sizeConstraint( which ); auto barHint = tabBar->sizeConstraint( which );
#if 1
/*
How to limit the constribution of the tabbar in a reasonable way ?
QTabWidget uses 200x200 - what is kind of random. TODO ...
*/
const qreal minBarSize = 200;
#endif
QSizeF hint; QSizeF hint;
if ( orientation() == Qt::Vertical ) if ( orientation() == Qt::Vertical )
{ {
barHint.setWidth( qMin( barHint.width(), minBarSize ) );
if ( constraint.width() >= 0.0 ) if ( constraint.width() >= 0.0 )
{ {
qreal w = qMax( constraint.width(), barHint.width() ); qreal w = qMax( constraint.width(), barHint.width() );
@ -240,6 +250,8 @@ QSizeF QskTabView::layoutSizeHint(
} }
else else
{ {
barHint.setHeight( qMin( barHint.height(), minBarSize ) );
if ( constraint.width() >= 0.0 ) if ( constraint.width() >= 0.0 )
{ {
qreal w = constraint.width() - barHint.width(); qreal w = constraint.width() - barHint.width();