2017-07-21 18:21:34 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskStackBox.h"
|
2017-08-31 08:50:24 +02:00
|
|
|
#include "QskLayoutConstraint.h"
|
2018-08-03 08:15:28 +02:00
|
|
|
#include "QskLayoutEngine.h"
|
|
|
|
#include "QskLayoutItem.h"
|
|
|
|
#include "QskStackBoxAnimator.h"
|
2017-08-31 08:50:24 +02:00
|
|
|
|
2018-07-19 14:10:48 +02:00
|
|
|
#include <qpointer.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2019-04-26 18:09:59 +02:00
|
|
|
static qreal qskConstrainedValue( QskLayoutConstraint::Type type,
|
|
|
|
const QskControl* control, qreal widthOrHeight )
|
|
|
|
{
|
|
|
|
using namespace QskLayoutConstraint;
|
|
|
|
|
|
|
|
auto constrainFunction =
|
|
|
|
( type == WidthForHeight ) ? widthForHeight : heightForWidth;
|
|
|
|
|
|
|
|
qreal constrainedValue = -1;
|
|
|
|
auto stackBox = static_cast< const QskStackBox* >( control );
|
|
|
|
|
|
|
|
for ( int i = 0; i < stackBox->itemCount(); i++ )
|
|
|
|
{
|
|
|
|
if ( const auto item = stackBox->itemAtIndex( i ) )
|
|
|
|
{
|
|
|
|
const qreal v = constrainFunction( item, widthOrHeight );
|
|
|
|
if ( v > constrainedValue )
|
|
|
|
constrainedValue = v;
|
|
|
|
}
|
2019-05-06 16:36:52 +02:00
|
|
|
}
|
2019-04-26 18:09:59 +02:00
|
|
|
|
|
|
|
return constrainedValue;
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
class QskStackBox::PrivateData
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
|
|
|
PrivateData()
|
|
|
|
: currentIndex( -1 )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int currentIndex;
|
|
|
|
QPointer< QskStackBoxAnimator > animator;
|
|
|
|
};
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
QskStackBox::QskStackBox( QQuickItem* parent )
|
|
|
|
: QskStackBox( false, parent )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
QskStackBox::QskStackBox( bool autoAddChildren, QQuickItem* parent )
|
|
|
|
: Inherited( parent )
|
|
|
|
, m_data( new PrivateData() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
setAutoAddChildren( autoAddChildren );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskStackBox::~QskStackBox()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskStackBox::setAnimator( QskStackBoxAnimator* animator )
|
|
|
|
{
|
|
|
|
if ( m_data->animator == animator )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( m_data->animator )
|
|
|
|
{
|
|
|
|
m_data->animator->stop();
|
|
|
|
delete m_data->animator;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( animator )
|
|
|
|
{
|
|
|
|
animator->stop();
|
|
|
|
animator->setParent( this );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_data->animator = animator;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QskStackBoxAnimator* QskStackBox::animator() const
|
|
|
|
{
|
|
|
|
return m_data->animator;
|
|
|
|
}
|
|
|
|
|
|
|
|
QskStackBoxAnimator* QskStackBox::animator()
|
|
|
|
{
|
|
|
|
return m_data->animator;
|
|
|
|
}
|
|
|
|
|
|
|
|
QskStackBoxAnimator* QskStackBox::effectiveAnimator()
|
|
|
|
{
|
|
|
|
if ( m_data->animator )
|
|
|
|
return m_data->animator;
|
|
|
|
|
|
|
|
// getting an animation from the skin. TODO ...
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
QQuickItem* QskStackBox::currentItem() const
|
|
|
|
{
|
|
|
|
return itemAtIndex( m_data->currentIndex );
|
|
|
|
}
|
|
|
|
|
|
|
|
int QskStackBox::currentIndex() const
|
|
|
|
{
|
|
|
|
return m_data->currentIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskStackBox::layoutItemRemoved( QskLayoutItem*, int index )
|
|
|
|
{
|
|
|
|
if ( index == m_data->currentIndex )
|
|
|
|
{
|
|
|
|
int newIndex = m_data->currentIndex;
|
|
|
|
if ( newIndex == itemCount() )
|
|
|
|
newIndex--;
|
|
|
|
|
|
|
|
m_data->currentIndex = -1;
|
|
|
|
|
|
|
|
if ( newIndex >= 0 )
|
|
|
|
setCurrentIndex( index );
|
|
|
|
}
|
2019-02-13 10:26:09 +01:00
|
|
|
else if ( index < m_data->currentIndex )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
m_data->currentIndex--;
|
|
|
|
// currentIndexChanged ???
|
|
|
|
}
|
2019-02-14 20:34:17 +01:00
|
|
|
|
|
|
|
auto& engine = this->engine();
|
|
|
|
if ( engine.itemCount() > 0 && engine.itemAt( 0, 0 ) == nullptr )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Using QGridLayoutEngine for a stack layout is actually
|
|
|
|
not a good ideas. Until we have a new implementation,
|
|
|
|
we need to work around situations, where the layout does
|
2019-05-06 16:36:52 +02:00
|
|
|
not work properly with having several items in the
|
|
|
|
same cell.
|
2019-02-14 20:34:17 +01:00
|
|
|
In this particular situation we need to fix, that we lost
|
|
|
|
the item from engine.q_grid[0].
|
|
|
|
Calling transpose has this side effect.
|
2019-05-06 16:36:52 +02:00
|
|
|
|
2019-02-14 20:34:17 +01:00
|
|
|
*/
|
|
|
|
engine.transpose();
|
|
|
|
engine.transpose(); // reverting the call before
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskStackBox::setCurrentIndex( int index )
|
|
|
|
{
|
|
|
|
if ( index < 0 || index >= itemCount() )
|
|
|
|
{
|
|
|
|
// hide the current item
|
|
|
|
index = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( index == m_data->currentIndex )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// stop and complete the running transition
|
2019-04-08 13:25:06 +02:00
|
|
|
auto animator = effectiveAnimator();
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( animator )
|
|
|
|
animator->stop();
|
|
|
|
|
2019-04-16 11:54:34 +02:00
|
|
|
if ( window() && isVisible() && isInitiallyPainted() && animator )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
// When being hidden, the geometry is not updated.
|
|
|
|
// So we do it now.
|
|
|
|
|
|
|
|
adjustItemAt( index );
|
|
|
|
|
|
|
|
// start the animation
|
|
|
|
animator->setStartIndex( m_data->currentIndex );
|
|
|
|
animator->setEndIndex( index );
|
|
|
|
animator->setWindow( window() );
|
|
|
|
animator->start();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-04-08 13:25:06 +02:00
|
|
|
auto item1 = itemAtIndex( m_data->currentIndex );
|
|
|
|
auto item2 = itemAtIndex( index );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
if ( item1 )
|
|
|
|
item1->setVisible( false );
|
|
|
|
|
|
|
|
if ( item2 )
|
|
|
|
item2->setVisible( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_data->currentIndex = index;
|
|
|
|
Q_EMIT currentIndexChanged( m_data->currentIndex );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskStackBox::setCurrentItem( const QQuickItem* item )
|
|
|
|
{
|
|
|
|
setCurrentIndex( indexOf( item ) );
|
|
|
|
}
|
|
|
|
|
2019-04-08 13:25:06 +02:00
|
|
|
QSizeF QskStackBox::layoutItemsSizeHint() const
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
qreal width = -1;
|
|
|
|
qreal height = -1;
|
|
|
|
|
|
|
|
QSizeF constraint( -1, -1 );
|
|
|
|
Qt::Orientations constraintOrientation = 0;
|
|
|
|
|
2019-04-08 13:25:06 +02:00
|
|
|
const auto& engine = this->engine();
|
2017-07-21 18:21:34 +02:00
|
|
|
for ( int i = 0; i < engine.itemCount(); i++ )
|
|
|
|
{
|
2019-04-08 13:25:06 +02:00
|
|
|
const auto layoutItem = engine.layoutItemAt( i );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
if ( layoutItem->hasDynamicConstraint() )
|
|
|
|
{
|
|
|
|
constraintOrientation |= layoutItem->dynamicConstraintOrientation();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const QSizeF hint = layoutItem->sizeHint( Qt::PreferredSize, constraint );
|
|
|
|
|
|
|
|
if ( hint.width() >= width )
|
|
|
|
width = hint.width();
|
|
|
|
|
|
|
|
if ( hint.height() >= height )
|
|
|
|
height = hint.height();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
// does this work ???
|
|
|
|
if ( constraintOrientation & Qt::Horizontal )
|
|
|
|
{
|
|
|
|
constraint.setWidth( -1 );
|
|
|
|
constraint.setHeight( height );
|
|
|
|
|
|
|
|
for ( int i = 0; i < engine.itemCount(); i++ )
|
|
|
|
{
|
2019-04-08 13:25:06 +02:00
|
|
|
const auto layoutItem = engine.layoutItemAt( i );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
if ( layoutItem->hasDynamicConstraint() &&
|
|
|
|
layoutItem->dynamicConstraintOrientation() == Qt::Horizontal )
|
|
|
|
{
|
|
|
|
const QSizeF hint = layoutItem->sizeHint( Qt::PreferredSize, constraint );
|
|
|
|
if ( hint.width() > width )
|
|
|
|
width = hint.width();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( constraintOrientation & Qt::Vertical )
|
|
|
|
{
|
|
|
|
constraint.setWidth( width );
|
|
|
|
constraint.setHeight( -1 );
|
|
|
|
|
|
|
|
for ( int i = 0; i < engine.itemCount(); i++ )
|
|
|
|
{
|
2019-04-08 13:25:06 +02:00
|
|
|
const auto layoutItem = engine.layoutItemAt( i );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
if ( layoutItem->hasDynamicConstraint() &&
|
|
|
|
layoutItem->dynamicConstraintOrientation() == Qt::Vertical )
|
|
|
|
{
|
|
|
|
const QSizeF hint = layoutItem->sizeHint( Qt::PreferredSize, constraint );
|
|
|
|
if ( hint.height() > height )
|
|
|
|
height = hint.height();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return QSizeF( width, height );
|
|
|
|
}
|
|
|
|
|
2017-08-31 08:50:24 +02:00
|
|
|
qreal QskStackBox::heightForWidth( qreal width ) const
|
|
|
|
{
|
2019-04-26 18:09:59 +02:00
|
|
|
return QskLayoutConstraint::constrainedMetric(
|
|
|
|
QskLayoutConstraint::HeightForWidth, this, width, qskConstrainedValue );
|
2017-08-31 08:50:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
qreal QskStackBox::widthForHeight( qreal height ) const
|
|
|
|
{
|
2019-04-26 18:09:59 +02:00
|
|
|
return QskLayoutConstraint::constrainedMetric(
|
|
|
|
QskLayoutConstraint::WidthForHeight, this, height, qskConstrainedValue );
|
2017-08-31 08:50:24 +02:00
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskStackBox::layoutItemInserted( QskLayoutItem* layoutItem, int index )
|
|
|
|
{
|
|
|
|
Q_UNUSED( index )
|
|
|
|
|
2017-08-31 08:50:24 +02:00
|
|
|
QQuickItem* item = layoutItem->item();
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( item == nullptr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
#if 1
|
2017-08-31 08:50:24 +02:00
|
|
|
/*
|
2017-07-21 18:21:34 +02:00
|
|
|
In general QGridLayoutEngine supports having multiple entries
|
|
|
|
in one cell, but well ...
|
|
|
|
So we have to go away from using it and doing the simple use case of
|
|
|
|
a stack layout manually. TODO ...
|
|
|
|
|
|
|
|
One problem we ran into is, that a cell is considered being hidden,
|
|
|
|
when the first entry is ignored. So for the moment we simply set the
|
|
|
|
retainSizeWhenHidden flag, with the cost of having geometry updates
|
|
|
|
for invisible updates.
|
|
|
|
*/
|
|
|
|
layoutItem->setRetainSizeWhenHidden( true );
|
|
|
|
#endif
|
|
|
|
if ( itemCount() == 1 )
|
|
|
|
{
|
|
|
|
m_data->currentIndex = 0;
|
|
|
|
item->setVisible( true );
|
|
|
|
|
|
|
|
Q_EMIT currentIndexChanged( m_data->currentIndex );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->setVisible( false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "moc_QskStackBox.cpp"
|