From 53e924a999591abda94bb4eac61206deb6fb4507 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 13 Mar 2020 07:39:31 +0100 Subject: [PATCH] flickable tabbars --- examples/tabview/main.cpp | 2 +- skins/material/QskMaterialSkin.cpp | 3 + skins/squiek/QskSquiekSkin.cpp | 3 + src/controls/QskControl.cpp | 9 ++ src/controls/QskControl.h | 2 + src/controls/QskFocusIndicator.cpp | 15 ++- src/controls/QskScrollArea.cpp | 80 ++----------- src/controls/QskScrollArea.h | 11 +- src/controls/QskScrollBox.cpp | 86 ++++++++++++++ src/controls/QskScrollBox.h | 12 ++ src/controls/QskScrollView.cpp | 1 - src/controls/QskTabBar.cpp | 174 ++++++++++++++++++++++++++++- src/controls/QskTabBar.h | 9 ++ src/controls/QskTabView.cpp | 14 ++- 14 files changed, 333 insertions(+), 88 deletions(-) diff --git a/examples/tabview/main.cpp b/examples/tabview/main.cpp index afa58203..1dbf7808 100644 --- a/examples/tabview/main.cpp +++ b/examples/tabview/main.cpp @@ -39,7 +39,7 @@ class TabView : public QskTabView TabView( QQuickItem* parent = nullptr ) : QskTabView( parent ) { - for ( int i = 0; i < 6; i++ ) + for ( int i = 0; i < 10; i++ ) { QString text; if ( i == 4 ) diff --git a/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp index 8400a5af..a43dd4f5 100644 --- a/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -569,6 +569,9 @@ void QskMaterialSkin::initTabBarHints() setBoxShape( Q::Panel, 0 ); setBoxBorderMetrics( Q::Panel, 0 ); setGradient( Q::Panel, QskGradient() ); + + // when flicking + setAnimation( Q::Panel | Metric, QskAnimationHint( 200, QEasingCurve::InCubic ) ); } void QskMaterialSkin::initTabViewHints() diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 8c88680a..5edc2d62 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -641,6 +641,9 @@ void QskSquiekSkin::initTabBarHints() setMargins( Q::Panel | Padding, 0 ); setMargins( Q::Panel | Margin, 0 ); setPanel( Q::Panel, NoPanel ); + + // when flicking + setAnimation( Q::Panel | Metric, QskAnimationHint( 200, QEasingCurve::OutCubic ) ); } void QskSquiekSkin::initTabViewHints() diff --git a/src/controls/QskControl.cpp b/src/controls/QskControl.cpp index 299b9d28..38bd9187 100644 --- a/src/controls/QskControl.cpp +++ b/src/controls/QskControl.cpp @@ -959,6 +959,15 @@ QRectF QskControl::focusIndicatorRect() const return contentsRect(); } +bool QskControl::hasFocusIndicatorClip() const +{ + /* + Often we want to clip the focus indicator, + when the control is clipped. + */ + return clip(); +} + void QskControl::updateLayout() { } diff --git a/src/controls/QskControl.h b/src/controls/QskControl.h index 69eb3d79..f777afff 100644 --- a/src/controls/QskControl.h +++ b/src/controls/QskControl.h @@ -91,7 +91,9 @@ class QSK_EXPORT QskControl : public QskQuickItem, public QskSkinnable virtual QRectF layoutRectForSize( const QSizeF& ) const; virtual QRectF gestureRect() const; + virtual QRectF focusIndicatorRect() const; + virtual bool hasFocusIndicatorClip() const; QRectF subControlRect( QskAspect::Subcontrol ) const; QRectF subControlRect( const QSizeF&, QskAspect::Subcontrol ) const; diff --git a/src/controls/QskFocusIndicator.cpp b/src/controls/QskFocusIndicator.cpp index c5fb2355..8e250ba8 100644 --- a/src/controls/QskFocusIndicator.cpp +++ b/src/controls/QskFocusIndicator.cpp @@ -103,17 +103,26 @@ void QskFocusIndicator::onFocusItemChanged() { 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 - 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 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() ) { // We want to be on top, but do we cover all corner cases ??? diff --git a/src/controls/QskScrollArea.cpp b/src/controls/QskScrollArea.cpp index d5bb69a2..ffba3f73 100644 --- a/src/controls/QskScrollArea.cpp +++ b/src/controls/QskScrollArea.cpp @@ -124,9 +124,13 @@ class QskScrollAreaClipItem final : public QskControl, public QQuickItemChangeLi return scrollArea()->subControlRect( QskScrollView::Viewport ); } + bool hasFocusIndicatorClip() const override + { + return scrollArea()->hasFocusIndicatorClip(); + } + protected: bool event( QEvent* event ) override; - void windowChangeEvent( QskWindowChangeEvent* ) override; void itemChange( ItemChange, const ItemChangeData& ) override; void geometryChanged( const QRectF&, const QRectF& ) override; @@ -151,9 +155,6 @@ class QskScrollAreaClipItem final : public QskControl, public QQuickItemChangeLi void updateNode( QSGNode* ) override; private: - void connectWindow( const QQuickWindow*, bool on ); - void onFocusItemChanged(); - inline QskScrollArea* scrollArea() { return static_cast< QskScrollArea* >( parentItem() ); @@ -172,8 +173,6 @@ QskScrollAreaClipItem::QskScrollAreaClipItem( QskScrollArea* scrollArea ) { setObjectName( QStringLiteral( "QskScrollAreaClipItem" ) ); setClip( true ); - - connectWindow( window(), true ); } QskScrollAreaClipItem::~QskScrollAreaClipItem() @@ -181,23 +180,6 @@ 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 ); @@ -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 ) { if ( event->type() == QEvent::LayoutRequest ) @@ -335,20 +303,11 @@ 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 ) - , autoScrollFocusItem( true ) { } @@ -369,7 +328,6 @@ class QskScrollArea::PrivateData QskScrollAreaClipItem* clipItem = nullptr; bool isItemResizable : 1; - bool autoScrollFocusItem : 1; }; /* @@ -401,16 +359,6 @@ 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(); @@ -457,18 +405,9 @@ void QskScrollArea::adjustItem() } } -void QskScrollArea::setAutoScrollFocusedItem( bool on ) +bool QskScrollArea::hasFocusIndicatorClip() const { - if ( m_data->autoScrollFocusItem != on ) - { - m_data->autoScrollFocusItem = on; - Q_EMIT autoScrollFocusedItemChanged(); - } -} - -bool QskScrollArea::autoScrollFocusItem() const -{ - return m_data->autoScrollFocusItem; + return true; } void QskScrollArea::setItemResizable( bool on ) @@ -476,7 +415,7 @@ void QskScrollArea::setItemResizable( bool on ) if ( on != m_data->isItemResizable ) { m_data->isItemResizable = on; - Q_EMIT itemResizableChanged(); + Q_EMIT itemResizableChanged( on ); if ( m_data->isItemResizable ) polish(); @@ -520,8 +459,7 @@ QQuickItem* QskScrollArea::scrolledItem() const void QskScrollArea::translateItem() { - auto item = m_data->clipItem->scrolledItem(); - if ( item ) + if ( auto item = m_data->clipItem->scrolledItem() ) { const QPointF pos = viewContentsRect().topLeft() - scrollPos(); item->setPosition( pos ); diff --git a/src/controls/QskScrollArea.h b/src/controls/QskScrollArea.h index 4de13d03..f0f46ead 100644 --- a/src/controls/QskScrollArea.h +++ b/src/controls/QskScrollArea.h @@ -18,9 +18,6 @@ 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: @@ -33,15 +30,11 @@ class QSK_EXPORT QskScrollArea : public QskScrollView void setItemResizable( bool on ); bool isItemResizable() const; - void setAutoScrollFocusedItem( bool on ); - bool autoScrollFocusItem() const; - - void ensureItemVisible( const QQuickItem* ); + bool hasFocusIndicatorClip() const override; Q_SIGNALS: - void itemResizableChanged(); void scrolledItemChanged(); - void autoScrollFocusedItemChanged(); + void itemResizableChanged( bool ); protected: void updateLayout() override; diff --git a/src/controls/QskScrollBox.cpp b/src/controls/QskScrollBox.cpp index c3a3bf70..90ed4809 100644 --- a/src/controls/QskScrollBox.cpp +++ b/src/controls/QskScrollBox.cpp @@ -9,6 +9,11 @@ #include "QskFlickAnimator.h" #include "QskGesture.h" #include "QskPanGestureRecognizer.h" +#include "QskQuick.h" + +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END namespace { @@ -95,6 +100,11 @@ namespace class QskScrollBox::PrivateData { public: + PrivateData() + : autoScrollFocusItem( true ) + { + } + QPointF scrollPos; QSizeF scrollableSize = QSize( 0.0, 0.0 ); @@ -105,6 +115,8 @@ class QskScrollBox::PrivateData ScrollAnimator scroller; const qreal viewportPadding = 10; + + bool autoScrollFocusItem : 1; }; QskScrollBox::QskScrollBox( QQuickItem* parent ) @@ -119,14 +131,52 @@ QskScrollBox::QskScrollBox( QQuickItem* parent ) setFiltersChildMouseEvents( true ); + setAcceptedMouseButtons( Qt::LeftButton ); setWheelEnabled( true ); + setFocusPolicy( Qt::StrongFocus ); + + connectWindow( window(), true ); } 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 ) { if ( timeout < 0 ) @@ -202,6 +252,17 @@ QRectF QskScrollBox::gestureRect() const 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 ) { const qreal margin = m_data->viewportPadding; @@ -282,6 +343,14 @@ void QskScrollBox::ensureVisible( const QRectF& itemRect ) setScrollPos( newPos ); } +void QskScrollBox::windowChangeEvent( QskWindowChangeEvent* event ) +{ + Inherited::windowChangeEvent( event ); + + connectWindow( event->oldWindow(), false ); + connectWindow( event->window(), true ); +} + void QskScrollBox::geometryChangeEvent( QskGeometryChangeEvent* event ) { 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 ) ); } +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" diff --git a/src/controls/QskScrollBox.h b/src/controls/QskScrollBox.h index e48b0ec3..c8a7899b 100644 --- a/src/controls/QskScrollBox.h +++ b/src/controls/QskScrollBox.h @@ -18,12 +18,18 @@ class QSK_EXPORT QskScrollBox : public QskControl Q_PROPERTY( Qt::Orientations flickableOrientations READ flickableOrientations WRITE setFlickableOrientations NOTIFY flickableOrientationsChanged FINAL ) + Q_PROPERTY( bool autoScrollFocusedItem READ autoScrollFocusItem + WRITE setAutoScrollFocusedItem NOTIFY autoScrollFocusedItemChanged FINAL ) + using Inherited = QskControl; public: QskScrollBox( QQuickItem* parent = nullptr ); ~QskScrollBox() override; + void setAutoScrollFocusedItem( bool on ); + bool autoScrollFocusItem() const; + void setFlickableOrientations( Qt::Orientations ); Qt::Orientations flickableOrientations() const; @@ -43,17 +49,21 @@ class QSK_EXPORT QskScrollBox : public QskControl void scrollPosChanged(); void scrollableSizeChanged( const QSizeF& ); + void autoScrollFocusedItemChanged( bool ); void flickableOrientationsChanged(); public Q_SLOTS: void setScrollPos( const QPointF& ); void scrollTo( const QPointF& ); + void ensureItemVisible( const QQuickItem* ); + void ensureFocusItemVisible(); void ensureVisible( const QPointF& ); void ensureVisible( const QRectF& ); protected: void geometryChangeEvent( QskGeometryChangeEvent* ) override; + void windowChangeEvent( QskWindowChangeEvent* ) override; void gestureEvent( QskGestureEvent* ) override; #ifndef QT_NO_WHEELEVENT @@ -66,6 +76,8 @@ class QSK_EXPORT QskScrollBox : public QskControl private: QPointF boundedScrollPos( const QPointF& ) const; + void onFocusItemChanged(); + void connectWindow( const QQuickWindow*, bool on ); class PrivateData; std::unique_ptr< PrivateData > m_data; diff --git a/src/controls/QskScrollView.cpp b/src/controls/QskScrollView.cpp index b3bd8a16..961b11f3 100644 --- a/src/controls/QskScrollView.cpp +++ b/src/controls/QskScrollView.cpp @@ -38,7 +38,6 @@ QskScrollView::QskScrollView( QQuickItem* parent ) : Inherited( parent ) , m_data( new PrivateData() ) { - setAcceptedMouseButtons( Qt::LeftButton ); } QskScrollView::~QskScrollView() diff --git a/src/controls/QskTabBar.cpp b/src/controls/QskTabBar.cpp index cfdb8591..570d751f 100644 --- a/src/controls/QskTabBar.cpp +++ b/src/controls/QskTabBar.cpp @@ -5,9 +5,12 @@ #include "QskTabBar.h" #include "QskAspect.h" +#include "QskScrollBox.h" #include "QskLinearBox.h" #include "QskTabButton.h" #include "QskTextOptions.h" +#include "QskAnimationHint.h" +#include "QskQuick.h" QSK_SUBCONTROL( QskTabBar, Panel ) @@ -21,8 +24,10 @@ static inline Qt::Orientation qskOrientation( int position ) namespace { - class ButtonBox : public QskLinearBox + class ButtonBox final : public QskLinearBox { + using Inherited = QskLinearBox; + public: ButtonBox( Qt::Orientation orientation, QQuickItem* 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 @@ -72,6 +213,7 @@ class QskTabBar::PrivateData } } + ScrollBox* scrollBox = nullptr; ButtonBox* buttonBox = nullptr; int currentIndex = -1; @@ -97,9 +239,17 @@ QskTabBar::QskTabBar( Qsk::Position position, QQuickItem* parent ) else 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 ) ); +#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, m_data->buttonBox, &ButtonBox::restack, Qt::QueuedConnection ); } @@ -120,6 +270,7 @@ void QskTabBar::setPosition( Qsk::Position position ) { setSizePolicy( sizePolicy( Qt::Vertical ), sizePolicy( Qt::Horizontal ) ); m_data->buttonBox->setOrientation( orientation ); + m_data->scrollBox->setOrientation( orientation ); } resetImplicitSize(); @@ -140,6 +291,20 @@ Qt::Orientation QskTabBar::orientation() const 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 ) { if ( options != m_data->textOptions ) @@ -366,6 +531,11 @@ int QskTabBar::indexOf( QskTabButton* button ) const return m_data->buttonBox->indexOf( button ); } +void QskTabBar::ensureButtonVisible( const QskTabButton* button ) +{ + m_data->scrollBox->ensureItemVisible( button ); +} + void QskTabBar::componentComplete() { Inherited::componentComplete(); diff --git a/src/controls/QskTabBar.h b/src/controls/QskTabBar.h index 19f7ebf2..141bc2d0 100644 --- a/src/controls/QskTabBar.h +++ b/src/controls/QskTabBar.h @@ -20,6 +20,9 @@ class QSK_EXPORT QskTabBar : public QskBox 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 currentIndex READ currentIndex @@ -43,6 +46,11 @@ class QSK_EXPORT QskTabBar : public QskBox Qt::Orientation orientation() const; + void setAutoScrollFocusedButton( bool on ); + bool autoScrollFocusButton() const; + + void ensureButtonVisible( const QskTabButton* ); + void setTextOptions( const QskTextOptions& ); QskTextOptions textOptions() const; @@ -86,6 +94,7 @@ class QSK_EXPORT QskTabBar : public QskBox void countChanged( int ); void textOptionsChanged( const QskTextOptions& ); void positionChanged( Qsk::Position ); + void autoScrollFocusedButtonChanged( bool ); protected: void componentComplete() override; diff --git a/src/controls/QskTabView.cpp b/src/controls/QskTabView.cpp index 5c6ed50b..bebc08c4 100644 --- a/src/controls/QskTabView.cpp +++ b/src/controls/QskTabView.cpp @@ -210,12 +210,22 @@ QSizeF QskTabView::layoutSizeHint( const auto& tabBar = m_data->tabBar; 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; if ( orientation() == Qt::Vertical ) { + barHint.setWidth( qMin( barHint.width(), minBarSize ) ); + if ( constraint.width() >= 0.0 ) { qreal w = qMax( constraint.width(), barHint.width() ); @@ -240,6 +250,8 @@ QSizeF QskTabView::layoutSizeHint( } else { + barHint.setHeight( qMin( barHint.height(), minBarSize ) ); + if ( constraint.width() >= 0.0 ) { qreal w = constraint.width() - barHint.width();