/**************************************************************************** ** ** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** ** This file is part of the Itemviews NG project on Trolltech Labs. ** ** This file may be used under the terms of the GNU General Public ** License version 2.0 or 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of ** this file. Please review the following information to ensure GNU ** General Public Licensing requirements will be met: ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and ** http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ****************************************************************************/ #include "qgraphicslistview.h" #include "qgraphicslistview_p.h" #include "qlistcontroller.h" #include "qlistmodelinterface.h" #include "qlistselectionmanager.h" #include #include #include #include #include #include #include #include #include /*! \class QtGraphicsListViewItem \brief The item that visually represents one visible entry in the list. The QtGraphicsListView delegates all the drawing and composition of individual list-items to this class. There will be one QtGraphicsListViewItem instance for each visible list item. By default the list items will use the current style to determine the looks. For altering the appearance of individual list items creating a subclass of this one is the preferred solution. The subclass can use various strategies to alter the individual list items appearance, for example;
  • Reimplement paint() to change the look for just this list.
  • Add any number of child QGraphicsItem objects that do the painting instead.
The QtGraphicsListView will create and delete QtGraphicsListViewItem instances as they are needed based on how many items are visible. It is possible for an item to be reused. When this happens the itemChanged() method will be called to make the item aware of this change. The default implmentation will make sure that paint() will be called. The default QtGraphicsListView::itemContentsSizeHint() will first ask the model for a Qt::SizeHintRole and then request the effectiveSizeHint() from the item to find an optimal size. To change the individual items sizes it is recommended to reimplement QtGraphicsListView::itemContentsSizeHint() which gives the greatest speed and flexibility. Changing the items minimumSize() and maxiumSize() or reimplementing the sizeHint() methods on the QtGraphicsListViewItem will likely be the easier solution. */ QtGraphicsListViewItem::QtGraphicsListViewItem(int index, QtGraphicsListView *view) : QGraphicsWidget(view, 0), d_ptr(new QtGraphicsListViewItemPrivate) { Q_D(QtGraphicsListViewItem); Q_ASSERT(view); //setCacheMode(QGraphicsItem::ItemCoordinateCache); // ### items are sometimes not repainted when we call update() d->q_ptr = this; d->view = view; d->index = index; } /*! */ QtGraphicsListViewItem::~QtGraphicsListViewItem() { delete d_ptr; } int QtGraphicsListViewItem::index() const { Q_D(const QtGraphicsListViewItem); return d->index; } /*! */ void QtGraphicsListViewItem::setIndex(int index) { Q_D(QtGraphicsListViewItem); if (d->index != index) { d->index = index; itemChanged(QList()); } } /*! */ QSizeF QtGraphicsListViewItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const { Q_D(const QtGraphicsListViewItem); d->view->initStyleOption(&d->option); d->view->initStyleOption(&d->option, d->index); return sizeHint(d->index, &d->option, which, constraint); } /*! */ QSizeF QtGraphicsListViewItem::sizeHint(int index, const QStyleOptionViewItemV4 *option, Qt::SizeHint which, const QSizeF &constraint) const { Q_D(const QtGraphicsListViewItem); switch (which) { case Qt::MinimumSize: case Qt::PreferredSize: { const QVariant value = d->view->d_func()->cachedData(index, Qt::SizeHintRole); if (value.isValid()) return qvariant_cast(value); else return d->view->style()->sizeFromContents(QStyle::CT_ItemViewItem, option, QSize(), option->widget); } case Qt::MaximumSize: return QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); default: break; } return QGraphicsWidget::sizeHint(which, constraint); } /*! */ void QtGraphicsListViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_D(QtGraphicsListViewItem); // ### this code is less than optimal, but we will hopefully get rid of the style calls // ### (by moving to a purely scene-graph based approach for items) d->view->copyStyleOptionState(option, &d->option); d->view->initStyleOption(&d->option); d->view->initStyleOption(&d->option, d->index); d->option.rect = rect().toRect(); d->view->style()->drawControl(QStyle::CE_ItemViewItem, &d->option, painter, widget); } /*! This callback method will be called whenever any part of the item has been changed. The default implementation always calls update(), reimplemnting this method allows items to only update based on the data that has actually changed. The \a roles will indicate what has actually changed, if the list is empty then all roles have changed. \sa QtListModelInterface::itemsChanged() */ void QtGraphicsListViewItem::itemChanged(const QList &roles) { Q_UNUSED(roles); // ### if the size changed, we need to let the layout know prepareGeometryChange(); update(); } QHash QtGraphicsListViewItem::data(const QList &roles) const { Q_D(const QtGraphicsListViewItem); if (d->view && d->view->model()) return d->view->model()->data(d->index, roles); return QHash(); } QtGraphicsListView *QtGraphicsListViewItem::view() const { Q_D(const QtGraphicsListViewItem); return d->view; } /*! \class QtGraphicsListViewItemCreatorBase */ /*! \class QtGraphicsListViewItemCreator */ // QtGraphicsListViewPrivate /*! \internal */ QtGraphicsListViewPrivate::QtGraphicsListViewPrivate() : q_ptr(0), controller(0), model(0), selectionManager(0), orientation(Qt::Horizontal), textElideMode(Qt::ElideMiddle), firstIndex(0), horizontalOffset(0), verticalOffset(0), cachedIndexOffset(0), cachedCoordinateOffset(0), cachedDataIndex(-1), layoutsBlocked(false), creator(new QtGraphicsListViewItemCreator()) { } /*! \internal */ QtGraphicsListViewPrivate::~QtGraphicsListViewPrivate() { delete creator; } /*! \internal */ void QtGraphicsListViewPrivate::_q_controllerDestroyed() { controller = 0; } /*! \internal */ void QtGraphicsListViewPrivate::_q_modelDestroyed() { model = 0; checkCache(0, 0); } /*! \internal */ void QtGraphicsListViewPrivate::_q_selectionsDestroyed() { selectionManager = 0; } /*! \internal */ void QtGraphicsListViewPrivate::_q_itemsChanged(int index, int count, const QList &roles) { Q_Q(QtGraphicsListView); checkCache(index, count); //qDebug() << "QtGraphicsListViewPrivate::_q_itemsChanged" << index << count; for (int i = 0; i < viewItems.count(); ++i) { QtGraphicsListViewItem *item = viewItems.at(i); int itemIndex = item->index(); if (itemIndex >= index + count) // change happened above break; //if (itemIndex < index + viewItems.count()) // break; // change happened below if (itemIndex >= index) item->itemChanged(roles); } q->updateLayout(); // ### FIXME: be more discriminate } /*! \internal */ void QtGraphicsListViewPrivate::_q_itemsInserted(int index, int count) { Q_Q(QtGraphicsListView); checkCache(index, count); //qDebug() << "QtGraphicsListViewPrivate::_q_itemsInserted" << index << count; q->updateLayout(); // ### FIXME: be more discriminate } /*! \internal */ void QtGraphicsListViewPrivate::_q_itemsRemoved(int index, int count) { Q_Q(QtGraphicsListView); checkCache(index, count); //qDebug() << "QtGraphicsListViewPrivate::_q_itemsRemoved" << index << count; q->updateLayout(); // ### FIXME: be more discriminate } /*! \internal */ void QtGraphicsListViewPrivate::_q_reset() { Q_Q(QtGraphicsListView); checkCache(0, 0); //qDebug() << "QtGraphicsListViewPrivate::_q_reset"; if (!layoutsBlocked) q->updateLayout(); } /*! \internal */ void QtGraphicsListViewPrivate::_q_selectionsChanged(const QtListSelectionChange &change) { if (viewItems.isEmpty()) return; const int from = viewItems.first()->index(); const int to = viewItems.last()->index(); if (from <= change.index() && (change.index() + change.count() - 1) <= to) { const QList indexes = change.indexes(); for (int i = 0; i < indexes.count(); ++i) { const int index = indexes.at(i); if (from <= index && index <= to) viewItems.at(index - from)->update(); } } } /*! \internal */ void QtGraphicsListViewPrivate::_q_currentChanged(int current, int previous) { //qDebug() << "QtGraphicsListViewPrivate::_q_currentChanged" << current << previous; const int from = viewItems.first()->index(); const int to = viewItems.last()->index(); if (from <= current && current <= to) viewItems.at(current - from)->update(); if (from <= previous && previous <= to) viewItems.at(current - from)->update(); } /*! \internal */ void QtGraphicsListViewPrivate::checkCache(int index, int count) { Q_UNUSED(count); if (cachedDataIndex >= index) { cachedDataHash.clear(); cachedDataIndex = -1; } if (cachedIndexOffset >= index) { cachedIndexOffset = 0; cachedCoordinateOffset = 0; } } /*! \internal */ QVariant QtGraphicsListViewPrivate::cachedData(int index, int role) const { // ### structure this list so the most used items are added first static const QList roles = (QList() << Qt::FontRole << Qt::TextAlignmentRole << Qt::ForegroundRole << Qt::CheckStateRole << Qt::DecorationRole << Qt::DisplayRole << Qt::SizeHintRole); if (index != cachedDataIndex && model) { cachedDataHash = model->data(index, roles); cachedDataIndex = index; } return cachedDataHash.value(role); } /*! \internal */ bool QtGraphicsListViewPrivate::isSelected(int index) const { return selectionManager ? selectionManager->isSelected(index) : false; } /*! \internal */ int QtGraphicsListViewPrivate::currentItem() const { return selectionManager ? selectionManager->currentItem() : -1; } /*! \internal */ void QtGraphicsListViewPrivate::recycleViewItems(int firstVisibleIndex) { if (!viewItems.isEmpty()) { // scrolling down or right - move from first to last while (viewItems.first()->index() < firstVisibleIndex) { const int lastIndex = viewItems.last()->index(); QtGraphicsListViewItem *item = viewItems.takeFirst(); item->setIndex(lastIndex + 1); viewItems.append(item); } // scrolling up or left - move from last to first const int firstNonVisibleIndex = firstVisibleIndex + viewItems.count(); while (viewItems.last()->index() >= firstNonVisibleIndex) { const int firstIndex = viewItems.first()->index(); QtGraphicsListViewItem *item = viewItems.takeLast(); item->setIndex(firstIndex - 1); viewItems.prepend(item); } } } /*! \internal */ void QtGraphicsListViewPrivate::removeViewItemsFrom(int i) { while (i < viewItems.count()) delete viewItems.takeLast(); } /*! \internal */ QtGraphicsListViewItem *QtGraphicsListViewPrivate::viewItemAt(int index, int firstVisibleIndex) { Q_Q(QtGraphicsListView); int i = index - firstVisibleIndex; QtGraphicsListViewItem *item = 0; if (i >= viewItems.count()) { item = creator->create(index, q); viewItems.append(item); } else { item = viewItems.at(i); Q_ASSERT(item->index() == index); } return item; } /*! \internal */ QSizeF QtGraphicsListViewPrivate::itemSize(const QStyleOptionViewItemV4 *option, int index, const QSizeF &constraint) const { return viewItems.isEmpty() ? QSizeF() : viewItems.first()->sizeHint(index, option, Qt::PreferredSize, constraint); } // QtGraphicsListView /*! \class QtGraphicsListView \brief This class positions the content of the list using graphicsItems to determine the way that the list looks. The QtGraphicsListView is meant to be used together with a QtListController and a QtListModelInterface. \sa QtListController */ /*! */ QtGraphicsListView::QtGraphicsListView(Qt::Orientation orientation, QGraphicsWidget *parent, Qt::WindowFlags wFlags) : QGraphicsWidget(parent, wFlags), d_ptr(new QtGraphicsListViewPrivate) { Q_D(QtGraphicsListView); d->q_ptr = this; d->orientation = orientation; } /*! */ QtGraphicsListView::QtGraphicsListView(QtGraphicsListViewPrivate &dd, Qt::Orientation orientation, QGraphicsWidget *parent, Qt::WindowFlags wFlags) : QGraphicsWidget(parent, wFlags), d_ptr(&dd) { Q_D(QtGraphicsListView); d->q_ptr = this; d->orientation = orientation; } /*! */ QtListController *QtGraphicsListView::controller() const { Q_D(const QtGraphicsListView); return d->controller; } /*! */ void QtGraphicsListView::setController(QtListController *controller) { Q_D(QtGraphicsListView); if (d->controller == controller) return; if (d->controller) { disconnect(d->controller, SIGNAL(destroyed()), this, SLOT(_q_controllerDestroyed())); } d->controller = controller; if (d->controller) { connect(d->controller, SIGNAL(destroyed()), this, SLOT(_q_controllerDestroyed())); } } /*! */ QtListModelInterface *QtGraphicsListView::model() const { Q_D(const QtGraphicsListView); return d->model; } /*! */ void QtGraphicsListView::setModel(QtListModelInterface *model) { Q_D(QtGraphicsListView); if (d->model == model) return; if (d->model) { disconnect(d->model, SIGNAL(destroyed()), this, SLOT(_q_modelDestroyed())); disconnect(d->model, SIGNAL(itemsChanged(int,int,const QList&)), this, SLOT(_q_itemsChanged(int,int,const QList&))); disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(_q_itemsInserted(int,int))); disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(_q_itemsRemoved(int,int))); } d->model = model; if (d->model) { connect(d->model, SIGNAL(destroyed()), this, SLOT(_q_modelDestroyed())); connect(d->model, SIGNAL(itemsChanged(int,int,const QList&)), this, SLOT(_q_itemsChanged(int,int,const QList&))); connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(_q_itemsInserted(int,int))); connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(_q_itemsRemoved(int,int))); } } /*! */ QtListSelectionManager *QtGraphicsListView::selectionManager() const { Q_D(const QtGraphicsListView); return d->selectionManager; } /*! */ void QtGraphicsListView::setSelectionManager(QtListSelectionManager *selectionManager) { Q_D(QtGraphicsListView); if (d->selectionManager == selectionManager) return; if (d->selectionManager) { disconnect(d->selectionManager, SIGNAL(destroyed()), this, SLOT(_q_selectionsDestroyed())); disconnect(d->selectionManager, SIGNAL(selectionsChanged(const QtListSelectionChange&)), this, SLOT(_q_selectionsChanged(const QtListSelectionChange&))); } d->selectionManager = selectionManager; if (d->selectionManager) { connect(d->selectionManager, SIGNAL(destroyed()), this, SLOT(_q_selectionsDestroyed())); connect(d->selectionManager, SIGNAL(selectionsChanged(const QtListSelectionChange&)), this, SLOT(_q_selectionsChanged(const QtListSelectionChange&))); } } /*! Returns the layout orientation. \sa setOrientation() */ Qt::Orientation QtGraphicsListView::orientation() const { Q_D(const QtGraphicsListView); return d->orientation; } /*! Sets the orientation of the list layout to be the given \a orientation. The orientation defines the scroll direction of the list. \sa orientation() */ void QtGraphicsListView::setOrientation(Qt::Orientation orientation) { Q_D(QtGraphicsListView); d->orientation = orientation; //d->checkCache(0, 0); // TODO updateLayout(); } /*! Returns the text elide mode. \sa setTextElideMode() */ Qt::TextElideMode QtGraphicsListView::textElideMode() const { Q_D(const QtGraphicsListView); return d->textElideMode; } /*! Sets the text elide mode to the specified \a mode. The default value is Qt::ElideMiddle. \sa textElideMode() */ void QtGraphicsListView::setTextElideMode(Qt::TextElideMode mode) { Q_D(QtGraphicsListView); d->textElideMode = mode; //d->checkCache(0, 0); } /*! */ QtGraphicsListView::~QtGraphicsListView() { delete d_ptr; } /*! Returns the index of the item at the given \a position. \sa updateLayout() */ int QtGraphicsListView::itemAt(const QPointF &position) const { Q_D(const QtGraphicsListView); if (!d->model) return -1; const QSizeF constraint = size(); QStyleOptionViewItemV4 option; initStyleOption(&option); int index = d->firstIndex; int count = d->model->count(); if (d->orientation == Qt::Vertical) { qreal y = -d->verticalOffset; while (y <= position.y() && index < count) { initStyleOption(&option, index); QSizeF size = d->itemSize(&option, index, constraint); y += size.height(); if (y >= position.y()) return index; ++index; } } else { qreal x = -d->horizontalOffset; while (x <= position.x() && index < count) { initStyleOption(&option, index); QSizeF size = d->itemSize(&option, index, constraint); x += size.width(); if (x >= position.x()) return index; ++index; } } return -1; } /*! Schedules an update of the item geometries. */ void QtGraphicsListView::updateLayout() { Q_D(QtGraphicsListView); if (!d->layoutTimer.isActive() && !d->layoutsBlocked) doLayout();//d->layoutTimer.start(0, this); } /*! Updates the item geometries. \sa itemAt() */ void QtGraphicsListView::doLayout() { Q_D(QtGraphicsListView); d->layoutTimer.stop(); QStyleOptionViewItemV4 option; initStyleOption(&option); const QRectF area = boundingRect(); const QSizeF constraint = area.size(); int index = d->firstIndex; int count = d->model ? d->model->count() : 0; qreal x = -d->horizontalOffset; qreal y = -d->verticalOffset; // find the visible items; caching helps us skip this most of the time if (d->orientation == Qt::Vertical) { if (y <= 0) { // ### // optimization: use the cached index and offset as starting points // The cached values are offsets from the start of the _contents_ // and the to index found at that offset. #if 0 // enable or disable the caching index += d->cachedIndexOffset; y += d->cachedCoordinateOffset; #endif // the visible area starts at y == 0 if (y < 0) { // the cached offset was above the visible area while (index < count) { initStyleOption(&option, index); const qreal height = d->itemSize(&option, index, constraint).height(); if (y + height > area.y()) break; y += height; ++index; } } else if (y > 0) { // the cached offset was below while (index >= 0 && y > 0) { initStyleOption(&option, index); const qreal height = d->itemSize(&option, index, constraint).height(); y -= height; --index; } } #if 0 // enable or disable the caching d->cachedIndexOffset = index - d->firstIndex; d->cachedCoordinateOffset = y + d->verticalOffset; #endif } // we are now at the visible items const int firstVisibleIndex = index; d->recycleViewItems(firstVisibleIndex); // ### make this one loop while (index >= 0 && index < count) { QtGraphicsListViewItem *item = d->viewItemAt(index, firstVisibleIndex); Q_ASSERT(item && item->index() == index); initStyleOption(&option, index); const QSizeF size = item->sizeHint(index, &option, Qt::PreferredSize, constraint); item->setGeometry(d->horizontalOffset, y, area.width(), size.height()); y += size.height(); ++index; if (y >= area.bottom()) {// no more space break; } } // remove unused items d->removeViewItemsFrom(index - firstVisibleIndex); } else { // Horizontal if (x <= 0) { // optimization: use the cached index and offset as starting points #if 0 // enable or disable the caching index += d->cachedIndexOffset; x += d->cachedCoordinateOffset; #endif if (x < area.x()) { // the cached offset was left of the visible area while (index < count) { initStyleOption(&option, index); const qreal width = d->itemSize(&option, index, constraint).width(); if (x + width > area.x()) break; x += width; ++index; } } else if (x > area.x()) { // the cached offset was to the right while (index >= 0 && x > area.x()) { initStyleOption(&option, index); const qreal width = d->itemSize(&option, index, constraint).width(); x -= width; --index; } } #if 0 // enable or disable the caching d->cachedIndexOffset = index - d->firstIndex; d->cachedCoordinateOffset = x + d->horizontalOffset; #endif } // we are now at the visible items const QSizeF constraint = geometry().size(); const int firstVisibleIndex = index; d->recycleViewItems(firstVisibleIndex); while (index >= 0 && index < count) { QtGraphicsListViewItem *item = d->viewItemAt(index, firstVisibleIndex); Q_ASSERT(item && item->index() == index); initStyleOption(&option, index); const QSizeF size = item->sizeHint(index, &option, Qt::PreferredSize, constraint); item->setGeometry(x, d->verticalOffset, size.width(), area.height()); x += size.width(); ++index; if (x >= area.right()) // no more space break; } // remove unused items d->removeViewItemsFrom(index - firstVisibleIndex); } emit layoutChanged(); } /*! Returns the maximum index that can be set to be the first visible item index. \sa setFirstIndex() */ int QtGraphicsListView::maximumFirstIndex() const { // ### cache this value Q_D(const QtGraphicsListView); const QSizeF constraint = size(); QStyleOptionViewItemV4 option; initStyleOption(&option); int last = (d->model ? d->model->count() : 0) - 1; int index = last; if (orientation() == Qt::Vertical) { qreal height = size().height() + d->verticalOffset; for (; index >= 0; --index) { initStyleOption(&option, index); height -= d->itemSize(&option, index, constraint).height(); if (height < 0) break; } } else { qreal width = size().width() + d->horizontalOffset; for (; index >= 0; --index) { initStyleOption(&option, index); width -= d->itemSize(&option, index, constraint).width(); if (width < 0) break; } } // the last item may be partially visible and // there may have been no items in the model if (index < last || index == -1) ++index; return index; } /*! Returns the maximum horizontal offset value that can be set on the view. \sa setHorizontalOffset() */ qreal QtGraphicsListView::maximumHorizontalOffset() const { // ### cache this value Q_D(const QtGraphicsListView); const QSizeF constraint = size(); QStyleOptionViewItemV4 option; initStyleOption(&option); qreal max = 0; qreal content = 0; int count = (d->model ? d->model->count() : 0); for (int index = 0; index < count; ++index) { initStyleOption(&option, index); QSizeF size = d->itemSize(&option, index, constraint); max = qMax(max, size.width()); content += size.width(); } if (d->orientation == Qt::Vertical) return qMax(max - size().width(), 0.0); else return content - size().width(); } /*! Returns the maximum vertical offset value that can be set on the view. \sa setVerticalOffset() */ qreal QtGraphicsListView::maximumVerticalOffset() const { // ### cache this value Q_D(const QtGraphicsListView); const QSizeF constraint = size(); QStyleOptionViewItemV4 option; initStyleOption(&option); qreal max = 0; qreal content = 0; int count = d->model ? d->model->count() : 0; for (int index = 0; index < count; ++index) { initStyleOption(&option, index); QSizeF size = d->itemSize(&option, index, constraint); content += size.height(); max = qMax(max, size.height()); } if (orientation() == Qt::Vertical) return content - size().height(); else return qMax(max - size().height(), 0.0); } /*! Sets the first index to be painted and that can be interacted with to be the item at the given \a index. \sa firstIndex() */ void QtGraphicsListView::setFirstIndex(int index) { Q_D(QtGraphicsListView); if (d->firstIndex == index) return; d->firstIndex = index; emit firstIndexChanged(index); updateLayout(); } /*! Returns the horizontal offset value. \sa setHorizontalOffset() */ qreal QtGraphicsListView::horizontalOffset() const { Q_D(const QtGraphicsListView); return d->horizontalOffset; } /*! Sets the offset value in the horizontal direction. The offset value is used when painting and interacting with the contents of the list. \sa horizontalOffset() */ void QtGraphicsListView::setHorizontalOffset(qreal offset) { Q_D(QtGraphicsListView); d->horizontalOffset = offset; emit horizontalOffsetChanged(offset); updateLayout(); } /*! Returns the vertical offset value. \sa setVerticalOffset() */ qreal QtGraphicsListView::verticalOffset() const { Q_D(const QtGraphicsListView); return d->verticalOffset; } /*! Sets the offset value in the vertical direction. The offset value is used when painting and interacting with the contents of the list. \sa verticalOffset() */ void QtGraphicsListView::setVerticalOffset(qreal offset) { Q_D(QtGraphicsListView); d->verticalOffset = offset; emit verticalOffsetChanged(offset); updateLayout(); } /*! Returns the index of the first item in the list that is painted and that can be interacted with. */ int QtGraphicsListView::firstIndex() const { Q_D(const QtGraphicsListView); return d->firstIndex; } /*! */ void QtGraphicsListView::setOffsetToEnsureIndexIsVisible(int index) { Q_UNUSED(index); // ### TODO } /*! */ void QtGraphicsListView::setFirstIndexToEnsureIndexIsVisible(int index) { Q_D(QtGraphicsListView); return; // ### this breaks views with a different layout // ### cache if (index > d->firstIndex) { const QSizeF constraint = size(); QStyleOptionViewItemV4 option; initStyleOption(&option); if (orientation() == Qt::Vertical) { qreal height = size().height() + d->verticalOffset; for (; index >= 0; --index) { initStyleOption(&option, index); height -= d->itemSize(&option, index, constraint).height(); if (height < 0) break; } } else { qreal width = size().width() + d->horizontalOffset; for (; index >= 0; --index) { initStyleOption(&option, index); width -= d->itemSize(&option, index, constraint).width(); if (width < 0) break; } } } if (index > -1) setFirstIndex(index); } /*! Initialize the given \a option struct with settings for this list view and the given \a device. */ void QtGraphicsListView::initStyleOption(QStyleOptionViewItemV4 *option) const { Q_ASSERT(option); option->widget = 0; option->font = QApplication::font(); option->fontMetrics = QFontMetrics(option->font); option->displayAlignment = Qt::AlignLeft|Qt::AlignVCenter; option->textElideMode = textElideMode(); option->showDecorationSelected = style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected); int pm = style()->pixelMetric(QStyle::PM_ListViewIconSize); //int pm = style()->pixelMetric(QStyle::PM_IconViewIconSize); option->decorationSize = QSize(pm, pm); bool vertical = (orientation() == Qt::Vertical); option->decorationPosition = vertical ? QStyleOptionViewItem::Left : QStyleOptionViewItem::Top; option->decorationAlignment = Qt::AlignCenter; option->displayAlignment = vertical ? Qt::AlignLeft|Qt::AlignVCenter : Qt::AlignCenter; option->locale = QLocale(); // ### FIXME option->locale.setNumberOptions(QLocale::OmitGroupSeparator); //if (d->wrapText) option->features |= QStyleOptionViewItemV2::WrapText; //if (d->alternate) option->features |= QStyleOptionViewItemV2::Alternate; } /*! Initialize the given \a option with the settings and data specific for the item at the given \a index. */ void QtGraphicsListView::initStyleOption(QStyleOptionViewItemV4 *option, int index) const { Q_D(const QtGraphicsListView); Q_ASSERT(option); // Current Item if (d->selectionManager && d->selectionManager->currentItem() == index) option->state |= QStyle::State_HasFocus; else option->state &= ~QStyle::State_HasFocus; // selectionManager if (d->selectionManager && d->selectionManager->isSelected(index)) option->state |= QStyle::State_Selected; else option->state &= ~QStyle::State_Selected; // Data QVariant value; // FontRole value = d->cachedData(index, Qt::FontRole); if (value.isValid()) { option->font = qvariant_cast(value).resolve(option->font); option->fontMetrics = QFontMetrics(option->font); } // TextAlignmentRole value = d->cachedData(index, Qt::TextAlignmentRole); if (value.isValid()) option->displayAlignment = (Qt::Alignment)value.toInt(); // ForegroundRole value = d->cachedData(index, Qt::ForegroundRole); if (qVariantCanConvert(value)) option->palette.setBrush(QPalette::Text, qvariant_cast(value)); // CheckStateRole value = d->cachedData(index, Qt::CheckStateRole); if (value.isValid()) { option->features |= QStyleOptionViewItemV2::HasCheckIndicator; option->checkState = static_cast(value.toInt()); } // DecorationRole value = d->cachedData(index, Qt::DecorationRole); if (value.isValid()) { option->features |= QStyleOptionViewItemV2::HasDecoration; switch (value.type()) { case QVariant::Icon: option->icon = qvariant_cast(value); break; case QVariant::Color: { QPixmap pixmap(option->decorationSize); pixmap.fill(qvariant_cast(value)); option->icon = QIcon(pixmap); } default: break; } } // DisplayRole value = d->cachedData(index, Qt::DisplayRole); if (value.isValid()) { option->features |= QStyleOptionViewItemV2::HasDisplay; switch (value.type()) { case QVariant::Double: option->text = option->locale.toString(value.toDouble()); break; case QVariant::Int: case QVariant::LongLong: option->text = option->locale.toString(value.toLongLong()); break; case QVariant::UInt: case QVariant::ULongLong: option->text = option->locale.toString(value.toULongLong()); break; case QVariant::Date: option->text = option->locale.toString(value.toDate(), QLocale::ShortFormat); break; case QVariant::Time: option->text = option->locale.toString(value.toTime(), QLocale::ShortFormat); break; case QVariant::DateTime: option->text = option->locale.toString(value.toDateTime().date(), QLocale::ShortFormat); option->text += QLatin1Char(' '); option->text += option->locale.toString(value.toDateTime().time(), QLocale::ShortFormat); break; default: { QString text = value.toString(); for (int i = 0; i < text.count(); ++i) if (text.at(i).unicode() == '\n') text[i] = QChar::LineSeparator; option->text = text; break; } } } } /*! */ void QtGraphicsListView::copyStyleOptionState(const QStyleOptionGraphicsItem *source, QStyleOptionViewItemV4 *dest) { if (source && dest) { dest->state = source->state; dest->direction = source->direction; dest->rect = source->rect; dest->fontMetrics = source->fontMetrics; dest->palette = source->palette; } } /*! */ bool QtGraphicsListView::layoutsBlocked() const { Q_D(const QtGraphicsListView); return d->layoutsBlocked; } /*! */ void QtGraphicsListView::setLayoutsBlocked(bool block) { Q_D(QtGraphicsListView); d->layoutsBlocked = block; } /*! */ bool QtGraphicsListView::event(QEvent *event) { Q_D(QtGraphicsListView); // forward events to the controller if (d->controller && d->controller->processEvent(event, transform())) { event->accept(); return true; } if (event->type() == QEvent::Timer && static_cast(event)->timerId() == d->layoutTimer.timerId()) doLayout(); return QGraphicsWidget::event(event); } /*! */ QtGraphicsListViewItemCreatorBase *QtGraphicsListView::viewItemCreator() const { Q_D(const QtGraphicsListView); return d->creator; } /*! */ void QtGraphicsListView::setViewItemCreator(QtGraphicsListViewItemCreatorBase *creator) { Q_D(QtGraphicsListView); if (d->creator) delete d->creator; d->creator = creator; } /*! */ const QList &QtGraphicsListView::viewItems() const { Q_D(const QtGraphicsListView); return d->viewItems; } /*! */ void QtGraphicsListView::itemGeometryChanged(QtGraphicsListViewItem *item) { Q_D(QtGraphicsListView); if (item && !d->layoutsBlocked) { // ### we may want a delayed mechanism for this updateLayout(); } } #include "moc_qgraphicslistview.cpp"