diff options
Diffstat (limited to 'src/widgets/itemviews')
22 files changed, 744 insertions, 167 deletions
diff --git a/src/widgets/itemviews/itemviews.pri b/src/widgets/itemviews/itemviews.pri index 0bc0309271..4e152d18bb 100644 --- a/src/widgets/itemviews/itemviews.pri +++ b/src/widgets/itemviews/itemviews.pri @@ -26,6 +26,7 @@ HEADERS += \ itemviews/qtreewidgetitemiterator.h \ itemviews/qdatawidgetmapper.h \ itemviews/qfileiconprovider.h \ + itemviews/qfileiconprovider_p.h \ itemviews/qcolumnviewgrip_p.h \ itemviews/qcolumnview.h \ itemviews/qcolumnview_p.h \ diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index 70c8f44a73..b79525b9df 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -1108,6 +1108,7 @@ void QAbstractItemView::reset() QAccessible::updateAccessibility(&accessibleEvent); } #endif + d->updateGeometry(); } /*! @@ -1124,6 +1125,7 @@ void QAbstractItemView::setRootIndex(const QModelIndex &index) } d->root = index; d->doDelayedItemsLayout(); + d->updateGeometry(); } /*! @@ -2668,8 +2670,10 @@ void QAbstractItemView::updateEditorGeometries() */ void QAbstractItemView::updateGeometries() { + Q_D(QAbstractItemView); updateEditorGeometries(); - d_func()->fetchMoreTimer.start(0, this); //fetch more later + d->fetchMoreTimer.start(0, this); //fetch more later + d->updateGeometry(); } /*! @@ -3231,6 +3235,7 @@ void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelInde QAccessible::updateAccessibility(&accessibleEvent); } #endif + d->updateGeometry(); } /*! @@ -3332,6 +3337,7 @@ void QAbstractItemViewPrivate::_q_rowsRemoved(const QModelIndex &index, int star QAccessible::updateAccessibility(&accessibleEvent); } #endif + updateGeometry(); } /*! @@ -3412,6 +3418,7 @@ void QAbstractItemViewPrivate::_q_columnsRemoved(const QModelIndex &index, int s QAccessible::updateAccessibility(&accessibleEvent); } #endif + updateGeometry(); } @@ -3435,6 +3442,7 @@ void QAbstractItemViewPrivate::_q_rowsInserted(const QModelIndex &index, int sta QAccessible::updateAccessibility(&accessibleEvent); } #endif + updateGeometry(); } /*! @@ -3459,6 +3467,7 @@ void QAbstractItemViewPrivate::_q_columnsInserted(const QModelIndex &index, int QAccessible::updateAccessibility(&accessibleEvent); } #endif + updateGeometry(); } /*! @@ -4084,7 +4093,14 @@ void QAbstractItemViewPrivate::interruptDelayedItemsLayout() const delayedPendingLayout = false; } - +void QAbstractItemViewPrivate::updateGeometry() +{ + Q_Q(QAbstractItemView); + if (sizeAdjustPolicy == QAbstractScrollArea::AdjustIgnored) + return; + if (sizeAdjustPolicy == QAbstractScrollArea::AdjustToContents || !shownOnce) + q->updateGeometry(); +} QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index, const QStyleOptionViewItem &options) diff --git a/src/widgets/itemviews/qabstractitemview_p.h b/src/widgets/itemviews/qabstractitemview_p.h index 86eab174ed..5da22615e2 100644 --- a/src/widgets/itemviews/qabstractitemview_p.h +++ b/src/widgets/itemviews/qabstractitemview_p.h @@ -127,6 +127,8 @@ public: void doDelayedItemsLayout(int delay = 0); void interruptDelayedItemsLayout() const; + void updateGeometry(); + void startAutoScroll() { // ### it would be nice to make this into a style hint one day int scrollInterval = (verticalScrollMode == QAbstractItemView::ScrollPerItem) ? 150 : 50; diff --git a/src/widgets/itemviews/qcolumnview.cpp b/src/widgets/itemviews/qcolumnview.cpp index 5aee78fab4..32a3dddca1 100644 --- a/src/widgets/itemviews/qcolumnview.cpp +++ b/src/widgets/itemviews/qcolumnview.cpp @@ -332,11 +332,14 @@ void QColumnView::scrollTo(const QModelIndex &index, ScrollHint hint) } #ifndef QT_NO_ANIMATION - d->currentAnimation.setEndValue(newScrollbarValue); - d->currentAnimation.start(); -#else - horizontalScrollBar()->setValue(newScrollbarValue); + if (style()->styleHint(QStyle::SH_Widget_Animate, 0, this)) { + d->currentAnimation.setEndValue(newScrollbarValue); + d->currentAnimation.start(); + } else #endif //QT_NO_ANIMATION + { + horizontalScrollBar()->setValue(newScrollbarValue); + } } /*! diff --git a/src/widgets/itemviews/qfileiconprovider.cpp b/src/widgets/itemviews/qfileiconprovider.cpp index 8a0736ec70..71697ddc40 100644 --- a/src/widgets/itemviews/qfileiconprovider.cpp +++ b/src/widgets/itemviews/qfileiconprovider.cpp @@ -40,9 +40,8 @@ ****************************************************************************/ #include "qfileiconprovider.h" +#include "qfileiconprovider_p.h" -#ifndef QT_NO_FILEICONPROVIDER -#include <qstyle.h> #include <qapplication.h> #include <qdir.h> #include <qpixmapcache.h> @@ -83,34 +82,15 @@ QT_BEGIN_NAMESPACE \value File */ -class QFileIconProviderPrivate -{ - Q_DECLARE_PUBLIC(QFileIconProvider) - -public: - QFileIconProviderPrivate(); - QIcon getIcon(QStyle::StandardPixmap name) const; - QIcon getIcon(const QFileInfo &fi) const; - - QFileIconProvider *q_ptr; - const QString homePath; - -private: - mutable QIcon file; - mutable QIcon fileLink; - mutable QIcon directory; - mutable QIcon directoryLink; - mutable QIcon harddisk; - mutable QIcon floppy; - mutable QIcon cdrom; - mutable QIcon ram; - mutable QIcon network; - mutable QIcon computer; - mutable QIcon desktop; - mutable QIcon trashcan; - mutable QIcon generic; - mutable QIcon home; -}; + +/*! + \enum QFileIconProvider::Option + \since 5.2 + + \value DontUseCustomDirectoryIcons Always use the default directory icon. + Some platforms allow the user to set a different icon. Custom icon lookup + cause a big performance impact over network or removable drives. +*/ QFileIconProviderPrivate::QFileIconProviderPrivate() : homePath(QDir::home().absolutePath()) @@ -193,6 +173,31 @@ QFileIconProvider::~QFileIconProvider() } /*! + \since 5.2 + Sets \a options that affect the icon provider. + \sa options() +*/ + +void QFileIconProvider::setOptions(QFileIconProvider::Options options) +{ + Q_D(QFileIconProvider); + d->options = options; +} + +/*! + \since 5.2 + Returns all the options that affect the icon provider. + By default, all options are disabled. + \sa setOptions() +*/ + +QFileIconProvider::Options QFileIconProvider::options() const +{ + Q_D(const QFileIconProvider); + return d->options; +} + +/*! Returns an icon set for the given \a type. */ @@ -220,6 +225,23 @@ QIcon QFileIconProvider::icon(IconType type) const return QIcon(); } +static bool isCacheable(const QFileInfo &fi) +{ + if (!fi.isFile()) + return false; + +#ifdef Q_OS_WIN + // On windows it's faster to just look at the file extensions. QTBUG-13182 + const QString fileExtension = fi.suffix(); + // Will return false for .exe, .lnk and .ico extensions + return fileExtension.compare(QLatin1String("exe"), Qt::CaseInsensitive) && + fileExtension.compare(QLatin1String("lnk"), Qt::CaseInsensitive) && + fileExtension.compare(QLatin1String("ico"), Qt::CaseInsensitive); +#else + return !fi.isExecutable() && !fi.isSymLink(); +#endif +} + QIcon QFileIconProviderPrivate::getIcon(const QFileInfo &fi) const { QIcon retIcon; @@ -231,10 +253,9 @@ QIcon QFileIconProviderPrivate::getIcon(const QFileInfo &fi) const if (sizes.isEmpty()) return retIcon; - const QString fileExtension = fi.suffix().toUpper(); const QString keyBase = QLatin1String("qt_.") + fi.suffix().toUpper(); - bool cacheable = fi.isFile() && !fi.isExecutable() && !fi.isSymLink() && fileExtension != QLatin1String("ICO"); + bool cacheable = isCacheable(fi); if (cacheable) { QPixmap pixmap; QPixmapCache::find(keyBase + QString::number(sizes.at(0)), pixmap); @@ -253,8 +274,12 @@ QIcon QFileIconProviderPrivate::getIcon(const QFileInfo &fi) const } } + QPlatformTheme::IconOptions iconOptions; + if (options & QFileIconProvider::DontUseCustomDirectoryIcons) + iconOptions |= QPlatformTheme::DontUseCustomDirectoryIcons; + Q_FOREACH (int size, sizes) { - QPixmap pixmap = theme->fileIconPixmap(fi, QSizeF(size, size)); + QPixmap pixmap = theme->fileIconPixmap(fi, QSizeF(size, size), iconOptions); if (!pixmap.isNull()) { retIcon.addPixmap(pixmap); if (cacheable) @@ -372,5 +397,3 @@ QString QFileIconProvider::type(const QFileInfo &info) const } QT_END_NAMESPACE - -#endif diff --git a/src/widgets/itemviews/qfileiconprovider.h b/src/widgets/itemviews/qfileiconprovider.h index cac135fe71..6f7e28395b 100644 --- a/src/widgets/itemviews/qfileiconprovider.h +++ b/src/widgets/itemviews/qfileiconprovider.h @@ -49,8 +49,6 @@ QT_BEGIN_NAMESPACE -#ifndef QT_NO_FILEICONPROVIDER - class QFileIconProviderPrivate; class Q_WIDGETS_EXPORT QFileIconProvider @@ -59,19 +57,27 @@ public: QFileIconProvider(); virtual ~QFileIconProvider(); enum IconType { Computer, Desktop, Trashcan, Network, Drive, Folder, File }; + + enum Option { + DontUseCustomDirectoryIcons = 0x00000001 + }; + Q_DECLARE_FLAGS(Options, Option) + virtual QIcon icon(IconType type) const; virtual QIcon icon(const QFileInfo &info) const; virtual QString type(const QFileInfo &info) const; + void setOptions(Options options); + Options options() const; + private: Q_DECLARE_PRIVATE(QFileIconProvider) QScopedPointer<QFileIconProviderPrivate> d_ptr; Q_DISABLE_COPY(QFileIconProvider) }; -#endif // QT_NO_FILEICONPROVIDER +Q_DECLARE_OPERATORS_FOR_FLAGS(QFileIconProvider::Options) QT_END_NAMESPACE #endif // QFILEICONPROVIDER_H - diff --git a/src/widgets/itemviews/qfileiconprovider_p.h b/src/widgets/itemviews/qfileiconprovider_p.h new file mode 100644 index 0000000000..9b2d536258 --- /dev/null +++ b/src/widgets/itemviews/qfileiconprovider_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtWidgets module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 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 the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILEICONPROVIDER_P_H +#define QFILEICONPROVIDER_P_H + +#include "qfileiconprovider.h" + +#include <QtCore/qstring.h> +#include <QtGui/qicon.h> +#include <QtWidgets/qstyle.h> + +QT_BEGIN_NAMESPACE + +class QFileInfo; + +class QFileIconProviderPrivate +{ + Q_DECLARE_PUBLIC(QFileIconProvider) + +public: + QFileIconProviderPrivate(); + QIcon getIcon(QStyle::StandardPixmap name) const; + QIcon getIcon(const QFileInfo &fi) const; + + QFileIconProvider *q_ptr; + const QString homePath; + QFileIconProvider::Options options; + +private: + mutable QIcon file; + mutable QIcon fileLink; + mutable QIcon directory; + mutable QIcon directoryLink; + mutable QIcon harddisk; + mutable QIcon floppy; + mutable QIcon cdrom; + mutable QIcon ram; + mutable QIcon network; + mutable QIcon computer; + mutable QIcon desktop; + mutable QIcon trashcan; + mutable QIcon generic; + mutable QIcon home; +}; + +QT_END_NAMESPACE + +#endif // QFILEICONPROVIDER_P_H diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp index 864a55d2ea..239cc84a0a 100644 --- a/src/widgets/itemviews/qheaderview.cpp +++ b/src/widgets/itemviews/qheaderview.cpp @@ -80,6 +80,8 @@ QDataStream &operator>>(QDataStream &in, QHeaderViewPrivate::SectionItem §io } #endif // QT_NO_DATASTREAM +static const int maxSizeSection = 1048575; // since section size is in a bitfield (uint 20). See qheaderview_p.h + // if this is changed then the docs in maximumSectionSize should be changed. /*! \class QHeaderView @@ -550,9 +552,25 @@ QSize QHeaderView::sizeHint() const } /*! + \reimp +*/ + +void QHeaderView::setVisible(bool v) +{ + bool actualChange = (v != isVisible()); + QAbstractItemView::setVisible(v); + if (actualChange) { + QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea*>(parentWidget()); + if (parent) + parent->updateGeometry(); + } +} + + +/*! Returns a suitable size hint for the section specified by \a logicalIndex. - \sa sizeHint(), defaultSectionSize(), minimumSectionSize(), + \sa sizeHint(), defaultSectionSize(), minimumSectionSize(), maximumSectionSize() Qt::SizeHintRole */ @@ -570,7 +588,7 @@ int QHeaderView::sectionSizeHint(int logicalIndex) const else size = sectionSizeFromContents(logicalIndex); int hint = d->orientation == Qt::Horizontal ? size.width() : size.height(); - return qMax(minimumSectionSize(), hint); + return qBound(minimumSectionSize(), hint, maximumSectionSize()); } /*! @@ -868,7 +886,7 @@ void QHeaderView::swapSections(int first, int second) void QHeaderView::resizeSection(int logical, int size) { Q_D(QHeaderView); - if (logical < 0 || logical >= count() || size < 0) + if (logical < 0 || logical >= count() || size < 0 || size > maxSizeSection) return; if (isSectionHidden(logical)) { @@ -918,6 +936,18 @@ void QHeaderView::resizeSection(int logical, int size) d->doDelayedResizeSections(); r = d->viewport->rect(); } + + // If the parent is a QAbstractScrollArea with QAbstractScrollArea::AdjustToContents + // then we want to change the geometry on that widget. Not doing it at once can/will + // cause scrollbars flicker as they would be shown at first but then removed. + // In the same situation it will also allow shrinking the whole view when stretchLastSection is set + // (It is default on QTreeViews - and it wouldn't shrink since the last stretch was made before the + // viewport was resized) + + QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea *>(parentWidget()); + if (parent && parent->sizeAdjustPolicy() == QAbstractScrollArea::AdjustToContents) + parent->updateGeometry(); + d->viewport->update(r.normalized()); emit sectionResized(logical, oldSize, size); } @@ -1225,7 +1255,7 @@ void QHeaderView::setSectionResizeMode(ResizeMode mode) property is set to true. This is the default for the horizontal headers provided by QTreeView. - \sa setStretchLastSection() + \sa setStretchLastSection(), resizeContentsPrecision() */ void QHeaderView::setSectionResizeMode(int logicalIndex, ResizeMode mode) @@ -1288,6 +1318,53 @@ QHeaderView::ResizeMode QHeaderView::sectionResizeMode(int logicalIndex) const return d->headerSectionResizeMode(visual); } +/*! + \since 5.2 + Sets how precise QHeaderView should calculate the size when ResizeToContents is used. + A low value will provide a less accurate but fast auto resize while a higher + value will provide a more accurate resize that however can be slow. + + The number \a precision specifies how many sections that should be consider + when calculating the preferred size. + + The default value is 1000 meaning that a horizontal column with auto-resize will look + at maximum 1000 rows on calculating when doing an auto resize. + + Special value 0 means that it will look at only the visible area. + Special value -1 will imply looking at all elements. + + This value is used in QTableView::sizeHintForColumn(), QTableView::sizeHintForRow() + and QTreeView::sizeHintForColumn(). Reimplementing these functions can make this + function not having an effect. + + If \a resizeSectionsNow is set to true (default) it will do adjustment of sections by calling + resizeSections(). (regardless if the precision was changed). + + \sa resizeContentsPrecision(), setSectionResizeMode(), resizeSections(), QTableView::sizeHintForColumn(), QTableView::sizeHintForRow(), QTreeView::sizeHintForColumn() +*/ + +void QHeaderView::setResizeContentsPrecision(int precision, bool resizeSectionsNow) +{ + Q_D(QHeaderView); + d->resizeContentsPrecision = precision; + if (resizeSectionsNow) + resizeSections(); +} + +/*! + \since 5.2 + Returns how precise QHeaderView will calculate on ResizeToContents. + + \sa setResizeContentsPrecision(), setSectionResizeMode() + +*/ + +int QHeaderView::resizeContentsPrecision() const +{ + Q_D(const QHeaderView); + return d->resizeContentsPrecision; +} + // ### Qt 6 - remove this obsolete function /*! \obsolete @@ -1492,7 +1569,7 @@ int QHeaderView::defaultSectionSize() const void QHeaderView::setDefaultSectionSize(int size) { Q_D(QHeaderView); - if (size < 0) + if (size < 0 || size > maxSizeSection) return; d->setDefaultSectionSize(size); } @@ -1527,12 +1604,52 @@ int QHeaderView::minimumSectionSize() const void QHeaderView::setMinimumSectionSize(int size) { Q_D(QHeaderView); - if (size < 0) + if (size < 0 || size > maxSizeSection) return; d->minimumSectionSize = size; + if (d->minimumSectionSize > maximumSectionSize()) + d->maximumSectionSize = size; } /*! + \since 5.2 + \property QHeaderView::maximumSectionSize + \brief the maximum size of the header sections. + + The maximum section size is the largest section size allowed. + The default value for this property is 1048575, which is also the largest + possible size for a section. Setting maximum to -1 will reset the value to + the largest section size. + + With exception of stretch this property is honored by all \l{ResizeMode}{resize modes} + + \sa setSectionResizeMode(), defaultSectionSize +*/ +int QHeaderView::maximumSectionSize() const +{ + Q_D(const QHeaderView); + if (d->maximumSectionSize == -1) + return maxSizeSection; + return d->maximumSectionSize; +} + +void QHeaderView::setMaximumSectionSize(int size) +{ + Q_D(QHeaderView); + if (size == -1) { + d->maximumSectionSize = maxSizeSection; + return; + } + if (size < 0 || size > maxSizeSection) + return; + if (minimumSectionSize() > size) + d->minimumSectionSize = size; + + d->maximumSectionSize = size; +} + + +/*! \since 4.1 \property QHeaderView::defaultAlignment \brief the default alignment of the text in each header section @@ -2341,7 +2458,8 @@ void QHeaderView::mouseMoveEvent(QMouseEvent *e) d->cascadingResize(visual, d->headerSectionSize(visual) + delta); } else { int delta = d->reverse() ? d->firstPos - pos : pos - d->firstPos; - resizeSection(d->section, qMax(d->originalSize + delta, minimumSectionSize())); + int newsize = qBound(minimumSectionSize(), d->originalSize + delta, maximumSectionSize()); + resizeSection(d->section, newsize); } d->lastPos = pos; return; @@ -3160,6 +3278,8 @@ void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool int logicalIndex = q->logicalIndex(i); sectionSize = qMax(viewSectionSizeHint(logicalIndex), q->sectionSizeHint(logicalIndex)); + if (sectionSize > q->maximumSectionSize()) + sectionSize = q->maximumSectionSize(); } section_sizes.append(sectionSize); lengthToStretch -= sectionSize; @@ -3492,7 +3612,7 @@ QHeaderView::ResizeMode QHeaderViewPrivate::headerSectionResizeMode(int visual) { if (visual < 0 || visual >= sectionItems.count()) return globalResizeMode; - return sectionItems.at(visual).resizeMode; + return static_cast<QHeaderView::ResizeMode>(sectionItems.at(visual).resizeMode); } void QHeaderViewPrivate::setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode) @@ -3530,6 +3650,19 @@ int QHeaderViewPrivate::adjustedVisualIndex(int visualIndex) const return visualIndex; } +void QHeaderViewPrivate::setScrollOffset(const QScrollBar *scrollBar, QAbstractItemView::ScrollMode scrollMode) +{ + Q_Q(QHeaderView); + if (scrollMode == QAbstractItemView::ScrollPerItem) { + if (scrollBar->maximum() > 0 && scrollBar->value() == scrollBar->maximum()) + q->setOffsetToLastSection(); + else + q->setOffsetToSectionPosition(scrollBar->value()); + } else { + q->setOffset(scrollBar->value()); + } +} + #ifndef QT_NO_DATASTREAM void QHeaderViewPrivate::write(QDataStream &out) const { @@ -3560,6 +3693,8 @@ void QHeaderViewPrivate::write(QDataStream &out) const out << int(globalResizeMode); out << sectionItems; + if (out.version() >= QDataStream::Qt_5_2) + out << resizeContentsPrecision; } bool QHeaderViewPrivate::read(QDataStream &in) @@ -3611,6 +3746,10 @@ bool QHeaderViewPrivate::read(QDataStream &in) } sectionItems = newSectionItems; recalcSectionStartPos(); + + if (in.version() >= QDataStream::Qt_5_2) + in >> resizeContentsPrecision; + return true; } diff --git a/src/widgets/itemviews/qheaderview.h b/src/widgets/itemviews/qheaderview.h index 8fcd8d7a36..b3ed666aa6 100644 --- a/src/widgets/itemviews/qheaderview.h +++ b/src/widgets/itemviews/qheaderview.h @@ -61,6 +61,7 @@ class Q_WIDGETS_EXPORT QHeaderView : public QAbstractItemView Q_PROPERTY(bool cascadingSectionResizes READ cascadingSectionResizes WRITE setCascadingSectionResizes) Q_PROPERTY(int defaultSectionSize READ defaultSectionSize WRITE setDefaultSectionSize) Q_PROPERTY(int minimumSectionSize READ minimumSectionSize WRITE setMinimumSectionSize) + Q_PROPERTY(int maximumSectionSize READ maximumSectionSize WRITE setMaximumSectionSize) Q_PROPERTY(Qt::Alignment defaultAlignment READ defaultAlignment WRITE setDefaultAlignment) Q_ENUMS(ResizeMode) @@ -84,6 +85,7 @@ public: int offset() const; int length() const; QSize sizeHint() const; + void setVisible(bool v); int sectionSizeHint(int logicalIndex) const; int visualIndexAt(int position) const; @@ -132,6 +134,10 @@ public: ResizeMode sectionResizeMode(int logicalIndex) const; void setSectionResizeMode(ResizeMode mode); void setSectionResizeMode(int logicalIndex, ResizeMode mode); + + void setResizeContentsPrecision(int precision, bool resizeNow = true); + int resizeContentsPrecision() const; + #if QT_DEPRECATED_SINCE(5, 0) inline QT_DEPRECATED void setResizeMode(ResizeMode mode) { setSectionResizeMode(mode); } @@ -161,6 +167,8 @@ public: int minimumSectionSize() const; void setMinimumSectionSize(int size); + int maximumSectionSize() const; + void setMaximumSectionSize(int size); Qt::Alignment defaultAlignment() const; void setDefaultAlignment(Qt::Alignment alignment); diff --git a/src/widgets/itemviews/qheaderview_p.h b/src/widgets/itemviews/qheaderview_p.h index f886430ba8..b2af821e9b 100644 --- a/src/widgets/itemviews/qheaderview_p.h +++ b/src/widgets/itemviews/qheaderview_p.h @@ -95,11 +95,13 @@ public: stretchSections(0), contentsSections(0), minimumSectionSize(-1), + maximumSectionSize(-1), lastSectionSize(0), sectionIndicatorOffset(0), sectionIndicator(0), globalResizeMode(QHeaderView::Interactive), - sectionStartposRecalc(true) + sectionStartposRecalc(true), + resizeContentsPrecision(1000) {} @@ -286,6 +288,7 @@ public: int contentsSections; int defaultSectionSize; int minimumSectionSize; + int maximumSectionSize; int lastSectionSize; // $$$ int sectionIndicatorOffset; Qt::Alignment defaultAlignment; @@ -293,26 +296,31 @@ public: QHeaderView::ResizeMode globalResizeMode; QList<QPersistentModelIndex> persistentHiddenSections; mutable bool sectionStartposRecalc; + int resizeContentsPrecision; // header sections struct SectionItem { - int size; + uint size : 20; + uint reservedForIsHidden : 1; + uint resizeMode : 5; // (holding QHeaderView::ResizeMode) + uint currentlyUnusedPadding : 6; + union { // This union is made in order to save space and ensure good vector performance (on remove) mutable int calculated_startpos; // <- this is the primary used member. mutable int tmpLogIdx; // When one of these 'tmp'-members has been used we call int tmpDataStreamSectionCount; // recalcSectionStartPos() or set sectionStartposRecalc to true }; // to ensure that calculated_startpos will be calculated afterwards. - QHeaderView::ResizeMode resizeMode; + inline SectionItem() : size(0), resizeMode(QHeaderView::Interactive) {} inline SectionItem(int length, QHeaderView::ResizeMode mode) - : size(length), calculated_startpos(-1), resizeMode(mode) {} + : size(length), resizeMode(mode), calculated_startpos(-1) {} inline int sectionSize() const { return size; } inline int calculatedEndPos() const { return calculated_startpos + size; } #ifndef QT_NO_DATASTREAM inline void write(QDataStream &out) const - { out << size; out << 1; out << (int)resizeMode; } + { out << static_cast<int>(size); out << 1; out << (int)resizeMode; } inline void read(QDataStream &in) - { in >> size; in >> tmpDataStreamSectionCount; int m; in >> m; resizeMode = (QHeaderView::ResizeMode)m; } + { int m; in >> m; size = m; in >> tmpDataStreamSectionCount; in >> m; resizeMode = m; } #endif }; @@ -343,6 +351,7 @@ public: // other int viewSectionSizeHint(int logical) const; int adjustedVisualIndex(int visualIndex) const; + void setScrollOffset(const QScrollBar *scrollBar, QAbstractItemView::ScrollMode scrollMode); #ifndef QT_NO_DATASTREAM void write(QDataStream &out) const; diff --git a/src/widgets/itemviews/qitemdelegate.cpp b/src/widgets/itemviews/qitemdelegate.cpp index c040322ba1..3e34568a12 100644 --- a/src/widgets/itemviews/qitemdelegate.cpp +++ b/src/widgets/itemviews/qitemdelegate.cpp @@ -1295,8 +1295,11 @@ bool QItemDelegate::editorEvent(QEvent *event, return false; } - Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked - ? Qt::Unchecked : Qt::Checked); + Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt()); + if (flags & Qt::ItemIsTristate) + state = ((Qt::CheckState)((state + 1) % 3)); + else + state = (state == Qt::Checked) ? Qt::Unchecked : Qt::Checked; return model->setData(index, state, Qt::CheckStateRole); } diff --git a/src/widgets/itemviews/qlistview.cpp b/src/widgets/itemviews/qlistview.cpp index 3690b15650..331748eedc 100644 --- a/src/widgets/itemviews/qlistview.cpp +++ b/src/widgets/itemviews/qlistview.cpp @@ -1833,18 +1833,18 @@ void QCommonListViewBase::removeHiddenRow(int row) dd->hiddenRows.remove(dd->model->index(row, 0, qq->rootIndex())); } -void QCommonListViewBase::updateHorizontalScrollBar(const QSize &step) +void QCommonListViewBase::updateHorizontalScrollBar(const QSize & /*step*/) { - horizontalScrollBar()->setSingleStep(step.width() + spacing()); horizontalScrollBar()->setPageStep(viewport()->width()); horizontalScrollBar()->setRange(0, contentsSize.width() - viewport()->width()); + // we do not want to overwrite (a possible user set) single step } -void QCommonListViewBase::updateVerticalScrollBar(const QSize &step) +void QCommonListViewBase::updateVerticalScrollBar(const QSize & /*step*/) { - verticalScrollBar()->setSingleStep(step.height() + spacing()); verticalScrollBar()->setPageStep(viewport()->height()); verticalScrollBar()->setRange(0, contentsSize.height() - viewport()->height()); + // we do not want to overwrite (a possible user set) single step } void QCommonListViewBase::scrollContentsBy(int dx, int dy, bool /*scrollElasticBand*/) diff --git a/src/widgets/itemviews/qlistwidget.cpp b/src/widgets/itemviews/qlistwidget.cpp index 39d03124ce..27abfb8eb4 100644 --- a/src/widgets/itemviews/qlistwidget.cpp +++ b/src/widgets/itemviews/qlistwidget.cpp @@ -304,7 +304,7 @@ void QListModel::sort(int column, Qt::SortOrder order) } LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan); - qSort(sorting.begin(), sorting.end(), compare); + std::sort(sorting.begin(), sorting.end(), compare); QModelIndexList fromIndexes; QModelIndexList toIndexes; for (int r = 0; r < sorting.count(); ++r) { @@ -338,7 +338,7 @@ void QListModel::ensureSorted(int column, Qt::SortOrder order, int start, int en } LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan); - qSort(sorting.begin(), sorting.end(), compare); + std::sort(sorting.begin(), sorting.end(), compare); QModelIndexList oldPersistentIndexes = persistentIndexList(); QModelIndexList newPersistentIndexes = oldPersistentIndexes; @@ -347,7 +347,11 @@ void QListModel::ensureSorted(int column, Qt::SortOrder order, int start, int en bool changed = false; for (int i = 0; i < count; ++i) { int oldRow = sorting.at(i).second; + int tmpitepos = lit - tmp.begin(); QListWidgetItem *item = tmp.takeAt(oldRow); + if (tmpitepos > tmp.size()) + --tmpitepos; + lit = tmp.begin() + tmpitepos; lit = sortedInsertionIterator(lit, tmp.end(), order, item); int newRow = qMax(lit - tmp.begin(), 0); lit = tmp.insert(lit, item); @@ -1828,7 +1832,7 @@ void QListWidget::dropEvent(QDropEvent *event) { if (persIndexes.contains(topIndex)) return; - qSort(persIndexes); // The dropped items will remain in the same visual order. + std::sort(persIndexes.begin(), persIndexes.end()); // The dropped items will remain in the same visual order. QPersistentModelIndex dropRow = model()->index(row, col, topIndex); diff --git a/src/widgets/itemviews/qstyleditemdelegate.cpp b/src/widgets/itemviews/qstyleditemdelegate.cpp index 7e1933ad1e..c1fa9fe28e 100644 --- a/src/widgets/itemviews/qstyleditemdelegate.cpp +++ b/src/widgets/itemviews/qstyleditemdelegate.cpp @@ -764,8 +764,11 @@ bool QStyledItemDelegate::editorEvent(QEvent *event, return false; } - Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked - ? Qt::Unchecked : Qt::Checked); + Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt()); + if (flags & Qt::ItemIsTristate) + state = ((Qt::CheckState)((state + 1) % 3)); + else + state = (state == Qt::Checked) ? Qt::Unchecked : Qt::Checked; return model->setData(index, state, Qt::CheckStateRole); } diff --git a/src/widgets/itemviews/qtableview.cpp b/src/widgets/itemviews/qtableview.cpp index a6623b9462..871ad656ea 100644 --- a/src/widgets/itemviews/qtableview.cpp +++ b/src/widgets/itemviews/qtableview.cpp @@ -931,6 +931,59 @@ void QTableViewPrivate::drawCell(QPainter *painter, const QStyleOptionViewItem & } /*! + \internal + Get sizeHint width for single Index (providing existing hint and style option) +*/ +int QTableViewPrivate::widthHintForIndex(const QModelIndex &index, int hint, const QStyleOptionViewItem &option) const +{ + Q_Q(const QTableView); + QWidget *editor = editorForIndex(index).widget.data(); + if (editor && persistent.contains(editor)) { + hint = qMax(hint, editor->sizeHint().width()); + int min = editor->minimumSize().width(); + int max = editor->maximumSize().width(); + hint = qBound(min, hint, max); + } + hint = qMax(hint, q->itemDelegate(index)->sizeHint(option, index).width()); + return hint; +} + +/*! + \internal + Get sizeHint height for single Index (providing existing hint and style option) +*/ +int QTableViewPrivate::heightHintForIndex(const QModelIndex &index, int hint, QStyleOptionViewItem &option) const +{ + Q_Q(const QTableView); + QWidget *editor = editorForIndex(index).widget.data(); + if (editor && persistent.contains(editor)) { + hint = qMax(hint, editor->sizeHint().height()); + int min = editor->minimumSize().height(); + int max = editor->maximumSize().height(); + hint = qBound(min, hint, max); + } + + if (wrapItemText) {// for wrapping boundaries + option.rect.setY(q->rowViewportPosition(index.row())); + int height = q->rowHeight(index.row()); + // if the option.height == 0 then q->itemDelegate(index)->sizeHint(option, index) will be wrong. + // The option.height == 0 is used to conclude that the text is not wrapped, and hence it will + // (exactly like widthHintForIndex) return a QSize with a long width (that we don't use) - + // and the height of the text if it was/is on one line. + // What we want is a height hint for the current width (and we know that this section is not hidden) + // Therefore we catch this special situation with: + if (height == 0) + height = 1; + option.rect.setHeight(height); + option.rect.setX(q->columnViewportPosition(index.column())); + option.rect.setWidth(q->columnWidth(index.column())); + } + hint = qMax(hint, q->itemDelegate(index)->sizeHint(option, index).height()); + return hint; +} + + +/*! \class QTableView \brief The QTableView class provides a default model/view @@ -1056,6 +1109,19 @@ QTableView::~QTableView() /*! \reimp */ +QSize QTableView::viewportSizeHint() const +{ + Q_D(const QTableView); + QSize result( (d->verticalHeader->isHidden() ? 0 : d->verticalHeader->width()) + d->horizontalHeader->length(), + (d->horizontalHeader->isHidden() ? 0 : d->horizontalHeader->height()) + d->verticalHeader->length()); + result += QSize(verticalScrollBar()->isVisible() ? verticalScrollBar()->width() : 0, + horizontalScrollBar()->isVisible() ? horizontalScrollBar()->height() : 0); + return result; +} + +/*! + \reimp +*/ void QTableView::setModel(QAbstractItemModel *model) { Q_D(QTableView); @@ -1113,15 +1179,7 @@ void QTableView::doItemsLayout() { Q_D(QTableView); QAbstractItemView::doItemsLayout(); - if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { - const int max = verticalScrollBar()->maximum(); - if (max > 0 && verticalScrollBar()->value() == max) - d->verticalHeader->setOffsetToLastSection(); - else - d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value()); - } else { - d->verticalHeader->setOffset(verticalScrollBar()->value()); - } + d->verticalHeader->d_func()->setScrollOffset(verticalScrollBar(), verticalScrollMode()); if (!d->verticalHeader->updatesEnabled()) d->verticalHeader->setUpdatesEnabled(true); } @@ -1258,29 +1316,19 @@ void QTableView::scrollContentsBy(int dx, int dy) dx = isRightToLeft() ? -dx : dx; if (dx) { + int oldOffset = d->horizontalHeader->offset(); + d->horizontalHeader->d_func()->setScrollOffset(horizontalScrollBar(), horizontalScrollMode()); if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) { - int oldOffset = d->horizontalHeader->offset(); - if (horizontalScrollBar()->value() == horizontalScrollBar()->maximum()) - d->horizontalHeader->setOffsetToLastSection(); - else - d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value()); int newOffset = d->horizontalHeader->offset(); dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset; - } else { - d->horizontalHeader->setOffset(horizontalScrollBar()->value()); } } if (dy) { + int oldOffset = d->verticalHeader->offset(); + d->verticalHeader->d_func()->setScrollOffset(verticalScrollBar(), verticalScrollMode()); if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { - int oldOffset = d->verticalHeader->offset(); - if (verticalScrollBar()->value() == verticalScrollBar()->maximum()) - d->verticalHeader->setOffsetToLastSection(); - else - d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value()); int newOffset = d->verticalHeader->offset(); dy = oldOffset - newOffset; - } else { - d->verticalHeader->setOffset(verticalScrollBar()->value()); } } d->scrollContentsBy(dx, dy); @@ -1760,13 +1808,13 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi visualRow = bottom; break; case MovePageUp: { - int newRow = rowAt(visualRect(current).top() - d->viewport->height()); + int newRow = rowAt(visualRect(current).bottom() - d->viewport->height()); if (newRow == -1) newRow = d->logicalRow(0); return d->model->index(newRow, current.column(), d->root); } case MovePageDown: { - int newRow = rowAt(visualRect(current).bottom() + d->viewport->height()); + int newRow = rowAt(visualRect(current).top() + d->viewport->height()); if (newRow == -1) newRow = d->logicalRow(bottom); return d->model->index(newRow, current.column(), d->root); @@ -2128,7 +2176,7 @@ void QTableView::updateGeometries() } else { // ScrollPerPixel horizontalScrollBar()->setPageStep(vsize.width()); horizontalScrollBar()->setRange(0, horizontalLength - vsize.width()); - horizontalScrollBar()->setSingleStep(qMax(vsize.width() / (columnsInViewport + 1), 2)); + // here we do not want to overwrite (a possible user set) single step } // vertical scroll bar @@ -2156,7 +2204,7 @@ void QTableView::updateGeometries() } else { // ScrollPerPixel verticalScrollBar()->setPageStep(vsize.height()); verticalScrollBar()->setRange(0, verticalLength - vsize.height()); - verticalScrollBar()->setSingleStep(qMax(vsize.height() / (rowsInViewport + 1), 2)); + // here we do not want to overwrite (a possible user set) single step } d->geometryRecursionBlock = false; @@ -2175,7 +2223,7 @@ void QTableView::updateGeometries() if a larger row height is required by either the vertical header or the item delegate, that width will be used instead. - \sa QWidget::sizeHint, verticalHeader() + \sa QWidget::sizeHint, verticalHeader(), QHeaderView::resizeContentsPrecision() */ int QTableView::sizeHintForRow(int row) const { @@ -2185,6 +2233,8 @@ int QTableView::sizeHintForRow(int row) const return -1; ensurePolished(); + const int maximumProcessCols = d->verticalHeader->resizeContentsPrecision(); + int left = qMax(0, d->horizontalHeader->visualIndexAt(0)); int right = d->horizontalHeader->visualIndexAt(d->viewport->width()); @@ -2195,27 +2245,55 @@ int QTableView::sizeHintForRow(int row) const int hint = 0; QModelIndex index; - for (int column = left; column <= right; ++column) { + int columnsProcessed = 0; + int column = left; + for (; column <= right; ++column) { int logicalColumn = d->horizontalHeader->logicalIndex(column); if (d->horizontalHeader->isSectionHidden(logicalColumn)) continue; index = d->model->index(row, logicalColumn, d->root); - if (d->wrapItemText) {// for wrapping boundaries - option.rect.setY(rowViewportPosition(index.row())); - option.rect.setHeight(rowHeight(index.row())); - option.rect.setX(columnViewportPosition(index.column())); - option.rect.setWidth(columnWidth(index.column())); - } + hint = d->heightHintForIndex(index, hint, option); + + ++columnsProcessed; + if (columnsProcessed == maximumProcessCols) + break; + } - QWidget *editor = d->editorForIndex(index).widget.data(); - if (editor && d->persistent.contains(editor)) { - hint = qMax(hint, editor->sizeHint().height()); - int min = editor->minimumSize().height(); - int max = editor->maximumSize().height(); - hint = qBound(min, hint, max); + int actualRight = d->model->columnCount(d->root) - 1; + int idxLeft = left; + int idxRight = column - 1; + + if (maximumProcessCols == 0) + columnsProcessed = 0; // skip the while loop + + while (columnsProcessed != maximumProcessCols && (idxLeft > 0 || idxRight < actualRight)) { + int logicalIdx = -1; + + if ((columnsProcessed % 2 && idxLeft > 0) || idxRight == actualRight) { + while (idxLeft > 0) { + --idxLeft; + int logcol = d->horizontalHeader->logicalIndex(idxLeft); + if (d->horizontalHeader->isSectionHidden(logcol)) + continue; + logicalIdx = logcol; + break; + } + } else { + while (idxRight < actualRight) { + ++idxRight; + int logcol = d->horizontalHeader->logicalIndex(idxRight); + if (d->horizontalHeader->isSectionHidden(logcol)) + continue; + logicalIdx = logcol; + break; + } } + if (logicalIdx < 0) + continue; - hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).height()); + index = d->model->index(row, logicalIdx, d->root); + hint = d->heightHintForIndex(index, hint, option); + ++columnsProcessed; } return d->showGrid ? hint + 1 : hint; @@ -2234,7 +2312,7 @@ int QTableView::sizeHintForRow(int row) const required by either the horizontal header or the item delegate, the larger width will be used instead. - \sa QWidget::sizeHint, horizontalHeader() + \sa QWidget::sizeHint, horizontalHeader(), QHeaderView::resizeContentsPrecision() */ int QTableView::sizeHintForColumn(int column) const { @@ -2244,6 +2322,7 @@ int QTableView::sizeHintForColumn(int column) const return -1; ensurePolished(); + const int maximumProcessRows = d->horizontalHeader->resizeContentsPrecision(); int top = qMax(0, d->verticalHeader->visualIndexAt(0)); int bottom = d->verticalHeader->visualIndexAt(d->viewport->height()); @@ -2253,22 +2332,56 @@ int QTableView::sizeHintForColumn(int column) const QStyleOptionViewItem option = d->viewOptions(); int hint = 0; + int rowsProcessed = 0; QModelIndex index; - for (int row = top; row <= bottom; ++row) { + int row = top; + for (; row <= bottom; ++row) { int logicalRow = d->verticalHeader->logicalIndex(row); if (d->verticalHeader->isSectionHidden(logicalRow)) continue; index = d->model->index(logicalRow, column, d->root); - QWidget *editor = d->editorForIndex(index).widget.data(); - if (editor && d->persistent.contains(editor)) { - hint = qMax(hint, editor->sizeHint().width()); - int min = editor->minimumSize().width(); - int max = editor->maximumSize().width(); - hint = qBound(min, hint, max); + hint = d->widthHintForIndex(index, hint, option); + ++rowsProcessed; + if (rowsProcessed == maximumProcessRows) + break; + } + + int actualBottom = d->model->rowCount(d->root) - 1; + int idxTop = top; + int idxBottom = row - 1; + + if (maximumProcessRows == 0) + rowsProcessed = 0; // skip the while loop + + while (rowsProcessed != maximumProcessRows && (idxTop > 0 || idxBottom < actualBottom)) { + int logicalIdx = -1; + + if ((rowsProcessed % 2 && idxTop > 0) || idxBottom == actualBottom) { + while (idxTop > 0) { + --idxTop; + int logrow = d->verticalHeader->logicalIndex(idxTop); + if (d->verticalHeader->isSectionHidden(logrow)) + continue; + logicalIdx = logrow; + break; + } + } else { + while (idxBottom < actualBottom) { + ++idxBottom; + int logrow = d->verticalHeader->logicalIndex(idxBottom); + if (d->verticalHeader->isSectionHidden(logrow)) + continue; + logicalIdx = logrow; + break; + } } + if (logicalIdx < 0) + continue; - hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).width()); + index = d->model->index(logicalIdx, column, d->root); + hint = d->widthHintForIndex(index, hint, option); + ++rowsProcessed; } return d->showGrid ? hint + 1 : hint; @@ -2934,6 +3047,8 @@ void QTableView::showColumn(int column) /*! Resizes the given \a row based on the size hints of the delegate used to render each item in the row. + + \sa resizeRowsToContents(), sizeHintForRow(), QHeaderView::resizeContentsPrecision() */ void QTableView::resizeRowToContents(int row) { @@ -2946,6 +3061,8 @@ void QTableView::resizeRowToContents(int row) /*! Resizes all rows based on the size hints of the delegate used to render each item in the rows. + + \sa resizeRowToContents(), sizeHintForRow(), QHeaderView::resizeContentsPrecision() */ void QTableView::resizeRowsToContents() { @@ -2959,6 +3076,8 @@ void QTableView::resizeRowsToContents() \note Only visible columns will be resized. Reimplement sizeHintForColumn() to resize hidden columns as well. + + \sa resizeColumnsToContents(), sizeHintForColumn(), QHeaderView::resizeContentsPrecision() */ void QTableView::resizeColumnToContents(int column) { @@ -2971,6 +3090,8 @@ void QTableView::resizeColumnToContents(int column) /*! Resizes all columns based on the size hints of the delegate used to render each item in the columns. + + \sa resizeColumnToContents(), sizeHintForColumn(), QHeaderView::resizeContentsPrecision() */ void QTableView::resizeColumnsToContents() { diff --git a/src/widgets/itemviews/qtableview.h b/src/widgets/itemviews/qtableview.h index 824348dbe8..db956480d6 100644 --- a/src/widgets/itemviews/qtableview.h +++ b/src/widgets/itemviews/qtableview.h @@ -118,6 +118,8 @@ public: void sortByColumn(int column, Qt::SortOrder order); + QSize viewportSizeHint() const; + public Q_SLOTS: void selectRow(int row); void selectColumn(int column); diff --git a/src/widgets/itemviews/qtableview_p.h b/src/widgets/itemviews/qtableview_p.h index 923beec253..d36dcd2c3a 100644 --- a/src/widgets/itemviews/qtableview_p.h +++ b/src/widgets/itemviews/qtableview_p.h @@ -181,6 +181,8 @@ public: const QStyleOptionViewItem &option, QBitArray *drawn, int firstVisualRow, int lastVisualRow, int firstVisualColumn, int lastVisualColumn); void drawCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index); + int widthHintForIndex(const QModelIndex &index, int hint, const QStyleOptionViewItem &option) const; + int heightHintForIndex(const QModelIndex &index, int hint, QStyleOptionViewItem &option) const; bool showGrid; Qt::PenStyle gridStyle; diff --git a/src/widgets/itemviews/qtablewidget.cpp b/src/widgets/itemviews/qtablewidget.cpp index d56314b23d..f15124605f 100644 --- a/src/widgets/itemviews/qtablewidget.cpp +++ b/src/widgets/itemviews/qtablewidget.cpp @@ -510,7 +510,7 @@ void QTableModel::sort(int column, Qt::SortOrder order) } LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan); - qStableSort(sortable.begin(), sortable.end(), compare); + std::stable_sort(sortable.begin(), sortable.end(), compare); QVector<QTableWidgetItem*> sorted_table(tableItems.count()); QModelIndexList from; @@ -558,7 +558,7 @@ void QTableModel::ensureSorted(int column, Qt::SortOrder order, } LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan); - qStableSort(sorting.begin(), sorting.end(), compare); + std::stable_sort(sorting.begin(), sorting.end(), compare); QModelIndexList oldPersistentIndexes = persistentIndexList(); QModelIndexList newPersistentIndexes = oldPersistentIndexes; diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp index 6aefbb5367..b5f1b12365 100644 --- a/src/widgets/itemviews/qtreeview.cpp +++ b/src/widgets/itemviews/qtreeview.cpp @@ -61,6 +61,8 @@ #include <private/qtreeview_p.h> #include <private/qheaderview_p.h> +#include <algorithm> + QT_BEGIN_NAMESPACE /*! @@ -335,6 +337,7 @@ void QTreeView::setHeader(QHeaderView *header) this, SLOT(updateGeometries())); setSortingEnabled(d->sortingEnabled); + d->updateGeometry(); } /*! @@ -952,6 +955,36 @@ bool QTreeView::wordWrap() const return d->wrapItemText; } +/*! + \since 5.2 + + This specifies that the tree structure should be placed at logical index \a index. + If \index is set to -1 then the tree will always follow visual index 0. + + \sa treePosition(), QHeaderView::swapSections(), QHeaderView::moveSection() +*/ + +void QTreeView::setTreePosition(int index) +{ + Q_D(QTreeView); + d->treePosition = index; + update(); +} + +/*! + \since 5.2 + + Return the logical index the tree is set on. If the return value is -1 then the + tree is placed on the visual index 0. + + \sa setTreePosition() +*/ + +int QTreeView::treePosition() const +{ + Q_D(const QTreeView); + return d->treePosition; +} /*! \reimp @@ -1067,7 +1100,7 @@ QRect QTreeView::visualRect(const QModelIndex &index) const int x = (spanning ? 0 : columnViewportPosition(index.column())); int w = (spanning ? d->header->length() : columnWidth(index.column())); // handle indentation - if (index.column() == 0) { + if (d->isTreePosition(index.column())) { int i = d->indentationForItem(vi); w -= i; if (!isRightToLeft()) @@ -1284,6 +1317,14 @@ void QTreeView::paintEvent(QPaintEvent *event) } } +int QTreeViewPrivate::logicalIndexForTree() const +{ + int index = treePosition; + if (index < 0) + index = header->logicalIndex(0); + return index; +} + void QTreeViewPrivate::paintAlternatingRowColors(QPainter *painter, QStyleOptionViewItem *option, int y, int bottom) const { Q_Q(const QTreeView); @@ -1516,7 +1557,7 @@ void QTreeViewPrivate::calcLogicalIndices(QVector<int> *logicalIndices, QVector< if (columnCount == 1 || (nextLogicalSection == 0 && prevLogicalSection == -1) || (headerSection == 0 && nextLogicalSection == -1) || spanning) pos = QStyleOptionViewItem::OnlyOne; - else if (headerSection == 0 || (nextLogicalSection != 0 && prevLogicalSection == -1)) + else if (isTreePosition(headerSection) || (nextLogicalSection != 0 && prevLogicalSection == -1)) pos = QStyleOptionViewItem::Beginning; else if (nextLogicalSection == 0 || nextLogicalSection == -1) pos = QStyleOptionViewItem::End; @@ -1526,6 +1567,23 @@ void QTreeViewPrivate::calcLogicalIndices(QVector<int> *logicalIndices, QVector< } } +/*! + \internal + Get sizeHint width for single index (providing existing hint and style option) and index in viewIndex i. +*/ +int QTreeViewPrivate::widthHintForIndex(const QModelIndex &index, int hint, const QStyleOptionViewItem &option, int i) const +{ + QWidget *editor = editorForIndex(index).widget.data(); + if (editor && persistent.contains(editor)) { + hint = qMax(hint, editor->sizeHint().width()); + int min = editor->minimumSize().width(); + int max = editor->maximumSize().width(); + hint = qBound(min, hint, max); + } + int xhint = delegateForIndex(index)->sizeHint(option, index).width(); + hint = qMax(hint, xhint + (isTreePosition(index.column()) ? indentationForItem(i) : 0)); + return hint; +} /*! Draws the row in the tree view that contains the model item \a index, @@ -1668,7 +1726,7 @@ void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, alternate row color was provided by the view. For backward compatibility, this is now delegated to the style using PE_PanelViewItemRow which does the appropriate fill */ - if (headerSection == 0) { + if (d->isTreePosition(headerSection)) { const int i = d->indentationForItem(d->current); QRect branches(reverse ? position + width - i : position, y, i, height); const bool setClipRect = branches.width() > width; @@ -2123,10 +2181,10 @@ QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie if (!current.isValid()) { int i = d->below(-1); int c = 0; - while (c < d->header->count() && d->header->isSectionHidden(c)) + while (c < d->header->count() && d->header->isSectionHidden(d->header->logicalIndex(c))) ++c; if (i < d->viewItems.count() && c < d->header->count()) { - return d->modelIndex(i, c); + return d->modelIndex(i, d->header->logicalIndex(c)); } return QModelIndex(); } @@ -2400,16 +2458,11 @@ void QTreeView::scrollContentsBy(int dx, int dy) dx = isRightToLeft() ? -dx : dx; if (dx) { + int oldOffset = d->header->offset(); + d->header->d_func()->setScrollOffset(horizontalScrollBar(), horizontalScrollMode()); if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) { - int oldOffset = d->header->offset(); - if (horizontalScrollBar()->value() == horizontalScrollBar()->maximum()) - d->header->setOffsetToLastSection(); - else - d->header->setOffsetToSectionPosition(horizontalScrollBar()->value()); int newOffset = d->header->offset(); dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset; - } else { - d->header->setOffset(horizontalScrollBar()->value()); } } @@ -2552,7 +2605,7 @@ void QTreeView::columnCountChanged(int oldCount, int newCount) /*! Resizes the \a column given to the size of its contents. - \sa columnWidth(), setColumnWidth() + \sa columnWidth(), setColumnWidth(), sizeHintForColumn(), QHeaderView::resizeContentsPrecision() */ void QTreeView::resizeColumnToContents(int column) { @@ -2619,6 +2672,35 @@ void QTreeView::selectAll() } /*! + \reimp +*/ +QSize QTreeView::viewportSizeHint() const +{ + Q_D(const QTreeView); + d->executePostedLayout(); // Make sure that viewItems are up to date. + + if (d->viewItems.size() == 0) + return QAbstractItemView::viewportSizeHint(); + + // Get rect for last item + const QRect deepestRect = visualRect(d->viewItems.last().index); + + if (!deepestRect.isValid()) + return QAbstractItemView::viewportSizeHint(); + + QSize result = QSize(d->header->length(), deepestRect.bottom() + 1); + + // add size for header + result += QSize(0, d->header->isVisible() ? d->header->height() : 0); + + // add size for scrollbars + result += QSize(verticalScrollBar()->isVisible() ? verticalScrollBar()->width() : 0, + horizontalScrollBar()->isVisible() ? horizontalScrollBar()->height() : 0); + + return result; +} + +/*! \since 4.2 Expands all expandable items. @@ -2740,7 +2822,6 @@ void QTreeView::updateGeometries() QRect vg = d->viewport->geometry(); QRect geometryRect(vg.left(), vg.top() - hint.height(), vg.width(), hint.height()); d->header->setGeometry(geometryRect); - //d->header->setOffset(horizontalScrollBar()->value()); // ### bug ??? QMetaObject::invokeMethod(d->header, "updateGeometries"); d->updateScrollBars(); d->geometryRecursionBlock = false; @@ -2760,7 +2841,7 @@ void QTreeView::updateGeometries() if a larger column width is required by either the view's header or the item delegate, that width will be used instead. - \sa QWidget::sizeHint, header() + \sa QWidget::sizeHint, header(), QHeaderView::resizeContentsPrecision() */ int QTreeView::sizeHintForColumn(int column) const { @@ -2773,28 +2854,58 @@ int QTreeView::sizeHintForColumn(int column) const QStyleOptionViewItem option = d->viewOptions(); const QVector<QTreeViewItem> viewItems = d->viewItems; - int start = 0; - int end = viewItems.count(); - if(end > 1000) { //if we have too many item this function would be too slow. - //we get a good approximation by only iterate over 1000 items. - start = qMax(0, d->firstVisibleItem() - 100); - end = qMin(end, start + 900); - } + const int maximumProcessRows = d->header->resizeContentsPrecision(); // To avoid this to take forever. + + int offset = 0; + int start = d->firstVisibleItem(&offset); + int end = d->lastVisibleItem(start, offset); - for (int i = start; i < end; ++i) { + int rowsProcessed = 0; + + for (int i = start; i <= end; ++i) { if (viewItems.at(i).spanning) continue; // we have no good size hint QModelIndex index = viewItems.at(i).index; index = index.sibling(index.row(), column); - QWidget *editor = d->editorForIndex(index).widget.data(); - if (editor && d->persistent.contains(editor)) { - w = qMax(w, editor->sizeHint().width()); - int min = editor->minimumSize().width(); - int max = editor->maximumSize().width(); - w = qBound(min, w, max); + w = d->widthHintForIndex(index, w, option, i); + ++rowsProcessed; + if (rowsProcessed == maximumProcessRows) + break; + } + + --end; + int actualBottom = viewItems.size() - 1; + + if (maximumProcessRows == 0) + rowsProcessed = 0; // skip the while loop + + while (rowsProcessed != maximumProcessRows && (start > 0 || end < actualBottom)) { + int idx = -1; + + if ((rowsProcessed % 2 && start > 0) || end == actualBottom) { + while (start > 0) { + --start; + if (viewItems.at(start).spanning) + continue; + idx = start; + break; + } + } else { + while (end < actualBottom) { + ++end; + if (viewItems.at(end).spanning) + continue; + idx = end; + break; + } } - int hint = d->delegateForIndex(index)->sizeHint(option, index).width(); - w = qMax(w, hint + (column == 0 ? d->indentationForItem(i) : 0)); + if (idx < 0) + continue; + + QModelIndex index = viewItems.at(idx).index; + index = index.sibling(index.row(), column); + w = d->widthHintForIndex(index, w, option, idx); + ++rowsProcessed; } return w; } @@ -2915,6 +3026,7 @@ void QTreeViewPrivate::initialize() header->setDefaultAlignment(Qt::AlignLeft|Qt::AlignVCenter); q->setHeader(header); #ifndef QT_NO_ANIMATION + animationsEnabled = q->style()->styleHint(QStyle::SH_Widget_Animate, 0, q); QObject::connect(&animatedOperation, SIGNAL(finished()), q, SLOT(_q_endAnimatedOperation())); #endif //QT_NO_ANIMATION } @@ -3314,7 +3426,7 @@ int QTreeViewPrivate::coordinateForItem(int item) const if (verticalScrollMode == QAbstractItemView::ScrollPerPixel) { if (uniformRowHeights) return (item * defaultItemHeight) - vbar->value(); - // ### optimize (spans or caching) + // ### optimize (maybe do like QHeaderView by letting items have startposition) int y = 0; for (int i = 0; i < viewItems.count(); ++i) { if (i == item) @@ -3481,7 +3593,7 @@ int QTreeViewPrivate::firstVisibleItem(int *offset) const *offset = -(value % defaultItemHeight); return value / defaultItemHeight; } - int y = 0; // ### optimize (use spans ?) + int y = 0; // ### (maybe do like QHeaderView by letting items have startposition) for (int i = 0; i < viewItems.count(); ++i) { y += itemHeight(i); // the height value is cached if (y > value) { @@ -3493,6 +3605,21 @@ int QTreeViewPrivate::firstVisibleItem(int *offset) const return -1; } +int QTreeViewPrivate::lastVisibleItem(int firstVisual, int offset) const +{ + if (firstVisual < 0 || offset < 0) + firstVisual = firstVisibleItem(&offset); + int y = - offset; + int value = viewport->height(); + + for (int i = firstVisual; i < viewItems.count(); ++i) { + y += itemHeight(i); // the height value is cached + if (y > value) + return i; + } + return viewItems.size() - 1; +} + int QTreeViewPrivate::columnAt(int x) const { return header->logicalIndexAt(x); @@ -3535,13 +3662,13 @@ void QTreeViewPrivate::updateScrollBars() int contentsHeight = 0; if (uniformRowHeights) { contentsHeight = defaultItemHeight * viewItems.count(); - } else { // ### optimize (spans or caching) + } else { // ### (maybe do like QHeaderView by letting items have startposition) for (int i = 0; i < viewItems.count(); ++i) contentsHeight += itemHeight(i); } vbar->setRange(0, contentsHeight - viewportSize.height()); vbar->setPageStep(viewportSize.height()); - vbar->setSingleStep(qMax(viewportSize.height() / (itemsInViewport + 1), 2)); + // here we do not want to overwrite (a possible user set) single step } const int columnCount = header->count(); @@ -3567,7 +3694,7 @@ void QTreeViewPrivate::updateScrollBars() viewportSize = maxSize; hbar->setPageStep(viewportSize.width()); hbar->setRange(0, qMax(horizontalLength - viewportSize.width(), 0)); - hbar->setSingleStep(qMax(viewportSize.width() / (columnsInViewport + 1), 2)); + // here we do not want to overwrite (a possible user set) single step } } @@ -3576,7 +3703,7 @@ int QTreeViewPrivate::itemDecorationAt(const QPoint &pos) const executePostedLayout(); int x = pos.x(); int column = header->logicalIndexAt(x); - if (column != 0) + if (!isTreePosition(column)) return -1; // no logical index at x int viewItemIndex = itemAtCoordinate(pos.y()); @@ -3598,8 +3725,8 @@ QRect QTreeViewPrivate::itemDecorationRect(const QModelIndex &index) const return QRect(); int itemIndentation = indentationForItem(viewItemIndex); - int position = header->sectionViewportPosition(0); - int size = header->sectionSize(0); + int position = header->sectionViewportPosition(logicalIndexForTree()); + int size = header->sectionSize(logicalIndexForTree()); QRect rect; if (q->isRightToLeft()) @@ -3633,7 +3760,7 @@ QList<QPair<int, int> > QTreeViewPrivate::columnRanges(const QModelIndex &topInd } } //let's sort the list - qSort(logicalIndexes.begin(), logicalIndexes.end()); + std::sort(logicalIndexes.begin(), logicalIndexes.end()); QList<QPair<int, int> > ret; QPair<int, int> current; diff --git a/src/widgets/itemviews/qtreeview.h b/src/widgets/itemviews/qtreeview.h index 73f11f1a48..d9c6cd9269 100644 --- a/src/widgets/itemviews/qtreeview.h +++ b/src/widgets/itemviews/qtreeview.h @@ -128,6 +128,9 @@ public: void setWordWrap(bool on); bool wordWrap() const; + void setTreePosition(int logicalIndex); + int treePosition() const; + void keyboardSearch(const QString &search); QRect visualRect(const QModelIndex &index) const; @@ -144,6 +147,8 @@ public: void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = QVector<int>()); void selectAll(); + QSize viewportSizeHint() const; + Q_SIGNALS: void expanded(const QModelIndex &index); void collapsed(const QModelIndex &index); diff --git a/src/widgets/itemviews/qtreeview_p.h b/src/widgets/itemviews/qtreeview_p.h index 90e83cb58c..89de435606 100644 --- a/src/widgets/itemviews/qtreeview_p.h +++ b/src/widgets/itemviews/qtreeview_p.h @@ -91,10 +91,16 @@ public: expandsOnDoubleClick(true), allColumnsShowFocus(false), current(0), spanning(false), animationsEnabled(false), columnResizeTimerID(0), - autoExpandDelay(-1), hoverBranch(-1), geometryRecursionBlock(false), hasRemovedItems(false) {} + autoExpandDelay(-1), hoverBranch(-1), geometryRecursionBlock(false), hasRemovedItems(false), + treePosition(0) {} ~QTreeViewPrivate() {} void initialize(); + int logicalIndexForTree() const; + inline bool isTreePosition(int logicalIndex) const + { + return logicalIndex == logicalIndexForTree(); + } QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const; void adjustViewOptionsForIndex(QStyleOptionViewItem *option, const QModelIndex ¤t) const; @@ -148,6 +154,7 @@ public: #endif int firstVisibleItem(int *offset = 0) const; + int lastVisibleItem(int firstVisual = -1, int offset = -1) const; int columnAt(int x) const; bool hasVisibleChildren( const QModelIndex& parent) const; @@ -171,7 +178,7 @@ public: // logicalIndices: vector of currently visibly logical indices // itemPositions: vector of view item positions (beginning/middle/end/onlyone) void calcLogicalIndices(QVector<int> *logicalIndices, QVector<QStyleOptionViewItem::ViewItemPosition> *itemPositions, int left, int right) const; - + int widthHintForIndex(const QModelIndex &index, int hint, const QStyleOptionViewItem &option, int i) const; QHeaderView *header; int indent; @@ -251,6 +258,9 @@ public: // If we should clean the set bool hasRemovedItems; + + // tree position + int treePosition; }; QT_END_NAMESPACE diff --git a/src/widgets/itemviews/qtreewidget.cpp b/src/widgets/itemviews/qtreewidget.cpp index f72abd20ab..f098236d5c 100644 --- a/src/widgets/itemviews/qtreewidget.cpp +++ b/src/widgets/itemviews/qtreewidget.cpp @@ -614,7 +614,7 @@ void QTreeModel::ensureSorted(int column, Qt::SortOrder order, } LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan); - qStableSort(sorting.begin(), sorting.end(), compare); + std::stable_sort(sorting.begin(), sorting.end(), compare); QModelIndexList oldPersistentIndexes; QModelIndexList newPersistentIndexes; @@ -623,7 +623,13 @@ void QTreeModel::ensureSorted(int column, Qt::SortOrder order, for (int i = 0; i < count; ++i) { int oldRow = sorting.at(i).second; + + int tmpitepos = lit - lst.begin(); QTreeWidgetItem *item = lst.takeAt(oldRow); + if (tmpitepos > lst.size()) + --tmpitepos; + lit = lst.begin() + tmpitepos; + lit = sortedInsertionIterator(lit, lst.end(), order, item); int newRow = qMax(lit - lst.begin(), 0); @@ -848,7 +854,7 @@ void QTreeModel::sortItems(QList<QTreeWidgetItem*> *items, int column, Qt::SortO // do the sorting LessThan compare = (order == Qt::AscendingOrder ? &itemLessThan : &itemGreaterThan); - qStableSort(sorting.begin(), sorting.end(), compare); + std::stable_sort(sorting.begin(), sorting.end(), compare); QModelIndexList fromList; QModelIndexList toList; |