QGridLayoutEngine replaced by QskGridLayoutEngine also for QskGridBox

This commit is contained in:
Uwe Rathmann 2019-07-27 12:36:52 +02:00
parent beb44e4004
commit f5ee8a3853
15 changed files with 1922 additions and 1678 deletions

View File

@ -7,6 +7,7 @@
#include "QskGridLayoutEngine.h" #include "QskGridLayoutEngine.h"
#include "QskLayoutConstraint.h" #include "QskLayoutConstraint.h"
#include "QskEvent.h" #include "QskEvent.h"
#include <algorithm>
static void qskSetItemActive( QObject* receiver, const QQuickItem* item, bool on ) static void qskSetItemActive( QObject* receiver, const QQuickItem* item, bool on )
{ {
@ -44,10 +45,44 @@ static void qskSetItemActive( QObject* receiver, const QQuickItem* item, bool on
} }
} }
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 class QskGridBox::PrivateData
{ {
public: public:
QskGridLayoutEngine engine; QskGridLayoutEngine engine;
bool blockAutoRemove = false;
}; };
QskGridBox::QskGridBox( QQuickItem* parent ) QskGridBox::QskGridBox( QQuickItem* parent )
@ -60,30 +95,67 @@ QskGridBox::~QskGridBox()
{ {
} }
void QskGridBox::addItem( QQuickItem* item, int QskGridBox::addItem( QQuickItem* item,
int row, int column, int rowSpan, int columnSpan, int row, int column, int rowSpan, int columnSpan,
Qt::Alignment alignment ) Qt::Alignment alignment )
{ {
if ( item == nullptr ) if ( item == nullptr || row < 0 || column < 0 )
return; return -1;
if ( item->parent() == nullptr ) rowSpan = qMax( rowSpan, -1 );
item->setParent( this ); columnSpan = qMax( columnSpan, -1 );
if ( item->parentItem() != this ) auto& engine = m_data->engine;
item->setParentItem( this );
qskSetItemActive( this, item, true ); const QRect itemGrid( column, row, columnSpan, rowSpan );
int index = -1;
// What about the focus tab chain - TODO ... ???? if ( item->parentItem() == this )
// check if item is already inserted ??? {
index = indexOf( item );
if ( index >= 0 )
{
if ( engine.gridAt( index ) == itemGrid )
{
if ( engine.setAlignmentAt( index, alignment ) )
polish();
m_data->engine.insertItem( return index;
item, row, column, rowSpan, columnSpan, alignment ); }
}
}
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, alignment );
}
if ( engine.count() > 1 )
qskUpdateFocusChain( this, &engine, item, itemGrid );
resetImplicitSize(); resetImplicitSize();
polish(); polish();
return index;
}
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 ) );
resetImplicitSize();
polish();
return index;
} }
void QskGridBox::removeAt( int index ) void QskGridBox::removeAt( int index )
@ -106,25 +178,29 @@ void QskGridBox::removeItem( const QQuickItem* item )
void QskGridBox::clear( bool autoDelete ) void QskGridBox::clear( bool autoDelete )
{ {
for ( int i = count() - 1; i >= 0; i-- ) m_data->blockAutoRemove = true;
for ( int i = 0; i < count(); i++ )
{ {
auto item = itemAtIndex( i ); if ( auto item = itemAtIndex( i ) )
removeAt( i );
if( item )
{ {
qskSetItemActive( this, item, false );
if( autoDelete && ( item->parent() == this ) ) if( autoDelete && ( item->parent() == this ) )
delete item; delete item;
else else
item->setParentItem( nullptr ); item->setParentItem( nullptr );
} }
} }
m_data->blockAutoRemove = false;
m_data->engine.clear();
} }
int QskGridBox::count() const int QskGridBox::count() const
{ {
return m_data->engine.itemCount(); return m_data->engine.count();
} }
int QskGridBox::rowCount() const int QskGridBox::rowCount() const
@ -157,47 +233,30 @@ int QskGridBox::indexAt( int row, int column ) const
return m_data->engine.indexAt( row, column ); return m_data->engine.indexAt( row, column );
} }
int QskGridBox::rowOfIndex( int index ) const QRect QskGridBox::gridOfIndex( int index ) const
{ {
return m_data->engine.rowOfIndex( index ); return m_data->engine.gridAt( index );
} }
int QskGridBox::rowSpanOfIndex( int index ) const QRect QskGridBox::effectiveGridOfIndex( int index ) const
{ {
return m_data->engine.rowSpanOfIndex( index ); return m_data->engine.effectiveGridAt( index );
} }
int QskGridBox::columnOfIndex( int index ) const void QskGridBox::setDefaultAlignment( Qt::Alignment alignment )
{ {
return m_data->engine.columnOfIndex( index ); if ( m_data->engine.setDefaultAlignment( alignment ) )
Q_EMIT defaultAlignmentChanged();
} }
int QskGridBox::columnSpanOfIndex( int index ) const Qt::Alignment QskGridBox::defaultAlignment() const
{ {
return m_data->engine.columnSpanOfIndex( index ); return m_data->engine.defaultAlignment();
} }
void QskGridBox::setSpacing( Qt::Orientations orientations, qreal spacing ) void QskGridBox::setSpacing( Qt::Orientations orientations, qreal spacing )
{ {
spacing = qMax( spacing, 0.0 ); if ( m_data->engine.setSpacing( spacing, orientations ) )
bool doUpdate = false;
auto& engine = m_data->engine;
for ( const auto o : { Qt::Horizontal, Qt::Vertical } )
{
if ( orientations & o )
{
if ( spacing != engine.spacing( o ) )
{
engine.setSpacing( o, spacing );
doUpdate = true;
}
}
}
if ( doUpdate )
{ {
resetImplicitSize(); resetImplicitSize();
polish(); polish();
@ -209,7 +268,7 @@ void QskGridBox::resetSpacing( Qt::Orientations orientations )
for ( const auto o : { Qt::Horizontal, Qt::Vertical } ) for ( const auto o : { Qt::Horizontal, Qt::Vertical } )
{ {
if ( orientations & o ) if ( orientations & o )
setSpacing( o, QskGridLayoutEngine::defaultSpacing( o ) ); setSpacing( o, m_data->engine.defaultSpacing( o ) );
} }
} }
@ -218,72 +277,26 @@ qreal QskGridBox::spacing( Qt::Orientation orientation ) const
return m_data->engine.spacing( orientation ); return m_data->engine.spacing( orientation );
} }
void QskGridBox::setRowSpacing( int row, qreal spacing )
{
spacing = qMax( spacing, 0.0 );
auto& engine = m_data->engine;
if ( spacing != engine.spacingAt( Qt::Vertical, row ) )
{
engine.setSpacingAt( Qt::Vertical, row, spacing );
polish();
}
}
qreal QskGridBox::rowSpacing( int row ) const
{
return m_data->engine.spacingAt( Qt::Vertical, row );
}
void QskGridBox::setColumnSpacing( int column, qreal spacing )
{
spacing = qMax( spacing, 0.0 );
auto& engine = m_data->engine;
if ( spacing != engine.spacingAt( Qt::Horizontal, column ) )
{
engine.setSpacingAt( Qt::Horizontal, column, spacing );
polish();
}
}
qreal QskGridBox::columnSpacing( int column ) const
{
return m_data->engine.spacingAt( Qt::Horizontal, column );
}
void QskGridBox::setRowStretchFactor( int row, int stretch ) void QskGridBox::setRowStretchFactor( int row, int stretch )
{ {
auto& engine = m_data->engine; if ( m_data->engine.setStretchFactor( row, stretch, Qt::Vertical ) )
if ( stretch != engine.stretchFactorAt( Qt::Vertical, row ) )
{
engine.setStretchFactorAt( Qt::Vertical, row, stretch );
polish(); polish();
}
} }
int QskGridBox::rowStretchFactor( int row ) const int QskGridBox::rowStretchFactor( int row ) const
{ {
return m_data->engine.stretchFactorAt( Qt::Vertical, row ); return m_data->engine.stretchFactor( row, Qt::Vertical );
} }
void QskGridBox::setColumnStretchFactor( int column, int stretch ) void QskGridBox::setColumnStretchFactor( int column, int stretch )
{ {
auto& engine = m_data->engine; if ( m_data->engine.setStretchFactor( column, stretch, Qt::Horizontal ) )
if ( stretch != engine.stretchFactorAt( Qt::Horizontal, column ) )
{
engine.setStretchFactorAt( Qt::Horizontal, column, stretch );
polish(); polish();
}
} }
int QskGridBox::columnStretchFactor( int column ) const int QskGridBox::columnStretchFactor( int column ) const
{ {
return m_data->engine.stretchFactorAt( Qt::Horizontal, column ); return m_data->engine.stretchFactor( column, Qt::Horizontal );
} }
void QskGridBox::setRowFixedHeight( int row, qreal height ) void QskGridBox::setRowFixedHeight( int row, qreal height )
@ -298,79 +311,46 @@ void QskGridBox::setColumnFixedWidth( int column, qreal width )
setColumnSizeHint( column, Qt::MaximumSize, width ); setColumnSizeHint( column, Qt::MaximumSize, width );
} }
void QskGridBox::setRowAlignment( int row, Qt::Alignment alignment )
{
auto& engine = m_data->engine;
if ( engine.alignmentAt( Qt::Vertical, row ) != alignment )
{
engine.setAlignmentAt( Qt::Vertical, row, alignment );
polish();
}
}
Qt::Alignment QskGridBox::rowAlignment( int row ) const
{
return m_data->engine.alignmentAt( Qt::Vertical, row );
}
void QskGridBox::setColumnAlignment( int column, Qt::Alignment alignment )
{
auto& engine = m_data->engine;
if ( engine.alignmentAt( Qt::Horizontal, column ) != alignment )
{
engine.setAlignmentAt( Qt::Horizontal, column, alignment );
polish();
}
}
Qt::Alignment QskGridBox::columnAlignment( int column ) const
{
return m_data->engine.alignmentAt( Qt::Horizontal, column );
}
void QskGridBox::setAlignment( const QQuickItem* item, Qt::Alignment alignment ) void QskGridBox::setAlignment( const QQuickItem* item, Qt::Alignment alignment )
{ {
auto& engine = m_data->engine; auto& engine = m_data->engine;
if ( engine.alignmentOf( item ) != alignment ) const int index = engine.indexOf( item );
if ( index >= 0 )
{ {
engine.setAlignmentOf( item, alignment ); if ( engine.setAlignmentAt( index, alignment ) )
polish(); polish();
} }
} }
Qt::Alignment QskGridBox::alignment( const QQuickItem* item ) const Qt::Alignment QskGridBox::alignment( const QQuickItem* item ) const
{ {
return m_data->engine.alignmentOf( item ); const auto& engine = m_data->engine;
return engine.alignmentAt( engine.indexOf( item ) );
} }
void QskGridBox::setRetainSizeWhenHidden( const QQuickItem* item, bool on ) void QskGridBox::setRetainSizeWhenHidden( const QQuickItem* item, bool on )
{ {
auto& engine = m_data->engine; auto& engine = m_data->engine;
if ( engine.retainSizeWhenHiddenOf( item ) != on ) const int index = engine.indexOf( item );
if ( index >= 0 )
{ {
engine.setRetainSizeWhenHiddenOf( item, on ); if ( engine.setRetainSizeWhenHiddenAt( index, on ) )
invalidate(); invalidate();
} }
} }
bool QskGridBox::retainSizeWhenHidden( const QQuickItem* item ) const bool QskGridBox::retainSizeWhenHidden( const QQuickItem* item ) const
{ {
return m_data->engine.retainSizeWhenHiddenOf( item ); const auto& engine = m_data->engine;
return engine.retainSizeWhenHiddenAt( engine.indexOf( item ) );
} }
void QskGridBox::setRowSizeHint( int row, Qt::SizeHint which, qreal height ) void QskGridBox::setRowSizeHint( int row, Qt::SizeHint which, qreal height )
{ {
auto& engine = m_data->engine; if ( m_data->engine.setRowSizeHint( row, which, height ) )
if ( height != engine.rowSizeHint( row, which ) )
{
engine.setRowSizeHint( row, which, height );
polish(); polish();
}
} }
qreal QskGridBox::rowSizeHint( int row, Qt::SizeHint which ) const qreal QskGridBox::rowSizeHint( int row, Qt::SizeHint which ) const
@ -380,13 +360,8 @@ qreal QskGridBox::rowSizeHint( int row, Qt::SizeHint which ) const
void QskGridBox::setColumnSizeHint( int column, Qt::SizeHint which, qreal width ) void QskGridBox::setColumnSizeHint( int column, Qt::SizeHint which, qreal width )
{ {
auto& engine = m_data->engine; if ( m_data->engine.setColumnSizeHint( column, which, width ) )
if ( width != engine.columnSizeHint( column, which ) )
{
engine.setColumnSizeHint( column, which, width );
polish(); polish();
}
} }
qreal QskGridBox::columnSizeHint( int column, Qt::SizeHint which ) const qreal QskGridBox::columnSizeHint( int column, Qt::SizeHint which ) const
@ -421,7 +396,8 @@ qreal QskGridBox::heightForWidth( qreal width ) const
auto constrainedHeight = auto constrainedHeight =
[this]( QskLayoutConstraint::Type, const QskControl*, qreal width ) [this]( QskLayoutConstraint::Type, const QskControl*, qreal width )
{ {
return m_data->engine.heightForWidth( width ); const QSizeF constraint( width, -1 );
return m_data->engine.sizeHint( Qt::PreferredSize, constraint ).height();
}; };
return QskLayoutConstraint::constrainedMetric( return QskLayoutConstraint::constrainedMetric(
@ -433,7 +409,8 @@ qreal QskGridBox::widthForHeight( qreal height ) const
auto constrainedWidth = auto constrainedWidth =
[this]( QskLayoutConstraint::Type, const QskControl*, qreal height ) [this]( QskLayoutConstraint::Type, const QskControl*, qreal height )
{ {
return m_data->engine.widthForHeight( height ); const QSizeF constraint( -1, height );
return m_data->engine.sizeHint( Qt::PreferredSize, constraint ).width();
}; };
return QskLayoutConstraint::constrainedMetric( return QskLayoutConstraint::constrainedMetric(
@ -456,7 +433,8 @@ void QskGridBox::itemChange( ItemChange change, const ItemChangeData& value )
{ {
case ItemChildRemovedChange: case ItemChildRemovedChange:
{ {
removeItem( value.item ); if ( !m_data->blockAutoRemove )
removeItem( value.item );
break; break;
} }
case QQuickItem::ItemVisibleHasChanged: case QQuickItem::ItemVisibleHasChanged:

View File

@ -12,6 +12,9 @@ class QSK_EXPORT QskGridBox : public QskBox
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY( Qt::Alignment defaultAlignment READ defaultAlignment
WRITE setDefaultAlignment NOTIFY defaultAlignmentChanged )
Q_PROPERTY( bool empty READ isEmpty() ) Q_PROPERTY( bool empty READ isEmpty() )
Q_PROPERTY( int count READ count ) Q_PROPERTY( int count READ count )
@ -21,14 +24,17 @@ class QSK_EXPORT QskGridBox : public QskBox
explicit QskGridBox( QQuickItem* parent = nullptr ); explicit QskGridBox( QQuickItem* parent = nullptr );
~QskGridBox() override; ~QskGridBox() override;
Q_INVOKABLE void addItem( Q_INVOKABLE int addItem(
QQuickItem*, int row, int column, int rowSpan, int columnSpan, QQuickItem*, int row, int column, int rowSpan, int columnSpan,
Qt::Alignment alignment = Qt::Alignment() ); Qt::Alignment alignment = Qt::Alignment() );
Q_INVOKABLE void addItem( Q_INVOKABLE int addItem(
QQuickItem*, int row, int column, QQuickItem*, int row, int column,
Qt::Alignment alignment = Qt::Alignment() ); Qt::Alignment alignment = Qt::Alignment() );
Q_INVOKABLE int addSpacer( qreal spacing,
int row, int column, int columnSpan = 1, int rowSpan = 1 );
void removeItem( const QQuickItem* ); void removeItem( const QQuickItem* );
void removeAt( int index ); void removeAt( int index );
@ -48,13 +54,11 @@ class QSK_EXPORT QskGridBox : public QskBox
Q_INVOKABLE QQuickItem* itemAt( int row, int column ) const; Q_INVOKABLE QQuickItem* itemAt( int row, int column ) const;
Q_INVOKABLE int indexAt( int row, int column ) const; Q_INVOKABLE int indexAt( int row, int column ) const;
Q_INVOKABLE int rowOfIndex( int index ) const; Q_INVOKABLE QRect gridOfIndex( int index ) const;
Q_INVOKABLE int rowSpanOfIndex( int index ) const; Q_INVOKABLE QRect effectiveGridOfIndex( int index ) const;
Q_INVOKABLE int columnOfIndex( int index ) const; void setDefaultAlignment( Qt::Alignment );
Q_INVOKABLE int columnSpanOfIndex( int index ) const; Qt::Alignment defaultAlignment() const;
// spacings
void setSpacing( Qt::Orientations, qreal spacing ); void setSpacing( Qt::Orientations, qreal spacing );
void resetSpacing( Qt::Orientations ); void resetSpacing( Qt::Orientations );
@ -70,24 +74,16 @@ class QSK_EXPORT QskGridBox : public QskBox
void setHorizontalSpacing( qreal spacing ) { setSpacing( Qt::Horizontal, spacing ); } void setHorizontalSpacing( qreal spacing ) { setSpacing( Qt::Horizontal, spacing ); }
qreal horizontalSpacing() const { return spacing( Qt::Horizontal ); } qreal horizontalSpacing() const { return spacing( Qt::Horizontal ); }
void setSpacing( qreal spacing ) { setSpacing( Qt::Horizontal | Qt::Vertical, spacing ); }
void setActive( bool ) {} void setActive( bool ) {}
bool isActive() const { return true; } bool isActive() const { return true; }
void setRowMinimumHeight( int column, qreal height ) void setRowMinimumHeight( int row, qreal height )
{ setRowSizeHint( column, Qt::MinimumSize, height ); } { setRowSizeHint( row, Qt::MinimumSize, height ); }
void setColumnMaximumWidth( int column, qreal width ) void setColumnMaximumWidth( int column, qreal width )
{ setColumnSizeHint( column, Qt::MaximumSize, width ); } { setColumnSizeHint( column, Qt::MaximumSize, width ); }
#endif #endif
Q_INVOKABLE void setRowSpacing( int row, qreal spacing );
Q_INVOKABLE qreal rowSpacing( int row ) const;
Q_INVOKABLE void setColumnSpacing( int column, qreal spacing );
Q_INVOKABLE qreal columnSpacing( int column ) const;
// stretch factors // stretch factors
Q_INVOKABLE void setRowStretchFactor( int row, int stretch ); Q_INVOKABLE void setRowStretchFactor( int row, int stretch );
Q_INVOKABLE int rowStretchFactor( int row ) const; Q_INVOKABLE int rowStretchFactor( int row ) const;
@ -108,12 +104,6 @@ class QSK_EXPORT QskGridBox : public QskBox
// alignments // alignments
Q_INVOKABLE void setRowAlignment( int row, Qt::Alignment alignment );
Q_INVOKABLE Qt::Alignment rowAlignment( int row ) const;
Q_INVOKABLE void setColumnAlignment( int column, Qt::Alignment alignment );
Q_INVOKABLE Qt::Alignment columnAlignment( int column ) const;
void setAlignment( const QQuickItem* item, Qt::Alignment alignment ); void setAlignment( const QQuickItem* item, Qt::Alignment alignment );
Qt::Alignment alignment( const QQuickItem* item ) const; Qt::Alignment alignment( const QQuickItem* item ) const;
@ -129,6 +119,9 @@ class QSK_EXPORT QskGridBox : public QskBox
void invalidate(); void invalidate();
void clear( bool autoDelete = false ); void clear( bool autoDelete = false );
Q_SIGNALS:
void defaultAlignmentChanged();
protected: protected:
bool event( QEvent* ) override; bool event( QEvent* ) override;
void geometryChangeEvent( QskGeometryChangeEvent* ) override; void geometryChangeEvent( QskGeometryChangeEvent* ) override;
@ -141,10 +134,10 @@ class QSK_EXPORT QskGridBox : public QskBox
std::unique_ptr< PrivateData > m_data; std::unique_ptr< PrivateData > m_data;
}; };
inline void QskGridBox::addItem( inline int QskGridBox::addItem(
QQuickItem* item, int row, int column, Qt::Alignment alignment ) QQuickItem* item, int row, int column, Qt::Alignment alignment )
{ {
addItem( item, row, column, 1, 1, alignment ); return addItem( item, row, column, 1, 1, alignment );
} }
inline bool QskGridBox::isEmpty() const inline bool QskGridBox::isEmpty() const

File diff suppressed because it is too large Load Diff

View File

@ -7,89 +7,68 @@
#define QSK_GRID_LAYOUT_ENGINE_H #define QSK_GRID_LAYOUT_ENGINE_H
#include "QskGlobal.h" #include "QskGlobal.h"
#include "QskLayoutEngine2D.h"
#include <qnamespace.h> #include <qnamespace.h>
#include <qrect.h>
#include <memory> #include <memory>
class QQuickItem; class QQuickItem;
class QSizeF;
class QRectF;
class QskGridLayoutEngine class QskGridLayoutEngine : public QskLayoutEngine2D
{ {
public: public:
QskGridLayoutEngine(); QskGridLayoutEngine();
~QskGridLayoutEngine(); ~QskGridLayoutEngine() override;
void setGeometries( const QRectF ); int count() const override final;
void invalidate();
void setVisualDirection( Qt::LayoutDirection ); bool setStretchFactor( int pos, int stretch, Qt::Orientation );
Qt::LayoutDirection visualDirection() const; int stretchFactor( int pos, Qt::Orientation ) const;
int itemCount() const; bool setRowSizeHint( int row, Qt::SizeHint, qreal height );
int rowCount() const;
int columnCount() const;
void insertItem( QQuickItem* item,
int row, int column, int rowSpan, int columnSpan,
Qt::Alignment );
void removeAt( int index );
int indexAt( int row, int column ) const;
int indexOf( const QQuickItem* item ) const;
QQuickItem* itemAt( int index ) const;
QQuickItem* itemAt( int row, int column ) const;
int rowOfIndex( int index ) const;
int rowSpanOfIndex( int index ) const;
int columnOfIndex( int index ) const;
int columnSpanOfIndex( int index ) const;
void setSpacing( Qt::Orientation, qreal spacing );
qreal spacing( Qt::Orientation ) const;
void setSpacingAt( Qt::Orientation, int cell, qreal spacing );
qreal spacingAt( Qt::Orientation, int cell ) const;
void setStretchFactorAt( Qt::Orientation, int cell, int stretch );
int stretchFactorAt( Qt::Orientation, int cell );
void setAlignmentAt( Qt::Orientation, int cell, Qt::Alignment );
Qt::Alignment alignmentAt( Qt::Orientation, int cell ) const;
void setAlignmentOf( const QQuickItem*, Qt::Alignment );
Qt::Alignment alignmentOf( const QQuickItem* ) const;
void setRetainSizeWhenHiddenOf( const QQuickItem*, bool on );
bool retainSizeWhenHiddenOf( const QQuickItem* ) const;
void setRowSizeHint( int row, Qt::SizeHint, qreal height );
qreal rowSizeHint( int row, Qt::SizeHint ) const; qreal rowSizeHint( int row, Qt::SizeHint ) const;
void setColumnSizeHint( int column, Qt::SizeHint, qreal width ); bool setColumnSizeHint( int column, Qt::SizeHint, qreal width );
qreal columnSizeHint( int column, Qt::SizeHint ) const; qreal columnSizeHint( int column, Qt::SizeHint ) const;
QSizeF sizeHint( Qt::SizeHint, const QSizeF& constraint = QSizeF() ) const; int insertItem( QQuickItem*, const QRect& grid, Qt::Alignment );
int insertSpacer( qreal spacing, const QRect& grid );
qreal widthForHeight( qreal height ) const; bool removeAt( int index );
qreal heightForWidth( qreal width ) const; bool clear();
static qreal defaultSpacing( Qt::Orientation ); QQuickItem* itemAt( int index ) const override final;
qreal spacerAt( int index ) const override final;
#if 1 QQuickItem* itemAt( int row, int column ) const;
QSize requiredCells() const; int indexAt( int row, int column ) const;
void adjustSpans( int numRows, int numColumns );
#endif bool setGridAt( int index, const QRect& );
QRect gridAt( int index ) const;
QRect effectiveGridAt( int index ) const;
bool setRetainSizeWhenHiddenAt( int index, bool on );
bool retainSizeWhenHiddenAt( int index ) const;
bool setAlignmentAt( int index, Qt::Alignment );
Qt::Alignment alignmentAt( int index ) const;
void transpose();
private: private:
Q_DISABLE_COPY(QskGridLayoutEngine) void layoutItems() override;
int effectiveCount( Qt::Orientation ) const override;
void invalidateElementCache() override;
void setupChain( Qt::Orientation,
const QskLayoutChain::Segments&, QskLayoutChain& ) const override;
class PrivateData; class PrivateData;
std::unique_ptr< PrivateData > m_data; PrivateData* m_data;
}; };
#endif #endif

View File

@ -13,6 +13,8 @@
#include <cmath> #include <cmath>
#endif #endif
#include <qdebug.h>
QskLayoutChain::QskLayoutChain() QskLayoutChain::QskLayoutChain()
{ {
} }
@ -29,13 +31,13 @@ void QskLayoutChain::invalidate()
void QskLayoutChain::reset( int count, qreal constraint ) void QskLayoutChain::reset( int count, qreal constraint )
{ {
m_cells.assign( count, Cell() ); m_cells.fill( CellData(), count );
m_constraint = constraint; m_constraint = constraint;
m_sumStretches = 0; m_sumStretches = 0;
m_validCells = 0; m_validCells = 0;
} }
void QskLayoutChain::expandTo( int index, const Cell& newCell ) void QskLayoutChain::narrowCell( int index, const CellData& newCell )
{ {
if ( !newCell.isValid ) if ( !newCell.isValid )
return; return;
@ -45,12 +47,109 @@ void QskLayoutChain::expandTo( int index, const Cell& newCell )
if ( !cell.isValid ) if ( !cell.isValid )
{ {
cell = newCell; cell = newCell;
cell.stretch = qMax( cell.stretch, 0 );
m_validCells++;
}
else
{
cell.canGrow &= newCell.canGrow;
if ( newCell.stretch >= 0 )
cell.stretch = qMax( cell.stretch, newCell.stretch );
if ( !newCell.hint.isDefault() )
{
cell.hint.setSizes(
qMax( cell.hint.minimum(), newCell.hint.minimum() ),
qMax( cell.hint.preferred(), newCell.hint.preferred() ),
qMin( cell.hint.maximum(), newCell.hint.maximum() )
);
cell.hint.normalize();
}
}
}
void QskLayoutChain::expandCell( int index, const CellData& newCell )
{
if ( !newCell.isValid )
return;
auto& cell = m_cells[ index ];
if ( !cell.isValid )
{
cell = newCell;
cell.stretch = qMax( cell.stretch, 0 );
m_validCells++;
} }
else else
{ {
cell.canGrow |= newCell.canGrow; cell.canGrow |= newCell.canGrow;
cell.stretch = qMax( cell.stretch, newCell.stretch ); cell.stretch = qMax( cell.stretch, newCell.stretch );
cell.hint.expandTo( newCell.hint );
cell.hint.setSizes(
qMax( cell.hint.minimum(), newCell.hint.minimum() ),
qMax( cell.hint.preferred(), newCell.hint.preferred() ),
qMax( cell.hint.maximum(), newCell.hint.maximum() )
);
}
}
void QskLayoutChain::expandCells(
int index, int count, const CellData& multiCell )
{
QskLayoutChain chain;
chain.reset( count, -1 );
for ( int i = 0; i < count; i++ )
{
chain.expandCell( i, m_cells[ index + i ] );
auto& cell = chain.m_cells[ i ];
#if 1
// what to do now ??
if ( !cell.isValid )
{
cell.isValid = true;
cell.canGrow = multiCell.canGrow;
cell.stretch = qMax( cell.stretch, 0 );
}
#endif
}
chain.m_validCells = count;
QVarLengthArray< QskLayoutHint > hints( count );
const auto& hint = multiCell.hint;
const auto chainHint = chain.boundingHint();
if ( hint.minimum() > chainHint.minimum() )
{
const auto segments = chain.segments( hint.minimum() );
for ( int i = 0; i < count; i++ )
hints[i].setMinimum( segments[i].length );
}
if ( hint.preferred() > chainHint.preferred() )
{
const auto segments = chain.segments( hint.preferred() );
for ( int i = 0; i < count; i++ )
hints[i].setPreferred( segments[i].length );
}
if ( hint.maximum() < chainHint.maximum() )
{
const auto segments = chain.segments( hint.maximum() );
for ( int i = 0; i < count; i++ )
hints[i].setMaximum( segments[i].length );
}
for ( int i = 0; i < count; i++ )
{
auto cell = multiCell;
cell.hint = hints[i];
expandCell( index + i, cell );
} }
} }
@ -119,24 +218,24 @@ bool QskLayoutChain::setSpacing( qreal spacing )
return false; return false;
} }
QVector< QskLayoutChain::Range > QskLayoutChain::geometries( qreal size ) const QskLayoutChain::Segments QskLayoutChain::segments( qreal size ) const
{ {
if ( m_validCells == 0 ) if ( m_validCells == 0 )
return QVector< Range >(); return Segments();
QVector< Range > ranges; Segments segments;
if ( size <= m_boundingHint.minimum() ) if ( size <= m_boundingHint.minimum() )
{ {
ranges = distributed( Qt::MinimumSize, 0.0, 0.0 ); segments = distributed( Qt::MinimumSize, 0.0, 0.0 );
} }
else if ( size < m_boundingHint.preferred() ) else if ( size < m_boundingHint.preferred() )
{ {
ranges = minimumExpanded( size ); segments = minimumExpanded( size );
} }
else if ( size <= m_boundingHint.maximum() ) else if ( size <= m_boundingHint.maximum() )
{ {
ranges = preferredStretched( size ); segments = preferredStretched( size );
} }
else else
{ {
@ -145,70 +244,70 @@ QVector< QskLayoutChain::Range > QskLayoutChain::geometries( qreal size ) const
qreal offset = 0.0; qreal offset = 0.0;
qreal extra = 0.0;; qreal extra = 0.0;;
if ( m_extraSpacingAt == Qt::LeftEdge ) switch( m_extraSpacingAt )
{ {
offset = padding; case Leading:
} offset = padding;
else if ( m_extraSpacingAt == Qt::RightEdge ) break;
{
offset = 0.0; case Trailing:
} break;
else if ( m_extraSpacingAt == ( Qt::LeftEdge | Qt::RightEdge ) )
{ case Leading | Trailing:
offset = 0.5 * padding; offset = 0.5 * padding;
} break;
else
{ default:
extra = padding / m_validCells; extra = padding / m_validCells;
} }
ranges = distributed( Qt::MaximumSize, offset, extra ); segments = distributed( Qt::MaximumSize, offset, extra );
} }
return ranges; return segments;
} }
QVector< QskLayoutChain::Range > QskLayoutChain::distributed( QskLayoutChain::Segments QskLayoutChain::distributed(
int which, qreal offset, const qreal extra ) const int which, qreal offset, const qreal extra ) const
{ {
qreal fillSpacing = 0.0; qreal fillSpacing = 0.0;
QVector< Range > ranges( m_cells.size() ); Segments segments( m_cells.size() );
for ( int i = 0; i < ranges.count(); i++ ) for ( int i = 0; i < segments.count(); i++ )
{ {
const auto& cell = m_cells[i]; const auto& cell = m_cells[i];
auto& range = ranges[i]; auto& segment = segments[i];
if ( !cell.isValid ) if ( !cell.isValid )
{ {
range.start = offset; segment.start = offset;
range.length = 0.0; segment.length = 0.0;
} }
else else
{ {
offset += fillSpacing; offset += fillSpacing;
fillSpacing = m_spacing; fillSpacing = m_spacing;
range.start = offset; segment.start = offset;
range.length = cell.hint.size( which ) + extra; segment.length = cell.hint.size( which ) + extra;
offset += range.length; offset += segment.length;
} }
} }
return ranges; return segments;
} }
QVector< QskLayoutChain::Range > QskLayoutChain::minimumExpanded( qreal size ) const QskLayoutChain::Segments QskLayoutChain::minimumExpanded( qreal size ) const
{ {
QVector< Range > ranges( m_cells.size() ); Segments segments( m_cells.size() );
qreal fillSpacing = 0.0; qreal fillSpacing = 0.0;
qreal offset = 0.0; qreal offset = 0.0;
/* /*
We have different options how to distribute the availabe space We have different options how to distribute the available space
- according to the preferred sizes - according to the preferred sizes
@ -231,7 +330,7 @@ QVector< QskLayoutChain::Range > QskLayoutChain::minimumExpanded( qreal size ) c
const qreal desired = m_boundingHint.preferred() - m_boundingHint.minimum(); const qreal desired = m_boundingHint.preferred() - m_boundingHint.minimum();
const qreal available = size - m_boundingHint.minimum(); const qreal available = size - m_boundingHint.minimum();
for ( uint i = 0; i < m_cells.size(); i++ ) for ( int i = 0; i < m_cells.size(); i++ )
{ {
const auto& cell = m_cells[i]; const auto& cell = m_cells[i];
if ( !cell.isValid ) if ( !cell.isValid )
@ -247,67 +346,67 @@ QVector< QskLayoutChain::Range > QskLayoutChain::minimumExpanded( qreal size ) c
} }
} }
for ( uint i = 0; i < m_cells.size(); i++ ) for ( int i = 0; i < m_cells.size(); i++ )
{ {
const auto& cell = m_cells[i]; const auto& cell = m_cells[i];
auto& range = ranges[i]; auto& segment = segments[i];
if ( !cell.isValid ) if ( !cell.isValid )
{ {
range.start = offset; segment.start = offset;
range.length = 0.0; segment.length = 0.0;
} }
else else
{ {
offset += fillSpacing; offset += fillSpacing;
fillSpacing = m_spacing; fillSpacing = m_spacing;
range.start = offset; segment.start = offset;
range.length = cell.hint.minimum() segment.length = cell.hint.minimum()
+ available * ( factors[i] / sumFactors ); + available * ( factors[i] / sumFactors );
offset += range.length; offset += segment.length;
} }
} }
#else #else
const qreal factor = ( size - m_boundingHint.minimum() ) / const qreal factor = ( size - m_boundingHint.minimum() ) /
( m_boundingHint.preferred() - m_boundingHint.minimum() ); ( m_boundingHint.preferred() - m_boundingHint.minimum() );
for ( uint i = 0; i < m_cells.size(); i++ ) for ( int i = 0; i < m_cells.count(); i++ )
{ {
const auto& cell = m_cells[i]; const auto& cell = m_cells[i];
auto& range = ranges[i]; auto& segment = segments[i];
if ( !cell.isValid ) if ( !cell.isValid )
{ {
range.start = offset; segment.start = offset;
range.length = 0.0; segment.length = 0.0;
} }
else else
{ {
offset += fillSpacing; offset += fillSpacing;
fillSpacing = m_spacing; fillSpacing = m_spacing;
range.start = offset; segment.start = offset;
range.length = cell.hint.minimum() segment.length = cell.hint.minimum()
+ factor * ( cell.hint.preferred() - cell.hint.minimum() ); + factor * ( cell.hint.preferred() - cell.hint.minimum() );
offset += range.length; offset += segment.length;
} }
} }
#endif #endif
return ranges; return segments;
} }
QVector< QskLayoutChain::Range > QskLayoutChain::preferredStretched( qreal size ) const QskLayoutChain::Segments QskLayoutChain::preferredStretched( qreal size ) const
{ {
const int count = m_cells.size(); const int count = m_cells.size();
qreal sumFactors = 0.0; qreal sumFactors = 0.0;
QVarLengthArray< qreal > factors( count ); QVarLengthArray< qreal > factors( count );
QVector< Range > ranges( count ); Segments segments( count );
for ( int i = 0; i < count; i++ ) for ( int i = 0; i < count; i++ )
{ {
@ -315,7 +414,7 @@ QVector< QskLayoutChain::Range > QskLayoutChain::preferredStretched( qreal size
if ( !cell.isValid ) if ( !cell.isValid )
{ {
ranges[i].length = 0.0; segments[i].length = 0.0;
factors[i] = -1.0; factors[i] = -1.0;
continue; continue;
} }
@ -355,7 +454,7 @@ QVector< QskLayoutChain::Range > QskLayoutChain::preferredStretched( qreal size
if ( boundedSize != size ) if ( boundedSize != size )
{ {
ranges[i].length = boundedSize; segments[i].length = boundedSize;
sumSizes -= boundedSize; sumSizes -= boundedSize;
sumFactors -= factors[i]; sumFactors -= factors[i];
factors[i] = -1.0; factors[i] = -1.0;
@ -374,7 +473,7 @@ QVector< QskLayoutChain::Range > QskLayoutChain::preferredStretched( qreal size
for ( int i = 0; i < count; i++ ) for ( int i = 0; i < count; i++ )
{ {
const auto& cell = m_cells[i]; const auto& cell = m_cells[i];
auto& range = ranges[i]; auto& segment = segments[i];
const auto& factor = factors[i]; const auto& factor = factors[i];
@ -384,37 +483,37 @@ QVector< QskLayoutChain::Range > QskLayoutChain::preferredStretched( qreal size
fillSpacing = m_spacing; fillSpacing = m_spacing;
} }
range.start = offset; segment.start = offset;
if ( factor >= 0.0 ) if ( factor >= 0.0 )
{ {
if ( factor > 0.0 ) if ( factor > 0.0 )
range.length = sumSizes * factor / sumFactors; segment.length = sumSizes * factor / sumFactors;
else else
range.length = cell.hint.preferred(); segment.length = cell.hint.preferred();
} }
offset += range.length; offset += segment.length;
} }
return ranges; return segments;
} }
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM
#include <qdebug.h> #include <qdebug.h>
QDebug operator<<( QDebug debug, const QskLayoutChain::Range& range ) QDebug operator<<( QDebug debug, const QskLayoutChain::Segment& segment )
{ {
QDebugStateSaver saver( debug ); QDebugStateSaver saver( debug );
debug.nospace(); debug.nospace();
debug << "( " << range.start << ", " << range.end() << " )"; debug << "( " << segment.start << ", " << segment.end() << " )";
return debug; return debug;
} }
QDebug operator<<( QDebug debug, const QskLayoutChain::Cell& cell ) QDebug operator<<( QDebug debug, const QskLayoutChain::CellData& cell )
{ {
QDebugStateSaver saver( debug ); QDebugStateSaver saver( debug );
debug.nospace(); debug.nospace();

View File

@ -7,16 +7,15 @@
#define QSK_LAYOUT_CHAIN_H #define QSK_LAYOUT_CHAIN_H
#include <QskLayoutHint.h> #include <QskLayoutHint.h>
#include <qglobal.h> #include <qrect.h>
#include <qvector.h> #include <qvector.h>
#include <vector>
class QDebug; class QDebug;
class QskLayoutChain class QskLayoutChain
{ {
public: public:
class Range class Segment
{ {
public: public:
inline qreal end() const { return start + length; } inline qreal end() const { return start + length; }
@ -25,21 +24,12 @@ class QskLayoutChain
qreal length = 0.0; qreal length = 0.0;
}; };
class Cell typedef QVector< Segment > Segments;
class CellData
{ {
public: public:
Cell() inline bool operator==( const CellData& other ) const
{
}
Cell( QskLayoutHint hint, int stretch )
: hint( hint )
, stretch( stretch )
, isValid( true )
{
}
inline bool operator==( const Cell& other ) const
{ {
return ( isValid == other.isValid ) return ( isValid == other.isValid )
&& ( canGrow == other.canGrow ) && ( canGrow == other.canGrow )
@ -47,65 +37,83 @@ class QskLayoutChain
&& ( hint == other.hint ); && ( hint == other.hint );
} }
inline bool operator!=( const Cell& other ) const inline bool operator!=( const CellData& other ) const
{ {
return !( *this == other ); return !( *this == other );
} }
inline qreal size( int which ) const
{
return hint.size( which );
}
inline void setSize( int which, qreal size )
{
hint.setSize( which, size );
}
QskLayoutHint hint; QskLayoutHint hint;
int stretch = 0; int stretch = 0;
bool canGrow = false; bool canGrow = false;
bool isValid = false; bool isValid = false;
}; };
enum ExtraSpacing
{
Leading = 1 << 0,
Trailing = 1 << 1
};
QskLayoutChain(); QskLayoutChain();
~QskLayoutChain(); ~QskLayoutChain();
void invalidate(); void invalidate();
void reset( int count, qreal constraint ); void reset( int count, qreal constraint );
void expandTo( int index, const Cell& ); void expandCell( int index, const CellData& );
void expandCells( int start, int end, const CellData& );
void narrowCell( int index, const CellData& );
void finish(); void finish();
const Cell& cell( int index ) const { return m_cells[ index ]; } const CellData& cell( int index ) const { return m_cells[ index ]; }
bool setSpacing( qreal spacing ); bool setSpacing( qreal spacing );
qreal spacing() const { return m_spacing; } qreal spacing() const { return m_spacing; }
void setExtraSpacingAt( Qt::Edges edges ) { m_extraSpacingAt = edges; } void setExtraSpacingAt( int extraSpacingAt ) { m_extraSpacingAt = extraSpacingAt; }
QVector< Range > geometries( qreal size ) const; Segments segments( qreal size ) const;
QskLayoutHint boundingHint() const { return m_boundingHint; } QskLayoutHint boundingHint() const { return m_boundingHint; }
inline qreal constraint() const { return m_constraint; } inline qreal constraint() const { return m_constraint; }
inline int count() const { return m_cells.size(); } inline int count() const { return m_cells.size(); }
private: private:
Q_DISABLE_COPY( QskLayoutChain ) Segments distributed( int which, qreal offset, qreal extra ) const;
Segments minimumExpanded( qreal size ) const;
QVector< Range > distributed( int which, qreal offset, qreal extra ) const; Segments preferredStretched( qreal size ) const;
QVector< Range > minimumExpanded( qreal size ) const;
QVector< Range > preferredStretched( qreal size ) const;
QskLayoutHint m_boundingHint; QskLayoutHint m_boundingHint;
qreal m_constraint = -2.0; qreal m_constraint = -2.0;
qreal m_spacing = 0; qreal m_spacing = 0;
Qt::Edges m_extraSpacingAt; int m_extraSpacingAt;
int m_sumStretches = 0; int m_sumStretches = 0;
int m_validCells = 0; int m_validCells = 0;
std::vector< Cell > m_cells;
QVector< CellData > m_cells;
}; };
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM
QDebug operator<<( QDebug, const QskLayoutChain::Range& ); QDebug operator<<( QDebug, const QskLayoutChain::Segment& );
QDebug operator<<( QDebug, const QskLayoutChain::Cell& ); QDebug operator<<( QDebug, const QskLayoutChain::CellData& );
#endif #endif
Q_DECLARE_TYPEINFO( QskLayoutChain::Range, Q_MOVABLE_TYPE ); Q_DECLARE_TYPEINFO( QskLayoutChain::Segment, Q_MOVABLE_TYPE );
Q_DECLARE_TYPEINFO( QskLayoutChain::Cell, Q_MOVABLE_TYPE ); Q_DECLARE_TYPEINFO( QskLayoutChain::CellData, Q_MOVABLE_TYPE );
#endif #endif

View File

@ -0,0 +1,455 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskLayoutEngine2D.h"
#include "QskLayoutChain.h"
#include "QskQuick.h"
#include <qguiapplication.h>
namespace
{
class LayoutData
{
public:
QRectF geometryAt( const QRect& grid ) const
{
const auto x1 = columns[ grid.left() ].start;
const auto x2 = columns[ grid.right() ].end();
const auto y1 = rows[ grid.top() ].start;
const auto y2 = rows[ grid.bottom() ].end();
return QRectF( rect.x() + x1, rect.y() + y1, x2 - x1, y2 - y1 );
}
Qt::LayoutDirection direction;
QRectF rect;
QskLayoutChain::Segments rows;
QskLayoutChain::Segments columns;
};
}
class QskLayoutEngine2D::PrivateData
{
public:
PrivateData()
: defaultAlignment( Qt::AlignLeft | Qt::AlignVCenter )
, extraSpacingAt( 0 )
, visualDirection( Qt::LeftToRight )
, constraintType( -1 )
, blockInvalidate( false )
{
}
inline QskLayoutChain& layoutChain( Qt::Orientation orientation )
{
return ( orientation == Qt::Horizontal ) ? columnChain : rowChain;
}
inline Qt::Alignment effectiveAlignment( Qt::Alignment alignment ) const
{
const auto align = static_cast< Qt::Alignment >( defaultAlignment );
if ( !( alignment & Qt::AlignVertical_Mask ) )
alignment |= ( align & Qt::AlignVertical_Mask );
if ( !( alignment & Qt::AlignHorizontal_Mask ) )
alignment |= ( align & Qt::AlignHorizontal_Mask );
return alignment;
}
QskLayoutChain columnChain;
QskLayoutChain rowChain;
QSizeF layoutSize;
QskLayoutChain::Segments rows;
QskLayoutChain::Segments columns;
const LayoutData* layoutData = nullptr;
unsigned int defaultAlignment : 8;
unsigned int extraSpacingAt : 4;
unsigned int visualDirection : 4;
int constraintType : 3;
/*
Some weired controls do lazy updates inside of their sizeHint calculation
that lead to LayoutRequest events. While being in the process of
updating the tables we can't - and don't need to - handle invalidations
because of them.
*/
bool blockInvalidate : 1;
};
QskLayoutEngine2D::QskLayoutEngine2D()
: m_data( new PrivateData )
{
m_data->columnChain.setSpacing( defaultSpacing( Qt::Horizontal ) );
m_data->rowChain.setSpacing( defaultSpacing( Qt::Vertical ) );
}
QskLayoutEngine2D::~QskLayoutEngine2D()
{
}
bool QskLayoutEngine2D::setVisualDirection( Qt::LayoutDirection direction )
{
if ( m_data->visualDirection != direction )
{
m_data->visualDirection = direction;
return true;
}
return false;
}
Qt::LayoutDirection QskLayoutEngine2D::visualDirection() const
{
return static_cast< Qt::LayoutDirection >( m_data->visualDirection );
}
bool QskLayoutEngine2D::setDefaultAlignment( Qt::Alignment alignment )
{
if ( defaultAlignment() != alignment )
{
m_data->defaultAlignment = alignment;
return true;
}
return false;
}
Qt::Alignment QskLayoutEngine2D::defaultAlignment() const
{
return static_cast< Qt::Alignment >( m_data->defaultAlignment );
}
qreal QskLayoutEngine2D::defaultSpacing( Qt::Orientation ) const
{
return 5.0; // should be from the skin
}
bool QskLayoutEngine2D::setSpacing(
qreal spacing, Qt::Orientations orientations )
{
if ( spacing < 0.0 )
spacing = 0.0;
bool isModified = false;
for ( auto o : { Qt::Horizontal, Qt::Vertical } )
{
if ( orientations & o )
isModified |= m_data->layoutChain( o ).setSpacing( spacing );
}
if ( isModified )
invalidate( LayoutCache );
return isModified;
}
qreal QskLayoutEngine2D::spacing( Qt::Orientation orientation ) const
{
return m_data->layoutChain( orientation ).spacing();
}
bool QskLayoutEngine2D::setExtraSpacingAt( Qt::Edges edges )
{
if ( edges == extraSpacingAt() )
return false;
m_data->extraSpacingAt = edges;
int value = 0;
if ( edges & Qt::LeftEdge )
value |= QskLayoutChain::Leading;
if ( edges & Qt::RightEdge )
value |= QskLayoutChain::Trailing;
m_data->columnChain.setExtraSpacingAt( value );
value = 0;
if ( edges & Qt::TopEdge )
value |= QskLayoutChain::Leading;
if ( edges & Qt::BottomEdge )
value |= QskLayoutChain::Trailing;
m_data->rowChain.setExtraSpacingAt( value );
invalidate();
return true;
}
int QskLayoutEngine2D::indexOf( const QQuickItem* item ) const
{
if ( item )
{
/*
indexOf is often called after inserting an item to
set additinal properties. So we search in reverse order
*/
for ( int i = count() - 1; i >= 0; --i )
{
if ( itemAt( i ) == item )
return i;
}
}
return -1;
}
Qt::Edges QskLayoutEngine2D::extraSpacingAt() const
{
return static_cast< Qt::Edges >( m_data->extraSpacingAt );
}
void QskLayoutEngine2D::setGeometries( const QRectF& rect )
{
if ( rowCount() < 1 || columnCount() < 1 )
return;
if ( m_data->layoutSize != rect.size() )
{
m_data->layoutSize = rect.size();
updateSegments( rect.size() );
}
/*
In case we have items that send LayoutRequest events on
geometry changes - what doesn't make much sense - we
better make a ( implicitely shared ) copy of the rows/columns.
*/
LayoutData data;
data.rows = m_data->rows;
data.columns = m_data->columns;
data.rect = rect;
data.direction = visualDirection();
if ( data.direction == Qt::LayoutDirectionAuto )
data.direction = QGuiApplication::layoutDirection();
m_data->layoutData = &data;
layoutItems();
m_data->layoutData = nullptr;
}
void QskLayoutEngine2D::layoutItem( QQuickItem* item,
const QRect& grid, Qt::Alignment alignment ) const
{
auto layoutData = m_data->layoutData;
if ( layoutData == nullptr || item == nullptr )
return;
alignment = m_data->effectiveAlignment( alignment );
QRectF rect = layoutData->geometryAt( grid );
rect = QskLayoutConstraint::itemRect(item, rect, alignment );
if ( layoutData->direction == Qt::RightToLeft )
{
const auto& r = layoutData->rect;
rect.moveRight( r.right() - ( rect.left() - r.left() ) );
}
qskSetItemGeometry( item, rect );
}
qreal QskLayoutEngine2D::widthForHeight( qreal height ) const
{
const QSizeF constraint( -1, height );
return sizeHint( Qt::PreferredSize, constraint ).width();
}
qreal QskLayoutEngine2D::heightForWidth( qreal width ) const
{
const QSizeF constraint( width, -1 );
return sizeHint( Qt::PreferredSize, constraint ).height();
}
QSizeF QskLayoutEngine2D::sizeHint(
Qt::SizeHint which, const QSizeF& constraint ) const
{
if ( effectiveCount( Qt::Horizontal ) <= 0 )
return QSizeF( 0.0, 0.0 );
auto& rowChain = m_data->rowChain;
auto& columnChain = m_data->columnChain;
m_data->blockInvalidate = true;
if ( ( constraint.width() >= 0 ) &&
( constraintType() == QskLayoutConstraint::HeightForWidth ) )
{
setupChain( Qt::Horizontal );
const auto constraints = columnChain.segments( constraint.width() );
setupChain( Qt::Vertical, constraints );
}
else if ( ( constraint.height() >= 0 ) &&
( constraintType() == QskLayoutConstraint::WidthForHeight ) )
{
setupChain( Qt::Vertical );
const auto constraints = rowChain.segments( constraint.height() );
setupChain( Qt::Horizontal, constraints );
}
else
{
setupChain( Qt::Horizontal );
setupChain( Qt::Vertical );
}
m_data->blockInvalidate = false;
const qreal width = columnChain.boundingHint().size( which );
const qreal height = rowChain.boundingHint().size( which );
return QSizeF( width, height );
}
void QskLayoutEngine2D::setupChain( Qt::Orientation orientation ) const
{
setupChain( orientation, QskLayoutChain::Segments() );
}
void QskLayoutEngine2D::setupChain( Qt::Orientation orientation,
const QskLayoutChain::Segments& constraints ) const
{
const auto count = effectiveCount( orientation );
const qreal constraint =
constraints.isEmpty() ? -1.0 : constraints.last().end();
auto& chain = m_data->layoutChain( orientation );
if ( ( chain.constraint() == constraint )
&& ( chain.count() == count ) )
{
return; // already up to date
}
chain.reset( count, constraint );
setupChain( orientation, constraints, chain );
chain.finish();
#if 0
qDebug() << "==" << this << orientation << chain.count();
for ( int i = 0; i < chain.count(); i++ )
qDebug() << i << ":" << chain.cell( i );
#endif
}
void QskLayoutEngine2D::updateSegments( const QSizeF& size ) const
{
auto& rowChain = m_data->rowChain;
auto& colLine = m_data->columnChain;
auto& rows = m_data->rows;
auto& columns = m_data->columns;
m_data->blockInvalidate = true;
switch( constraintType() )
{
case QskLayoutConstraint::WidthForHeight:
{
setupChain( Qt::Vertical );
rows = rowChain.segments( size.height() );
setupChain( Qt::Horizontal, rows );
columns = colLine.segments( size.width() );
break;
}
case QskLayoutConstraint::HeightForWidth:
{
setupChain( Qt::Horizontal );
columns = colLine.segments( size.width() );
setupChain( Qt::Vertical, m_data->columns );
rows = rowChain.segments( size.height() );
break;
}
default:
{
setupChain( Qt::Horizontal );
columns = colLine.segments( size.width() );
setupChain( Qt::Vertical );
rows = rowChain.segments( size.height() );
}
}
m_data->blockInvalidate = false;
}
void QskLayoutEngine2D::invalidate( int what )
{
if ( m_data->blockInvalidate )
return;
if ( what & ElementCache )
{
m_data->constraintType = -1;
invalidateElementCache();
}
if ( what & LayoutCache )
{
m_data->rowChain.invalidate();
m_data->columnChain.invalidate();
m_data->layoutSize = QSize();
m_data->rows.clear();
m_data->columns.clear();
}
}
QskLayoutConstraint::Type QskLayoutEngine2D::constraintType() const
{
if ( m_data->constraintType < 0 )
{
auto constraintType = QskLayoutConstraint::Unconstrained;
for ( int i = 0; i < count(); i++ )
{
const auto type = QskLayoutConstraint::constraintType( itemAt( i ) );
using namespace QskLayoutConstraint;
if ( type != Unconstrained )
{
if ( constraintType == Unconstrained )
{
constraintType = type;
}
else if ( constraintType != type )
{
qWarning( "QskLayoutEngine2D: conflicting constraints");
constraintType = Unconstrained;
}
}
}
m_data->constraintType = constraintType;
}
return static_cast< QskLayoutConstraint::Type >( m_data->constraintType );
}

View File

@ -0,0 +1,104 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_LAYOUT_ENGINE_2D_H
#define QSK_LAYOUT_ENGINE_2D_H
#include "QskGlobal.h"
#include "QskLayoutChain.h"
#include "QskLayoutConstraint.h"
#include <qnamespace.h>
#include <memory>
class QskLayoutEngine2D
{
public:
QskLayoutEngine2D();
virtual ~QskLayoutEngine2D();
virtual int count() const = 0;
virtual QQuickItem* itemAt( int index ) const = 0;
virtual qreal spacerAt( int index ) const = 0;
int indexOf( const QQuickItem* ) const;
int rowCount() const;
int columnCount() const;
bool setVisualDirection( Qt::LayoutDirection );
Qt::LayoutDirection visualDirection() const;
bool setDefaultAlignment( Qt::Alignment );
Qt::Alignment defaultAlignment() const;
bool setExtraSpacingAt( Qt::Edges edges );
Qt::Edges extraSpacingAt() const;
bool setSpacing( qreal spacing, Qt::Orientations );
qreal spacing( Qt::Orientation ) const;
qreal defaultSpacing( Qt::Orientation ) const;
void invalidate();
qreal widthForHeight( qreal height ) const;
qreal heightForWidth( qreal width ) const;
QSizeF sizeHint( Qt::SizeHint, const QSizeF& constraint ) const;
void setGeometries( const QRectF& );
protected:
void layoutItem( QQuickItem*,
const QRect& grid, Qt::Alignment ) const;
enum
{
ElementCache = 1 << 0,
LayoutCache = 1 << 1
};
void invalidate( int what );
private:
Q_DISABLE_COPY( QskLayoutEngine2D )
void updateSegments( const QSizeF& ) const;
virtual void layoutItems() = 0;
virtual int effectiveCount( Qt::Orientation ) const = 0;
virtual void invalidateElementCache() = 0;
QskLayoutConstraint::Type constraintType() const;
void setupChain( Qt::Orientation ) const;
void setupChain( Qt::Orientation, const QskLayoutChain::Segments& ) const;
virtual void setupChain( Qt::Orientation,
const QskLayoutChain::Segments&, QskLayoutChain& ) const = 0;
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
inline void QskLayoutEngine2D::invalidate()
{
invalidate( ElementCache | LayoutCache );
}
inline int QskLayoutEngine2D::rowCount() const
{
return effectiveCount( Qt::Vertical );
}
inline int QskLayoutEngine2D::columnCount() const
{
return effectiveCount( Qt::Horizontal );
}
#endif

View File

@ -58,15 +58,6 @@ void QskLayoutHint::setSize( int which, qreal size )
} }
} }
void QskLayoutHint::expandTo( const QskLayoutHint& other )
{
const auto hint = other.normalized();
m_minimum = qMax( m_minimum, hint.m_minimum );
m_preferred = qMax( m_preferred, hint.m_preferred );
m_maximum = qMax( m_maximum, hint.m_maximum );
}
void QskLayoutHint::normalize() void QskLayoutHint::normalize()
{ {
m_minimum = qMax( m_minimum, qreal( 0.0 ) ); m_minimum = qMax( m_minimum, qreal( 0.0 ) );

View File

@ -17,7 +17,6 @@ class QSK_EXPORT QskLayoutHint
QskLayoutHint(); QskLayoutHint();
QskLayoutHint( qreal minimum, qreal preferred, qreal maximum ); QskLayoutHint( qreal minimum, qreal preferred, qreal maximum );
void expandTo( const QskLayoutHint& );
void normalize(); void normalize();
QskLayoutHint normalized() const; QskLayoutHint normalized() const;
@ -38,6 +37,8 @@ class QSK_EXPORT QskLayoutHint
void setMaximum( qreal value ); void setMaximum( qreal value );
qreal maximum() const; qreal maximum() const;
void setSizes( qreal minimum, qreal preferred, qreal maximum );
private: private:
qreal m_minimum; qreal m_minimum;
qreal m_preferred; qreal m_preferred;
@ -74,6 +75,14 @@ inline void QskLayoutHint::setMaximum( qreal value )
m_maximum = value; m_maximum = value;
} }
inline void QskLayoutHint::setSizes(
qreal minimum, qreal preferred, qreal maximum )
{
m_minimum = minimum;
m_preferred = preferred;
m_maximum = maximum;
}
inline bool QskLayoutHint::operator==( const QskLayoutHint& other ) const inline bool QskLayoutHint::operator==( const QskLayoutHint& other ) const
{ {
return ( m_preferred == other.m_preferred ) return ( m_preferred == other.m_preferred )

View File

@ -94,26 +94,7 @@ QQuickItem* QskLinearBox::itemAtIndex( int index ) const
int QskLinearBox::indexOf( const QQuickItem* item ) const int QskLinearBox::indexOf( const QQuickItem* item ) const
{ {
if ( item ) return m_data->engine.indexOf( item );
{
/*
Linear search might become slow for many items,
better introduce some sort of hash table TODO ...
indexOf is often used for configuring an item
after inserting it. So we iterate in reverse order
*/
const auto& engine = m_data->engine;
for ( int i = engine.count() - 1; i >= 0; --i )
{
if ( engine.itemAt( i ) == item )
return i;
}
}
return -1;
} }
void QskLinearBox::removeAt( int index ) void QskLinearBox::removeAt( int index )
@ -398,16 +379,16 @@ Qt::Edges QskLinearBox::extraSpacingAt() const
return m_data->engine.extraSpacingAt(); return m_data->engine.extraSpacingAt();
} }
void QskLinearBox::addItem( QQuickItem* item, Qt::Alignment alignment ) int QskLinearBox::addItem( QQuickItem* item, Qt::Alignment alignment )
{ {
insertItem( -1, item, alignment ); return insertItem( -1, item, alignment );
} }
void QskLinearBox::insertItem( int QskLinearBox::insertItem(
int index, QQuickItem* item, Qt::Alignment alignment ) int index, QQuickItem* item, Qt::Alignment alignment )
{ {
if ( item == nullptr ) if ( item == nullptr )
return; return -1;
auto& engine = m_data->engine; auto& engine = m_data->engine;
@ -424,7 +405,7 @@ void QskLinearBox::insertItem(
( doAppend && oldIndex == engine.count() - 1 ) ) ( doAppend && oldIndex == engine.count() - 1 ) )
{ {
// already at its position, nothing to do // already at its position, nothing to do
return; return oldIndex;
} }
removeAt( oldIndex ); removeAt( oldIndex );
@ -433,11 +414,7 @@ void QskLinearBox::insertItem(
reparentItem( item ); reparentItem( item );
const int numItems = engine.count(); index = engine.insertItem( item, index );
if ( index < 0 || index > numItems )
index = numItems;
engine.insertItem( item, index );
engine.setAlignmentAt( index, alignment ); engine.setAlignmentAt( index, alignment );
// Re-ordering the child items to have a a proper focus tab chain // Re-ordering the child items to have a a proper focus tab chain
@ -472,14 +449,16 @@ void QskLinearBox::insertItem(
resetImplicitSize(); resetImplicitSize();
polish(); polish();
#endif #endif
return index;
} }
void QskLinearBox::addSpacer( qreal spacing, int stretchFactor ) int QskLinearBox::addSpacer( qreal spacing, int stretchFactor )
{ {
insertSpacer( -1, spacing, stretchFactor ); return insertSpacer( -1, spacing, stretchFactor );
} }
void QskLinearBox::insertSpacer( int index, qreal spacing, int stretchFactor ) int QskLinearBox::insertSpacer( int index, qreal spacing, int stretchFactor )
{ {
auto& engine = m_data->engine; auto& engine = m_data->engine;
@ -487,7 +466,7 @@ void QskLinearBox::insertSpacer( int index, qreal spacing, int stretchFactor )
if ( index < 0 || index > numItems ) if ( index < 0 || index > numItems )
index = numItems; index = numItems;
engine.insertSpacerAt( index, spacing ); index = engine.insertSpacerAt( index, spacing );
stretchFactor = qMax( stretchFactor, 0 ); stretchFactor = qMax( stretchFactor, 0 );
engine.setStretchFactorAt( index, stretchFactor ); engine.setStretchFactorAt( index, stretchFactor );
@ -497,16 +476,18 @@ void QskLinearBox::insertSpacer( int index, qreal spacing, int stretchFactor )
resetImplicitSize(); resetImplicitSize();
polish(); polish();
#endif #endif
return index;
} }
void QskLinearBox::addStretch( int stretchFactor ) int QskLinearBox::addStretch( int stretchFactor )
{ {
insertSpacer( -1, 0, stretchFactor ); return insertSpacer( -1, 0, stretchFactor );
} }
void QskLinearBox::insertStretch( int index, int stretchFactor ) int QskLinearBox::insertStretch( int index, int stretchFactor )
{ {
insertSpacer( index, 0, stretchFactor ); return insertSpacer( index, 0, stretchFactor );
} }
void QskLinearBox::setAlignment( int index, Qt::Alignment alignment ) void QskLinearBox::setAlignment( int index, Qt::Alignment alignment )

View File

@ -75,17 +75,17 @@ class QSK_EXPORT QskLinearBox : public QskIndexedLayoutBox
void resetSpacing(); void resetSpacing();
qreal spacing() const; qreal spacing() const;
Q_INVOKABLE void addItem( Q_INVOKABLE int addItem(
QQuickItem*, Qt::Alignment alignment = Qt::Alignment() ); QQuickItem*, Qt::Alignment alignment = Qt::Alignment() );
Q_INVOKABLE void insertItem( Q_INVOKABLE int insertItem(
int index, QQuickItem*, Qt::Alignment alignment = Qt::Alignment() ); int index, QQuickItem*, Qt::Alignment alignment = Qt::Alignment() );
Q_INVOKABLE void addSpacer( qreal spacing, int stretchFactor = 0 ); Q_INVOKABLE int addSpacer( qreal spacing, int stretchFactor = 0 );
Q_INVOKABLE void insertSpacer( int index, qreal spacing, int stretchFactor = 0 ); Q_INVOKABLE int insertSpacer( int index, qreal spacing, int stretchFactor = 0 );
Q_INVOKABLE void addStretch( int stretchFactor = 0 ); Q_INVOKABLE int addStretch( int stretchFactor = 0 );
Q_INVOKABLE void insertStretch( int index, int stretchFactor = 0 ); Q_INVOKABLE int insertStretch( int index, int stretchFactor = 0 );
Q_INVOKABLE void setStretchFactor( int index, int stretchFactor ); Q_INVOKABLE void setStretchFactor( int index, int stretchFactor );
Q_INVOKABLE int stretchFactor( int index ) const; Q_INVOKABLE int stretchFactor( int index ) const;

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@
#define QSK_LINEAR_LAYOUT_ENGINE_H #define QSK_LINEAR_LAYOUT_ENGINE_H
#include "QskGlobal.h" #include "QskGlobal.h"
#include "QskLayoutEngine2D.h"
#include <qnamespace.h> #include <qnamespace.h>
#include <memory> #include <memory>
@ -15,11 +16,11 @@ class QQuickItem;
class QSizeF; class QSizeF;
class QRectF; class QRectF;
class QskLinearLayoutEngine class QskLinearLayoutEngine : public QskLayoutEngine2D
{ {
public: public:
QskLinearLayoutEngine( Qt::Orientation, uint dimension ); QskLinearLayoutEngine( Qt::Orientation, uint dimension );
~QskLinearLayoutEngine(); ~QskLinearLayoutEngine() override;
Qt::Orientation orientation() const; Qt::Orientation orientation() const;
bool setOrientation( Qt::Orientation ); bool setOrientation( Qt::Orientation );
@ -27,36 +28,19 @@ class QskLinearLayoutEngine
bool setDimension( uint dimension ); bool setDimension( uint dimension );
uint dimension() const; uint dimension() const;
bool setDefaultAlignment( Qt::Alignment ); int count() const override final;
Qt::Alignment defaultAlignment() const;
bool setExtraSpacingAt( Qt::Edges ); int insertItem( QQuickItem*, int index );
Qt::Edges extraSpacingAt() const; int addItem( QQuickItem* );
bool setVisualDirection( Qt::LayoutDirection ); int insertSpacerAt( int index, qreal spacing );
Qt::LayoutDirection visualDirection() const; int addSpacer( qreal spacing );
bool setSpacing( qreal spacing, Qt::Orientations ); bool removeAt( int index );
qreal spacing( Qt::Orientation ) const; bool clear();
qreal defaultSpacing( Qt::Orientation ) const; QQuickItem* itemAt( int index ) const override final;
qreal spacerAt( int index ) const override final;
int count() const;
int rowCount() const;
int columnCount() const;
void insertItem( QQuickItem*, int index );
void addItem( QQuickItem* );
void insertSpacerAt( int index, qreal spacing );
void addSpacer( qreal spacing );
void removeAt( int index );
void clear();
QQuickItem* itemAt( int index ) const;
int spacerAt( int index ) const;
bool setRetainSizeWhenHiddenAt( int index, bool on ); bool setRetainSizeWhenHiddenAt( int index, bool on );
bool retainSizeWhenHiddenAt( int index ) const; bool retainSizeWhenHiddenAt( int index ) const;
@ -67,30 +51,31 @@ class QskLinearLayoutEngine
bool setStretchFactorAt( int index, int stretchFactor ); bool setStretchFactorAt( int index, int stretchFactor );
int stretchFactorAt( int index ) const; int stretchFactorAt( int index ) const;
void setGeometries( const QRectF& );
qreal widthForHeight( qreal height ) const;
qreal heightForWidth( qreal width ) const;
QSizeF sizeHint( Qt::SizeHint, const QSizeF& contraint ) const;
enum
{
EntryCache = 1 << 0,
CellCache = 1 << 1,
LayoutCache = 1 << 2
};
void invalidate( int what = EntryCache | CellCache | LayoutCache );
private:
void updateCellGeometries( const QSizeF& );
private: private:
Q_DISABLE_COPY(QskLinearLayoutEngine) Q_DISABLE_COPY(QskLinearLayoutEngine)
void layoutItems() override;
int effectiveCount() const;
int effectiveCount( Qt::Orientation ) const override;
void invalidateElementCache() override;
virtual void setupChain( Qt::Orientation,
const QskLayoutChain::Segments&, QskLayoutChain& ) const override;
class PrivateData; class PrivateData;
std::unique_ptr< PrivateData > m_data; std::unique_ptr< PrivateData > m_data;
}; };
inline int QskLinearLayoutEngine::addItem( QQuickItem* item )
{
return insertItem( item, -1 );
}
inline int QskLinearLayoutEngine::addSpacer( qreal spacing )
{
return insertSpacerAt( -1, spacing );
}
#endif #endif

View File

@ -9,6 +9,8 @@ QSK_SUBDIRS = common graphic nodes controls layouts dialogs inputpanel
INCLUDEPATH *= $${QSK_SUBDIRS} INCLUDEPATH *= $${QSK_SUBDIRS}
DEPENDPATH *= $${QSK_SUBDIRS} DEPENDPATH *= $${QSK_SUBDIRS}
# DEFINES += QSK_LAYOUT_COMPAT
HEADERS += \ HEADERS += \
common/QskAspect.h \ common/QskAspect.h \
common/QskBoxBorderColors.h \ common/QskBoxBorderColors.h \
@ -236,8 +238,9 @@ HEADERS += \
layouts/QskGridLayoutEngine.h \ layouts/QskGridLayoutEngine.h \
layouts/QskIndexedLayoutBox.h \ layouts/QskIndexedLayoutBox.h \
layouts/QskLayoutConstraint.h \ layouts/QskLayoutConstraint.h \
layouts/QskLayoutHint.h \
layouts/QskLayoutChain.h \ layouts/QskLayoutChain.h \
layouts/QskLayoutEngine2D.cpp \
layouts/QskLayoutHint.h \
layouts/QskLinearBox.h \ layouts/QskLinearBox.h \
layouts/QskLinearLayoutEngine.h \ layouts/QskLinearLayoutEngine.h \
layouts/QskStackBoxAnimator.h \ layouts/QskStackBoxAnimator.h \
@ -247,9 +250,10 @@ SOURCES += \
layouts/QskGridBox.cpp \ layouts/QskGridBox.cpp \
layouts/QskGridLayoutEngine.cpp \ layouts/QskGridLayoutEngine.cpp \
layouts/QskIndexedLayoutBox.cpp \ layouts/QskIndexedLayoutBox.cpp \
layouts/QskLayoutConstraint.cpp \
layouts/QskLayoutHint.cpp \
layouts/QskLayoutChain.cpp \ layouts/QskLayoutChain.cpp \
layouts/QskLayoutConstraint.cpp \
layouts/QskLayoutEngine2D.cpp \
layouts/QskLayoutHint.cpp \
layouts/QskLinearBox.cpp \ layouts/QskLinearBox.cpp \
layouts/QskLinearLayoutEngine.cpp \ layouts/QskLinearLayoutEngine.cpp \
layouts/QskStackBoxAnimator.cpp \ layouts/QskStackBoxAnimator.cpp \