qskinny/src/layouts/QskGridBox.cpp

422 lines
9.8 KiB
C++
Raw Normal View History

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
*****************************************************************************/
#include "QskGridBox.h"
#include "QskGridLayoutEngine.h"
#include "QskEvent.h"
#include <algorithm>
2017-07-21 16:21:34 +00:00
static void qskSetItemActive( QObject* receiver, const QQuickItem* item, bool on )
2017-07-21 16:21:34 +00:00
{
if ( ( item == nullptr ) || ( qskControlCast( item ) != nullptr ) )
return;
/*
For QQuickItems not being derived from QskControl we manually
send QEvent::LayoutRequest events.
*/
if ( on )
{
auto sendLayoutRequest =
[receiver]()
{
QEvent event( QEvent::LayoutRequest );
QCoreApplication::sendEvent( receiver, &event );
};
QObject::connect( item, &QQuickItem::implicitWidthChanged,
receiver, sendLayoutRequest );
QObject::connect( item, &QQuickItem::implicitHeightChanged,
receiver, sendLayoutRequest );
QObject::connect( item, &QQuickItem::visibleChanged,
receiver, sendLayoutRequest );
}
else
2017-07-21 16:21:34 +00:00
{
QObject::disconnect( item, &QQuickItem::implicitWidthChanged, receiver, nullptr );
QObject::disconnect( item, &QQuickItem::implicitHeightChanged, receiver, nullptr );
QObject::disconnect( item, &QQuickItem::visibleChanged, receiver, nullptr );
2017-07-21 16:21:34 +00:00
}
}
2017-07-21 16:21:34 +00:00
static void qskUpdateFocusChain(
QskGridBox* box, const QskGridLayoutEngine* engine,
QQuickItem* item, const QRect& grid )
{
auto comparePosition =
[item, engine]( const QPoint& pos, const QQuickItem* child )
{
if ( item != child )
{
const int index = engine->indexOf( child );
if ( index >= 0 )
{
const auto grid = engine->gridAt( index );
if ( pos.y() < grid.y() )
return true;
if ( pos.y() == grid.y() && pos.x() < grid.x() )
return true;
}
}
return false;
};
const auto children = box->childItems();
auto it = std::upper_bound( children.begin(), children.end(),
grid.topLeft(), comparePosition );
if ( it != children.end() )
item->stackBefore( *it );
}
class QskGridBox::PrivateData
{
public:
QskGridLayoutEngine engine;
bool blockAutoRemove = false;
2017-07-21 16:21:34 +00:00
};
2018-08-03 06:15:28 +00:00
QskGridBox::QskGridBox( QQuickItem* parent )
: QskBox( false, parent )
2018-08-03 06:15:28 +00:00
, m_data( new PrivateData() )
2017-07-21 16:21:34 +00:00
{
}
QskGridBox::~QskGridBox()
{
}
int QskGridBox::addItem( QQuickItem* item,
int row, int column, int rowSpan, int columnSpan )
2017-07-21 16:21:34 +00:00
{
if ( item == nullptr || row < 0 || column < 0 )
return -1;
2017-07-21 16:21:34 +00:00
rowSpan = qMax( rowSpan, -1 );
columnSpan = qMax( columnSpan, -1 );
2017-07-21 16:21:34 +00:00
auto& engine = m_data->engine;
const QRect itemGrid( column, row, columnSpan, rowSpan );
int index = -1;
2017-07-21 16:21:34 +00:00
if ( item->parentItem() == this )
{
index = indexOf( item );
if ( index >= 0 )
{
if ( engine.gridAt( index ) == itemGrid )
return index;
}
}
if ( index < 0 )
{
if ( item->parent() == nullptr )
item->setParent( this );
if ( item->parentItem() != this )
item->setParentItem( this );
qskSetItemActive( this, item, true );
index = engine.insertItem( item, itemGrid );
}
if ( engine.count() > 1 )
qskUpdateFocusChain( this, &engine, item, itemGrid );
resetImplicitSize();
polish();
2017-07-21 16:21:34 +00:00
return index;
}
2017-07-21 16:21:34 +00:00
int QskGridBox::addSpacer( qreal spacing,
int row, int column, int rowSpan, int columnSpan )
{
const int index = m_data->engine.insertSpacer(
spacing, QRect( column, row, columnSpan, rowSpan ) );
2017-07-21 16:21:34 +00:00
resetImplicitSize();
polish();
2017-07-21 16:21:34 +00:00
return index;
2017-07-21 16:21:34 +00:00
}
void QskGridBox::removeAt( int index )
2017-07-21 16:21:34 +00:00
{
auto& engine = m_data->engine;
2017-07-21 16:21:34 +00:00
if ( auto item = engine.itemAt( index ) )
qskSetItemActive( this, item, false );
2017-07-21 16:21:34 +00:00
engine.removeAt( index );
2017-07-21 16:21:34 +00:00
resetImplicitSize();
polish();
2017-07-21 16:21:34 +00:00
}
void QskGridBox::removeItem( const QQuickItem* item )
2017-07-21 16:21:34 +00:00
{
removeAt( indexOf( item ) );
2017-07-21 16:21:34 +00:00
}
void QskGridBox::clear( bool autoDelete )
2017-07-21 16:21:34 +00:00
{
m_data->blockAutoRemove = true;
2017-07-21 16:21:34 +00:00
for ( int i = 0; i < count(); i++ )
{
if ( auto item = itemAtIndex( i ) )
2017-07-21 16:21:34 +00:00
{
qskSetItemActive( this, item, false );
if( autoDelete && ( item->parent() == this ) )
delete item;
else
item->setParentItem( nullptr );
2017-07-21 16:21:34 +00:00
}
}
m_data->blockAutoRemove = false;
m_data->engine.clear();
2017-07-21 16:21:34 +00:00
}
int QskGridBox::count() const
2017-07-21 16:21:34 +00:00
{
return m_data->engine.count();
}
2017-07-21 16:21:34 +00:00
int QskGridBox::rowCount() const
{
return m_data->engine.rowCount();
}
2017-07-21 16:21:34 +00:00
int QskGridBox::columnCount() const
{
return m_data->engine.columnCount();
}
2017-07-21 16:21:34 +00:00
QQuickItem* QskGridBox::itemAtIndex( int index ) const
{
return m_data->engine.itemAt( index );
}
2017-07-21 16:21:34 +00:00
int QskGridBox::indexOf( const QQuickItem* item ) const
{
return m_data->engine.indexOf( item );
2017-07-21 16:21:34 +00:00
}
QQuickItem* QskGridBox::itemAt( int row, int column ) const
2017-07-21 16:21:34 +00:00
{
return m_data->engine.itemAt( row, column );
2017-07-21 16:21:34 +00:00
}
int QskGridBox::indexAt( int row, int column ) const
2017-07-21 16:21:34 +00:00
{
return m_data->engine.indexAt( row, column );
}
2017-07-21 16:21:34 +00:00
QRect QskGridBox::gridOfIndex( int index ) const
{
return m_data->engine.gridAt( index );
}
2017-07-21 16:21:34 +00:00
QRect QskGridBox::effectiveGridOfIndex( int index ) const
{
return m_data->engine.effectiveGridAt( index );
2017-07-21 16:21:34 +00:00
}
void QskGridBox::setDefaultAlignment( Qt::Alignment alignment )
2017-07-21 16:21:34 +00:00
{
if ( m_data->engine.setDefaultAlignment( alignment ) )
Q_EMIT defaultAlignmentChanged();
2017-07-21 16:21:34 +00:00
}
Qt::Alignment QskGridBox::defaultAlignment() const
2017-07-21 16:21:34 +00:00
{
return m_data->engine.defaultAlignment();
2017-07-21 16:21:34 +00:00
}
void QskGridBox::setSpacing( Qt::Orientations orientations, qreal spacing )
2017-07-21 16:21:34 +00:00
{
if ( m_data->engine.setSpacing( spacing, orientations ) )
{
resetImplicitSize();
polish();
2017-07-21 16:21:34 +00:00
}
}
void QskGridBox::resetSpacing( Qt::Orientations orientations )
2017-07-21 16:21:34 +00:00
{
for ( const auto o : { Qt::Horizontal, Qt::Vertical } )
{
if ( orientations & o )
setSpacing( o, m_data->engine.defaultSpacing( o ) );
}
2017-07-21 16:21:34 +00:00
}
qreal QskGridBox::spacing( Qt::Orientation orientation ) const
2017-07-21 16:21:34 +00:00
{
return m_data->engine.spacing( orientation );
2017-07-21 16:21:34 +00:00
}
void QskGridBox::setRowStretchFactor( int row, int stretch )
{
if ( m_data->engine.setStretchFactor( row, stretch, Qt::Vertical ) )
polish();
2017-07-21 16:21:34 +00:00
}
int QskGridBox::rowStretchFactor( int row ) const
{
return m_data->engine.stretchFactor( row, Qt::Vertical );
2017-07-21 16:21:34 +00:00
}
void QskGridBox::setColumnStretchFactor( int column, int stretch )
{
if ( m_data->engine.setStretchFactor( column, stretch, Qt::Horizontal ) )
polish();
2017-07-21 16:21:34 +00:00
}
int QskGridBox::columnStretchFactor( int column ) const
{
return m_data->engine.stretchFactor( column, Qt::Horizontal );
2017-07-21 16:21:34 +00:00
}
void QskGridBox::setRowFixedHeight( int row, qreal height )
2017-07-21 16:21:34 +00:00
{
setRowSizeHint( row, Qt::MinimumSize, height );
setRowSizeHint( row, Qt::MaximumSize, height );
2017-07-21 16:21:34 +00:00
}
void QskGridBox::setColumnFixedWidth( int column, qreal width )
2017-07-21 16:21:34 +00:00
{
setColumnSizeHint( column, Qt::MinimumSize, width );
setColumnSizeHint( column, Qt::MaximumSize, width );
2017-07-21 16:21:34 +00:00
}
void QskGridBox::setRowSizeHint( int row, Qt::SizeHint which, qreal height )
2017-07-21 16:21:34 +00:00
{
if ( m_data->engine.setRowSizeHint( row, which, height ) )
polish();
2017-07-21 16:21:34 +00:00
}
qreal QskGridBox::rowSizeHint( int row, Qt::SizeHint which ) const
2017-07-21 16:21:34 +00:00
{
return m_data->engine.rowSizeHint( row, which );
2017-07-21 16:21:34 +00:00
}
void QskGridBox::setColumnSizeHint( int column, Qt::SizeHint which, qreal width )
2017-07-21 16:21:34 +00:00
{
if ( m_data->engine.setColumnSizeHint( column, which, width ) )
polish();
2017-07-21 16:21:34 +00:00
}
qreal QskGridBox::columnSizeHint( int column, Qt::SizeHint which ) const
2017-07-21 16:21:34 +00:00
{
return m_data->engine.columnSizeHint( column, which );
2017-07-21 16:21:34 +00:00
}
void QskGridBox::invalidate()
2017-07-21 16:21:34 +00:00
{
m_data->engine.invalidate();
resetImplicitSize();
polish();
2017-07-21 16:21:34 +00:00
}
void QskGridBox::updateLayout()
2017-07-21 16:21:34 +00:00
{
2019-06-23 10:53:38 +00:00
if ( !maybeUnresized() )
m_data->engine.setGeometries( layoutRect() );
2017-07-21 16:21:34 +00:00
}
QSizeF QskGridBox::layoutSizeHint(
Qt::SizeHint which, const QSizeF& constraint ) const
2017-07-21 16:21:34 +00:00
{
if ( which == Qt::MaximumSize )
{
// we can extend beyond the maximum size of the children
return QSizeF();
}
return m_data->engine.sizeHint( which, constraint );
2017-07-21 16:21:34 +00:00
}
void QskGridBox::geometryChangeEvent( QskGeometryChangeEvent* event )
2017-07-21 16:21:34 +00:00
{
Inherited::geometryChangeEvent( event );
2017-07-21 16:21:34 +00:00
if ( event->isResized() )
polish();
2017-07-21 16:21:34 +00:00
}
void QskGridBox::itemChange( ItemChange change, const ItemChangeData& value )
2017-07-21 16:21:34 +00:00
{
Inherited::itemChange( change, value );
switch ( change )
{
case ItemChildRemovedChange:
{
if ( !m_data->blockAutoRemove )
removeItem( value.item );
break;
}
case QQuickItem::ItemVisibleHasChanged:
{
if ( value.boolValue )
polish();
break;
}
case QQuickItem::ItemSceneChange:
{
if ( value.window )
polish();
break;
}
default:
break;
}
}
bool QskGridBox::event( QEvent* event )
{
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 );
polish();
break;
}
case QEvent::ContentsRectChange:
{
polish();
break;
}
default:
break;
2017-07-21 16:21:34 +00:00
}
return Inherited::event( event );
2017-07-21 16:21:34 +00:00
}
#include "moc_QskGridBox.cpp"