replacing QGridLayoutEngine by QskLinearLayoutEngine, QskStackBox

without QGridLayoutEngine
This commit is contained in:
Uwe Rathmann 2019-06-19 14:08:45 +02:00
parent f8c57f7a7e
commit ee65ee1857
34 changed files with 3652 additions and 2156 deletions

View File

@ -33,7 +33,7 @@ MainWindow::MainWindow()
layout->setStretchFactor( header, 1 );
layout->addItem( content );
layout->setStretchFactor( content, 10 );
layout->setStretchFactor( content, 8 );
layout->addItem( footer );
layout->setStretchFactor( footer, 1 );

View File

@ -389,8 +389,8 @@ SoundControl::SoundControl( QQuickItem* parent )
auto layout = new QskGridBox( this );
layout->setMargins( QMarginsF( 40, 20, 40, 20 ) );
layout->setVerticalSpacing( 10 );
layout->setHorizontalSpacing( 60 );
layout->setSpacing( Qt::Vertical, 10 );
layout->setSpacing( Qt::Horizontal, 60 );
layout->setColumnStretchFactor( 0, 1 );
layout->setColumnStretchFactor( 1, 2 );

View File

@ -105,16 +105,13 @@ Box::Box( QQuickItem* parent )
void Box::flip()
{
setActive( false );
for ( int i = 0; i < itemCount(); i++ )
for ( int i = 0; i < entryCount(); i++ )
{
if ( Control* control = dynamic_cast< Control* >( itemAtIndex( i ) ) )
if ( auto control = dynamic_cast< Control* >( itemAtIndex( i ) ) )
control->transpose();
}
transpose();
setActive( true );
}
void Box::addControl( Control* control )

View File

@ -76,7 +76,7 @@ namespace
void addRectangle( const char* colorName )
{
auto rect = new TestRectangle( colorName );
rect->setText( QString::number( itemCount() + 1 ) );
rect->setText( QString::number( entryCount() + 1 ) );
addItem( rect, Qt::AlignCenter );
}

View File

@ -73,7 +73,7 @@ namespace
void addRectangle( const char* colorName )
{
auto rect = new TestRectangle( colorName );
rect->setText( QString::number( itemCount() + 1 ) );
rect->setText( QString::number( entryCount() + 1 ) );
addItem( rect, Qt::AlignCenter );
}

View File

@ -131,7 +131,7 @@ class SliderBox : public QskLinearBox
customSlider->setStepSize( 10 );
customSlider->setPageSize( 10 );
for ( int i = 0; i < itemCount(); i++ )
for ( int i = 0; i < entryCount(); i++ )
{
if ( auto slider = qobject_cast< QskSlider* >( itemAtIndex( i ) ) )
{
@ -151,7 +151,7 @@ class SliderBox : public QskLinearBox
{
setOrientation( inverted( orientation() ) );
for ( int i = 0; i < itemCount(); i++ )
for ( int i = 0; i < entryCount(); i++ )
{
if ( auto slider = qobject_cast< QskSlider* >( itemAtIndex( i ) ) )
{
@ -159,7 +159,7 @@ class SliderBox : public QskLinearBox
slider->setOrientation( orientation );
if ( i >= itemCount() - 1 )
if ( i >= entryCount() - 1 )
{
// we didn't implement the vertical mode of the heavily
// customized slider yet.

View File

@ -61,7 +61,6 @@ void Window::flipOrientation()
newBox->setMargins( m_layoutBox->margins() );
m_orientation = invertedOrientation();
m_layoutBox->setActive( false );
const QVector< QskDialogButtonBox* > boxes = dialogBoxes();
for ( QskDialogButtonBox* box : boxes )
@ -88,7 +87,7 @@ void Window::centerButtons()
QVector< QskDialogButtonBox* > Window::dialogBoxes() const
{
QVector< QskDialogButtonBox* > boxes;
for ( int i = 0; i < m_layoutBox->itemCount(); i++ )
for ( int i = 0; i < m_layoutBox->entryCount(); i++ )
{
if ( auto box = qobject_cast< QskDialogButtonBox* >( m_layoutBox->itemAtIndex( i ) ) )
{

View File

@ -0,0 +1,58 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskLayoutQml.h"
void QskGridBoxQml::setHorizontalSpacing( qreal spacing )
{
const auto oldSpacing = QskGridBox::spacing( Qt::Horizontal );
QskGridBox::setSpacing( Qt::Horizontal, spacing );
if ( oldSpacing != QskGridBox::spacing( Qt::Horizontal ) )
Q_EMIT verticalSpacingChanged();
}
qreal QskGridBoxQml::horizontalSpacing() const
{
return QskGridBox::spacing( Qt::Horizontal );
}
void QskGridBoxQml::resetHorizontalSpacing()
{
const auto orientation = Qt::Horizontal;
const auto oldSpacing = QskGridBox::spacing( orientation );
QskGridBox::resetSpacing( orientation );
if ( oldSpacing != QskGridBox::spacing( orientation ) )
Q_EMIT horizontalSpacingChanged();
}
void QskGridBoxQml::setVerticalSpacing( qreal spacing )
{
const auto oldSpacing = QskGridBox::spacing( Qt::Vertical );
QskGridBox::setSpacing( Qt::Vertical, spacing );
if ( oldSpacing != QskGridBox::spacing( Qt::Vertical ) )
Q_EMIT verticalSpacingChanged();
}
qreal QskGridBoxQml::verticalSpacing() const
{
return QskGridBox::spacing( Qt::Vertical );
}
void QskGridBoxQml::resetVerticalSpacing()
{
const auto orientation = Qt::Vertical;
const auto oldSpacing = QskGridBox::spacing( orientation );
QskGridBox::resetSpacing( orientation );
if ( oldSpacing != QskGridBox::spacing( orientation ) )
Q_EMIT verticalSpacingChanged();
}
#include "moc_QskLayoutQml.cpp"

View File

@ -36,7 +36,7 @@ class QskLayoutBoxQml : public LayoutBox
{
return LayoutBox::indexOf( item );
}
Q_INVOKABLE void removeAt( int index )
{
return LayoutBox::removeAt( index );
@ -46,7 +46,7 @@ class QskLayoutBoxQml : public LayoutBox
{
// QML does not like a const version
LayoutBox::removeItem( item );
}
}
Q_INVOKABLE void setAlignment( QQuickItem* item, Qt::Alignment alignment )
{
@ -63,14 +63,14 @@ class QskStackBoxQml : public QskLayoutBoxQml< QskStackBox >
{
Q_OBJECT
Q_INVOKABLE void setAlignment( int index, Qt::Alignment alignment )
Q_INVOKABLE void setAlignmentAt( int index, Qt::Alignment alignment )
{
QskStackBox::setAlignment( index, alignment );
QskStackBox::setAlignmentAt( index, alignment );
}
Q_INVOKABLE Qt::Alignment alignment( int index ) const
Q_INVOKABLE Qt::Alignment alignmentAt( int index ) const
{
return QskStackBox::alignment( index );
return QskStackBox::alignmentAt( index );
}
};
@ -115,7 +115,22 @@ class QskGridBoxQml : public QskLayoutBoxQml< QskGridBox >
{
Q_OBJECT
Q_PROPERTY( qreal horizontalSpacing READ horizontalSpacing
WRITE setHorizontalSpacing RESET resetHorizontalSpacing
NOTIFY horizontalSpacingChanged )
Q_PROPERTY( qreal verticalSpacing READ verticalSpacing
WRITE setVerticalSpacing RESET resetVerticalSpacing
NOTIFY verticalSpacingChanged )
public:
void setHorizontalSpacing( qreal );
void resetHorizontalSpacing();
qreal horizontalSpacing() const;
void setVerticalSpacing( qreal );
void resetVerticalSpacing();
qreal verticalSpacing() const;
Q_INVOKABLE bool retainSizeWhenHidden( QQuickItem* item ) const
{
@ -126,6 +141,10 @@ class QskGridBoxQml : public QskLayoutBoxQml< QskGridBox >
{
QskGridBox::setRetainSizeWhenHidden( item, on );
}
Q_SIGNALS:
void verticalSpacingChanged();
void horizontalSpacingChanged();
};
#endif

View File

@ -14,6 +14,7 @@ HEADERS += \
SOURCES += \
QskShortcutQml.cpp \
QskLayoutQml.cpp \
QskQml.cpp
target.path = $${QSK_INSTALL_LIBS}

View File

@ -41,7 +41,7 @@ namespace
QskControl::keyPressEvent.
*/
for ( int i = 0; i < itemCount(); i++ )
for ( int i = 0; i < entryCount(); i++ )
{
if ( auto button = itemAtIndex( i ) )
button->setZ( i == currentIndex ? 0.001 : 0.0 );
@ -179,8 +179,8 @@ int QskTabBar::insertTab( int index, QskTabButton* button )
{
auto buttonBox = m_data->buttonBox;
if ( index < 0 || index >= buttonBox->itemCount() )
index = buttonBox->itemCount();
if ( index < 0 || index >= buttonBox->entryCount() )
index = buttonBox->entryCount();
if ( isComponentComplete() )
{
@ -323,7 +323,7 @@ int QskTabBar::currentIndex() const
int QskTabBar::count() const
{
return m_data->buttonBox->itemCount();
return m_data->buttonBox->entryCount();
}
QskTabButton* QskTabBar::buttonAt( int position )

View File

@ -160,9 +160,6 @@ void QskDialogButtonBox::rearrangeButtons()
auto layoutBox = m_data->layoutBox;
const bool isActive = layoutBox->isActive();
layoutBox->setActive( false );
layoutBox->clear();
const int* currentLayout = effectiveSkin()->dialogButtonLayout( orientation() );
@ -226,8 +223,6 @@ void QskDialogButtonBox::rearrangeButtons()
if ( m_data->centeredButtons )
layoutBox->addStretch( 1 );
layoutBox->setActive( isActive );
// reorganizing the tab chain ???
}
@ -472,7 +467,10 @@ QskDialog::Action QskDialogButtonBox::clickedAction() const
bool QskDialogButtonBox::event( QEvent* event )
{
if ( event->type() == QEvent::LayoutRequest )
resetImplicitSize();
{
if ( !m_data->dirtyLayout )
resetImplicitSize();
}
return Inherited::event( event );
}

View File

@ -4,24 +4,54 @@
*****************************************************************************/
#include "QskGridBox.h"
#include "QskLayoutEngine.h"
#include "QskLayoutItem.h"
#include "QskGridLayoutEngine.h"
#include "QskLayoutConstraint.h"
#include "QskEvent.h"
static void qskSetItemActive( QObject* receiver, const QQuickItem* item, bool on )
{
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
{
QObject::disconnect( item, &QQuickItem::implicitWidthChanged, receiver, nullptr );
QObject::disconnect( item, &QQuickItem::implicitHeightChanged, receiver, nullptr );
QObject::disconnect( item, &QQuickItem::visibleChanged, receiver, nullptr );
}
}
class QskGridBox::PrivateData
{
public:
PrivateData()
: isExpanding( false )
, unlimitedSpanned( 0 )
{
}
bool isExpanding;
unsigned int unlimitedSpanned;
QskGridLayoutEngine engine;
};
QskGridBox::QskGridBox( QQuickItem* parent )
: QskLayoutBox( parent )
: QskBox( false, parent )
, m_data( new PrivateData() )
{
}
@ -34,400 +64,444 @@ void QskGridBox::addItem( QQuickItem* item,
int row, int column, int rowSpan, int columnSpan,
Qt::Alignment alignment )
{
auto layoutItem = new QskLayoutItem( item, row, column, rowSpan, columnSpan );
layoutItem->setAlignment( alignment );
if ( item == nullptr )
return;
const int index = itemCount(); // position and index doesn't match
if ( item->parent() == nullptr )
item->setParent( this );
setupLayoutItem( layoutItem, index );
insertItemInternal( layoutItem, -1 );
layoutItemInserted( layoutItem, index );
if ( item->parentItem() != this )
item->setParentItem( this );
qskSetItemActive( this, item, true );
// What about the focus tab chain - TODO ... ????
// check if item is already inserted ???
m_data->engine.insertItem(
item, row, column, rowSpan, columnSpan, alignment );
resetImplicitSize();
polish();
}
void QskGridBox::removeAt( int index )
{
auto& engine = m_data->engine;
if ( auto item = engine.itemAt( index ) )
qskSetItemActive( this, item, false );
engine.removeAt( index );
resetImplicitSize();
polish();
}
void QskGridBox::removeItem( const QQuickItem* item )
{
removeAt( indexOf( item ) );
}
void QskGridBox::clear( bool autoDelete )
{
for ( int i = itemCount() - 1; i >= 0; i-- )
{
auto item = itemAtIndex( i );
removeAt( i );
if( item )
{
if( autoDelete && ( item->parent() == this ) )
delete item;
else
item->setParentItem( nullptr );
}
}
}
int QskGridBox::itemCount() const
{
return m_data->engine.itemCount();
}
int QskGridBox::rowCount() const
{
return engine().rowCount();
return m_data->engine.rowCount();
}
int QskGridBox::columnCount() const
{
return engine().columnCount();
return m_data->engine.columnCount();
}
QQuickItem* QskGridBox::itemAtIndex( int index ) const
{
return m_data->engine.itemAt( index );
}
int QskGridBox::indexOf( const QQuickItem* item ) const
{
return m_data->engine.indexOf( item );
}
QQuickItem* QskGridBox::itemAt( int row, int column ) const
{
if ( const auto layoutItem = engine().layoutItemAt( row, column ) )
return layoutItem->item();
return nullptr;
return m_data->engine.itemAt( row, column );
}
int QskGridBox::indexAt( int row, int column ) const
{
return engine().indexAt( row, column );
return m_data->engine.indexAt( row, column );
}
int QskGridBox::rowOfIndex( int index ) const
{
if ( auto layoutItem = engine().layoutItemAt( index ) )
return layoutItem->firstRow();
return -1;
return m_data->engine.rowOfIndex( index );
}
int QskGridBox::rowSpanOfIndex( int index ) const
{
if ( auto layoutItem = engine().layoutItemAt( index ) )
return layoutItem->rowSpan();
return 0;
return m_data->engine.rowSpanOfIndex( index );
}
int QskGridBox::columnOfIndex( int index ) const
{
if ( auto layoutItem = engine().layoutItemAt( index ) )
return layoutItem->firstColumn();
return -1;
return m_data->engine.columnOfIndex( index );
}
int QskGridBox::columnSpanOfIndex( int index ) const
{
if ( auto layoutItem = engine().layoutItemAt( index ) )
return layoutItem->columnSpan();
return 0;
return m_data->engine.columnSpanOfIndex( index );
}
void QskGridBox::setupLayoutItem( QskLayoutItem* layoutItem, int index )
{
Q_UNUSED( index )
auto& engine = this->engine();
m_data->isExpanding = ( layoutItem->lastColumn() >= engine.columnCount() ) ||
( layoutItem->lastRow() >= engine.rowCount() );
}
void QskGridBox::layoutItemInserted( QskLayoutItem* layoutItem, int index )
{
Q_UNUSED( index )
if ( m_data->isExpanding )
{
// the new item has extended the number of rows/columns and
// we need to adjust all items without fixed spanning
if ( m_data->unlimitedSpanned > 0 )
engine().adjustSpans( columnCount(), rowCount() );
}
if ( layoutItem->hasUnlimitedSpan() )
{
// the item itself might need to be adjusted
if ( layoutItem->hasUnlimitedSpan( Qt::Horizontal ) )
{
const int span = columnCount() - layoutItem->firstColumn();
layoutItem->setRowSpan( span, Qt::Horizontal );
}
if ( layoutItem->hasUnlimitedSpan( Qt::Vertical ) )
{
const int span = rowCount() - layoutItem->firstRow();
layoutItem->setRowSpan( span, Qt::Vertical );
}
m_data->unlimitedSpanned++;
}
}
void QskGridBox::layoutItemRemoved( QskLayoutItem* layoutItem, int index )
{
Q_UNUSED( index )
if ( layoutItem->hasUnlimitedSpan() )
m_data->unlimitedSpanned--;
QskLayoutEngine& engine = this->engine();
// cleanup rows/columns
const QSize cells = engine.requiredCells();
const int numPendingColumns = engine.columnCount() - cells.width();
const int numPendingRows = engine.rowCount() - cells.height();
if ( numPendingColumns > 0 || numPendingRows > 0 )
{
if ( m_data->unlimitedSpanned > 0 )
engine.adjustSpans( cells.height(), cells.width() );
engine.removeRows( cells.width(), numPendingColumns, Qt::Horizontal );
engine.removeRows( cells.height(), numPendingRows, Qt::Vertical );
}
}
void QskGridBox::setSpacing( qreal spacing )
{
setHorizontalSpacing( spacing );
setVerticalSpacing( spacing );
}
void QskGridBox::setHorizontalSpacing( qreal spacing )
void QskGridBox::setSpacing( Qt::Orientations orientations, qreal spacing )
{
spacing = qMax( spacing, 0.0 );
if ( spacing != engine().spacing( Qt::Horizontal ) )
{
engine().setSpacing( spacing, Qt::Horizontal );
activate();
bool doUpdate = false;
Q_EMIT horizontalSpacingChanged();
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();
polish();
}
}
qreal QskGridBox::horizontalSpacing() const
void QskGridBox::resetSpacing( Qt::Orientations orientations )
{
return engine().spacing( Qt::Horizontal );
}
void QskGridBox::resetHorizontalSpacing()
{
const qreal spacing = QskLayoutEngine::defaultSpacing( Qt::Horizontal );
setHorizontalSpacing( spacing );
}
void QskGridBox::setVerticalSpacing( qreal spacing )
{
spacing = qMax( spacing, 0.0 );
if ( spacing != engine().spacing( Qt::Vertical ) )
for ( const auto o : { Qt::Horizontal, Qt::Vertical } )
{
engine().setSpacing( spacing, Qt::Vertical );
activate();
Q_EMIT verticalSpacingChanged();
if ( orientations & o )
setSpacing( o, QskGridLayoutEngine::defaultSpacing( o ) );
}
}
qreal QskGridBox::verticalSpacing() const
qreal QskGridBox::spacing( Qt::Orientation orientation ) const
{
return engine().spacing( Qt::Vertical );
}
void QskGridBox::resetVerticalSpacing()
{
const qreal spacing = QskLayoutEngine::defaultSpacing( Qt::Vertical );
setVerticalSpacing( spacing );
return m_data->engine.spacing( orientation );
}
void QskGridBox::setRowSpacing( int row, qreal spacing )
{
spacing = qMax( spacing, 0.0 );
if ( spacing != engine().rowSpacing( row, Qt::Vertical ) )
auto& engine = m_data->engine;
if ( spacing != engine.spacingAt( Qt::Vertical, row ) )
{
engine().setRowSpacing( row, spacing, Qt::Vertical );
activate();
engine.setSpacingAt( Qt::Vertical, row, spacing );
polish();
}
}
qreal QskGridBox::rowSpacing( int row ) const
{
return engine().rowSpacing( row, Qt::Vertical );
return m_data->engine.spacingAt( Qt::Vertical, row );
}
void QskGridBox::setColumnSpacing( int column, qreal spacing )
{
spacing = qMax( spacing, 0.0 );
if ( spacing != engine().rowSpacing( column, Qt::Horizontal ) )
auto& engine = m_data->engine;
if ( spacing != engine.spacingAt( Qt::Horizontal, column ) )
{
engine().setRowSpacing( column, spacing, Qt::Horizontal );
activate();
engine.setSpacingAt( Qt::Horizontal, column, spacing );
polish();
}
}
qreal QskGridBox::columnSpacing( int column ) const
{
return engine().rowSpacing( column, Qt::Horizontal );
return m_data->engine.spacingAt( Qt::Horizontal, column );
}
void QskGridBox::setRowStretchFactor( int row, int stretch )
{
if ( stretch != engine().rowStretchFactor( row, Qt::Vertical ) )
auto& engine = m_data->engine;
if ( stretch != engine.stretchFactorAt( Qt::Vertical, row ) )
{
engine().setRowStretchFactor( row, stretch, Qt::Vertical );
activate();
engine.setStretchFactorAt( Qt::Vertical, row, stretch );
polish();
}
}
int QskGridBox::rowStretchFactor( int row ) const
{
return engine().rowStretchFactor( row, Qt::Vertical );
return m_data->engine.stretchFactorAt( Qt::Vertical, row );
}
void QskGridBox::setColumnStretchFactor( int column, int stretch )
{
if ( stretch != engine().rowStretchFactor( column, Qt::Horizontal ) )
auto& engine = m_data->engine;
if ( stretch != engine.stretchFactorAt( Qt::Horizontal, column ) )
{
engine().setRowStretchFactor( column, stretch, Qt::Horizontal );
activate();
engine.setStretchFactorAt( Qt::Horizontal, column, stretch );
polish();
}
}
int QskGridBox::columnStretchFactor( int column ) const
{
return engine().rowStretchFactor( column, Qt::Horizontal );
}
void QskGridBox::setRowMinimumHeight( int row, qreal height )
{
setRowSizeHint( Qt::MinimumSize, row, height, Qt::Vertical );
}
qreal QskGridBox::rowMinimumHeight( int row ) const
{
return engine().rowSizeHint( Qt::MinimumSize, row, Qt::Vertical );
}
void QskGridBox::setRowPreferredHeight( int row, qreal height )
{
setRowSizeHint( Qt::PreferredSize, row, height, Qt::Vertical );
}
qreal QskGridBox::rowPreferredHeight( int row ) const
{
return engine().rowSizeHint( Qt::PreferredSize, row, Qt::Vertical );
}
void QskGridBox::setRowMaximumHeight( int row, qreal height )
{
setRowSizeHint( Qt::MaximumSize, row, height, Qt::Vertical );
}
qreal QskGridBox::rowMaximumHeight( int row ) const
{
return engine().rowSizeHint( Qt::MaximumSize, row, Qt::Vertical );
return m_data->engine.stretchFactorAt( Qt::Horizontal, column );
}
void QskGridBox::setRowFixedHeight( int row, qreal height )
{
setRowMinimumHeight( row, height );
setRowMaximumHeight( row, height );
}
void QskGridBox::setColumnMinimumWidth( int column, qreal width )
{
setRowSizeHint( Qt::MinimumSize, column, width, Qt::Horizontal );
}
qreal QskGridBox::columnMinimumWidth( int column ) const
{
return engine().rowSizeHint( Qt::MinimumSize, column, Qt::Horizontal );
}
void QskGridBox::setColumnPreferredWidth( int column, qreal width )
{
setRowSizeHint( Qt::PreferredSize, column, width, Qt::Horizontal );
}
qreal QskGridBox::columnPreferredWidth( int column ) const
{
return engine().rowSizeHint( Qt::PreferredSize, column, Qt::Horizontal );
}
void QskGridBox::setColumnMaximumWidth( int column, qreal width )
{
setRowSizeHint( Qt::MaximumSize, column, width, Qt::Horizontal );
}
qreal QskGridBox::columnMaximumWidth( int column ) const
{
return engine().rowSizeHint( Qt::MaximumSize, column, Qt::Horizontal );
setRowSizeHint( row, Qt::MinimumSize, height );
setRowSizeHint( row, Qt::MaximumSize, height );
}
void QskGridBox::setColumnFixedWidth( int column, qreal width )
{
setColumnMinimumWidth( column, width );
setColumnMaximumWidth( column, width );
setColumnSizeHint( column, Qt::MinimumSize, width );
setColumnSizeHint( column, Qt::MaximumSize, width );
}
void QskGridBox::setRowAlignment( int row, Qt::Alignment alignment )
{
if ( engine().rowAlignment( row, Qt::Vertical ) != alignment )
auto& engine = m_data->engine;
if ( engine.alignmentAt( Qt::Vertical, row ) != alignment )
{
engine().setRowAlignment( row, alignment, Qt::Vertical );
activate();
engine.setAlignmentAt( Qt::Vertical, row, alignment );
polish();
}
}
Qt::Alignment QskGridBox::rowAlignment( int row ) const
{
return engine().rowAlignment( row, Qt::Vertical );
return m_data->engine.alignmentAt( Qt::Vertical, row );
}
void QskGridBox::setColumnAlignment( int column, Qt::Alignment alignment )
{
if ( alignment != engine().rowAlignment( column, Qt::Horizontal ) )
auto& engine = m_data->engine;
if ( engine.alignmentAt( Qt::Horizontal, column ) != alignment )
{
engine().setRowAlignment( column, alignment, Qt::Horizontal );
activate();
engine.setAlignmentAt( Qt::Horizontal, column, alignment );
polish();
}
}
Qt::Alignment QskGridBox::columnAlignment( int column ) const
{
return engine().rowAlignment( column, Qt::Horizontal );
return m_data->engine.alignmentAt( Qt::Horizontal, column );
}
void QskGridBox::setAlignment( const QQuickItem* item, Qt::Alignment alignment )
{
QskLayoutItem* layoutItem = engine().layoutItemOf( item );
if ( layoutItem && layoutItem->alignment() != alignment )
auto& engine = m_data->engine;
if ( engine.alignmentOf( item ) != alignment )
{
layoutItem->setAlignment( alignment );
activate();
engine.setAlignmentOf( item, alignment );
polish();
}
}
Qt::Alignment QskGridBox::alignment( const QQuickItem* item ) const
{
QskLayoutItem* layoutItem = engine().layoutItemOf( item );
if ( layoutItem )
return layoutItem->alignment();
return Qt::Alignment();
return m_data->engine.alignmentOf( item );
}
void QskGridBox::setRetainSizeWhenHidden( const QQuickItem* item, bool on )
{
QskLayoutItem* layoutItem = engine().layoutItemOf( item );
if ( layoutItem && on != layoutItem->retainSizeWhenHidden() )
auto& engine = m_data->engine;
if ( engine.retainSizeWhenHiddenOf( item ) != on )
{
layoutItem->setRetainSizeWhenHidden( on );
engine.setRetainSizeWhenHiddenOf( item, on );
invalidate();
}
}
bool QskGridBox::retainSizeWhenHidden( const QQuickItem* item ) const
{
QskLayoutItem* layoutItem = engine().layoutItemOf( item );
if ( layoutItem )
return layoutItem->retainSizeWhenHidden();
return false;
return m_data->engine.retainSizeWhenHiddenOf( item );
}
void QskGridBox::setRowSizeHint(
Qt::SizeHint which, int row, qreal size, Qt::Orientation orientation )
void QskGridBox::setRowSizeHint( int row, Qt::SizeHint which, qreal height )
{
if ( size != engine().rowSizeHint( which, row, orientation ) )
auto& engine = m_data->engine;
if ( height != engine.rowSizeHint( row, which ) )
{
engine().setRowSizeHint( which, row, size, orientation );
activate();
engine.setRowSizeHint( row, which, height );
polish();
}
}
qreal QskGridBox::rowSizeHint( int row, Qt::SizeHint which ) const
{
return m_data->engine.rowSizeHint( row, which );
}
void QskGridBox::setColumnSizeHint( int column, Qt::SizeHint which, qreal width )
{
auto& engine = m_data->engine;
if ( width != engine.columnSizeHint( column, which ) )
{
engine.setColumnSizeHint( column, which, width );
polish();
}
}
qreal QskGridBox::columnSizeHint( int column, Qt::SizeHint which ) const
{
return m_data->engine.columnSizeHint( column, which );
}
void QskGridBox::invalidate()
{
m_data->engine.invalidate();
resetImplicitSize();
polish();
}
void QskGridBox::updateLayout()
{
m_data->engine.setGeometries( layoutRect() );
}
QSizeF QskGridBox::contentsSizeHint() const
{
if ( itemCount() == 0 )
return QSizeF( 0, 0 );
return m_data->engine.sizeHint( Qt::PreferredSize, QSizeF() );
}
qreal QskGridBox::heightForWidth( qreal width ) const
{
auto constrainedHeight =
[this]( QskLayoutConstraint::Type, const QskControl*, qreal width )
{
return m_data->engine.heightForWidth( width );
};
return QskLayoutConstraint::constrainedMetric(
QskLayoutConstraint::HeightForWidth, this, width, constrainedHeight );
}
qreal QskGridBox::widthForHeight( qreal height ) const
{
auto constrainedWidth =
[this]( QskLayoutConstraint::Type, const QskControl*, qreal height )
{
return m_data->engine.widthForHeight( height );
};
return QskLayoutConstraint::constrainedMetric(
QskLayoutConstraint::WidthForHeight, this, height, constrainedWidth );
}
void QskGridBox::geometryChangeEvent( QskGeometryChangeEvent* event )
{
Inherited::geometryChangeEvent( event );
if ( event->isResized() )
polish();
}
void QskGridBox::itemChange( ItemChange change, const ItemChangeData& value )
{
Inherited::itemChange( change, value );
switch ( change )
{
case ItemChildRemovedChange:
{
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() )
{
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;
}
return Inherited::event( event );
}
#include "moc_QskGridBox.cpp"

View File

@ -6,21 +6,16 @@
#ifndef QSK_GRID_BOX_H
#define QSK_GRID_BOX_H
#include "QskLayoutBox.h"
#include "QskBox.h"
class QSK_EXPORT QskGridBox : public QskLayoutBox
class QSK_EXPORT QskGridBox : public QskBox
{
Q_OBJECT
Q_PROPERTY( qreal horizontalSpacing READ horizontalSpacing
WRITE setHorizontalSpacing RESET resetHorizontalSpacing
NOTIFY horizontalSpacingChanged )
Q_PROPERTY( bool empty READ isEmpty() )
Q_PROPERTY( int itemCount READ itemCount() )
Q_PROPERTY( qreal verticalSpacing READ verticalSpacing
WRITE setVerticalSpacing RESET resetVerticalSpacing
NOTIFY verticalSpacingChanged )
using Inherited = QskLayoutBox;
using Inherited = QskBox;
public:
explicit QskGridBox( QQuickItem* parent = nullptr );
@ -34,9 +29,18 @@ class QSK_EXPORT QskGridBox : public QskLayoutBox
QQuickItem*, int row, int column,
Qt::Alignment alignment = Qt::Alignment() );
void removeItem( const QQuickItem* );
void removeAt( int index );
Q_INVOKABLE int rowCount() const;
Q_INVOKABLE int columnCount() const;
int itemCount() const;
QQuickItem* itemAtIndex( int index ) const;
int indexOf( const QQuickItem* ) const;
bool isEmpty() const;
Q_INVOKABLE QQuickItem* itemAt( int row, int column ) const;
Q_INVOKABLE int indexAt( int row, int column ) const;
@ -47,15 +51,29 @@ class QSK_EXPORT QskGridBox : public QskLayoutBox
Q_INVOKABLE int columnSpanOfIndex( int index ) const;
// spacings
void setSpacing( qreal spacing );
void setHorizontalSpacing( qreal spacing );
void resetHorizontalSpacing();
qreal horizontalSpacing() const;
void setSpacing( Qt::Orientations, qreal spacing );
void resetSpacing( Qt::Orientations );
qreal spacing( Qt::Orientation ) const;
void setVerticalSpacing( qreal spacing );
void resetVerticalSpacing();
qreal verticalSpacing() const;
#ifdef QSK_LAYOUT_COMPAT
void setVerticalSpacing( qreal spacing ) { setSpacing( Qt::Vertical, spacing ); }
qreal verticalSpacing() const { return spacing( Qt::Vertical ); }
void setHorizontalSpacing( qreal spacing ) { setSpacing( Qt::Horizontal, spacing ); }
qreal horizontalSpacing() const { return spacing( Qt::Horizontal ); }
void setSpacing( qreal spacing ) { setSpacing( Qt::Horizontal | Qt::Vertical, spacing ); }
void setActive( bool ) {}
bool isActive() const { return true; }
void setRowMinimumHeight( int column, qreal height )
{ setRowSizeHint( column, Qt::MinimumSize, height ); }
void setColumnMaximumWidth( int column, qreal width )
{ setColumnSizeHint( column, Qt::MaximumSize, width ); }
#endif
Q_INVOKABLE void setRowSpacing( int row, qreal spacing );
Q_INVOKABLE qreal rowSpacing( int row ) const;
@ -71,26 +89,14 @@ class QSK_EXPORT QskGridBox : public QskLayoutBox
Q_INVOKABLE int columnStretchFactor( int column ) const;
// row/column size hints
Q_INVOKABLE void setRowMinimumHeight( int row, qreal height );
Q_INVOKABLE qreal rowMinimumHeight( int row ) const;
Q_INVOKABLE void setRowPreferredHeight( int row, qreal height );
Q_INVOKABLE qreal rowPreferredHeight( int row ) const;
Q_INVOKABLE void setColumnSizeHint( int column, Qt::SizeHint, qreal width );
Q_INVOKABLE qreal columnSizeHint( int column, Qt::SizeHint ) const;
Q_INVOKABLE void setRowMaximumHeight( int row, qreal height );
Q_INVOKABLE qreal rowMaximumHeight( int row ) const;
Q_INVOKABLE void setRowSizeHint( int row, Qt::SizeHint, qreal height );
Q_INVOKABLE qreal rowSizeHint( int row, Qt::SizeHint ) const;
Q_INVOKABLE void setRowFixedHeight( int row, qreal height );
Q_INVOKABLE void setColumnMinimumWidth( int column, qreal width );
Q_INVOKABLE qreal columnMinimumWidth( int column ) const;
Q_INVOKABLE void setColumnPreferredWidth( int column, qreal width );
Q_INVOKABLE qreal columnPreferredWidth( int column ) const;
Q_INVOKABLE void setColumnMaximumWidth( int column, qreal width );
Q_INVOKABLE qreal columnMaximumWidth( int column ) const;
Q_INVOKABLE void setColumnFixedWidth( int column, qreal width );
// alignments
@ -107,20 +113,23 @@ class QSK_EXPORT QskGridBox : public QskLayoutBox
bool retainSizeWhenHidden( const QQuickItem* ) const;
void setRetainSizeWhenHidden( const QQuickItem*, bool on );
Q_SIGNALS:
void verticalSpacingChanged();
void horizontalSpacingChanged();
QSizeF contentsSizeHint() const override;
qreal heightForWidth( qreal width ) const override;
qreal widthForHeight( qreal height ) const override;
public Q_SLOTS:
void invalidate();
void clear( bool autoDelete = false );
protected:
void setupLayoutItem( QskLayoutItem*, int index ) override;
void layoutItemInserted( QskLayoutItem*, int index ) override;
void layoutItemRemoved( QskLayoutItem*, int index ) override;
bool event( QEvent* ) override;
void geometryChangeEvent( QskGeometryChangeEvent* ) override;
void itemChange( ItemChange, const ItemChangeData& ) override;
void updateLayout() override;
private:
void setRowSizeHint(
Qt::SizeHint which, int row, qreal size,
Qt::Orientation orientation );
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
@ -131,4 +140,9 @@ inline void QskGridBox::addItem(
addItem( item, row, column, 1, 1, alignment );
}
inline bool QskGridBox::isEmpty() const
{
return itemCount() <= 0;
}
#endif

View File

@ -0,0 +1,554 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskGridLayoutEngine.h"
#include "QskLayoutConstraint.h"
#include "QskSizePolicy.h"
#include "QskControl.h"
#include "QskQuick.h"
QSK_QT_PRIVATE_BEGIN
#include <private/qgridlayoutengine_p.h>
QSK_QT_PRIVATE_END
namespace
{
class LayoutStyleInfo final : public QAbstractLayoutStyleInfo
{
public:
qreal spacing( Qt::Orientation ) const override
{
// later from the theme !!
return 5.0;
}
qreal windowMargin( Qt::Orientation ) const override
{
// later from the theme !!
return 0;
}
bool hasChangedCore() const override
{
return false; // never changes
}
};
class LayoutItem final : public QGridLayoutItem
{
public:
LayoutItem( QQuickItem* item, int row, int column,
int rowSpan, int columnSpan, Qt::Alignment alignment )
: QGridLayoutItem( row, column,
qMax( rowSpan, 1 ), qMax( columnSpan, 1 ), alignment )
, m_item( item )
, m_retainSizeWhenHidden( false )
, m_unlimitedRowSpan( rowSpan <= 0 )
, m_unlimitedColumnSpan( columnSpan <= 0 )
{
}
QQuickItem* item() const
{
return m_item;
}
void setGeometry( const QRectF& rect ) override
{
qskSetItemGeometry( m_item, rect );
}
QLayoutPolicy::Policy sizePolicy( Qt::Orientation orientation ) const override
{
const auto policy = QskLayoutConstraint::sizePolicy( m_item );
return static_cast< QLayoutPolicy::Policy >( policy.policy( orientation ) );
}
QSizeF sizeHint( Qt::SizeHint which, const QSizeF& constraint ) const override
{
return QskLayoutConstraint::sizeHint( m_item, which, constraint );
}
bool hasDynamicConstraint() const override
{
using namespace QskLayoutConstraint;
return constraintType( m_item ) != Unconstrained;
}
Qt::Orientation dynamicConstraintOrientation() const override
{
Qt::Orientation orientation = Qt::Vertical;
if ( auto control = qskControlCast( m_item ) )
{
const auto policy = control->sizePolicy().horizontalPolicy();
return ( policy == QskSizePolicy::Constrained )
? Qt::Horizontal : Qt::Vertical;
}
return orientation;
}
bool isIgnored() const override final
{
if ( m_item && !qskIsVisibleToParent( m_item ) )
return !m_retainSizeWhenHidden;
return false;
}
QLayoutPolicy::ControlTypes controlTypes( LayoutSide ) const override
{
return QLayoutPolicy::DefaultType;
}
bool retainSizeWhenHidden() const
{
return m_retainSizeWhenHidden;
}
void setRetainSizeWhenHidden( bool on )
{
m_retainSizeWhenHidden = on;
}
bool hasUnlimitedSpan() const
{
return m_unlimitedColumnSpan || m_unlimitedRowSpan;
}
bool hasUnlimitedSpan( Qt::Orientation orientation ) const
{
return ( orientation == Qt::Horizontal )
? m_unlimitedColumnSpan : m_unlimitedRowSpan;
}
private:
QQuickItem* m_item;
bool m_retainSizeWhenHidden : 1;
bool m_unlimitedRowSpan : 1;
bool m_unlimitedColumnSpan : 1;
};
class LayoutEngine : public QGridLayoutEngine
{
public:
LayoutEngine()
: QGridLayoutEngine( Qt::AlignVCenter, false /*snapToPixelGrid*/ )
{
/*
snapToPixelGrid rounds x/y, what might lead to losing a pixel.
F.e. when having a text in elideMode we end up with an elided text
because of this.
*/
}
LayoutItem* layoutItemAt( int index ) const
{
if ( index < 0 || index >= q_items.count() )
return nullptr;
return static_cast< LayoutItem* >( q_items[ index ] );
}
LayoutItem* layoutItemAt( int row, int column ) const
{
if ( row < 0 || row >= rowCount() || column < 0 || column >= columnCount() )
return nullptr;
return static_cast< LayoutItem* >( itemAt( row, column ) );
}
inline LayoutItem* layoutItemOf( const QQuickItem* item ) const
{
return layoutItemAt( indexOf( item ) );
}
int indexAt( int row, int column ) const
{
const auto item = layoutItemAt( row, column );
if ( item )
return q_items.indexOf( item );
return -1;
}
int indexOf( const QQuickItem* item ) const
{
// linear search might become slow for many items,
// better introduce some sort of hash table TODO ...
for ( int i = q_items.count() - 1; i >= 0; --i )
{
const auto layoutItem = static_cast< const LayoutItem* >( q_items[ i ] );
if ( layoutItem->item() == item )
return i;
}
return -1;
}
qreal spacing( Qt::Orientation orientation ) const
{
const LayoutStyleInfo styleInfo;
return QGridLayoutEngine::spacing( orientation, &styleInfo );
}
};
}
class QskGridLayoutEngine::PrivateData
{
public:
/*
For the moment we use QGridLayoutEngine, but sooner and later
it should be replaced by a new implementation, that uses
the same backend as QskLinearLayoutEngine.
*/
LayoutEngine qengine;
unsigned int unlimitedSpanned = 0;
};
QskGridLayoutEngine::QskGridLayoutEngine()
: m_data( new PrivateData() )
{
}
QskGridLayoutEngine::~QskGridLayoutEngine()
{
}
void QskGridLayoutEngine::setGeometries( const QRectF rect )
{
const LayoutStyleInfo styleInfo;
m_data->qengine.setGeometries( rect, &styleInfo );
}
void QskGridLayoutEngine::invalidate()
{
m_data->qengine.invalidate();
}
void QskGridLayoutEngine::setVisualDirection( Qt::LayoutDirection direction )
{
m_data->qengine.setVisualDirection( direction );
}
Qt::LayoutDirection QskGridLayoutEngine::visualDirection() const
{
return m_data->qengine.visualDirection();
}
int QskGridLayoutEngine::itemCount() const
{
return m_data->qengine.itemCount();
}
int QskGridLayoutEngine::rowCount() const
{
return m_data->qengine.rowCount();
}
int QskGridLayoutEngine::columnCount() const
{
return m_data->qengine.columnCount();
}
void QskGridLayoutEngine::insertItem( QQuickItem* item,
int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment )
{
auto& qengine = m_data->qengine;
auto layoutItem = new LayoutItem(
item, row, column, rowSpan, columnSpan, alignment );
const bool isExpanding = ( layoutItem->lastColumn() >= qengine.columnCount() ) ||
( layoutItem->lastRow() >= qengine.rowCount() );
qengine.insertItem( layoutItem, -1 );
if ( isExpanding )
{
// the new item has extended the number of rows/columns and
// we need to adjust all items without fixed spanning
if ( m_data->unlimitedSpanned > 0 )
adjustSpans( columnCount(), rowCount() );
}
if ( layoutItem->hasUnlimitedSpan() )
{
// the item itself might need to be adjusted
if ( layoutItem->hasUnlimitedSpan( Qt::Horizontal ) )
{
const int span = columnCount() - layoutItem->firstColumn();
layoutItem->setRowSpan( span, Qt::Horizontal );
}
if ( layoutItem->hasUnlimitedSpan( Qt::Vertical ) )
{
const int span = rowCount() - layoutItem->firstRow();
layoutItem->setRowSpan( span, Qt::Vertical );
}
m_data->unlimitedSpanned++;
}
}
void QskGridLayoutEngine::removeAt( int index )
{
auto& qengine = m_data->qengine;
auto layoutItem = qengine.layoutItemAt( index );
if ( layoutItem == nullptr )
return;
qengine.removeItem( layoutItem );
if ( layoutItem->hasUnlimitedSpan() )
m_data->unlimitedSpanned--;
// cleanup rows/columns
const QSize cells = requiredCells();
const int numPendingColumns = qengine.columnCount() - cells.width();
const int numPendingRows = qengine.rowCount() - cells.height();
if ( numPendingColumns > 0 || numPendingRows > 0 )
{
if ( m_data->unlimitedSpanned > 0 )
adjustSpans( cells.height(), cells.width() );
qengine.removeRows( cells.width(), numPendingColumns, Qt::Horizontal );
qengine.removeRows( cells.height(), numPendingRows, Qt::Vertical );
}
delete layoutItem;
}
QQuickItem* QskGridLayoutEngine::itemAt( int index ) const
{
if ( const auto layoutItem = m_data->qengine.layoutItemAt( index ) )
return layoutItem->item();
return nullptr;
}
QQuickItem* QskGridLayoutEngine::itemAt( int row, int column ) const
{
if ( const auto layoutItem = m_data->qengine.layoutItemAt( row, column ) )
return layoutItem->item();
return nullptr;
}
int QskGridLayoutEngine::indexAt( int row, int column ) const
{
return m_data->qengine.indexAt( row, column );
}
int QskGridLayoutEngine::indexOf( const QQuickItem* item ) const
{
if ( item == nullptr )
return -1;
return m_data->qengine.indexOf( item );
}
int QskGridLayoutEngine::rowOfIndex( int index ) const
{
if ( auto layoutItem = m_data->qengine.layoutItemAt( index ) )
return layoutItem->firstRow();
return -1;
}
int QskGridLayoutEngine::rowSpanOfIndex( int index ) const
{
if ( auto layoutItem = m_data->qengine.layoutItemAt( index ) )
return layoutItem->rowSpan();
return 0;
}
int QskGridLayoutEngine::columnOfIndex( int index ) const
{
if ( auto layoutItem = m_data->qengine.layoutItemAt( index ) )
return layoutItem->firstColumn();
return -1;
}
int QskGridLayoutEngine::columnSpanOfIndex( int index ) const
{
if ( auto layoutItem = m_data->qengine.layoutItemAt( index ) )
return layoutItem->columnSpan();
return 0;
}
void QskGridLayoutEngine::setSpacing(
Qt::Orientation orientation, qreal spacing )
{
m_data->qengine.setSpacing( spacing, orientation );
}
qreal QskGridLayoutEngine::spacing( Qt::Orientation orientation ) const
{
return m_data->qengine.spacing( orientation );
}
void QskGridLayoutEngine::setSpacingAt(
Qt::Orientation orientation, int cell, qreal spacing )
{
// is this a spacer ???
m_data->qengine.setRowSpacing( cell, spacing, orientation );
}
qreal QskGridLayoutEngine::spacingAt(
Qt::Orientation orientation, int cell ) const
{
return m_data->qengine.rowSpacing( cell, orientation );
}
void QskGridLayoutEngine::setStretchFactorAt(
Qt::Orientation orientation, int cell, int stretch )
{
m_data->qengine.setRowStretchFactor( cell, stretch, orientation );
}
int QskGridLayoutEngine::stretchFactorAt( Qt::Orientation orientation, int cell )
{
return m_data->qengine.rowStretchFactor( cell, orientation );
}
void QskGridLayoutEngine::setAlignmentAt(
Qt::Orientation orientation, int cell, Qt::Alignment alignment )
{
m_data->qengine.setRowAlignment( cell, alignment, orientation );
}
Qt::Alignment QskGridLayoutEngine::alignmentAt(
Qt::Orientation orientation, int cell ) const
{
return m_data->qengine.rowAlignment( cell, orientation );
}
void QskGridLayoutEngine::setAlignmentOf(
const QQuickItem* item, Qt::Alignment alignment )
{
if ( auto layoutItem = m_data->qengine.layoutItemOf( item ) )
layoutItem->setAlignment( alignment );
}
Qt::Alignment QskGridLayoutEngine::alignmentOf( const QQuickItem* item ) const
{
if ( const auto layoutItem = m_data->qengine.layoutItemOf( item ) )
return layoutItem->alignment();
return Qt::Alignment();
}
void QskGridLayoutEngine::setRetainSizeWhenHiddenOf( const QQuickItem* item, bool on )
{
if ( auto layoutItem = m_data->qengine.layoutItemOf( item ) )
layoutItem->setRetainSizeWhenHidden( on );
}
bool QskGridLayoutEngine::retainSizeWhenHiddenOf( const QQuickItem* item ) const
{
if ( const auto layoutItem = m_data->qengine.layoutItemOf( item ) )
return layoutItem->retainSizeWhenHidden();
return false;
}
void QskGridLayoutEngine::setRowSizeHint( int row, Qt::SizeHint which, qreal height )
{
m_data->qengine.setRowSizeHint( which, row, height, Qt::Vertical );
}
qreal QskGridLayoutEngine::rowSizeHint( int row, Qt::SizeHint which ) const
{
return m_data->qengine.rowSizeHint( which, row, Qt::Vertical );
}
void QskGridLayoutEngine::setColumnSizeHint( int column, Qt::SizeHint which, qreal width )
{
m_data->qengine.setRowSizeHint( which, column, width, Qt::Horizontal );
}
qreal QskGridLayoutEngine::columnSizeHint( int column, Qt::SizeHint which ) const
{
return m_data->qengine.rowSizeHint( which, column, Qt::Horizontal );
}
QSizeF QskGridLayoutEngine::sizeHint( Qt::SizeHint which, const QSizeF& constraint ) const
{
const LayoutStyleInfo styleInfo;
return m_data->qengine.sizeHint( which, constraint, &styleInfo );
}
qreal QskGridLayoutEngine::widthForHeight( qreal height ) const
{
const QSizeF constraint( -1, height );
return sizeHint( Qt::PreferredSize, constraint ).width();
}
qreal QskGridLayoutEngine::heightForWidth( qreal width ) const
{
const QSizeF constraint( width, -1 );
return sizeHint( Qt::PreferredSize, constraint ).height();
}
qreal QskGridLayoutEngine::defaultSpacing( Qt::Orientation orientation )
{
return LayoutStyleInfo().spacing( orientation );
}
QSize QskGridLayoutEngine::requiredCells() const
{
int lastRow = -1;
int lastColumn = -1;
for ( int i = 0; i < m_data->qengine.itemCount(); i++ )
{
const auto layoutItem = m_data->qengine.layoutItemAt( i );
if ( layoutItem->isIgnored() )
continue;
const int col = layoutItem->hasUnlimitedSpan( Qt::Horizontal )
? layoutItem->firstColumn() + 1 : layoutItem->lastColumn();
if ( col > lastColumn )
lastColumn = col;
const int row = layoutItem->hasUnlimitedSpan( Qt::Vertical )
? layoutItem->firstRow() + 1 : layoutItem->lastRow();
if ( row > lastRow )
lastRow = row;
}
return QSize( lastColumn + 1, lastRow + 1 );
}
void QskGridLayoutEngine::adjustSpans( int numRows, int numColumns )
{
for ( int i = 0; i < m_data->qengine.itemCount(); i++ )
{
auto layoutItem = m_data->qengine.layoutItemAt( i );
if ( layoutItem->hasUnlimitedSpan( Qt::Horizontal ) )
layoutItem->setRowSpan( numColumns - layoutItem->firstColumn(), Qt::Horizontal );
if ( layoutItem->hasUnlimitedSpan( Qt::Vertical ) )
layoutItem->setRowSpan( numRows - layoutItem->firstRow(), Qt::Vertical );
}
}

View File

@ -0,0 +1,95 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_GRID_LAYOUT_ENGINE_H
#define QSK_GRID_LAYOUT_ENGINE_H
#include "QskGlobal.h"
#include <qnamespace.h>
#include <qrect.h>
#include <memory>
class QQuickItem;
class QskGridLayoutEngine
{
public:
QskGridLayoutEngine();
~QskGridLayoutEngine();
void setGeometries( const QRectF );
void invalidate();
void setVisualDirection( Qt::LayoutDirection );
Qt::LayoutDirection visualDirection() const;
int itemCount() const;
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;
void setColumnSizeHint( int column, Qt::SizeHint, qreal width );
qreal columnSizeHint( int column, Qt::SizeHint ) const;
QSizeF sizeHint( Qt::SizeHint, const QSizeF& constraint = QSizeF() ) const;
qreal widthForHeight( qreal height ) const;
qreal heightForWidth( qreal width ) const;
static qreal defaultSpacing( Qt::Orientation );
#if 1
QSize requiredCells() const;
void adjustSpans( int numRows, int numColumns );
#endif
private:
Q_DISABLE_COPY(QskGridLayoutEngine)
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
#endif

View File

@ -4,8 +4,6 @@
*****************************************************************************/
#include "QskIndexedLayoutBox.h"
#include "QskLayoutEngine.h"
#include "QskLayoutItem.h"
#include "QskQuick.h"
class QskIndexedLayoutBox::PrivateData
@ -14,30 +12,15 @@ class QskIndexedLayoutBox::PrivateData
PrivateData()
: autoAddChildren( true )
, blockChildAdded( false )
, defaultAlignment( Qt::AlignLeft | Qt::AlignVCenter )
{
}
bool autoAddChildren : 1;
bool blockChildAdded : 1;
/*
QGridLayoutEngine is supposed to find the alignment
( see: QGridLayoutEngine::effectiveAlignment ) by looking up in:
item -> row/column -> layout default.
Unfortunatly the layout default can't be modified without accessing
private methods and - for some reason worse - QGridLayoutEngine::effectiveAlignment
does not fall back to the layout default for the horizontal alignment.
But as we don't offer setting the row/column alignment at the public API
of QskIndexedLayoutBox we work around by using them instead.
*/
Qt::Alignment defaultAlignment;
};
QskIndexedLayoutBox::QskIndexedLayoutBox( QQuickItem* parent )
: QskLayoutBox( parent )
: QskBox( false, parent )
, m_data( new PrivateData() )
{
// classBegin/componentComplete -> setActive( false/true ) ?
@ -61,142 +44,6 @@ bool QskIndexedLayoutBox::autoAddChildren() const
return m_data->autoAddChildren;
}
void QskIndexedLayoutBox::setDefaultAlignment( Qt::Alignment alignment )
{
bool hasChanged = false;
const Qt::Alignment alignV = alignment & Qt::AlignVertical_Mask;
if ( alignV != ( m_data->defaultAlignment & Qt::AlignVertical_Mask ) )
{
hasChanged = true;
for ( int row = 0; row < engine().rowCount(); row++ )
engine().setRowAlignment( row, alignV, Qt::Vertical );
}
const Qt::Alignment alignH = alignment & Qt::AlignHorizontal_Mask;
if ( alignH != ( m_data->defaultAlignment & Qt::AlignHorizontal_Mask ) )
{
hasChanged = true;
for ( int col = 0; col < engine().columnCount(); col++ )
engine().setRowAlignment( col, alignH, Qt::Horizontal );
}
if ( hasChanged )
{
m_data->defaultAlignment = alignment;
Q_EMIT defaultAlignmentChanged();
}
}
Qt::Alignment QskIndexedLayoutBox::defaultAlignment() const
{
return m_data->defaultAlignment;
}
void QskIndexedLayoutBox::addItem(
QQuickItem* item, Qt::Alignment alignment )
{
insertItem( -1, item, alignment );
}
void QskIndexedLayoutBox::insertItem(
int index, QQuickItem* item, Qt::Alignment alignment )
{
if ( item == nullptr )
return;
if ( item->parentItem() == this )
{
const int oldIndex = indexOf( item );
if ( oldIndex >= 0 )
{
// the item has been inserted before
const bool doAppend = index < 0 || index >= itemCount();
if ( ( index == oldIndex ) ||
( doAppend && oldIndex == itemCount() - 1 ) )
{
// already at its position, nothing to do
return;
}
removeAt( oldIndex );
}
}
auto layoutItem = new QskLayoutItem( item, 0, 0 );
layoutItem->setAlignment( alignment );
insertLayoutItem( layoutItem, index );
}
void QskIndexedLayoutBox::setAlignment( int index, Qt::Alignment alignment )
{
auto layoutItem = engine().layoutItemAt( index );
if ( layoutItem && ( alignment != layoutItem->alignment() ) )
{
layoutItem->setAlignment( alignment );
activate(); // invalidate() ???
}
}
Qt::Alignment QskIndexedLayoutBox::alignment( int index ) const
{
const auto layoutItem = engine().layoutItemAt( index );
if ( layoutItem )
return layoutItem->alignment();
return Qt::Alignment();
}
void QskIndexedLayoutBox::setAlignment(
const QQuickItem* item, Qt::Alignment alignment )
{
setAlignment( engine().indexOf( item ), alignment );
}
Qt::Alignment QskIndexedLayoutBox::alignment( const QQuickItem* item ) const
{
return alignment( engine().indexOf( item ) );
}
void QskIndexedLayoutBox::insertLayoutItem(
QskLayoutItem* layoutItem, int index )
{
const int numItems = itemCount();
if ( index < 0 || index > numItems )
index = numItems;
setupLayoutItem( layoutItem, index );
const int rowCount = engine().rowCount();
const int columnCount = engine().columnCount();
// not exception safe !!
m_data->blockChildAdded = true;
insertItemInternal( layoutItem, index );
m_data->blockChildAdded = false;
if ( rowCount != engine().rowCount() )
{
const Qt::Alignment alignV = m_data->defaultAlignment & Qt::AlignVertical_Mask;
for ( int row = 0; row < engine().rowCount(); row++ )
engine().setRowAlignment( row, alignV, Qt::Vertical );
}
if ( columnCount != engine().columnCount() )
{
const Qt::Alignment alignH = m_data->defaultAlignment & Qt::AlignHorizontal_Mask;
for ( int col = 0; col < engine().columnCount(); col++ )
engine().setRowAlignment( col, alignH, Qt::Horizontal );
}
layoutItemInserted( layoutItem, index );
}
void QskIndexedLayoutBox::itemChange(
QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& value )
{
@ -207,16 +54,27 @@ void QskIndexedLayoutBox::itemChange(
if ( m_data->autoAddChildren && !m_data->blockChildAdded )
{
if ( !qskIsTransparentForPositioner( value.item ) )
addItem( value.item );
autoAddItem( value.item );
}
break;
}
case QQuickItem::ItemChildRemovedChange:
{
removeItem( value.item );
autoRemoveItem( value.item );
break;
}
#if 1
case QQuickItem::ItemSceneChange:
{
// when changing the window we should run into polish anyway
if ( value.window )
polish();
break;
}
#endif
default:
{
break;
@ -226,4 +84,17 @@ void QskIndexedLayoutBox::itemChange(
return Inherited::itemChange( change, value );
}
void QskIndexedLayoutBox::reparentItem( QQuickItem* item )
{
if ( item->parent() == nullptr )
item->setParent( this );
if ( item->parentItem() != this )
{
m_data->blockChildAdded = true;
item->setParentItem( this );
m_data->blockChildAdded = false;
}
}
#include "moc_QskIndexedLayoutBox.cpp"

View File

@ -6,19 +6,16 @@
#ifndef QSK_INDEXED_LAYOUT_BOX_H
#define QSK_INDEXED_LAYOUT_BOX_H
#include "QskLayoutBox.h"
#include "QskBox.h"
class QSK_EXPORT QskIndexedLayoutBox : public QskLayoutBox
class QSK_EXPORT QskIndexedLayoutBox : public QskBox
{
Q_OBJECT
Q_PROPERTY( bool autoAddChildren READ autoAddChildren
WRITE setAutoAddChildren NOTIFY autoAddChildrenChanged )
Q_PROPERTY( Qt::Alignment defaultAlignment READ defaultAlignment
WRITE setDefaultAlignment NOTIFY defaultAlignmentChanged )
using Inherited = QskLayoutBox;
using Inherited = QskBox;
public:
explicit QskIndexedLayoutBox( QQuickItem* parent = nullptr );
@ -27,28 +24,15 @@ class QSK_EXPORT QskIndexedLayoutBox : public QskLayoutBox
void setAutoAddChildren( bool );
bool autoAddChildren() const;
void setDefaultAlignment( Qt::Alignment );
Qt::Alignment defaultAlignment() const;
Q_INVOKABLE void addItem(
QQuickItem*, Qt::Alignment alignment = Qt::Alignment() );
Q_INVOKABLE void insertItem(
int index, QQuickItem*, Qt::Alignment alignment = Qt::Alignment() );
void setAlignment( int index, Qt::Alignment );
Qt::Alignment alignment( int index ) const;
void setAlignment( const QQuickItem*, Qt::Alignment );
Qt::Alignment alignment( const QQuickItem* ) const;
Q_SIGNALS:
void autoAddChildrenChanged();
void defaultAlignmentChanged();
protected:
void itemChange( ItemChange, const ItemChangeData& ) override;
void insertLayoutItem( QskLayoutItem*, int index );
void reparentItem( QQuickItem* );
virtual void autoAddItem( QQuickItem* ) = 0;
virtual void autoRemoveItem( QQuickItem* ) = 0;
private:
class PrivateData;

View File

@ -1,403 +0,0 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskLayoutBox.h"
#include "QskEvent.h"
#include "QskLayoutEngine.h"
#include "QskLayoutItem.h"
#include "QskLayoutConstraint.h"
class QskLayoutBox::PrivateData
{
public:
PrivateData()
: isActive( true )
{
}
bool isActive : 1;
QskLayoutEngine engine;
};
QskLayoutBox::QskLayoutBox( QQuickItem* parent )
: QskBox( false, parent )
, m_data( new PrivateData() )
{
}
QskLayoutBox::~QskLayoutBox()
{
disconnect( this, 0, this, 0 ); // destructor runs on invalidate else
setActive( false );
}
void QskLayoutBox::setActive( bool on )
{
if ( on == m_data->isActive )
return;
m_data->isActive = on;
for ( int i = 0; i < itemCount(); ++i )
{
if( auto item = itemAtIndex( i ) )
setItemActive( item, on );
}
if ( on )
{
resetImplicitSize();
polish();
}
Q_EMIT activeChanged( m_data->isActive );
}
bool QskLayoutBox::isActive() const
{
return m_data->isActive;
}
int QskLayoutBox::itemCount() const
{
return m_data->engine.itemCount();
}
QQuickItem* QskLayoutBox::itemAtIndex( int index ) const
{
if ( auto layoutItem = m_data->engine.layoutItemAt( index ) )
return layoutItem->item();
return nullptr;
}
int QskLayoutBox::indexOf( const QQuickItem* item ) const
{
if ( item != nullptr )
return m_data->engine.indexOf( item );
return -1;
}
void QskLayoutBox::insertItemInternal( QskLayoutItem* layoutItem, int index )
{
// check if item is already inserted ???
auto item = layoutItem->item();
if ( index > itemCount() )
index = -1; // append
auto& engine = this->engine();
if ( item )
{
if ( item->parent() == nullptr )
item->setParent( this );
if ( item->parentItem() != this )
item->setParentItem( this );
/*
Re-ordering the child items to have a a proper focus tab chain
*/
bool reordered = false;
if ( ( index >= 0 ) && ( index < itemCount() - 1 ) )
{
for ( int i = index; i < engine.itemCount(); i++ )
{
auto layoutItem = engine.layoutItemAt( i );
if ( layoutItem && layoutItem->item() )
{
item->stackBefore( layoutItem->item() );
reordered = true;
break;
}
}
}
if ( !reordered )
{
const auto children = childItems();
if ( item != children.last() )
item->stackAfter( children.last() );
}
}
engine.insertLayoutItem( layoutItem, index );
if ( m_data->isActive )
{
setItemActive( item, true );
resetImplicitSize();
polish();
}
}
void QskLayoutBox::removeAt( int index )
{
auto& engine = this->engine();
auto layoutItem = engine.layoutItemAt( index );
if ( layoutItem == nullptr )
return;
setItemActive( layoutItem->item(), false );
engine.removeItem( layoutItem );
layoutItemRemoved( layoutItem, index );
delete layoutItem;
if ( m_data->isActive )
{
resetImplicitSize();
polish();
}
}
void QskLayoutBox::removeItem( const QQuickItem* item )
{
removeAt( indexOf( item ) );
}
void QskLayoutBox::clear( bool autoDelete )
{
const bool isActive = m_data->isActive;
setActive( false );
for ( int i = itemCount() - 1; i >= 0; i-- )
{
auto item = itemAtIndex( i );
removeAt( i );
if( item )
{
if( autoDelete && ( item->parent() == this ) )
delete item;
else
item->setParentItem( nullptr );
}
}
setActive( isActive );
}
void QskLayoutBox::setupLayoutItem( QskLayoutItem* layoutItem, int index )
{
Q_UNUSED( layoutItem )
Q_UNUSED( index )
}
void QskLayoutBox::layoutItemInserted( QskLayoutItem* layoutItem, int index )
{
Q_UNUSED( layoutItem )
Q_UNUSED( index )
}
void QskLayoutBox::layoutItemRemoved( QskLayoutItem* layoutItem, int index )
{
Q_UNUSED( layoutItem )
Q_UNUSED( index )
}
void QskLayoutBox::activate()
{
if ( m_data->isActive )
polish();
}
void QskLayoutBox::invalidate()
{
engine().invalidate();
activate();
resetImplicitSize();
}
void QskLayoutBox::adjustItem( const QQuickItem* item )
{
adjustItemAt( indexOf( item ) );
}
void QskLayoutBox::adjustItemAt( int index )
{
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 );
}
void QskLayoutBox::updateLayout()
{
if ( m_data->isActive )
engine().setGeometries( alignedLayoutRect( layoutRect() ) );
}
QRectF QskLayoutBox::alignedLayoutRect( const QRectF& rect ) const
{
return rect;
}
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, QSizeF() );
}
qreal QskLayoutBox::heightForWidth( qreal width ) const
{
auto constrainedHeight =
[this]( QskLayoutConstraint::Type, const QskControl*, qreal width )
{
return engine().heightForWidth( width );
};
return QskLayoutConstraint::constrainedMetric(
QskLayoutConstraint::HeightForWidth, this, width, constrainedHeight );
}
qreal QskLayoutBox::widthForHeight( qreal height ) const
{
auto constrainedWidth =
[this]( QskLayoutConstraint::Type, const QskControl*, qreal height )
{
return engine().widthForHeight( height );
};
return QskLayoutConstraint::constrainedMetric(
QskLayoutConstraint::WidthForHeight, this, height, constrainedWidth );
}
void QskLayoutBox::geometryChangeEvent( QskGeometryChangeEvent* event )
{
Inherited::geometryChangeEvent( event );
if ( event->isResized() )
activate();
}
void QskLayoutBox::setItemActive( const QQuickItem* item, bool on )
{
if ( item == nullptr )
return;
// QskControl sends QEvent::LayoutRequest
const bool hasLayoutRequests = qskControlCast( item );
if ( !hasLayoutRequests )
{
if ( on )
{
connect( item, &QQuickItem::implicitWidthChanged,
this, &QskLayoutBox::invalidate );
connect( item, &QQuickItem::implicitHeightChanged,
this, &QskLayoutBox::invalidate );
}
else
{
disconnect( item, &QQuickItem::implicitWidthChanged,
this, &QskLayoutBox::invalidate );
disconnect( item, &QQuickItem::implicitHeightChanged,
this, &QskLayoutBox::invalidate );
}
}
if ( on )
connect( item, &QQuickItem::visibleChanged, this, &QskLayoutBox::activate );
else
disconnect( item, &QQuickItem::visibleChanged, this, &QskLayoutBox::activate );
}
QskLayoutEngine& QskLayoutBox::engine()
{
return m_data->engine;
}
const QskLayoutEngine& QskLayoutBox::engine() const
{
return m_data->engine;
}
void QskLayoutBox::itemChange( ItemChange change, const ItemChangeData& value )
{
Inherited::itemChange( change, value );
switch ( change )
{
case ItemChildRemovedChange:
{
removeItem( value.item );
break;
}
case QQuickItem::ItemVisibleHasChanged:
{
if ( value.boolValue )
activate();
break;
}
case QQuickItem::ItemSceneChange:
{
if ( value.window )
activate();
break;
}
default:
break;
}
}
bool QskLayoutBox::event( QEvent* event )
{
switch ( event->type() )
{
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 );
}
#include "moc_QskLayoutBox.cpp"

View File

@ -1,90 +0,0 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_LAYOUT_BOX_H
#define QSK_LAYOUT_BOX_H
#include "QskBox.h"
class QskLayoutEngine;
class QskLayoutItem;
class QSK_EXPORT QskLayoutBox : public QskBox
{
Q_OBJECT
// signals ???
Q_PROPERTY( int itemCount READ itemCount() )
Q_PROPERTY( bool empty READ isEmpty() )
Q_PROPERTY( bool active READ isActive
WRITE setActive NOTIFY activeChanged )
using Inherited = QskBox;
public:
explicit QskLayoutBox( QQuickItem* parent = nullptr );
~QskLayoutBox() override;
bool isEmpty() const;
int itemCount() const;
QQuickItem* itemAtIndex( int index ) const;
int indexOf( const QQuickItem* ) const;
void removeItem( const QQuickItem* );
void removeAt( int index );
void setActive( bool );
bool isActive() const;
void adjustItem( const QQuickItem* );
void adjustItemAt( int index );
QSizeF contentsSizeHint() const override;
qreal heightForWidth( qreal width ) const override;
qreal widthForHeight( qreal height ) const override;
Q_SIGNALS:
void activeChanged( bool );
public Q_SLOTS:
void activate();
void invalidate();
void clear( bool autoDelete = false );
protected:
bool event( QEvent* ) override;
void geometryChangeEvent( QskGeometryChangeEvent* ) override;
void itemChange( ItemChange, const ItemChangeData& ) override;
void updateLayout() override;
QskLayoutEngine& engine();
const QskLayoutEngine& engine() const;
void setItemActive( const QQuickItem*, bool on );
void insertItemInternal( QskLayoutItem*, int index );
virtual void setupLayoutItem( QskLayoutItem*, int index );
virtual void layoutItemInserted( QskLayoutItem*, int index );
virtual void layoutItemRemoved( QskLayoutItem*, int index );
virtual QRectF alignedLayoutRect( const QRectF& ) const;
virtual QSizeF layoutItemsSizeHint() const;
private:
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
inline bool QskLayoutBox::isEmpty() const
{
return itemCount() <= 0;
}
#endif

View File

@ -103,10 +103,10 @@ QskLayoutConstraint::Type QskLayoutConstraint::constraintType( const QQuickItem*
Type constraintType = Unconstrained;
if ( auto control = qskControlCast( item ) )
{
{
const auto policy = control->sizePolicy();
if ( policy.horizontalPolicy() == QskSizePolicy::Constrained )
{
{
constraintType = WidthForHeight;
}
else if ( policy.verticalPolicy() == QskSizePolicy::Constrained )
@ -125,7 +125,7 @@ QskLayoutConstraint::Type QskLayoutConstraint::constraintType( const QQuickItem*
constraintType = HeightForWidth;
}
}
return constraintType;
}
@ -366,7 +366,7 @@ qreal QskLayoutConstraint::sizeHint( const QQuickItem* item,
QSizeF QskLayoutConstraint::sizeHint( const QQuickItem* item,
Qt::SizeHint whichHint, const QSizeF& constraint )
{
if ( item == nullptr || whichHint < Qt::MinimumSize || whichHint > Qt::MaximumSize )
if ( item == nullptr || whichHint < Qt::MinimumSize || whichHint > Qt::MaximumSize )
return QSizeF( 0, 0 );
QSizeF hint( 0, 0 );
@ -438,7 +438,7 @@ QRectF QskLayoutConstraint::itemRect( const QQuickItem* item,
switch( constraintType( item ) )
{
case HeightForWidth:
{
{
if ( size.width() > rect.width() )
size = qskExpandedSize( item, QSizeF( rect.width(), -1 ) );

View File

@ -1,239 +0,0 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskLayoutEngine.h"
#include "QskLayoutItem.h"
static inline bool qskIsColliding(
const QskLayoutEngine* engine, QskLayoutItem* item )
{
for ( int row = item->firstRow(); row <= item->lastRow(); row++ )
{
for ( int col = item->firstColumn(); col <= item->lastColumn(); col++ )
{
if ( engine->itemAt( row, col ) )
return true;
}
}
return false;
}
namespace
{
class MessageHandler
{
public:
MessageHandler()
{
m_defaultHandler = qInstallMessageHandler( suppressWarning );
}
~MessageHandler()
{
qInstallMessageHandler( m_defaultHandler );
}
private:
static void suppressWarning( QtMsgType type,
const QMessageLogContext& context, const QString& message )
{
if ( type == QtWarningMsg )
return;
if ( m_defaultHandler )
( *m_defaultHandler )( type, context, message );
}
static QtMessageHandler m_defaultHandler;
};
QtMessageHandler MessageHandler::m_defaultHandler;
class LayoutStyleInfo final : public QAbstractLayoutStyleInfo
{
public:
LayoutStyleInfo()
{
}
qreal spacing( Qt::Orientation ) const override
{
// later from the theme !!
return 5.0;
}
qreal windowMargin( Qt::Orientation ) const override
{
// later from the theme !!
return 0;
}
bool hasChangedCore() const override
{
return false; // never changes
}
};
}
QskLayoutEngine::QskLayoutEngine()
: QGridLayoutEngine( Qt::AlignVCenter, false /*snapToPixelGrid*/ )
{
/*
snapToPixelGrid rounds x/y, what might lead to losing a pixel.
F.e. when having a text in elideMode we end up with an elided text
because of this.
*/
}
QskLayoutEngine::~QskLayoutEngine()
{
}
void QskLayoutEngine::setGeometries( const QRectF rect )
{
const LayoutStyleInfo styleInfo;
QGridLayoutEngine::setGeometries( rect, &styleInfo );
}
void QskLayoutEngine::insertLayoutItem( QskLayoutItem* item, int index )
{
if ( qskIsColliding( this, item ) )
{
// It is totally valid to have more than one item in the same cell
// and we make use of it f.e in QskStackLayout. So we better
// suppress the corresponding warning to avoid confusion.
MessageHandler suppressWarning;
insertItem( item, index );
}
else
{
insertItem( item, index );
}
}
int QskLayoutEngine::indexAt( int row, int column ) const
{
const auto item = layoutItemAt( row, column );
if ( item )
return q_items.indexOf( item );
return -1;
}
QskLayoutItem* QskLayoutEngine::layoutItemAt( int index ) const
{
if ( index < 0 || index >= q_items.count() )
return nullptr;
return static_cast< QskLayoutItem* >( q_items[ index ] );
}
QskLayoutItem* QskLayoutEngine::layoutItemAt( int row, int column ) const
{
if ( row < 0 || row >= rowCount() || column < 0 || column >= columnCount() )
return nullptr;
return static_cast< QskLayoutItem* >( itemAt( row, column ) );
}
QskLayoutItem* QskLayoutEngine::layoutItemAt(
int row, int column, Qt::Orientation orientation ) const
{
if ( orientation == Qt::Horizontal )
return layoutItemAt( row, column );
else
return layoutItemAt( column, row );
}
int QskLayoutEngine::indexOf( const QQuickItem* item ) const
{
// linear search might become slow for many items,
// better introduce some sort of hash table TODO ...
for ( int i = q_items.count() - 1; i >= 0; --i )
{
const auto layoutItem = static_cast< const QskLayoutItem* >( q_items[ i ] );
if ( layoutItem->item() == item )
return i;
}
return -1;
}
QSizeF QskLayoutEngine::sizeHint( Qt::SizeHint which, const QSizeF& constraint ) const
{
const LayoutStyleInfo styleInfo;
return QGridLayoutEngine::sizeHint( which, constraint, &styleInfo );
}
qreal QskLayoutEngine::widthForHeight( qreal height ) const
{
const QSizeF constraint( -1, height );
return sizeHint( Qt::PreferredSize, constraint ).width();
}
qreal QskLayoutEngine::heightForWidth( qreal width ) const
{
const QSizeF constraint( width, -1 );
return sizeHint( Qt::PreferredSize, constraint ).height();
}
qreal QskLayoutEngine::spacing( Qt::Orientation orientation ) const
{
const LayoutStyleInfo styleInfo;
return QGridLayoutEngine::spacing( orientation, &styleInfo );
}
qreal QskLayoutEngine::defaultSpacing( Qt::Orientation orientation )
{
return LayoutStyleInfo().spacing( orientation );
}
#if 1
// QGridLayout or here ???
QSize QskLayoutEngine::requiredCells() const
{
int lastRow = -1;
int lastColumn = -1;
for ( int i = 0; i < itemCount(); i++ )
{
const auto layoutItem = layoutItemAt( i );
if ( layoutItem->isIgnored() )
continue;
const int col = layoutItem->hasUnlimitedSpan( Qt::Horizontal )
? layoutItem->firstColumn() + 1 : layoutItem->lastColumn();
if ( col > lastColumn )
lastColumn = col;
const int row = layoutItem->hasUnlimitedSpan( Qt::Vertical )
? layoutItem->firstRow() + 1 : layoutItem->lastRow();
if ( row > lastRow )
lastRow = row;
}
return QSize( lastColumn + 1, lastRow + 1 );
}
void QskLayoutEngine::adjustSpans( int numRows, int numColumns )
{
for ( int i = 0; i < itemCount(); i++ )
{
auto layoutItem = layoutItemAt( i );
if ( layoutItem->hasUnlimitedSpan( Qt::Horizontal ) )
layoutItem->setRowSpan( numColumns - layoutItem->firstColumn(), Qt::Horizontal );
if ( layoutItem->hasUnlimitedSpan( Qt::Vertical ) )
layoutItem->setRowSpan( numRows - layoutItem->firstRow(), Qt::Vertical );
}
}
#endif

View File

@ -1,61 +0,0 @@
/******************************************************************************
* 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_H
#define QSK_LAYOUT_ENGINE_H
#include "QskGlobal.h"
QSK_QT_PRIVATE_BEGIN
/*
QskLayoutEngine.h should be hidden into some cpp file
as it needs private headers. TODO
*/
#include <private/qgridlayoutengine_p.h>
QSK_QT_PRIVATE_END
class QskLayoutItem;
class QQuickItem;
class QskLayoutEngine : public QGridLayoutEngine
{
public:
QskLayoutEngine();
~QskLayoutEngine();
void setGeometries( const QRectF );
void insertLayoutItem( QskLayoutItem* item, int index );
QskLayoutItem* layoutItemAt( int index ) const;
QskLayoutItem* layoutItemAt( int row, int column ) const;
QskLayoutItem* layoutItemAt( int row, int column, Qt::Orientation ) const;
int indexAt( int row, int column ) const;
QskLayoutItem* layoutItemOf( const QQuickItem* ) const;
int indexOf( const QQuickItem* item ) const;
QSizeF sizeHint( Qt::SizeHint, const QSizeF& constraint = QSizeF() ) const;
qreal widthForHeight( qreal height ) const;
qreal heightForWidth( qreal width ) const;
qreal spacing( Qt::Orientation ) const;
static qreal defaultSpacing( Qt::Orientation );
#if 1
QSize requiredCells() const;
void adjustSpans( int numRows, int numColumns );
#endif
};
inline QskLayoutItem* QskLayoutEngine::layoutItemOf( const QQuickItem* item ) const
{
return layoutItemAt( indexOf( item ) );
}
#endif

View File

@ -1,187 +0,0 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskLayoutItem.h"
#include "QskControl.h"
#include "QskLayoutConstraint.h"
#include "QskQuick.h"
QskLayoutItem::QskLayoutItem( QQuickItem* item, int row, int column, int rowSpan, int columnSpan )
: Inherited( row, column, qMax( rowSpan, 1 ), qMax( columnSpan, 1 ), Qt::Alignment() )
, m_item( item )
, m_isGeometryDirty( false )
, m_isStretchable( false )
, m_retainSizeWhenHidden( false )
, m_unlimitedRowSpan( rowSpan <= 0 )
, m_unlimitedColumnSpan( columnSpan <= 0 )
, m_updateMode( UpdateWhenVisible )
{
}
QskLayoutItem::QskLayoutItem( const QSizeF& size, int stretch, int row, int column )
: Inherited( row, column, 1, 1, Qt::Alignment() )
, m_item( nullptr )
, m_spacingHint( size )
, m_isGeometryDirty( false )
, m_isStretchable( stretch > 0 )
, m_retainSizeWhenHidden( false )
, m_unlimitedRowSpan( false )
, m_unlimitedColumnSpan( false )
, m_updateMode( UpdateWhenVisible )
{
}
QskLayoutItem::~QskLayoutItem()
{
}
QskLayoutItem::UpdateMode QskLayoutItem::updateMode() const
{
return m_updateMode;
}
void QskLayoutItem::setUpdateMode( UpdateMode mode )
{
m_updateMode = mode;
}
bool QskLayoutItem::retainSizeWhenHidden() const
{
return m_retainSizeWhenHidden;
}
void QskLayoutItem::setRetainSizeWhenHidden( bool on )
{
m_retainSizeWhenHidden = on;
}
QSizeF QskLayoutItem::spacingHint() const
{
return m_spacingHint;
}
void QskLayoutItem::setSpacingHint( const QSizeF& hint )
{
m_spacingHint = hint;
}
QSizeF QskLayoutItem::sizeHint(
Qt::SizeHint whichHint, const QSizeF& constraint ) const
{
if ( m_item == nullptr )
{
if ( whichHint < Qt::MinimumSize || whichHint > Qt::MaximumSize )
return QSizeF( 0, 0 );
// a spacer item
if ( whichHint == Qt::MaximumSize )
{
if ( m_isStretchable )
return QSizeF( QskLayoutConstraint::unlimited, QskLayoutConstraint::unlimited );
if ( m_spacingHint.width() < 0 )
return QSizeF( QskLayoutConstraint::unlimited, m_spacingHint.height() );
else
return QSizeF( m_spacingHint.width(), QskLayoutConstraint::unlimited );
}
else
{
if ( m_spacingHint.width() < 0 )
return QSizeF( 0, m_spacingHint.height() );
else
return QSizeF( m_spacingHint.width(), 0 );
}
}
else
{
return QskLayoutConstraint::sizeHint( m_item, whichHint, constraint );
}
}
QLayoutPolicy::Policy QskLayoutItem::sizePolicy( Qt::Orientation orientation ) const
{
auto policy = QskLayoutConstraint::sizePolicy( m_item ).policy( orientation );
#if 0
if ( ( policy == QskSizePolicy::Preferred ) && m_item )
{
// QskSizePolicy::Preferred without having a preferred size is the default
// setting of QskControl - taken from what QWidget does - but this combination
// doesn't make much sense. Usually every derived control is supposed
// to set specific values, but in case it has been forgotten we better
// ignore the preferred size then.
const QSizeF hint = QskLayoutConstraint::effectiveConstraint( m_item, Qt::PreferredSize );
const qreal value = ( orientation == Qt::Horizontal ) ? hint.width() : hint.height();
if ( value <= 0 )
policy = QskSizePolicy::Ignored;
}
#endif
return static_cast< QLayoutPolicy::Policy >( policy );
}
void QskLayoutItem::setGeometry( const QRectF& rect )
{
if ( m_item == nullptr )
return;
if ( m_updateMode == UpdateNone )
{
if ( !m_isGeometryDirty )
m_isGeometryDirty = ( rect != qskItemGeometry( m_item ) );
return;
}
if ( m_updateMode == UpdateWhenVisible )
{
if ( !m_item->isVisible() )
return;
}
m_isGeometryDirty = false;
qskSetItemGeometry( m_item, rect );
}
bool QskLayoutItem::hasDynamicConstraint() const
{
if ( m_item )
{
using namespace QskLayoutConstraint;
return constraintType( m_item ) != Unconstrained;
}
return false;
}
Qt::Orientation QskLayoutItem::dynamicConstraintOrientation() const
{
Qt::Orientation orientation = Qt::Vertical;
if ( auto control = qskControlCast( m_item ) )
{
const auto policy = control->sizePolicy().horizontalPolicy();
return ( policy == QskSizePolicy::Constrained )
? Qt::Horizontal : Qt::Vertical;
}
return orientation;
}
bool QskLayoutItem::isIgnored() const
{
if ( m_item && !qskIsVisibleToParent( m_item ) )
return !m_retainSizeWhenHidden;
return false;
}
QLayoutPolicy::ControlTypes QskLayoutItem::controlTypes( LayoutSide side ) const
{
return Inherited::controlTypes( side );
}

View File

@ -1,106 +0,0 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_LAYOUT_ITEM_H
#define QSK_LAYOUT_ITEM_H
#include "QskGlobal.h"
QSK_QT_PRIVATE_BEGIN
/*
QskLayoutItem.h should be hidden into some cpp file
as it needs private headers. TODO
*/
#include <private/qgridlayoutengine_p.h>
QSK_QT_PRIVATE_END
class QQuickItem;
class QskLayoutItem : public QGridLayoutItem
{
using Inherited = QGridLayoutItem;
public:
enum UpdateMode
{
UpdateNone,
UpdateWhenVisible,
UpdateAlways
};
QskLayoutItem( QQuickItem* item, int row, int column,
int rowSpan = 1, int columnSpan = 1 );
QskLayoutItem( const QSizeF& size, int stretch, int row, int column );
~QskLayoutItem() override;
QQuickItem* item();
const QQuickItem* item() const;
QLayoutPolicy::Policy sizePolicy( Qt::Orientation ) const override final;
QSizeF sizeHint( Qt::SizeHint, const QSizeF& ) const override final;
void setGeometry( const QRectF& ) override final;
bool hasDynamicConstraint() const override final;
Qt::Orientation dynamicConstraintOrientation() const override final;
bool isIgnored() const override final;
QLayoutPolicy::ControlTypes controlTypes( LayoutSide side ) const override final;
bool retainSizeWhenHidden() const;
void setRetainSizeWhenHidden( bool on );
UpdateMode updateMode() const;
void setUpdateMode( UpdateMode );
QSizeF spacingHint() const;
void setSpacingHint( const QSizeF& );
bool isGeometryDirty() const;
bool hasUnlimitedSpan() const;
bool hasUnlimitedSpan( Qt::Orientation orientation ) const;
private:
QQuickItem* m_item;
QSizeF m_spacingHint;
bool m_isGeometryDirty : 1;
bool m_isStretchable : 1;
bool m_retainSizeWhenHidden : 1;
bool m_unlimitedRowSpan : 1;
bool m_unlimitedColumnSpan : 1;
UpdateMode m_updateMode : 2;
};
inline QQuickItem* QskLayoutItem::item()
{
return m_item;
}
inline const QQuickItem* QskLayoutItem::item() const
{
return m_item;
}
inline bool QskLayoutItem::isGeometryDirty() const
{
return m_isGeometryDirty;
}
inline bool QskLayoutItem::hasUnlimitedSpan( Qt::Orientation orientation ) const
{
return ( orientation == Qt::Horizontal )
? m_unlimitedColumnSpan : m_unlimitedRowSpan;
}
inline bool QskLayoutItem::hasUnlimitedSpan() const
{
return m_unlimitedColumnSpan || m_unlimitedRowSpan;
}
#endif

View File

@ -4,26 +4,57 @@
*****************************************************************************/
#include "QskLinearBox.h"
#include "QskLayoutEngine.h"
#include "QskLayoutItem.h"
#include "QskLinearLayoutEngine.h"
#include <qendian.h>
#include "QskLayoutConstraint.h"
#include "QskEvent.h"
#include "QskQuick.h"
static void qskSetItemActive( QObject* receiver, const QQuickItem* item, bool on )
{
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
{
QObject::disconnect( item, &QQuickItem::implicitWidthChanged, receiver, nullptr );
QObject::disconnect( item, &QQuickItem::implicitHeightChanged, receiver, nullptr );
QObject::disconnect( item, &QQuickItem::visibleChanged, receiver, nullptr );
}
}
class QskLinearBox::PrivateData
{
public:
PrivateData( Qt::Orientation orient, uint dim )
: dimension( dim )
, orientation( orient )
, transposeAlignments( false )
PrivateData( Qt::Orientation orientation, uint dimension )
: engine( orientation, dimension )
{
}
uint dimension;
Qt::Edges extraSpacingAt;
Qt::Orientation orientation : 2;
bool transposeAlignments : 1;
QskLinearLayoutEngine engine;
};
QskLinearBox::QskLinearBox( QQuickItem* parent )
@ -36,9 +67,8 @@ QskLinearBox::QskLinearBox( Qt::Orientation orientation, QQuickItem* parent )
{
}
QskLinearBox::QskLinearBox(
Qt::Orientation orientation, uint dimension, QQuickItem* parent )
: Inherited( parent )
QskLinearBox::QskLinearBox( Qt::Orientation orientation, uint dimension, QQuickItem* parent )
: QskIndexedLayoutBox( parent )
, m_data( new PrivateData( orientation, dimension ) )
{
}
@ -47,97 +77,308 @@ QskLinearBox::~QskLinearBox()
{
}
int QskLinearBox::entryCount() const
{
return m_data->engine.count();
}
QQuickItem* QskLinearBox::itemAtIndex( int index ) const
{
return m_data->engine.itemAt( index );
}
int QskLinearBox::indexOf( const QQuickItem* item ) const
{
if ( 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 )
{
removeItemInternal( index, false );
}
void QskLinearBox::removeItemInternal( int index, bool unparent )
{
auto& engine = m_data->engine;
if ( index < 0 || index >= engine.count() )
return;
auto item = engine.itemAt( index );
engine.removeAt( index );
if ( item )
{
qskSetItemActive( this, engine.itemAt( index ), false );
if ( !unparent )
{
if ( item->parentItem() == this )
item->setParentItem( nullptr );
}
}
resetImplicitSize();
polish();
}
void QskLinearBox::removeItem( const QQuickItem* item )
{
removeAt( indexOf( item ) );
}
void QskLinearBox::clear( bool autoDelete )
{
auto& engine = m_data->engine;
// do we have visible entries
const bool hasVisibleEntries = engine.rowCount() > 0;
for ( int i = engine.count() - 1; i >= 0; i-- )
{
auto item = engine.itemAt( i );
engine.removeAt( i );
if( item )
{
qskSetItemActive( this, item, false );
if( autoDelete && ( item->parent() == this ) )
delete item;
else
item->setParentItem( nullptr );
}
}
if ( hasVisibleEntries )
resetImplicitSize();
}
void QskLinearBox::autoAddItem( QQuickItem* item )
{
insertItem( -1, item );
}
void QskLinearBox::autoRemoveItem( QQuickItem* item )
{
removeItemInternal( indexOf( item ), true );
}
void QskLinearBox::activate()
{
polish();
}
void QskLinearBox::invalidate()
{
m_data->engine.invalidate();
resetImplicitSize();
polish();
}
void QskLinearBox::updateLayout()
{
m_data->engine.setGeometries( layoutRect() );
}
QSizeF QskLinearBox::contentsSizeHint() const
{
return m_data->engine.sizeHint( Qt::PreferredSize, QSizeF() );
}
qreal QskLinearBox::heightForWidth( qreal width ) const
{
auto constrainedHeight =
[this]( QskLayoutConstraint::Type, const QskControl*, qreal width )
{
return m_data->engine.heightForWidth( width );
};
return QskLayoutConstraint::constrainedMetric(
QskLayoutConstraint::HeightForWidth, this, width, constrainedHeight );
}
qreal QskLinearBox::widthForHeight( qreal height ) const
{
auto constrainedWidth =
[this]( QskLayoutConstraint::Type, const QskControl*, qreal height )
{
return m_data->engine.widthForHeight( height );
};
return QskLayoutConstraint::constrainedMetric(
QskLayoutConstraint::WidthForHeight, this, height, constrainedWidth );
}
void QskLinearBox::geometryChangeEvent( QskGeometryChangeEvent* event )
{
Inherited::geometryChangeEvent( event );
if ( event->isResized() )
polish();
}
void QskLinearBox::itemChange( ItemChange change, const ItemChangeData& value )
{
Inherited::itemChange( change, value );
#if 1
if ( change == QQuickItem::ItemVisibleHasChanged )
{
// when becoming visible we should run into polish anyway
if ( value.boolValue )
polish();
}
#endif
}
bool QskLinearBox::event( QEvent* event )
{
switch ( event->type() )
{
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;
}
return Inherited::event( event );
}
void QskLinearBox::setDimension( uint dimension )
{
if ( dimension < 1 )
dimension = 1;
if ( dimension != m_data->dimension )
{
m_data->dimension = dimension;
auto& engine = m_data->engine;
if ( dimension != engine.dimension() )
{
engine.setDimension( dimension );
rearrange();
polish();
resetImplicitSize();
Q_EMIT dimensionChanged();
}
}
uint QskLinearBox::dimension() const
{
return m_data->dimension;
return m_data->engine.dimension();
}
void QskLinearBox::setOrientation( Qt::Orientation orientation )
{
if ( m_data->orientation != orientation )
transpose();
auto& engine = m_data->engine;
if ( engine.orientation() != orientation )
{
engine.setOrientation( orientation );
polish();
resetImplicitSize();
Q_EMIT orientationChanged();
}
}
Qt::Orientation QskLinearBox::orientation() const
{
return m_data->orientation;
return m_data->engine.orientation();
}
void QskLinearBox::transpose()
{
const Qt::Orientation orientation =
( m_data->orientation == Qt::Horizontal ) ? Qt::Vertical : Qt::Horizontal;
auto& engine = m_data->engine;
const int numItems = itemCount();
#if 0
#include <qendian.h>
if ( numItems > 0 )
for ( int i = 0; i < engine.itemCount(); i++ )
{
for ( int i = 0; i < numItems; i++ )
{
QskLayoutItem* layoutItem = engine().layoutItemAt( i );
const int row = layoutItem->firstColumn();
const int col = layoutItem->firstRow();
engine().removeItem( layoutItem );
layoutItem->setFirstRow( row, Qt::Vertical );
layoutItem->setFirstRow( col, Qt::Horizontal );
#if 1
if ( m_data->transposeAlignments )
{
// Is it worth to blow the API with this flag, or would
// it be even better to have an indvidual flag for each
// item - and what about the size policies: do we want to
// transpose them too ?
const auto alignment = static_cast< Qt::Alignment >(
qbswap( static_cast< quint16 >( layoutItem->alignment() ) ) );
layoutItem->setAlignment( alignment );
}
#endif
if ( layoutItem->item() == nullptr )
{
// a spacing or stretch
layoutItem->setSpacingHint(
layoutItem->spacingHint().transposed() );
}
engine().insertLayoutItem( layoutItem, i );
}
invalidate();
auto alignment = engine.alignmentAt( i );
qbswap( static_cast< quint16 >( alignment ) );
engine.setAlignmentAt( i, alignment );
}
m_data->orientation = orientation;
Q_EMIT orientationChanged();
// extraSpacingAt ???
#endif
if ( engine.orientation() == Qt::Horizontal )
setOrientation( Qt::Vertical );
else
setOrientation( Qt::Horizontal );
}
void QskLinearBox::setDefaultAlignment( Qt::Alignment alignment )
{
auto& engine = m_data->engine;
if ( alignment != engine.defaultAlignment() )
{
engine.setDefaultAlignment( alignment );
Q_EMIT defaultAlignmentChanged();
}
}
Qt::Alignment QskLinearBox::defaultAlignment() const
{
return m_data->engine.defaultAlignment();
}
void QskLinearBox::setSpacing( qreal spacing )
{
/*
we should have setSpacing( qreal, Qt::Orientations ),
but need to create an API for Qml in QskQml
using qmlAttachedPropertiesObject then. TODO ...
*/
spacing = qMax( spacing, 0.0 );
if ( spacing != engine().spacing( Qt::Horizontal ) )
auto& engine = m_data->engine;
if ( spacing != engine.spacing( Qt::Horizontal ) )
{
engine().setSpacing( spacing, Qt::Horizontal | Qt::Vertical );
activate();
engine.setSpacing( spacing, Qt::Horizontal | Qt::Vertical );
polish();
Q_EMIT spacingChanged();
}
@ -145,22 +386,22 @@ void QskLinearBox::setSpacing( qreal spacing )
void QskLinearBox::resetSpacing()
{
const qreal spacing = QskLayoutEngine::defaultSpacing( Qt::Horizontal );
const qreal spacing = m_data->engine.defaultSpacing( Qt::Horizontal );
setSpacing( spacing );
}
qreal QskLinearBox::spacing() const
{
// do we always want to have the same spacing for both orientations
return engine().spacing( Qt::Horizontal );
return m_data->engine.spacing( Qt::Horizontal );
}
void QskLinearBox::setExtraSpacingAt( Qt::Edges edges )
{
if ( edges != m_data->extraSpacingAt )
if ( edges != m_data->engine.extraSpacingAt() )
{
m_data->extraSpacingAt = edges;
activate();
m_data->engine.setExtraSpacingAt( edges );
polish();
Q_EMIT extraSpacingAtChanged();
}
@ -168,7 +409,84 @@ void QskLinearBox::setExtraSpacingAt( Qt::Edges edges )
Qt::Edges QskLinearBox::extraSpacingAt() const
{
return m_data->extraSpacingAt;
return m_data->engine.extraSpacingAt();
}
void QskLinearBox::addItem( QQuickItem* item, Qt::Alignment alignment )
{
insertItem( -1, item, alignment );
}
void QskLinearBox::insertItem(
int index, QQuickItem* item, Qt::Alignment alignment )
{
if ( item == nullptr )
return;
auto& engine = m_data->engine;
if ( item->parentItem() == this )
{
const int oldIndex = indexOf( item );
if ( oldIndex >= 0 )
{
// the item has been inserted before
const bool doAppend = index < 0 || index >= engine.count();
if ( ( index == oldIndex ) ||
( doAppend && oldIndex == engine.count() - 1 ) )
{
// already at its position, nothing to do
return;
}
removeAt( oldIndex );
}
}
reparentItem( item );
const int numItems = engine.count();
if ( index < 0 || index > numItems )
index = numItems;
engine.insertItem( item, index );
engine.setAlignmentAt( index, alignment );
// Re-ordering the child items to have a a proper focus tab chain
bool reordered = false;
if ( index < engine.count() - 1 )
{
for ( int i = index; i < engine.count(); i++ )
{
if ( auto nextItem = engine.itemAt( i ) )
{
item->stackBefore( nextItem );
reordered = true;
break;
}
}
}
if ( !reordered )
{
const auto children = childItems();
if ( item != children.last() )
item->stackAfter( children.last() );
}
qskSetItemActive( this, item, true );
#if 1
// Is there a way to block consecutive calls ???
resetImplicitSize();
polish();
#endif
}
void QskLinearBox::addSpacer( qreal spacing, int stretchFactor )
@ -178,21 +496,22 @@ void QskLinearBox::addSpacer( qreal spacing, int stretchFactor )
void QskLinearBox::insertSpacer( int index, qreal spacing, int stretchFactor )
{
spacing = qMax( spacing, 0.0 );
stretchFactor = qMax( stretchFactor, 0 );
auto& engine = m_data->engine;
QskLayoutItem* layoutItem;
if ( m_data->orientation == Qt::Horizontal )
layoutItem = new QskLayoutItem( QSizeF( spacing, -1.0 ), stretchFactor, 0, 0 );
else
layoutItem = new QskLayoutItem( QSizeF( -1.0, spacing ), stretchFactor, 0, 0 );
const int numItems = engine.count();
if ( index < 0 || index > numItems )
index = numItems;
engine.insertSpacerAt( index, spacing );
stretchFactor = qMax( stretchFactor, 0 );
engine.setStretchFactorAt( index, stretchFactor );
#if 1
if ( stretchFactor >= 0 )
layoutItem->setStretchFactor( stretchFactor, m_data->orientation ); // already above ???
// Is there a way to block consecutive calls ???
resetImplicitSize();
polish();
#endif
insertLayoutItem( layoutItem, index );
}
void QskLinearBox::addStretch( int stretchFactor )
@ -205,270 +524,82 @@ void QskLinearBox::insertStretch( int index, int stretchFactor )
insertSpacer( index, 0, stretchFactor );
}
void QskLinearBox::setAlignment( int index, Qt::Alignment alignment )
{
if ( alignment != m_data->engine.alignmentAt( index ) )
{
m_data->engine.setAlignmentAt( index, alignment );
polish();
}
}
Qt::Alignment QskLinearBox::alignment( int index ) const
{
return m_data->engine.alignmentAt( index );
}
void QskLinearBox::setAlignment( const QQuickItem* item, Qt::Alignment alignment )
{
setAlignment( indexOf( item ), alignment );
}
Qt::Alignment QskLinearBox::alignment( const QQuickItem* item ) const
{
return alignment( indexOf( item ) );
}
void QskLinearBox::setStretchFactor( int index, int stretchFactor )
{
if ( QskLayoutItem* layoutItem = engine().layoutItemAt( index ) )
auto& engine = m_data->engine;
if ( engine.stretchFactorAt( index ) != stretchFactor )
{
if ( layoutItem->stretchFactor( m_data->orientation ) != stretchFactor )
{
layoutItem->setStretchFactor( stretchFactor, m_data->orientation );
// activate();
}
engine.setStretchFactorAt( index, stretchFactor );
polish();
}
}
int QskLinearBox::stretchFactor( int index ) const
{
if ( QskLayoutItem* layoutItem = engine().layoutItemAt( index ) )
return layoutItem->stretchFactor( m_data->orientation );
return 0;
return m_data->engine.stretchFactorAt( index );
}
void QskLinearBox::setStretchFactor( const QQuickItem* item, int stretch )
{
setStretchFactor( engine().indexOf( item ), stretch );
setStretchFactor( indexOf( item ), stretch );
}
int QskLinearBox::stretchFactor( const QQuickItem* item ) const
{
return stretchFactor( engine().indexOf( item ) );
return stretchFactor( indexOf( item ) );
}
void QskLinearBox::setRetainSizeWhenHidden( int index, bool on )
{
auto layoutItem = engine().layoutItemAt( index );
if ( layoutItem && on != layoutItem->retainSizeWhenHidden() )
auto& engine = m_data->engine;
if ( engine.retainSizeWhenHiddenAt( index ) != on )
{
layoutItem->setRetainSizeWhenHidden( on );
invalidate();
engine.setRetainSizeWhenHiddenAt( index, on );
resetImplicitSize();
polish();
}
}
bool QskLinearBox::retainSizeWhenHidden( int index ) const
{
if ( const auto layoutItem = engine().layoutItemAt( index ) )
return layoutItem->retainSizeWhenHidden();
return false;
return m_data->engine.retainSizeWhenHiddenAt( index );
}
void QskLinearBox::setRetainSizeWhenHidden( const QQuickItem* item, bool on )
{
setRetainSizeWhenHidden( engine().indexOf( item ), on );
setRetainSizeWhenHidden( indexOf( item ), on );
}
bool QskLinearBox::retainSizeWhenHidden( const QQuickItem* item ) const
{
return retainSizeWhenHidden( engine().indexOf( item ) );
}
void QskLinearBox::setRowSpacing( int row, qreal spacing )
{
if ( row >= 0 )
{
engine().setRowSpacing( row, spacing, Qt::Horizontal );
activate();
}
}
qreal QskLinearBox::rowSpacing( int row ) const
{
return engine().rowSpacing( row, Qt::Horizontal );
}
void QskLinearBox::setColumnSpacing( int column, qreal spacing )
{
if ( column >= 0 )
{
engine().setRowSpacing( column, spacing, Qt::Vertical );
activate();
}
}
qreal QskLinearBox::columnSpacing( int column ) const
{
return engine().rowSpacing( column, Qt::Vertical );
}
void QskLinearBox::setRowStretchFactor( int row, int stretchFactor )
{
if ( row >= 0 )
{
engine().setRowStretchFactor( row, stretchFactor, Qt::Vertical );
activate();
}
}
int QskLinearBox::rowStretchFactor( int row ) const
{
return engine().rowStretchFactor( row, Qt::Vertical );
}
void QskLinearBox::setColumnStretchFactor( int column, int stretchFactor )
{
if ( column >= 0 )
{
engine().setRowStretchFactor( column, stretchFactor, Qt::Horizontal );
activate();
}
}
int QskLinearBox::columnStretchFactor( int column ) const
{
return engine().rowStretchFactor( column, Qt::Horizontal );
}
void QskLinearBox::setupLayoutItem( QskLayoutItem* layoutItem, int index )
{
int col = index % m_data->dimension;
int row = index / m_data->dimension;
if ( m_data->orientation == Qt::Vertical )
qSwap( col, row );
layoutItem->setFirstRow( col, Qt::Horizontal );
layoutItem->setFirstRow( row, Qt::Vertical );
}
void QskLinearBox::layoutItemInserted( QskLayoutItem*, int index )
{
if ( index < itemCount() - 1 )
rearrange();
}
void QskLinearBox::layoutItemRemoved( QskLayoutItem*, int index )
{
Q_UNUSED( index )
rearrange();
}
void QskLinearBox::rearrange()
{
bool doInvalidate = false;
const int numItems = itemCount();
for ( int i = 0; i < numItems; i++ )
{
int row = i / m_data->dimension;
int col = i % m_data->dimension;
if ( m_data->orientation == Qt::Vertical )
qSwap( col, row );
auto layoutItem = engine().layoutItemAt( i );
if ( layoutItem->firstColumn() != col || layoutItem->firstRow() != row )
{
engine().removeItem( layoutItem );
layoutItem->setFirstRow( col, Qt::Horizontal );
layoutItem->setFirstRow( row, Qt::Vertical );
engine().insertLayoutItem( layoutItem, i );
doInvalidate = true;
}
}
if ( doInvalidate )
invalidate();
}
QRectF QskLinearBox::alignedLayoutRect( const QRectF& rect ) const
{
if ( m_data->extraSpacingAt == 0 )
return rect;
const QskLayoutEngine& engine = this->engine();
QRectF r = rect;
// not 100% sure if this works for dynamic constraints
// and having extraSpacingAt for both directions ...
if ( ( m_data->extraSpacingAt & Qt::LeftEdge ) ||
( m_data->extraSpacingAt & Qt::RightEdge ) )
{
bool isExpandable = false;
for ( int i = 0; i < engine.itemCount(); i++ )
{
const QskLayoutItem* item = engine.layoutItemAt( i );
if ( !item->isIgnored() &&
( item->sizePolicy( Qt::Horizontal ) & QskSizePolicy::GrowFlag ) )
{
isExpandable = true;
break;
}
}
if ( !isExpandable )
{
const qreal w = engine.widthForHeight( r.height() );
if ( m_data->extraSpacingAt & Qt::LeftEdge )
{
if ( m_data->extraSpacingAt & Qt::RightEdge )
{
r.moveLeft( r.center().x() - w / 2 );
r.setWidth( w );
}
else
{
r.setLeft( r.right() - w );
}
}
else
{
r.setRight( r.left() + w );
}
}
}
if ( ( m_data->extraSpacingAt & Qt::TopEdge ) ||
( m_data->extraSpacingAt & Qt::BottomEdge ) )
{
bool isExpandable = false;
for ( int i = 0; i < engine.itemCount(); i++ )
{
const QskLayoutItem* item = engine.layoutItemAt( i );
if ( !item->isIgnored() &&
( item->sizePolicy( Qt::Vertical ) & QskSizePolicy::GrowFlag ) )
{
isExpandable = true;
break;
}
}
if ( !isExpandable )
{
const qreal h = engine.heightForWidth( r.width() );
if ( m_data->extraSpacingAt & Qt::TopEdge )
{
if ( m_data->extraSpacingAt & Qt::BottomEdge )
{
r.moveTop( r.center().y() - h / 2 );
r.setHeight( h );
}
else
{
r.setTop( r.bottom() - h );
}
}
else
{
r.setBottom( r.top() + h );
}
}
}
return r;
return retainSizeWhenHidden( indexOf( item ) );
}
#include "moc_QskLinearBox.cpp"

View File

@ -19,12 +19,17 @@ class QSK_EXPORT QskLinearBox : public QskIndexedLayoutBox
WRITE setDimension NOTIFY dimensionChanged FINAL )
Q_PROPERTY( qreal spacing READ spacing
WRITE setSpacing RESET resetSpacing
NOTIFY spacingChanged FINAL )
WRITE setSpacing RESET resetSpacing NOTIFY spacingChanged FINAL )
Q_PROPERTY( Qt::Alignment defaultAlignment READ defaultAlignment
WRITE setDefaultAlignment NOTIFY defaultAlignmentChanged )
Q_PROPERTY( Qt::Edges extraSpacingAt READ extraSpacingAt
WRITE setExtraSpacingAt NOTIFY extraSpacingAtChanged )
Q_PROPERTY( int entryCount READ entryCount() )
Q_PROPERTY( bool empty READ isEmpty() )
using Inherited = QskIndexedLayoutBox;
public:
@ -32,9 +37,26 @@ class QSK_EXPORT QskLinearBox : public QskIndexedLayoutBox
explicit QskLinearBox( Qt::Orientation, QQuickItem* parent = nullptr );
QskLinearBox( Qt::Orientation, uint dimension, QQuickItem* parent = nullptr );
~QskLinearBox() override;
bool isEmpty() const;
int entryCount() const; // items and spacers
#ifdef QSK_LAYOUT_COMPAT
int itemCount() const { return entryCount(); } // items and spacers
#endif
QQuickItem* itemAtIndex( int index ) const;
int indexOf( const QQuickItem* ) const;
void removeItem( const QQuickItem* );
void removeAt( int index );
QSizeF contentsSizeHint() const override;
qreal heightForWidth( qreal width ) const override;
qreal widthForHeight( qreal height ) const override;
Qt::Orientation orientation() const;
void setOrientation( Qt::Orientation );
@ -44,10 +66,19 @@ class QSK_EXPORT QskLinearBox : public QskIndexedLayoutBox
void setExtraSpacingAt( Qt::Edges );
Qt::Edges extraSpacingAt() const;
void setDefaultAlignment( Qt::Alignment );
Qt::Alignment defaultAlignment() const;
void setSpacing( qreal spacing );
void resetSpacing();
qreal spacing() const;
Q_INVOKABLE void addItem(
QQuickItem*, Qt::Alignment alignment = Qt::Alignment() );
Q_INVOKABLE void insertItem(
int index, QQuickItem*, Qt::Alignment alignment = Qt::Alignment() );
Q_INVOKABLE void addSpacer( qreal spacing, int stretchFactor = 0 );
Q_INVOKABLE void insertSpacer( int index, qreal spacing, int stretchFactor = 0 );
@ -60,47 +91,51 @@ class QSK_EXPORT QskLinearBox : public QskIndexedLayoutBox
void setStretchFactor( const QQuickItem*, int stretchFactor );
int stretchFactor( const QQuickItem* ) const;
void setAlignment( int index, Qt::Alignment );
Qt::Alignment alignment( int index ) const;
void setAlignment( const QQuickItem*, Qt::Alignment );
Qt::Alignment alignment( const QQuickItem* ) const;
Q_INVOKABLE bool retainSizeWhenHidden( int index ) const;
Q_INVOKABLE void setRetainSizeWhenHidden( int index, bool on );
bool retainSizeWhenHidden( const QQuickItem* ) const;
void setRetainSizeWhenHidden( const QQuickItem*, bool on );
#if 1
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;
Q_INVOKABLE void setRowStretchFactor( int row, int stretchFactor );
Q_INVOKABLE int rowStretchFactor( int row ) const;
Q_INVOKABLE void setColumnStretchFactor( int column, int stretchFactor );
Q_INVOKABLE int columnStretchFactor( int column ) const;
#endif
public Q_SLOTS:
void transpose();
void activate();
void invalidate();
void clear( bool autoDelete = false );
Q_SIGNALS:
void orientationChanged();
void dimensionChanged();
void defaultAlignmentChanged();
void spacingChanged();
void extraSpacingAtChanged();
protected:
QRectF alignedLayoutRect( const QRectF& ) const override;
bool event( QEvent* ) override;
void geometryChangeEvent( QskGeometryChangeEvent* ) override;
void itemChange( ItemChange, const ItemChangeData& ) override;
void updateLayout() override;
void autoAddItem( QQuickItem* ) override final;
void autoRemoveItem( QQuickItem* ) override final;
private:
void setupLayoutItem( QskLayoutItem*, int index ) override;
void layoutItemInserted( QskLayoutItem*, int index ) override;
void layoutItemRemoved( QskLayoutItem*, int index ) override;
void rearrange();
void removeItemInternal( int index, bool autoDelete );
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
inline bool QskLinearBox::isEmpty() const
{
return entryCount() <= 0;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,91 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_LINEAR_LAYOUT_ENGINE_H
#define QSK_LINEAR_LAYOUT_ENGINE_H
#include <qnamespace.h>
#include <qrect.h>
#include <memory>
class QQuickItem;
class QskLinearLayoutEngine
{
public:
QskLinearLayoutEngine( Qt::Orientation, uint dimension );
~QskLinearLayoutEngine();
Qt::Orientation orientation() const;
void setOrientation( Qt::Orientation );
void setDimension( uint dimension );
uint dimension() const;
void setDefaultAlignment( Qt::Alignment );
Qt::Alignment defaultAlignment() const;
void setExtraSpacingAt( Qt::Edges );
Qt::Edges extraSpacingAt() const;
int count() const;
int rowCount() const;
int columnCount() const;
void setSpacing( qreal spacing, Qt::Orientations );
qreal spacing( Qt::Orientation ) const;
qreal defaultSpacing( Qt::Orientation ) const;
void insertItem( QQuickItem*, int index );
void addItem( QQuickItem* );
void insertSpacerAt( int index, qreal spacing );
void addSpacer( qreal spacing );
void removeAt( int index );
QQuickItem* itemAt( int index ) const;
int spacerAt( int index ) const;
void setRetainSizeWhenHiddenAt( int index, bool on );
bool retainSizeWhenHiddenAt( int index ) const;
void setAlignmentAt( int index, Qt::Alignment );
Qt::Alignment alignmentAt( int index ) const;
void setStretchFactorAt( int index, int stretchFactor );
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;
void setVisualDirection( Qt::LayoutDirection );
Qt::LayoutDirection visualDirection() const;
enum
{
EntryCache = 1 << 0,
CellCache = 1 << 1,
LayoutCache = 1 << 2
};
void invalidate( int what = EntryCache | CellCache | LayoutCache );
private:
void updateCellGeometries( const QSizeF& );
private:
Q_DISABLE_COPY(QskLinearLayoutEngine)
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
#endif

View File

@ -4,12 +4,12 @@
*****************************************************************************/
#include "QskStackBox.h"
#include "QskLayoutConstraint.h"
#include "QskLayoutEngine.h"
#include "QskLayoutItem.h"
#include "QskStackBoxAnimator.h"
#include "QskLayoutConstraint.h"
#include "QskEvent.h"
#include "QskQuick.h"
#include <qpointer.h>
#include <QPointer>
static qreal qskConstrainedValue( QskLayoutConstraint::Type type,
const QskControl* control, qreal widthOrHeight )
@ -35,16 +35,35 @@ static qreal qskConstrainedValue( QskLayoutConstraint::Type type,
return constrainedValue;
}
namespace
{
class ItemInfo
{
public:
inline ItemInfo()
: item( nullptr )
{
}
inline ItemInfo( Qt::Alignment alignment, QQuickItem* item )
: alignment( alignment )
, item( item )
{
}
Qt::Alignment alignment;
QQuickItem* item;
};
}
class QskStackBox::PrivateData
{
public:
PrivateData()
: currentIndex( -1 )
{
}
int currentIndex;
QVector< ItemInfo > itemInfos;
QPointer< QskStackBoxAnimator > animator;
int currentIndex = -1;
Qt::Alignment defaultAlignment = Qt::AlignLeft | Qt::AlignVCenter;
};
QskStackBox::QskStackBox( QQuickItem* parent )
@ -53,7 +72,7 @@ QskStackBox::QskStackBox( QQuickItem* parent )
}
QskStackBox::QskStackBox( bool autoAddChildren, QQuickItem* parent )
: Inherited( parent )
: QskIndexedLayoutBox( parent )
, m_data( new PrivateData() )
{
setAutoAddChildren( autoAddChildren );
@ -63,6 +82,22 @@ QskStackBox::~QskStackBox()
{
}
void QskStackBox::setDefaultAlignment( Qt::Alignment alignment )
{
if ( alignment != m_data->defaultAlignment )
{
m_data->defaultAlignment = alignment;
Q_EMIT defaultAlignmentChanged( alignment );
polish();
}
}
Qt::Alignment QskStackBox::defaultAlignment() const
{
return m_data->defaultAlignment;
}
void QskStackBox::setAnimator( QskStackBoxAnimator* animator )
{
if ( m_data->animator == animator )
@ -103,6 +138,33 @@ QskStackBoxAnimator* QskStackBox::effectiveAnimator()
return nullptr;
}
int QskStackBox::itemCount() const
{
return m_data->itemInfos.count();
}
QQuickItem* QskStackBox::itemAtIndex( int index ) const
{
if ( index >= 0 && index < m_data->itemInfos.count() )
return m_data->itemInfos[ index ].item;
return nullptr;
}
int QskStackBox::indexOf( const QQuickItem* item ) const
{
if ( item && ( item->parentItem() != this ) )
{
for ( int i = 0; i < m_data->itemInfos.count(); i++ )
{
if ( item == m_data->itemInfos[i].item )
return i;
}
}
return -1;
}
QQuickItem* QskStackBox::currentItem() const
{
return itemAtIndex( m_data->currentIndex );
@ -113,44 +175,6 @@ 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 );
}
else if ( index < m_data->currentIndex )
{
m_data->currentIndex--;
// currentIndexChanged ???
}
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
not work properly with having several items in the
same cell.
In this particular situation we need to fix, that we lost
the item from engine.q_grid[0].
Calling transpose has this side effect.
*/
engine.transpose();
engine.transpose(); // reverting the call before
}
}
void QskStackBox::setCurrentIndex( int index )
{
if ( index < 0 || index >= itemCount() )
@ -169,11 +193,6 @@ void QskStackBox::setCurrentIndex( int index )
if ( window() && isVisible() && isInitiallyPainted() && animator )
{
// 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 );
@ -201,26 +220,229 @@ void QskStackBox::setCurrentItem( const QQuickItem* item )
setCurrentIndex( indexOf( item ) );
}
QSizeF QskStackBox::layoutItemsSizeHint() const
void QskStackBox::addItem( QQuickItem* item, Qt::Alignment alignment )
{
qreal width = -1;
qreal height = -1;
insertItem( -1, item, alignment );
}
QSizeF constraint( -1, -1 );
Qt::Orientations constraintOrientation = 0;
void QskStackBox::insertItem(
int index, QQuickItem* item, Qt::Alignment alignment )
{
if ( item == nullptr )
return;
const auto& engine = this->engine();
for ( int i = 0; i < engine.itemCount(); i++ )
reparentItem( item );
if ( qskIsTransparentForPositioner( item ) )
{
const auto layoutItem = engine.layoutItemAt( i );
// giving a warning, or ignoring the insert ???
qskSetTransparentForPositioner( item, false );
}
if ( layoutItem->hasDynamicConstraint() )
const bool doAppend = ( index < 0 ) || ( index >= itemCount() );
if ( item->parentItem() == this )
{
const int oldIndex = indexOf( item );
if ( oldIndex >= 0 )
{
constraintOrientation |= layoutItem->dynamicConstraintOrientation();
// the item had been inserted before
if ( ( index == oldIndex ) || ( doAppend && ( oldIndex == itemCount() - 1 ) ) )
{
// already in place
auto& itemInfo = m_data->itemInfos[oldIndex];
if ( alignment != itemInfo.alignment )
{
itemInfo.alignment = alignment;
polish();
}
return;
}
m_data->itemInfos.removeAt( oldIndex );
}
}
if ( doAppend )
index = itemCount();
m_data->itemInfos.insert( index, { alignment, item } );
const int oldCurrentIndex = m_data->currentIndex;
if ( m_data->itemInfos.count() == 1 )
{
m_data->currentIndex = 0;
item->setVisible( true );
}
else
{
item->setVisible( false );
if ( index <= m_data->currentIndex )
m_data->currentIndex++;
}
if ( oldCurrentIndex != m_data->currentIndex )
Q_EMIT currentIndexChanged( m_data->currentIndex );
resetImplicitSize();
polish();
}
void QskStackBox::removeAt( int index )
{
removeItemInternal( index, false );
}
void QskStackBox::removeItemInternal( int index, bool unparent )
{
if ( index < 0 || index >= m_data->itemInfos.count() )
return;
if ( !unparent )
{
if ( auto item = m_data->itemInfos[ index ].item )
{
if ( item->parentItem() == this )
item->setParentItem( nullptr );
}
}
m_data->itemInfos.removeAt( index );
if ( index <= m_data->currentIndex )
Q_EMIT currentIndexChanged( --m_data->currentIndex );
resetImplicitSize();
polish();
}
void QskStackBox::removeItem( const QQuickItem* item )
{
removeAt( indexOf( item ) );
}
void QskStackBox::autoAddItem( QQuickItem* item )
{
removeAt( indexOf( item ) );
}
void QskStackBox::autoRemoveItem( QQuickItem* item )
{
removeItemInternal( indexOf( item ), false );
}
void QskStackBox::clear( bool autoDelete )
{
for ( const auto& itemInfo : qskAsConst( m_data->itemInfos ) )
{
auto item = itemInfo.item;
if( autoDelete && ( item->parent() == this ) )
{
delete item;
}
else
{
const QSizeF hint = layoutItem->sizeHint( Qt::PreferredSize, constraint );
item->setParentItem( nullptr );
}
}
m_data->itemInfos.clear();
if ( m_data->currentIndex >= 0 )
{
m_data->currentIndex = -1;
Q_EMIT currentIndexChanged( m_data->currentIndex );
}
}
void QskStackBox::setAlignment( const QQuickItem* item, Qt::Alignment alignment )
{
setAlignmentAt( indexOf( item ), alignment );
}
Qt::Alignment QskStackBox::alignment( const QQuickItem* item ) const
{
return alignmentAt( indexOf( item ) );
}
void QskStackBox::setAlignmentAt( int index, Qt::Alignment alignment )
{
if ( index < 0 || index >= m_data->itemInfos.count() )
return;
m_data->itemInfos[ index ].alignment = alignment;
if ( index == m_data->currentIndex )
polish();
}
Qt::Alignment QskStackBox::alignmentAt( int index ) const
{
if ( index >= 0 && index < m_data->itemInfos.count() )
return m_data->itemInfos[ index ].alignment;
return Qt::Alignment();
}
QRectF QskStackBox::geometryForItemAt( int index ) const
{
const auto r = layoutRect();
if ( index >= 0 && index < m_data->itemInfos.count() )
{
const auto& info = m_data->itemInfos[ index ];
const auto align = info.alignment ? info.alignment : m_data->defaultAlignment;
return QskLayoutConstraint::itemRect( info.item, r, align );
}
return QRectF( r.x(), r.y(), 0.0, 0.0 );
}
void QskStackBox::updateLayout()
{
const auto idx = m_data->currentIndex;
if ( idx >= 0 )
{
const auto rect = geometryForItemAt( idx );
qskSetItemGeometry( m_data->itemInfos[ idx ].item, rect );
}
}
QSizeF QskStackBox::contentsSizeHint() const
{
#if 1
if ( itemCount() == 0 )
return QSizeF( 0, 0 );
#endif
qreal width = -1;
qreal height = -1;
using namespace QskLayoutConstraint;
int constraintTypes = Unconstrained;
for ( const auto& itemInfo : qskAsConst( m_data->itemInfos ) )
{
const auto item = itemInfo.item;
const auto type = constraintType( item );
if ( type != Unconstrained )
{
constraintTypes |= type;
}
else
{
const QSizeF hint = effectiveConstraint( item, Qt::PreferredSize );
if ( hint.width() >= width )
width = hint.width();
@ -232,40 +454,39 @@ QSizeF QskStackBox::layoutItemsSizeHint() const
#if 1
// does this work ???
if ( constraintOrientation & Qt::Horizontal )
if ( constraintTypes & WidthForHeight )
{
constraint.setWidth( -1 );
constraint.setHeight( height );
const QSizeF constraint( -1, height );
for ( int i = 0; i < engine.itemCount(); i++ )
for ( const auto& itemInfo : qskAsConst( m_data->itemInfos ) )
{
const auto layoutItem = engine.layoutItemAt( i );
const auto item = itemInfo.item;
if ( layoutItem->hasDynamicConstraint() &&
layoutItem->dynamicConstraintOrientation() == Qt::Horizontal )
if ( constraintType( item ) == WidthForHeight )
{
const QSizeF hint = layoutItem->sizeHint( Qt::PreferredSize, constraint );
if ( hint.width() > width )
width = hint.width();
const QSizeF hint = QskLayoutConstraint::sizeHint(
item, Qt::PreferredSize, constraint );
width = qMax( width, hint.width() );
}
}
}
if ( constraintOrientation & Qt::Vertical )
if ( constraintTypes & HeightForWidth )
{
constraint.setWidth( width );
constraint.setHeight( -1 );
const QSizeF constraint( width, -1 );
for ( int i = 0; i < engine.itemCount(); i++ )
for ( const auto& itemInfo : qskAsConst( m_data->itemInfos ) )
{
const auto layoutItem = engine.layoutItemAt( i );
const auto item = itemInfo.item;
if ( layoutItem->hasDynamicConstraint() &&
layoutItem->dynamicConstraintOrientation() == Qt::Vertical )
if ( constraintType( item ) == HeightForWidth )
{
const QSizeF hint = layoutItem->sizeHint( Qt::PreferredSize, constraint );
if ( hint.height() > height )
height = hint.height();
const QSizeF hint = QskLayoutConstraint::sizeHint(
item, Qt::PreferredSize, constraint );
height = qMax( height, hint.height() );
}
}
}
@ -286,39 +507,25 @@ qreal QskStackBox::widthForHeight( qreal height ) const
QskLayoutConstraint::WidthForHeight, this, height, qskConstrainedValue );
}
void QskStackBox::layoutItemInserted( QskLayoutItem* layoutItem, int index )
bool QskStackBox::event( QEvent* event )
{
Q_UNUSED( index )
QQuickItem* item = layoutItem->item();
if ( item == nullptr )
return;
#if 1
/*
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 )
switch ( static_cast< int >( event->type() ) )
{
m_data->currentIndex = 0;
item->setVisible( true );
case QEvent::LayoutRequest:
{
resetImplicitSize();
polish();
break;
}
case QEvent::ContentsRectChange:
case QskEvent::GeometryChange:
{
polish();
break;
}
}
Q_EMIT currentIndexChanged( m_data->currentIndex );
}
else
{
item->setVisible( false );
}
return Inherited::event( event );
}
#include "moc_QskStackBox.cpp"

View File

@ -20,7 +20,7 @@ class QSK_EXPORT QskStackBox : public QskIndexedLayoutBox
Q_PROPERTY( QQuickItem* currentItem READ currentItem
WRITE setCurrentItem NOTIFY currentItemChanged )
using Inherited = QskIndexedLayoutBox;
using Inherited = QskBox;
public:
explicit QskStackBox( QQuickItem* parent = nullptr );
@ -28,36 +28,74 @@ class QSK_EXPORT QskStackBox : public QskIndexedLayoutBox
~QskStackBox() override;
bool isEmpty() const;
int itemCount() const;
QQuickItem* itemAtIndex( int index ) const;
int indexOf( const QQuickItem* ) const;
void addItem(
QQuickItem*, Qt::Alignment alignment = Qt::Alignment() );
void insertItem(
int index, QQuickItem*, Qt::Alignment alignment = Qt::Alignment() );
void removeItem( const QQuickItem* );
void removeAt( int index );
QQuickItem* currentItem() const;
int currentIndex() const;
qreal heightForWidth( qreal width ) const override;
qreal widthForHeight( qreal height ) const override;
void setDefaultAlignment( Qt::Alignment );
Qt::Alignment defaultAlignment() const;
void setAlignmentAt( int index, Qt::Alignment );
Qt::Alignment alignmentAt( int index ) const;
void setAlignment( const QQuickItem*, Qt::Alignment );
Qt::Alignment alignment( const QQuickItem* ) const;
void setAnimator( QskStackBoxAnimator* );
const QskStackBoxAnimator* animator() const;
QskStackBoxAnimator* animator();
QSizeF contentsSizeHint() const override;
qreal heightForWidth( qreal width ) const override;
qreal widthForHeight( qreal height ) const override;
QRectF geometryForItemAt( int index ) const;
Q_SIGNALS:
void defaultAlignmentChanged( Qt::Alignment );
public Q_SLOTS:
void setCurrentIndex( int index );
void setCurrentItem( const QQuickItem* );
void clear( bool autoDelete = false );
Q_SIGNALS:
void currentIndexChanged( int index );
void currentItemChanged( QQuickItem* );
protected:
bool event( QEvent* ) override;
void updateLayout() override;
void autoAddItem( QQuickItem* ) override final;
void autoRemoveItem( QQuickItem* ) override final;
QskStackBoxAnimator* effectiveAnimator();
QSizeF layoutItemsSizeHint() const override;
private:
friend class QskStackBoxAnimator;
void layoutItemInserted( QskLayoutItem*, int index ) override;
void layoutItemRemoved( QskLayoutItem*, int index ) override;
void removeItemInternal( int index, bool autoDelete );
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
inline bool QskStackBox::isEmpty() const
{
return itemCount() <= 0;
}
#endif

View File

@ -4,9 +4,9 @@
*****************************************************************************/
#include "QskStackBoxAnimator.h"
#include "QskLayoutEngine.h"
#include "QskLayoutItem.h"
#include "QskStackBox.h"
#include "QskEvent.h"
#include "QskQuick.h"
static Qsk::Direction qskDirection(
Qt::Orientation orientation, int from, int to, int itemCount )
@ -91,17 +91,19 @@ QskStackBox* QskStackBoxAnimator::stackBox() const
return static_cast< QskStackBox* >( parent() );
}
QskLayoutItem* QskStackBoxAnimator::layoutItemAt( int index ) const
QQuickItem* QskStackBoxAnimator::itemAt( int index ) const
{
return stackBox()->engine().layoutItemAt(
return stackBox()->itemAtIndex(
( index == 0 ) ? m_startIndex : m_endIndex );
}
QskStackBoxAnimator1::QskStackBoxAnimator1( QskStackBox* parent )
: QskStackBoxAnimator( parent )
, m_orientation( Qt::Horizontal )
, m_isDirty( false )
, m_hasClip( false )
{
// catching geometryChanges to know about resizing
}
QskStackBoxAnimator1::~QskStackBoxAnimator1()
@ -131,97 +133,72 @@ void QskStackBoxAnimator1::setup()
m_direction = qskDirection( m_orientation,
startIndex(), endIndex(), stackBox->itemCount() );
for ( int i = 0; i < 2; i++ )
{
QskLayoutItem* layoutItem = layoutItemAt( i );
if ( layoutItem )
{
QQuickItem* item = layoutItem->item();
const Qt::Orientation orientation = this->orientation();
m_itemOffset[ i ] =
( orientation == Qt::Horizontal ) ? item->x() : item->y();
if ( i == 1 )
{
// now move the new item outside of
// the visible area and then "show" it
if ( orientation == Qt::Horizontal )
item->setX( stackBox->width() );
else
item->setY( stackBox->height() );
item->setVisible( true );
}
// we don't want the engine() to interfere, when
// controlling the item by the animation
layoutItem->setUpdateMode( QskLayoutItem::UpdateNone );
}
}
m_hasClip = stackBox->clip();
if ( !m_hasClip )
stackBox->setClip( true );
stackBox->installEventFilter( this );
m_isDirty = true;
}
void QskStackBoxAnimator1::advance( qreal value )
{
auto stackBox = this->stackBox();
const bool isHorizontal = m_orientation == Qt::Horizontal;
for ( int i = 0; i < 2; i++ )
{
QskLayoutItem* layoutItem = layoutItemAt( i );
if ( layoutItem == nullptr )
continue;
if ( layoutItem->isGeometryDirty() )
if ( auto item = itemAt( i ) )
{
// the layout tried to replace the item, but we
// want to have control over the position. But we
// also lost resizing - that's why we have to do it here
// manually
QRectF rect = qskItemGeometry( item );
stackBox->adjustItemAt( ( i == 0 ) ? startIndex() : endIndex() );
if ( m_isDirty )
{
const int index = ( i == 0 ) ? startIndex() : endIndex();
rect = stackBox->geometryForItemAt( index );
QQuickItem* item = layoutItem->item();
m_itemOffset[ i ] =
( m_orientation == Qt::Horizontal ) ? item->x() : item->y();
}
m_itemOffset[ i ] = isHorizontal ? rect.x() : rect.y();
}
QQuickItem* item = layoutItem->item();
qreal x, y;
if ( m_orientation == Qt::Horizontal )
{
const qreal off = stackBox->width() * ( value - i );
if ( isHorizontal )
{
qreal off = stackBox->width() * ( value - i );
if ( m_direction == Qsk::LeftToRight )
off = -off;
if ( m_direction == Qsk::LeftToRight )
item->setX( m_itemOffset[ i ] - off );
x = m_itemOffset[ i ] + off;
y = rect.y();
}
else
item->setX( m_itemOffset[ i ] + off );
}
else
{
const qreal off = stackBox->height() * ( value - i );
{
qreal off = stackBox->height() * ( value - i );
if ( m_direction == Qsk::BottomToTop )
off = -off;
if ( m_direction == Qsk::TopToBottom )
item->setY( m_itemOffset[ i ] + off );
else
item->setY( m_itemOffset[ i ] - off );
x = rect.x();
y = m_itemOffset[ i ] + off;
}
qskSetItemGeometry( item, x, y, rect.width(), rect.height() );
if ( !item->isVisible() )
item->setVisible( true );
}
}
m_isDirty = false;
}
void QskStackBoxAnimator1::done()
{
for ( int i = 0; i < 2; i++ )
{
if ( QskLayoutItem* layoutItem = layoutItemAt( i ) )
if ( auto item = itemAt( i ) )
{
layoutItem->setUpdateMode( QskLayoutItem::UpdateWhenVisible );
layoutItem->item()->setVisible( i == 1 );
item->removeEventFilter( this );
item->setVisible( i == 1 );
}
}
@ -229,6 +206,25 @@ void QskStackBoxAnimator1::done()
stackBox()->setClip( false );
}
bool QskStackBoxAnimator1::eventFilter( QObject* object, QEvent* event )
{
if ( !m_isDirty && object == stackBox() )
{
switch( static_cast< int >( event->type() ) )
{
case QskEvent::GeometryChange:
case QskEvent::ContentsRectChange:
case QskEvent::LayoutRequest:
{
m_isDirty = true;
break;
}
}
}
return QObject::eventFilter( object, event );
}
QskStackBoxAnimator3::QskStackBoxAnimator3( QskStackBox* parent )
: QskStackBoxAnimator( parent )
{
@ -240,33 +236,30 @@ QskStackBoxAnimator3::~QskStackBoxAnimator3()
void QskStackBoxAnimator3::setup()
{
QskLayoutItem* layoutItem = layoutItemAt( 1 );
if ( layoutItem )
if ( auto item = itemAt( 1 ) )
{
layoutItem->item()->setOpacity( 0.0 );
layoutItem->item()->setVisible( true );
item->setOpacity( 0.0 );
item->setVisible( true );
}
}
void QskStackBoxAnimator3::advance( qreal value )
{
QskLayoutItem* layoutItem1 = layoutItemAt( 0 );
if ( layoutItem1 )
layoutItem1->item()->setOpacity( 1.0 - value );
if ( auto item1 = itemAt( 0 ) )
item1->setOpacity( 1.0 - value );
QskLayoutItem* layoutItem2 = layoutItemAt( 1 );
if ( layoutItem2 )
layoutItem2->item()->setOpacity( value );
if ( auto item2 = itemAt( 1 ) )
item2->setOpacity( value );
}
void QskStackBoxAnimator3::done()
{
for ( int i = 0; i < 2; i++ )
{
if ( QskLayoutItem* layoutItem = layoutItemAt( i ) )
if ( auto item = itemAt( i ) )
{
layoutItem->item()->setOpacity( 1.0 );
layoutItem->item()->setVisible( i == 1 ); // not here !!
item->setOpacity( 1.0 );
item->setVisible( i == 1 ); // not here !!
}
}
}

View File

@ -12,14 +12,14 @@
#include <qobject.h>
class QskStackBox;
class QskLayoutItem;
class QQuickItem;
class QSK_EXPORT QskStackBoxAnimator : public QObject, public QskAnimator
{
Q_OBJECT
public:
QskStackBoxAnimator( QskStackBox* parent );
QskStackBoxAnimator( QskStackBox* );
~QskStackBoxAnimator() override;
void setStartIndex( int index );
@ -30,7 +30,7 @@ class QSK_EXPORT QskStackBoxAnimator : public QObject, public QskAnimator
protected:
QskStackBox* stackBox() const;
QskLayoutItem* layoutItemAt( int index ) const;
QQuickItem* itemAt( int index ) const;
private:
int m_startIndex;
@ -42,13 +42,15 @@ class QSK_EXPORT QskStackBoxAnimator1 : public QskStackBoxAnimator
Q_OBJECT
public:
QskStackBoxAnimator1( QskStackBox* parent );
QskStackBoxAnimator1( QskStackBox* );
~QskStackBoxAnimator1() override;
void setOrientation( Qt::Orientation );
Qt::Orientation orientation() const;
protected:
bool eventFilter( QObject*, QEvent* ) override;
void setup() override;
void advance( qreal value ) override;
void done() override;
@ -58,6 +60,7 @@ class QSK_EXPORT QskStackBoxAnimator1 : public QskStackBoxAnimator
Qt::Orientation m_orientation : 2;
Qsk::Direction m_direction : 4;
bool m_isDirty : 1;
bool m_hasClip : 1;
};
@ -66,7 +69,7 @@ class QSK_EXPORT QskStackBoxAnimator3 : public QskStackBoxAnimator
Q_OBJECT
public:
QskStackBoxAnimator3( QskStackBox* parent );
QskStackBoxAnimator3( QskStackBox* );
~QskStackBoxAnimator3() override;
protected:

View File

@ -233,25 +233,23 @@ SOURCES += \
HEADERS += \
layouts/QskGridBox.h \
layouts/QskGridLayoutEngine.h \
layouts/QskIndexedLayoutBox.h \
layouts/QskLayoutEngine.h \
layouts/QskLayoutBox.h \
layouts/QskLayoutConstraint.h \
layouts/QskLayoutHint.h \
layouts/QskLayoutItem.h \
layouts/QskLinearBox.h \
layouts/QskLinearLayoutEngine.h \
layouts/QskStackBoxAnimator.h \
layouts/QskStackBox.h
SOURCES += \
layouts/QskGridBox.cpp \
layouts/QskGridLayoutEngine.cpp \
layouts/QskIndexedLayoutBox.cpp \
layouts/QskLayoutBox.cpp \
layouts/QskLayoutConstraint.cpp \
layouts/QskLayoutHint.cpp \
layouts/QskLayoutEngine.cpp \
layouts/QskLayoutItem.cpp \
layouts/QskLinearBox.cpp \
layouts/QskLinearLayoutEngine.cpp \
layouts/QskStackBoxAnimator.cpp \
layouts/QskStackBox.cpp