2017-07-21 16:21:34 +00:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
#include "QskLayoutBox.h"
|
2017-07-21 16:21:34 +00:00
|
|
|
#include "QskEvent.h"
|
2018-08-03 06:15:28 +00:00
|
|
|
#include "QskLayoutEngine.h"
|
|
|
|
#include "QskLayoutItem.h"
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
class QskLayoutBox::PrivateData
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2018-08-03 06:15:28 +00:00
|
|
|
public:
|
|
|
|
PrivateData()
|
|
|
|
: isActive( true )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isActive : 1;
|
|
|
|
|
|
|
|
QskLayoutEngine engine;
|
|
|
|
};
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
QskLayoutBox::QskLayoutBox( QQuickItem* parent )
|
2018-08-03 06:15:28 +00:00
|
|
|
: QskControl( parent )
|
|
|
|
, m_data( new PrivateData() )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
QskLayoutBox::~QskLayoutBox()
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
disconnect( this, 0, this, 0 ); // destructor runs on invalidate else
|
|
|
|
setActive( false );
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::setActive( bool on )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
if ( on == m_data->isActive )
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_data->isActive = on;
|
|
|
|
|
|
|
|
for ( int i = 0; i < itemCount(); ++i )
|
|
|
|
{
|
2019-04-26 09:56:09 +00:00
|
|
|
if( auto item = itemAtIndex( i ) )
|
2017-07-21 16:21:34 +00:00
|
|
|
setItemActive( item, on );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( on )
|
|
|
|
{
|
|
|
|
resetImplicitSize();
|
|
|
|
polish();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
bool QskLayoutBox::isActive() const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
return m_data->isActive;
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
int QskLayoutBox::itemCount() const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
return m_data->engine.itemCount();
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
QQuickItem* QskLayoutBox::itemAtIndex( int index ) const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2019-04-26 09:56:09 +00:00
|
|
|
if ( auto layoutItem = m_data->engine.layoutItemAt( index ) )
|
2017-07-21 16:21:34 +00:00
|
|
|
return layoutItem->item();
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
int QskLayoutBox::indexOf( const QQuickItem* item ) const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
if ( item != nullptr )
|
|
|
|
return m_data->engine.indexOf( item );
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::insertItemInternal( QskLayoutItem* layoutItem, int index )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
// check if item is already inserted ???
|
|
|
|
|
2019-04-26 09:56:09 +00:00
|
|
|
auto item = layoutItem->item();
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2017-11-01 11:48:41 +00:00
|
|
|
if ( index > itemCount() )
|
|
|
|
index = -1; // append
|
|
|
|
|
2019-04-26 09:56:09 +00:00
|
|
|
auto& engine = this->engine();
|
2017-11-01 11:48:41 +00:00
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
if ( item )
|
|
|
|
{
|
|
|
|
if ( item->parent() == nullptr )
|
|
|
|
item->setParent( this );
|
|
|
|
|
|
|
|
if ( item->parentItem() != this )
|
|
|
|
item->setParentItem( this );
|
|
|
|
|
2018-03-20 13:57:29 +00:00
|
|
|
/*
|
|
|
|
Re-ordering the child items to have a a proper focus tab chain
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool reordered = false;
|
2017-11-01 11:48:41 +00:00
|
|
|
|
2018-03-20 13:57:29 +00:00
|
|
|
if ( ( index >= 0 ) && ( index < itemCount() - 1 ) )
|
|
|
|
{
|
2017-11-01 11:48:41 +00:00
|
|
|
for ( int i = index; i < engine.itemCount(); i++ )
|
|
|
|
{
|
2018-03-20 13:57:29 +00:00
|
|
|
auto layoutItem = engine.layoutItemAt( i );
|
2017-11-01 11:48:41 +00:00
|
|
|
if ( layoutItem && layoutItem->item() )
|
2018-03-20 13:57:29 +00:00
|
|
|
{
|
2017-11-01 11:48:41 +00:00
|
|
|
item->stackBefore( layoutItem->item() );
|
2018-03-20 13:57:29 +00:00
|
|
|
reordered = true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2017-11-01 11:48:41 +00:00
|
|
|
}
|
|
|
|
}
|
2018-03-20 13:57:29 +00:00
|
|
|
|
|
|
|
if ( !reordered )
|
|
|
|
{
|
|
|
|
const auto children = childItems();
|
|
|
|
if ( item != children.last() )
|
|
|
|
item->stackAfter( children.last() );
|
|
|
|
}
|
2017-11-01 11:48:41 +00:00
|
|
|
}
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2017-11-01 11:48:41 +00:00
|
|
|
engine.insertLayoutItem( layoutItem, index );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
if ( m_data->isActive )
|
|
|
|
{
|
|
|
|
setItemActive( item, true );
|
|
|
|
|
|
|
|
resetImplicitSize();
|
|
|
|
polish();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::removeAt( int index )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2019-04-26 09:56:09 +00:00
|
|
|
auto& engine = this->engine();
|
2017-07-21 16:21:34 +00:00
|
|
|
|
2019-04-26 09:56:09 +00:00
|
|
|
auto layoutItem = engine.layoutItemAt( index );
|
2017-07-21 16:21:34 +00:00
|
|
|
if ( layoutItem == nullptr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
setItemActive( layoutItem->item(), false );
|
|
|
|
engine.removeItem( layoutItem );
|
|
|
|
|
|
|
|
layoutItemRemoved( layoutItem, index );
|
|
|
|
|
|
|
|
delete layoutItem;
|
|
|
|
|
|
|
|
if ( m_data->isActive )
|
|
|
|
{
|
|
|
|
resetImplicitSize();
|
|
|
|
polish();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::removeItem( QQuickItem* item )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
removeAt( indexOf( item ) );
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::clear( bool autoDelete )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
const bool isActive = m_data->isActive;
|
|
|
|
setActive( false );
|
|
|
|
|
|
|
|
for ( int i = itemCount() - 1; i >= 0; i-- )
|
2019-03-31 08:33:23 +00:00
|
|
|
{
|
|
|
|
auto item = itemAtIndex( i );
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
removeAt( i );
|
|
|
|
|
2019-03-31 08:33:23 +00:00
|
|
|
if( item )
|
|
|
|
{
|
|
|
|
if( autoDelete && ( item->parent() == this ) )
|
|
|
|
delete item;
|
|
|
|
else
|
|
|
|
item->setParentItem( nullptr );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-21 16:21:34 +00:00
|
|
|
setActive( isActive );
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::setupLayoutItem( QskLayoutItem* layoutItem, int index )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED( layoutItem )
|
|
|
|
Q_UNUSED( index )
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::layoutItemInserted( QskLayoutItem* layoutItem, int index )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED( layoutItem )
|
|
|
|
Q_UNUSED( index )
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::layoutItemRemoved( QskLayoutItem* layoutItem, int index )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
Q_UNUSED( layoutItem )
|
|
|
|
Q_UNUSED( index )
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::activate()
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
if ( m_data->isActive )
|
|
|
|
polish();
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::invalidate()
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
engine().invalidate();
|
|
|
|
activate();
|
|
|
|
|
|
|
|
resetImplicitSize();
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::adjustItem( const QQuickItem* item )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
adjustItemAt( indexOf( item ) );
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::adjustItemAt( int index )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
QskLayoutItem* layoutItem = engine().layoutItemAt( index );
|
|
|
|
if ( layoutItem == nullptr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// setting UpdateNone to all others ???
|
|
|
|
layoutItem->setUpdateMode( QskLayoutItem::UpdateAlways );
|
|
|
|
engine().setGeometries( alignedLayoutRect( layoutRect() ) );
|
|
|
|
layoutItem->setUpdateMode( QskLayoutItem::UpdateWhenVisible );
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::updateLayout()
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
if ( m_data->isActive )
|
|
|
|
engine().setGeometries( alignedLayoutRect( layoutRect() ) );
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
QRectF QskLayoutBox::alignedLayoutRect( const QRectF& rect ) const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:25:06 +00:00
|
|
|
QSizeF QskLayoutBox::contentsSizeHint() const
|
|
|
|
{
|
|
|
|
if ( !isActive() )
|
|
|
|
return QSizeF( -1, -1 );
|
|
|
|
|
|
|
|
if ( itemCount() == 0 )
|
|
|
|
return QSizeF( 0, 0 );
|
|
|
|
|
|
|
|
return layoutItemsSizeHint();
|
|
|
|
}
|
|
|
|
|
|
|
|
QSizeF QskLayoutBox::layoutItemsSizeHint() const
|
|
|
|
{
|
|
|
|
return engine().sizeHint( Qt::PreferredSize );
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal QskLayoutBox::heightForWidth( qreal width ) const
|
|
|
|
{
|
|
|
|
const auto m = margins();
|
|
|
|
width -= m.left() + m.right();
|
|
|
|
|
|
|
|
qreal height = engine().heightForWidth( width );
|
|
|
|
|
|
|
|
height += m.top() + m.bottom();
|
|
|
|
return height;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal QskLayoutBox::widthForHeight( qreal height ) const
|
|
|
|
{
|
|
|
|
const auto m = margins();
|
|
|
|
height -= m.top() + m.bottom();
|
|
|
|
|
|
|
|
qreal width = engine().widthForHeight( height );
|
|
|
|
|
|
|
|
width += m.left() + m.right();
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::geometryChangeEvent( QskGeometryChangeEvent* event )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
Inherited::geometryChangeEvent( event );
|
|
|
|
|
|
|
|
if ( event->isResized() )
|
|
|
|
activate();
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::setItemActive( const QQuickItem* item, bool on )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
if ( item == nullptr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// QskControl sends QEvent::LayoutRequest
|
|
|
|
|
2019-04-26 09:56:09 +00:00
|
|
|
const bool hasLayoutRequests = qskControlCast( item );
|
2017-07-21 16:21:34 +00:00
|
|
|
if ( !hasLayoutRequests )
|
|
|
|
{
|
|
|
|
if ( on )
|
|
|
|
{
|
|
|
|
connect( item, &QQuickItem::implicitWidthChanged,
|
2019-04-08 11:08:58 +00:00
|
|
|
this, &QskLayoutBox::invalidate );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
connect( item, &QQuickItem::implicitHeightChanged,
|
2019-04-08 11:08:58 +00:00
|
|
|
this, &QskLayoutBox::invalidate );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
disconnect( item, &QQuickItem::implicitWidthChanged,
|
2019-04-08 11:08:58 +00:00
|
|
|
this, &QskLayoutBox::invalidate );
|
2017-07-21 16:21:34 +00:00
|
|
|
|
|
|
|
disconnect( item, &QQuickItem::implicitHeightChanged,
|
2019-04-08 11:08:58 +00:00
|
|
|
this, &QskLayoutBox::invalidate );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( on )
|
2019-04-08 11:08:58 +00:00
|
|
|
connect( item, &QQuickItem::visibleChanged, this, &QskLayoutBox::activate );
|
2017-07-21 16:21:34 +00:00
|
|
|
else
|
2019-04-08 11:08:58 +00:00
|
|
|
disconnect( item, &QQuickItem::visibleChanged, this, &QskLayoutBox::activate );
|
2017-07-21 16:21:34 +00:00
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
QskLayoutEngine& QskLayoutBox::engine()
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
return m_data->engine;
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
const QskLayoutEngine& QskLayoutBox::engine() const
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
return m_data->engine;
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
void QskLayoutBox::itemChange( ItemChange change, const ItemChangeData& value )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
Inherited::itemChange( change, value );
|
|
|
|
|
2018-08-03 06:15:28 +00:00
|
|
|
switch ( change )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
case ItemChildRemovedChange:
|
|
|
|
{
|
|
|
|
removeItem( value.item );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QQuickItem::ItemVisibleHasChanged:
|
|
|
|
{
|
|
|
|
if ( value.boolValue )
|
|
|
|
activate();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QQuickItem::ItemSceneChange:
|
|
|
|
{
|
|
|
|
if ( value.window )
|
|
|
|
activate();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
bool QskLayoutBox::event( QEvent* event )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
2018-08-03 06:15:28 +00:00
|
|
|
switch ( event->type() )
|
2017-07-21 16:21:34 +00:00
|
|
|
{
|
|
|
|
case QEvent::LayoutRequest:
|
|
|
|
{
|
|
|
|
invalidate();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QEvent::LayoutDirectionChange:
|
|
|
|
{
|
|
|
|
m_data->engine.setVisualDirection(
|
|
|
|
layoutMirroring() ? Qt::RightToLeft : Qt::LeftToRight );
|
|
|
|
|
|
|
|
activate();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QEvent::ContentsRectChange:
|
|
|
|
{
|
|
|
|
activate();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Inherited::event( event );
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:08:58 +00:00
|
|
|
#include "moc_QskLayoutBox.cpp"
|