KDAB contributions to Qt 5.0 (part 3)
As KDAB took on the role of Itemviews Maintainer in Qt 5, naturally we were the primary contributor and reviewer of all itemmodels and itemviews patches.
Several relevant changes have been made in Qt 5. To start with code location, some classes have been moved around to different Qt modules, making them more useful to non-widget applications. In Qt 4 times, the QAbstractProxyModel, QSortFilterProxyModel, QIdentityProxyModel, QStringListModel and QItemSelectionModel were in QtGui. Now they have been moved to QtCore. Similarly, the QStandardItemModel remains in the QtGui module in Qt 5, but most other itemviews related code, including the QDirModel and QFileSystemModel, was moved to the QtWidgets module. The QProxyModel class, obsolete since Qt 4.1 or so, was entirely removed, and a copy was placed in the UI Helpers repository for those who want to use it.
Some APIs were also marked as virtual in Qt 5. For example, QAbstractItemModel::supportedDragActions was made virtual, and setSupportedDragOptions was deprecated. The idea for this change came in 2009 when I first read the Advanced Qt book, which recommends overriding the supportedDragActions method, despite it not being virtual. The snippet happens to work because supportedDropActions (which is virtual) is actually overridden, and because of how the two concepts interact with each other. In Qt 5, the code has been changed to match the expectations of the book :). The QAbstractItemModel::roleNames method has also been made virtual, for consistency with the rest of the QAbstractItemModel access API.
The QAbstractItemModel::sibling method was made virtual too, but for separate reasoning. The default implementation of sibling() is to get the parent QModelIndex, and then get a particular child of that parent. Calculating QModelIndexes, particularly for the parent() and particularly if there are proxy models involved, can be expensive. A concrete implementation of a model typically has a way to get a sibling index more efficiently, and now that it is virtual, that can be used. The sibling method is now also reimplemented in QSortFilterProxyModel and QIdentityProxyModel to take advantage of this.
Also on the subject of optimizations in itemmodels, the dataChanged and layoutChange signals have gained new parameters to be more expressive about how a model or its data changes when it does. Specifically, the dataChanged signal now has an optional QVector<int> to specify which roles have actually changed. This means for example that QML can be more efficient and effective in what bindings it updates when a model changes data.
The layout change signals were also extended with optional hints to allow the developer to be more specific about what, exactly, is changing. It is now possible to specify a list of parent QModelIndexes whose children are being relayouted. This is useful if you have a deep tree model, but only sort one section of it. It is also now possible in Qt 5 to specify what kind of change is happening. For example, the data may be sorted, or rows may be moved around to different locations in the tree. Together, these new APIs can allow for speed improvements of over 98% in cases of very wide table models and other large structures.
One of the confusing parts of the Qt 4 API was that the QModelIndex::internalId returned a qint64, but QAbstractItemModel::createIndex overloads only accept a quint32 (Note difference in size and in signedness). One of the reasons that is hard to solve is that making the internalId has always been stored as a void*. trying to store a quin64 in that would result in loss of data on a 32 bit system. Changing the data storage to quint64 would be inefficient on 32 bit systems. To solve that, all of the API has been normalized to use quintptr, meaning that it is a int type the same size as the size of a void pointer. This also had the effect of making the QModelIndex API more compatible with the C++11 constexpr keyword.
QModelIndex is now also a built-in metatype in Qt 5, so using Q_DECLARE_METATYPE(QModelIndex), or qRegisterMetaType<QModelIndex>() is never needed anymore.
The default value of whether a QSortFilterProxyModel sorts dynamically (dynamicSortFilter) has been changed from false to true. In Qt 4 times, this is a constant source of bugs when people (myself included) forget to set it to true, even though that is what most people want.
We have made many other bug fixes, performance enhancements and other changes to the itemmodels APIs. The number of unevaluated itemviews bugs is ever decreasing, and new bugs are quickly evaluated.
More effective C++
Qt 5.0.0 has been an opportunity to make changes which were not possible in Qt 4 releases, mostly due to binary compatibility concerns. These changes included marking methods as const where they can and should be, and marking constructors as explicit where possible. In terms of C++98, the explicit keyword is usually only needed for types which can be created with only one argument. For example, imagine there was no explicit keyword used in the QObject constructor, and consider this code:
QObject someObject = new QObject;
Do you see the bug? A QObject pointer is assigned to a local non-pointer QObject. If there is no explicit keyword used, then a compiler will ‘helpfully’ try to perform an implicit conversino and call a constructor which takes only one argument if the types match. So the above would be treated the same way as:
QObject *parent = new QObject;
This is usually not what the programmer intended. Use of the explicit keyword helps the compiler find bugs like that, so we’ve added it where it was missing before.
The situation is a little bit more complex with C++11, because now brace-initialization can be used to cause implicit conversion for more than one constructor argument. This could have unintended consequences, but is probably less likely today. Nevertheless, some instances of it have been marked as explicit too.
For exception safety and speed, many copy operations in Qt were changed to use the copy-swap idiom. This also required adding member-swap to more of the classes in Qt 5.
So many things changed in Qt 5 compared to Qt 4, that there was a lot of clean-up to do. This is the kind of work that is not very exciting, but still has to be done in order to maintain a clean codebase. For example, KDAB wrote patches removing some symbian support code (no longer supported in Qt 5), QWS code (made obsolete by QPA plugins), and other long-obsolete or deprecated methods and classes.
We also worked on documentation clean-ups requires as part of modularisation, and as part of keeping the documentation up to date with the code, compile fixes when certain features are removed (eg QT_NO_CLIPBOARD) and replacing some use of Q_WS_* defines (Q_WS_WIN, Q_WS_X11 etc).
The Q_WS_* defines are obsolete and those macros are never defined. In some cases the code within them needs to be changed to operating-system specific (using a Q_OS_* define), and in some cases, it needs to be changed to a runtime check, if it really does relate to the window system only. This work is not complete. There is still a lot of code in Qt 5 which is wrapped in Q_WS_ defines, and is therefore effectively commented out.