А какой еще можно использовать виджет для этих же целей?
Судя по-всему у троллей баг в QListView - они при расчёте лейаута элементов
всегда учитывают размер скроллбара, если у него полиси ScrollBarAsNeeded
Чтобы решить эту проблему, нужно установить другой скроллбар полиси либо использовать что-нибудь другое, например FlowLayout:
C++ (Qt)
#include <QApplication>
#include <QScrollArea>
#include <QStyle>
#include <QPushButton>
#include <QLayout>
//
class FlowLayout : public QLayout
{
//Q_OBJECT // uncomment in the header
public:
FlowLayout( QWidget* parent = 0, int margin = -1, int hSpacing = -1, int vSpacing = -1 );
virtual ~FlowLayout();
void addItem( QLayoutItem *item );
int horizontalSpacing() const;
int verticalSpacing() const;
Qt::Orientations expandingDirections() const;
bool hasHeightForWidth() const;
int heightForWidth( int ) const;
int count() const;
QLayoutItem *itemAt( int index ) const;
QSize minimumSize() const;
void setGeometry( const QRect& rect );
QSize sizeHint() const;
QLayoutItem *takeAt( int index );
void insertWidget( int index, QWidget* w );
protected:
int doLayout( const QRect& rect, bool testOnly ) const;
int smartSpacing( QStyle::PixelMetric pm ) const;
private:
QList< QLayoutItem* > itemList_;
int h_space_;
int v_space_;
QSize max_size_;
};
//
FlowLayout::FlowLayout( QWidget* parent, int margin, int hSpacing, int vSpacing )
: QLayout( parent )
, h_space_( hSpacing )
, v_space_( vSpacing )
{
parent->setLayout( this );
setContentsMargins( margin, margin, margin, margin );
}
FlowLayout::~FlowLayout()
{
QLayoutItem *item;
while( ( item = takeAt( 0 ) ) )
delete item;
}
void FlowLayout::addItem( QLayoutItem *item )
{
QSize sz( item->widget()->sizeHint() );
if( sz.width() > max_size_.width() ||
sz.height() > max_size_.height() )
{
max_size_ = item->widget()->sizeHint();
QLayoutItem *item;
foreach( item, itemList_ )
{
item->widget()->resize( max_size_ );
}
}
item->widget()->resize( max_size_ );
itemList_.append( item );
}
void FlowLayout::insertWidget( int index, QWidget* w )
{
bool is_shown = w->isVisible();
if( is_shown )
w->hide();
addWidget( w );
if( index >= 0 && index < itemList_.count() - 1 )
itemList_.move( itemList_.count() - 1, index );
if( is_shown )
w->show();
}
int FlowLayout::horizontalSpacing() const
{
if( h_space_ >= 0 )
return h_space_;
else
return smartSpacing( QStyle::PM_LayoutHorizontalSpacing );
}
int FlowLayout::verticalSpacing() const
{
if( v_space_ >= 0 )
return v_space_;
else
return smartSpacing( QStyle::PM_LayoutVerticalSpacing );
}
int FlowLayout::count() const
{
return itemList_.size();
}
QLayoutItem *FlowLayout::itemAt( int index ) const
{
return itemList_.value( index );
}
QLayoutItem *FlowLayout::takeAt( int index )
{
QLayoutItem* taked_item = 0;
if( index >= 0 && index < itemList_.size() )
{
taked_item = itemList_.takeAt( index );
max_size_ = QSize( 0, 0 );
QLayoutItem *item;
foreach( item, itemList_ )
{
QSize sz( item->widget()->sizeHint() );
if( sz.width() > max_size_.width() ||
sz.height() > max_size_.height() )
max_size_ = sz;
}
foreach( item, itemList_ )
{
item->widget()->resize( max_size_ );
}
}
return taked_item;
}
Qt::Orientations FlowLayout::expandingDirections() const
{
return 0;
}
bool FlowLayout::hasHeightForWidth() const
{
return true;
}
int FlowLayout::heightForWidth( int width ) const
{
return doLayout( QRect( 0, 0, width, 0 ), true );
}
void FlowLayout::setGeometry( const QRect& rect )
{
QLayout::setGeometry( rect );
doLayout( rect, false );
}
QSize FlowLayout::sizeHint() const
{
return minimumSize();
}
QSize FlowLayout::minimumSize() const
{
QSize size( QLayout::minimumSize() );
if( count() )
size = max_size_;
size += QSize( 2 * margin(), 2 * margin() );
return size;
}
int FlowLayout::doLayout( const QRect& rect, bool testOnly ) const
{
int left, top, right, bottom;
getContentsMargins( &left, &top, &right, &bottom );
QRect effectiveRect = rect.adjusted( +left, +top, -right, -bottom );
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
QLayoutItem *item;
foreach( item, itemList_ )
{
QWidget *wid = item->widget();
if( wid->isHidden() )
continue;
int spaceX = horizontalSpacing();
if( spaceX == -1 )
spaceX = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
int spaceY = verticalSpacing();
if( spaceY == -1 )
spaceY = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
int nextX = x + max_size_.width() + spaceX;
if( nextX - spaceX > effectiveRect.right() && lineHeight > 0 )
{
x = effectiveRect.x();
y = y + lineHeight + spaceY;
nextX = x + max_size_.width() + spaceX;
lineHeight = 0;
}
if( !testOnly )
item->setGeometry( QRect( QPoint( x, y ), max_size_ ) );
x = nextX;
lineHeight = qMax( lineHeight, max_size_.height() );
}
return y + lineHeight - rect.y() + bottom;
}
int FlowLayout::smartSpacing( QStyle::PixelMetric pm ) const
{
QObject *parent = this->parent();
if( !parent )
{
return -1;
}
else if( parent->isWidgetType() )
{
QWidget *pw = qobject_cast< QWidget* >( parent );
return pw->style()->pixelMetric( pm, 0, pw );
}
else
{
return qobject_cast< QLayout* >( parent )->spacing();
}
}
class FlowView : public QScrollArea
{
//Q_OBJECT // uncomment in the header
public:
FlowView( QWidget* parent = 0 );
void addWidget( QWidget* w )
{
widget()->layout()->addWidget( w );
}
void insertWidget( int index, QWidget* w )
{
#if 0 // uncomment in the header
FlowLayout* l = qobject_cast< FlowLayout* >( widget()->layout() );
#else
FlowLayout* l = dynamic_cast< FlowLayout* >( widget()->layout() );
#endif
l->insertWidget( index, w );
}
};
//
FlowView::FlowView( QWidget* parent )
: QScrollArea( parent )
{
setWidgetResizable( true );
QWidget* w = new QWidget( this );
setWidget( w );
new FlowLayout( w, 4, 4, 2 );
setMinimumSize( 20, 20 );
}
int main( int argc, char** argv )
{
QApplication app( argc, argv );
FlowView fw;
for( int i = 1; i <= 50; i++ )
fw.addWidget(
new QPushButton( QString( "%1" ).arg( i, 2, 10, QChar( '0' ) ) ) );
fw.show();
return app.exec();
}
За основу взят
Qt-шный FlowLayout и добавлено приведение всех элементов к одному размеру.