aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@qt.io>2018-08-21 00:01:31 +0200
committerOswald Buddenhagen <oswald.buddenhagen@qt.io>2018-08-21 00:01:31 +0200
commite33080767e73c1fe9156a5423ff4a7c3e00e8470 (patch)
tree3bfb5c17674bf475943fb807b9a063728ea65047
parent36ea464b3dcce6319c5252553d48dd5ac6ba8f41 (diff)
parente47bd1fc0e6fc2b2e3ca82c48ccb2f8f7dd5ee92 (diff)
Merge dev into 5.12
-rw-r--r--examples/quick/delegatechooser/delegatechooser.pro8
-rw-r--r--examples/quick/delegatechooser/delegatechooser.qml145
-rw-r--r--examples/quick/delegatechooser/main.cpp51
-rw-r--r--examples/quick/delegatechooser/qml.qrc5
-rw-r--r--examples/quick/quick.pro1
-rw-r--r--qtdeclarative.doxy2466
-rw-r--r--src/3rdparty/masm/wtf/Assertions.h4
-rw-r--r--src/imports/imports.pro3
-rw-r--r--src/imports/labsmodels/labsmodels.pro11
-rw-r--r--src/imports/labsmodels/plugin.cpp83
-rw-r--r--src/imports/labsmodels/qmldir3
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h2
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp23
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp3
-rw-r--r--src/qml/compiler/qv4bytecodehandler_p.h6
-rw-r--r--src/qml/jit/qv4baselinejit.cpp18
-rw-r--r--src/qml/jit/qv4baselinejit_p.h5
-rw-r--r--src/qml/qml/qqml.h32
-rw-r--r--src/qml/qml/qqmlcomponent_p.h2
-rw-r--r--src/qml/qml/qqmlengine.cpp8
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp10
-rw-r--r--src/qml/qml/qqmlvaluetype.cpp60
-rw-r--r--src/qml/types/qqmlconnections_p.h2
-rw-r--r--src/qml/types/qqmldelegatecomponent.cpp285
-rw-r--r--src/qml/types/qqmldelegatecomponent_p.h155
-rw-r--r--src/qml/types/qqmldelegatemodel.cpp74
-rw-r--r--src/qml/types/qqmldelegatemodel_p.h1
-rw-r--r--src/qml/types/qqmldelegatemodel_p_p.h4
-rw-r--r--src/qml/types/qqmlmodelsmodule.cpp10
-rw-r--r--src/qml/types/qqmlmodelsmodule_p.h1
-rw-r--r--src/qml/types/qqmltableinstancemodel.cpp32
-rw-r--r--src/qml/types/qqmltableinstancemodel_p.h6
-rw-r--r--src/qml/types/types.pri6
-rw-r--r--src/qml/util/qqmladaptormodel.cpp5
-rw-r--r--src/qml/util/qqmladaptormodel_p.h3
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp2
-rw-r--r--src/quick/items/qquickflickable.cpp2
-rw-r--r--src/quick/items/qquickitem.cpp15
-rw-r--r--src/quick/items/qquickitem_p.h22
-rw-r--r--src/quick/items/qquicktableview.cpp182
-rw-r--r--src/quick/items/qquicktableview_p_p.h29
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp15
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h9
-rw-r--r--src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp295
-rw-r--r--src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h4
-rw-r--r--src/quick/scenegraph/util/qsgareaallocator.cpp144
-rw-r--r--src/quick/scenegraph/util/qsgareaallocator_p.h4
-rw-r--r--src/quick/util/qquicktransitionmanager.cpp59
-rw-r--r--tests/auto/qmltest/listview/data/MultiDelegate.qml106
-rw-r--r--tests/auto/qmltest/listview/data/MultiDelegate2.qml101
-rw-r--r--tests/auto/qmltest/listview/data/logo.pngbin0 -> 1478 bytes
-rw-r--r--tests/auto/qmltest/listview/listview.pro1
-rw-r--r--tests/auto/qmltest/listview/tst_listview.qml51
-rw-r--r--tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop.qml13
-rw-r--r--tests/auto/quick/qquickitem2/tst_qquickitem.cpp17
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp123
-rw-r--r--tests/manual/tableview/abstracttablemodel/main.cpp48
-rw-r--r--tests/manual/tableview/abstracttablemodel/main.qml17
58 files changed, 4477 insertions, 315 deletions
diff --git a/examples/quick/delegatechooser/delegatechooser.pro b/examples/quick/delegatechooser/delegatechooser.pro
new file mode 100644
index 0000000000..f06864ba89
--- /dev/null
+++ b/examples/quick/delegatechooser/delegatechooser.pro
@@ -0,0 +1,8 @@
+TEMPLATE = app
+QT += quick qml
+
+SOURCES += main.cpp
+RESOURCES += qml.qrc ../shared/shared.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/delegatechooser
+INSTALLS += target
diff --git a/examples/quick/delegatechooser/delegatechooser.qml b/examples/quick/delegatechooser/delegatechooser.qml
new file mode 100644
index 0000000000..afb8518abf
--- /dev/null
+++ b/examples/quick/delegatechooser/delegatechooser.qml
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQml.Models 2.12
+import QtQuick.Layouts 1.12
+import Qt.labs.qmlmodels 1.0
+
+Rectangle {
+ visible: true
+ width: 640
+ height: 640
+
+ ListModel {
+ id: listModel
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image"; source: "../shared/images/qt-logo.png" }
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image"; source: "../shared/images/qt-logo.png" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ }
+
+ ListModel {
+ id: listModel2
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image"; source: "../shared/images/qt-logo.png" }
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image"; source: "../shared/images/qt-logo.png" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "rect"; color: "lightsteelblue" }
+ ListElement { dataType: "rect"; color: "fuchsia" }
+ ListElement { dataType: "rect"; color: "lime" }
+ }
+
+ DelegateChooser {
+ id: fancyDelegate
+ role: "dataType"
+ DelegateChoice {
+ roleValue: "rect"
+ delegate: DelegateChooser {
+ DelegateChoice {
+ row: 0
+ Rectangle {
+ width: parent.width
+ height: 50
+ color: "red"
+ border.color: "black"
+ border.width: 1
+ }
+ }
+ DelegateChoice {
+ Rectangle {
+ width: parent.width
+ height: 50
+ color: model.color
+ border.color: "black"
+ border.width: 1
+ }
+ }
+ }
+ }
+ DelegateChoice {
+ roleValue: "image"
+ delegate: Image {
+ width: parent.width
+ height: 100
+ source: model.source
+ fillMode: Image.PreserveAspectFit
+ }
+ }
+ }
+
+ Item {
+ anchors.fill: parent
+ id: ite
+ RowLayout {
+ ListView {
+ Layout.preferredHeight: ite.height
+ Layout.preferredWidth: ite.width * 0.5
+ model: listModel
+ delegate: fancyDelegate
+ }
+ ListView {
+ Layout.preferredHeight: ite.height
+ Layout.preferredWidth: ite.width * 0.5
+ model: listModel2
+ delegate: fancyDelegate
+ }
+ }
+ }
+}
diff --git a/examples/quick/delegatechooser/main.cpp b/examples/quick/delegatechooser/main.cpp
new file mode 100644
index 0000000000..5df9207e24
--- /dev/null
+++ b/examples/quick/delegatechooser/main.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "../shared/shared.h"
+DECLARATIVE_EXAMPLE_MAIN(delegatechooser)
diff --git a/examples/quick/delegatechooser/qml.qrc b/examples/quick/delegatechooser/qml.qrc
new file mode 100644
index 0000000000..0788f84c64
--- /dev/null
+++ b/examples/quick/delegatechooser/qml.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>delegatechooser.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/quick/quick.pro b/examples/quick/quick.pro
index deef437046..0bb90e737b 100644
--- a/examples/quick/quick.pro
+++ b/examples/quick/quick.pro
@@ -25,6 +25,7 @@ SUBDIRS = quick-accessibility \
imageresponseprovider \
window \
particles \
+ delegatechooser \
shapes
#OpenGL Support Required
diff --git a/qtdeclarative.doxy b/qtdeclarative.doxy
new file mode 100644
index 0000000000..ce21e34d3d
--- /dev/null
+++ b/qtdeclarative.doxy
@@ -0,0 +1,2466 @@
+# Doxyfile 1.8.14
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = QtDeclarative
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = 5.12
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "The QML engine and Qt Quick"
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO = tests/testapplications/qsgimage/qt-logo.png
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = ../qtdeclarative-doc
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 1
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = YES
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER = "function git_filter { LC_ALL=C git ls-files -s $1 | awk '{print $2}'; }; git_filter"
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE = doxygen.log
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = src
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.d \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.idl \
+ *.odl \
+ *.cs \
+ *.php \
+ *.php3 \
+ *.inc \
+ *.m \
+ *.mm \
+ *.dox \
+ *.py
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = *moc_* \
+ *c++test*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = NO
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 151
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 46
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via Javascript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have Javascript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = YES
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: https://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = YES
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = YES
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE = qtdeclarative.qch
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = YES
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = YES
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = YES
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = YES
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH = /usr/bin
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/src/3rdparty/masm/wtf/Assertions.h b/src/3rdparty/masm/wtf/Assertions.h
index e2c04ac0bb..be25d43826 100644
--- a/src/3rdparty/masm/wtf/Assertions.h
+++ b/src/3rdparty/masm/wtf/Assertions.h
@@ -171,7 +171,7 @@ WTF_EXPORT_PRIVATE void WTFInstallReportBacktraceOnCrashHook();
#define CRASH() \
(WTFReportBacktrace(), \
WTFInvokeCrashHook(), \
- (*reinterpret_cast<int *>(uintptr_t(0xbbadbeef)) = 0), \
+ (*(int *)(uintptr_t)0xbbadbeef = 0), \
__builtin_trap())
#else
#define CRASH() \
@@ -256,7 +256,7 @@ inline void assertUnused(T& x) { (void)x; }
(void)0)
#define ASSERT_NOT_REACHED() do { \
- WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, nullptr); \
+ WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, NULL); \
CRASH(); \
} while (0)
diff --git a/src/imports/imports.pro b/src/imports/imports.pro
index b5f5155502..828382ba39 100644
--- a/src/imports/imports.pro
+++ b/src/imports/imports.pro
@@ -4,7 +4,8 @@ SUBDIRS += \
builtins \
qtqml \
folderlistmodel \
- models
+ models \
+ labsmodels
qtHaveModule(sql): SUBDIRS += localstorage
qtConfig(settings): SUBDIRS += settings
diff --git a/src/imports/labsmodels/labsmodels.pro b/src/imports/labsmodels/labsmodels.pro
new file mode 100644
index 0000000000..1795ae5e43
--- /dev/null
+++ b/src/imports/labsmodels/labsmodels.pro
@@ -0,0 +1,11 @@
+CXX_MODULE = qml
+TARGET = labsmodelsplugin
+TARGETPATH = Qt/labs/qmlmodels
+IMPORT_VERSION = 1.0
+
+SOURCES += \
+ plugin.cpp
+
+QT = qml-private
+
+load(qml_plugin)
diff --git a/src/imports/labsmodels/plugin.cpp b/src/imports/labsmodels/plugin.cpp
new file mode 100644
index 0000000000..e18f08b70b
--- /dev/null
+++ b/src/imports/labsmodels/plugin.cpp
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+
+#include <private/qqmlmodelsmodule_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlmodule Qt.labs.qmlmodels 1.0
+ \title Qt QML Models experimental QML Types
+ \ingroup qmlmodules
+ \brief Provides QML experimental types for data models
+ \since 5.12
+
+ This QML module contains experimental QML types related to data models.
+
+ To use the types in this module, import the module with the following line:
+
+ \code
+ import Qt.labs.qmlmodels 1.0
+ \endcode
+*/
+
+//![class decl]
+class QtQmlLabsModelsPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+public:
+ QtQmlLabsModelsPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
+ void registerTypes(const char *uri) override
+ {
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.qmlmodels"));
+ Q_UNUSED(uri);
+ QQmlModelsModule::defineLabsModule();
+
+ qmlRegisterModule(uri, 1, 0);
+ }
+};
+//![class decl]
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/imports/labsmodels/qmldir b/src/imports/labsmodels/qmldir
new file mode 100644
index 0000000000..9c735711c4
--- /dev/null
+++ b/src/imports/labsmodels/qmldir
@@ -0,0 +1,3 @@
+module Qt.labs.qmlmodels
+plugin labsmodelsplugin
+classname QtQmlLabsModelsPlugin
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
index b1f7053c36..d0b104dfad 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
@@ -72,7 +72,7 @@ class QV4DebugServiceImpl : public QQmlConfigurableDebugService<QV4DebugService>
{
Q_OBJECT
public:
- explicit QV4DebugServiceImpl(QObject *parent = 0);
+ explicit QV4DebugServiceImpl(QObject *parent = nullptr);
~QV4DebugServiceImpl() override;
void engineAdded(QJSEngine *engine) override;
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index 1695f3e5d6..a03f69576d 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -44,6 +44,7 @@
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
+#include <private/qqmldelegatecomponent_p.h>
#define COMPILE_EXCEPTION(token, desc) \
{ \
@@ -804,13 +805,21 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex);
auto *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
Q_ASSERT(tr);
- if (tr->type.isValid()) {
- if (tr->type.metaObject() == &QQmlComponent::staticMetaObject)
- continue;
- } else if (tr->compilationUnit) {
- if (tr->compilationUnit->rootPropertyCache()->firstCppMetaObject() == &QQmlComponent::staticMetaObject)
- continue;
- }
+
+ const QMetaObject *firstMetaObject = nullptr;
+ if (tr->type.isValid())
+ firstMetaObject = tr->type.metaObject();
+ else if (tr->compilationUnit)
+ firstMetaObject = tr->compilationUnit->rootPropertyCache()->firstCppMetaObject();
+ // 1: test for QQmlComponent
+ if (firstMetaObject && firstMetaObject == &QQmlComponent::staticMetaObject)
+ continue;
+ // 2: test for QQmlAbstractDelegateComponent
+ while (firstMetaObject && firstMetaObject != &QQmlAbstractDelegateComponent::staticMetaObject)
+ firstMetaObject = firstMetaObject->superClass();
+ if (firstMetaObject)
+ continue;
+ // if here, not a QQmlComponent or a QQmlAbstractDelegateComponent, so needs wrapping
QQmlPropertyData *pd = nullptr;
if (binding->propertyNameIndex != quint32(0)) {
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp
index d3c5582b28..e2533a0505 100644
--- a/src/qml/compiler/qv4bytecodehandler.cpp
+++ b/src/qml/compiler/qv4bytecodehandler.cpp
@@ -56,8 +56,9 @@ ByteCodeHandler::~ByteCodeHandler()
{ \
INSTR_##instr(MOTH_DECODE_WITH_BASE) \
Q_UNUSED(base_ptr); \
+ _currentOffset = _nextOffset; \
+ _nextOffset = code - start; \
startInstruction(Instr::Type::instr); \
- _offset = code - start; \
INSTR_##instr(DISPATCH) \
endInstruction(Instr::Type::instr); \
continue; \
diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h
index 5f1121306f..ca6abf3dc3 100644
--- a/src/qml/compiler/qv4bytecodehandler_p.h
+++ b/src/qml/compiler/qv4bytecodehandler_p.h
@@ -91,7 +91,8 @@ public:
void decode(const char *code, uint len);
- int instructionOffset() const { return _offset; }
+ int currentInstructionOffset() const { return _currentOffset; }
+ int nextInstructionOffset() const { return _nextOffset; }
static std::vector<int> collectLabelsInBytecode(const char *code, uint len);
@@ -102,7 +103,8 @@ protected:
virtual void endInstruction(Moth::Instr::Type instr) = 0;
private:
- int _offset = 0;
+ int _currentOffset = 0;
+ int _nextOffset = 0;
};
} // Moth namespace
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index bf442de741..2e0f357574 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -73,7 +73,7 @@ void BaselineJIT::generate()
// qDebug()<<"done";
}
-#define STORE_IP() as->storeInstructionPointer(instructionOffset())
+#define STORE_IP() as->storeInstructionPointer(nextInstructionOffset())
#define STORE_ACC() as->saveAccumulatorInFrame()
void BaselineJIT::generate_Ret()
@@ -562,7 +562,7 @@ void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv)
void BaselineJIT::generate_SetUnwindHandler(int offset)
{
if (offset)
- as->setUnwindHandler(instructionOffset() + offset);
+ as->setUnwindHandler(absoluteOffsetForJump(offset));
else
as->clearUnwindHandler();
}
@@ -574,7 +574,7 @@ void BaselineJIT::generate_UnwindDispatch()
void BaselineJIT::generate_UnwindToLabel(int level, int offset)
{
- as->unwindToLabel(level, instructionOffset() + offset);
+ as->unwindToLabel(level, absoluteOffsetForJump(offset));
}
@@ -824,11 +824,11 @@ void BaselineJIT::generate_ToObject()
}
-void BaselineJIT::generate_Jump(int offset) { as->jump(instructionOffset() + offset); }
-void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(instructionOffset() + offset); }
-void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(instructionOffset() + offset); }
-void BaselineJIT::generate_JumpNoException(int offset) { as->jumpNoException(instructionOffset() + offset); }
-void BaselineJIT::generate_JumpNotUndefined(int offset) { as->jumpNotUndefined(instructionOffset() + offset); }
+void BaselineJIT::generate_Jump(int offset) { as->jump(absoluteOffsetForJump(offset)); }
+void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(absoluteOffsetForJump(offset)); }
+void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(absoluteOffsetForJump(offset)); }
+void BaselineJIT::generate_JumpNoException(int offset) { as->jumpNoException(absoluteOffsetForJump(offset)); }
+void BaselineJIT::generate_JumpNotUndefined(int offset) { as->jumpNotUndefined(absoluteOffsetForJump(offset)); }
void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); }
void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); }
@@ -932,7 +932,7 @@ void BaselineJIT::generate_LoadQmlImportedScripts(int result)
void BaselineJIT::startInstruction(Instr::Type /*instr*/)
{
if (hasLabel())
- as->addLabel(instructionOffset());
+ as->addLabel(currentInstructionOffset());
}
void BaselineJIT::endInstruction(Instr::Type instr)
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 71b9dda9b9..6f066bb9a5 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -214,7 +214,10 @@ public:
protected:
bool hasLabel() const
- { return std::find(labels.cbegin(), labels.cend(), instructionOffset()) != labels.cend(); }
+ { return std::find(labels.cbegin(), labels.cend(), currentInstructionOffset()) != labels.cend(); }
+
+ int absoluteOffsetForJump(int relativeOffset) const
+ { return nextInstructionOffset() + relativeOffset; }
private:
QV4::Function *function;
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 2a8e236905..05a9f70247 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -496,6 +496,38 @@ int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor,
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
}
+template<typename T, int metaObjectRevision>
+int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor,
+ const char *qmlName, QQmlCustomParser *parser)
+{
+ QML_GETTYPENAMES
+
+ QQmlPrivate::RegisterType type = {
+ 1,
+
+ qRegisterNormalizedMetaType<T *>(pointerName.constData()),
+ qRegisterNormalizedMetaType<QQmlListProperty<T> >(listName.constData()),
+ sizeof(T), QQmlPrivate::createInto<T>,
+ QString(),
+
+ uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject,
+
+ QQmlPrivate::attachedPropertiesFunc<T>(),
+ QQmlPrivate::attachedPropertiesMetaObject<T>(),
+
+ QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(),
+ QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(),
+ QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(),
+
+ nullptr, nullptr,
+
+ parser,
+ metaObjectRevision
+ };
+
+ return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
+}
+
template<typename T, typename E>
int qmlRegisterCustomExtendedType(const char *uri, int versionMajor, int versionMinor,
const char *qmlName, QQmlCustomParser *parser)
diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h
index 9b2db4bccf..4d9e4c6c15 100644
--- a/src/qml/qml/qqmlcomponent_p.h
+++ b/src/qml/qml/qqmlcomponent_p.h
@@ -88,7 +88,7 @@ public:
void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate);
static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v);
- void incubateObject(
+ virtual void incubateObject(
QQmlIncubator *incubationTask,
QQmlComponent *component,
QQmlEngine *engine,
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index e487aec4ab..26187ca086 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -220,13 +220,15 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int
qmlRegisterType<QObject>(uri,versionMajor,versionMinor,"QtObject");
qmlRegisterType<QQmlBind>(uri, versionMajor, versionMinor,"Binding");
qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8
- qmlRegisterType<QQmlConnections,1>(uri, versionMajor, (versionMinor < 3 ? 3 : versionMinor), "Connections"); //Only available in >=2.3
- qmlRegisterType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections");
+ qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, 0, "Connections", new QQmlConnectionsParser);
+ if (!strcmp(uri, "QtQuick"))
+ qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 7, "Connections", new QQmlConnectionsParser); //Only available in QtQuick >=2.7
+ else
+ qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 3, "Connections", new QQmlConnectionsParser); //Only available in QtQml >=2.3
#if QT_CONFIG(qml_animation)
qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer");
#endif
qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1
- qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser);
qmlRegisterType<QQmlInstanceModel>();
qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, 8, "LoggingCategory"); //Only available in >=2.8
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 88eda9c020..0388215630 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -1360,19 +1360,21 @@ template <typename StringVisitor>
int visitEnumerations(const QMetaObject &mo, StringVisitor visitString)
{
const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
- const int intsPerEnumerator = 4;
+ const int intsPerEnumerator = priv->revision >= 8 ? 5 : 4;
int fieldCount = priv->enumeratorCount * intsPerEnumerator;
for (int i = 0; i < priv->enumeratorCount; ++i) {
const uint *enumeratorData = mo.d.data + priv->enumeratorData + i * intsPerEnumerator;
- const uint keyCount = enumeratorData[2];
+ const uint keyCount = enumeratorData[intsPerEnumerator == 5 ? 3 : 2];
fieldCount += keyCount * 2;
visitString(enumeratorData[0]); // name
+ if (intsPerEnumerator == 5)
+ visitString(enumeratorData[1]); // enum name
- const uint keyOffset = enumeratorData[3];
+ const uint keyOffset = enumeratorData[intsPerEnumerator == 5 ? 4 : 3];
for (uint j = 0; j < keyCount; ++j) {
visitString(mo.d.data[keyOffset + 2 * j]);
@@ -1412,7 +1414,7 @@ bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fie
int *stringCount)
{
const QMetaObjectPrivate *priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
- if (priv->revision != 7) {
+ if (priv->revision < 7 || priv->revision > 8) {
return false;
}
diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp
index 270414a676..2b21591017 100644
--- a/src/qml/qml/qqmlvaluetype.cpp
+++ b/src/qml/qml/qqmlvaluetype.cpp
@@ -67,8 +67,7 @@ struct QQmlValueTypeFactoryImpl
QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl()
{
- for (unsigned int ii = 0; ii < QVariant::UserType; ++ii)
- valueTypes[ii] = nullptr;
+ std::fill_n(valueTypes, int(QVariant::UserType), nullptr);
// See types wrapped in qqmlmodelindexvaluetype_p.h
qRegisterMetaType<QItemSelectionRange>();
@@ -521,38 +520,33 @@ void QQmlEasingValueType::setBezierCurve(const QVariantList &customCurveVariant)
if (customCurveVariant.isEmpty())
return;
- QVariantList variantList = customCurveVariant;
- if ((variantList.count() % 6) == 0) {
- bool allRealsOk = true;
- QVector<qreal> reals;
- const int variantListCount = variantList.count();
- reals.reserve(variantListCount);
- for (int i = 0; i < variantListCount; i++) {
- bool ok;
- const qreal real = variantList.at(i).toReal(&ok);
- reals.append(real);
- if (!ok)
- allRealsOk = false;
- }
- if (allRealsOk) {
- QEasingCurve newEasingCurve(QEasingCurve::BezierSpline);
- for (int i = 0; i < reals.count() / 6; i++) {
- const qreal c1x = reals.at(i * 6);
- const qreal c1y = reals.at(i * 6 + 1);
- const qreal c2x = reals.at(i * 6 + 2);
- const qreal c2y = reals.at(i * 6 + 3);
- const qreal c3x = reals.at(i * 6 + 4);
- const qreal c3y = reals.at(i * 6 + 5);
-
- const QPointF c1(c1x, c1y);
- const QPointF c2(c2x, c2y);
- const QPointF c3(c3x, c3y);
-
- newEasingCurve.addCubicBezierSegment(c1, c2, c3);
- v = newEasingCurve;
- }
- }
+ if ((customCurveVariant.count() % 6) != 0)
+ return;
+
+ auto convert = [](const QVariant &v, qreal &r) {
+ bool ok;
+ r = v.toReal(&ok);
+ return ok;
+ };
+
+ QEasingCurve newEasingCurve(QEasingCurve::BezierSpline);
+ for (int i = 0, ei = customCurveVariant.size(); i < ei; i += 6) {
+ qreal c1x, c1y, c2x, c2y, c3x, c3y;
+ if (!convert(customCurveVariant.at(i ), c1x)) return;
+ if (!convert(customCurveVariant.at(i + 1), c1y)) return;
+ if (!convert(customCurveVariant.at(i + 2), c2x)) return;
+ if (!convert(customCurveVariant.at(i + 3), c2y)) return;
+ if (!convert(customCurveVariant.at(i + 4), c3x)) return;
+ if (!convert(customCurveVariant.at(i + 5), c3y)) return;
+
+ const QPointF c1(c1x, c1y);
+ const QPointF c2(c2x, c2y);
+ const QPointF c3(c3x, c3y);
+
+ newEasingCurve.addCubicBezierSegment(c1, c2, c3);
}
+
+ v = newEasingCurve;
}
QVariantList QQmlEasingValueType::bezierCurve() const
diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h
index 6b063d5602..bd03d7e152 100644
--- a/src/qml/types/qqmlconnections_p.h
+++ b/src/qml/types/qqmlconnections_p.h
@@ -69,7 +69,7 @@ class Q_AUTOTEST_EXPORT QQmlConnections : public QObject, public QQmlParserStatu
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged)
- Q_REVISION(1) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged REVISION 1)
Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals)
public:
diff --git a/src/qml/types/qqmldelegatecomponent.cpp b/src/qml/types/qqmldelegatecomponent.cpp
new file mode 100644
index 0000000000..e52a7879a4
--- /dev/null
+++ b/src/qml/types/qqmldelegatecomponent.cpp
@@ -0,0 +1,285 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmldelegatecomponent_p.h"
+#include <QtQml/private/qqmladaptormodel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlAbstractDelegateComponent::QQmlAbstractDelegateComponent(QObject *parent)
+ : QQmlComponent(parent)
+{
+}
+
+QQmlAbstractDelegateComponent::~QQmlAbstractDelegateComponent()
+{
+}
+
+QVariant QQmlAbstractDelegateComponent::value(QQmlAdaptorModel *adaptorModel, int row, int column, const QString &role) const
+{
+ if (!adaptorModel)
+ return QVariant();
+ return adaptorModel->value(adaptorModel->indexAt(row, column), role);
+}
+
+/*!
+ \qmltype DelegateChoice
+ \instantiates QQmlDelegateChoice
+ \inqmlmodule Qt.labs.qmlmodels
+ \brief Encapsulates a delegate and when to use it
+
+ The DelegateChoice type wraps a delegate and defines the circumstances
+ in which it should be chosen.
+ DelegateChoices can be nested inside a DelegateChooser.
+
+ \sa DelegateChooser
+*/
+
+/*!
+ \qmlproperty string QtQml.Models::DelegateChoice::roleValue
+ This property holds the value used to match the role data for the role provided by \l DefaultDelegateChooser::role.
+*/
+QVariant QQmlDelegateChoice::roleValue() const
+{
+ return m_value;
+}
+
+void QQmlDelegateChoice::setRoleValue(const QVariant &value)
+{
+ if (m_value == value)
+ return;
+ m_value = value;
+ emit roleValueChanged();
+ emit changed();
+}
+
+/*!
+ \qmlproperty index QtQml.Models::DelegateChoice::row
+ This property holds the value used to match the row value of model elements.
+ With models that have only the index property (and thus only one column), this property
+ should be intended as index, and set to the desired index value.
+
+ \note Setting both row and index has undefined behavior. The two are equivalent and only
+ one should be used.
+
+ \sa QtQml.Models::DelegateChoice::index
+*/
+
+/*!
+ \qmlproperty index QtQml.Models::DelegateChoice::index
+ This property holds the value used to match the index value of model elements.
+ This is effectively an alias for \l QtQml.Models::DelegateChoice::row
+
+ \sa QtQml.Models::DelegateChoice::row
+*/
+int QQmlDelegateChoice::row() const
+{
+ return m_row;
+}
+
+void QQmlDelegateChoice::setRow(int r)
+{
+ if (m_row == r)
+ return;
+ m_row = r;
+ emit rowChanged();
+ emit indexChanged();
+ emit changed();
+}
+
+/*!
+ \qmlproperty index QtQml.Models::DelegateChoice::column
+ This property holds the value used to match the column value of model elements.
+*/
+int QQmlDelegateChoice::column() const
+{
+ return m_column;
+}
+
+void QQmlDelegateChoice::setColumn(int c)
+{
+ if (m_column == c)
+ return;
+ m_column = c;
+ emit columnChanged();
+ emit changed();
+}
+
+QQmlComponent *QQmlDelegateChoice::delegate() const
+{
+ return m_delegate;
+}
+
+/*!
+ \qmlproperty Component QtQml.Models::DelegateChoice::delegate
+ This property holds the delegate to use if this choice matches the model item.
+*/
+void QQmlDelegateChoice::setDelegate(QQmlComponent *delegate)
+{
+ if (m_delegate == delegate)
+ return;
+ QQmlAbstractDelegateComponent *adc = static_cast<QQmlAbstractDelegateComponent *>(m_delegate);
+ if (adc)
+ disconnect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, &QQmlDelegateChoice::delegateChanged);
+ m_delegate = delegate;
+ adc = static_cast<QQmlAbstractDelegateComponent *>(delegate);
+ if (adc)
+ connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, &QQmlDelegateChoice::delegateChanged);
+ emit delegateChanged();
+ emit changed();
+}
+
+bool QQmlDelegateChoice::match(int row, int column, const QVariant &value) const
+{
+ if (!m_value.isValid() && m_row < 0 && m_column < 0)
+ return true;
+
+ const bool roleMatched = (m_value.isValid()) ? value == m_value : true;
+ const bool rowMatched = (m_row < 0 ) ? true : m_row == row;
+ const bool columnMatched = (m_column < 0 ) ? true : m_column == column;
+ return roleMatched && rowMatched && columnMatched;
+}
+
+/*!
+ \qmltype QQmlDelegateChooser
+ \instantiates QQmlDelegateChooser
+ \inqmlmodule Qt.labs.qmlmodels
+ \brief Allows a view to use different delegates for different types of items in the model.
+
+ The DelegateChooser is a special \l Component type intended for those scenarios where a Component is required
+ by a view and used as a delegate.
+ DelegateChooser encapsulates a set of \l DelegateChoices.
+ These choices are used determine the delegate that will be instantiated for each
+ item in the model.
+ The selection of the choice is performed based on the value that a model item has for \l role,
+ and also based on index.
+
+ \note This type is intended to transparently work only with TableView and any DelegateModel-based view.
+ Views (including user-defined views) that aren't internally based on a DelegateModel need to explicitly support
+ this type of component to make it function as described.
+
+ \sa DelegateChoice
+*/
+
+/*!
+ \qmlproperty string QtQml.Models::DelegateChooser::role
+ This property holds the role used to determine the delegate for a given model item.
+
+ \sa DelegateChoice
+*/
+void QQmlDelegateChooser::setRole(const QString &role)
+{
+ if (m_role == role)
+ return;
+ m_role = role;
+ emit roleChanged();
+}
+
+/*!
+ \qmlproperty list<DelegateChoice> QtQml.Models::DelegateChooser::choices
+ \default
+ The list of DelegateChoices for the chooser.
+
+ The list is treated as an ordered list, where the first DelegateChoice to match
+ will be used be a view.
+
+ It should not generally be necessary to refer to the \c choices property,
+ as it is the default property for DefaultDelegateChooser and thus all child items are
+ automatically assigned to this property.
+*/
+
+QQmlListProperty<QQmlDelegateChoice> QQmlDelegateChooser::choices()
+{
+ return QQmlListProperty<QQmlDelegateChoice>(this, nullptr,
+ QQmlDelegateChooser::choices_append,
+ QQmlDelegateChooser::choices_count,
+ QQmlDelegateChooser::choices_at,
+ QQmlDelegateChooser::choices_clear);
+}
+
+void QQmlDelegateChooser::choices_append(QQmlListProperty<QQmlDelegateChoice> *prop, QQmlDelegateChoice *choice)
+{
+ QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
+ q->m_choices.append(choice);
+ connect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged);
+ q->delegateChanged();
+}
+
+int QQmlDelegateChooser::choices_count(QQmlListProperty<QQmlDelegateChoice> *prop)
+{
+ QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object);
+ return q->m_choices.count();
+}
+
+QQmlDelegateChoice *QQmlDelegateChooser::choices_at(QQmlListProperty<QQmlDelegateChoice> *prop, int index)
+{
+ QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object);
+ return q->m_choices.at(index);
+}
+
+void QQmlDelegateChooser::choices_clear(QQmlListProperty<QQmlDelegateChoice> *prop)
+{
+ QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
+ for (QQmlDelegateChoice *choice : q->m_choices)
+ disconnect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged);
+ q->m_choices.clear();
+ q->delegateChanged();
+}
+
+QQmlComponent *QQmlDelegateChooser::delegate(QQmlAdaptorModel *adaptorModel, int row, int column) const
+{
+ QVariant v;
+ if (!m_role.isNull())
+ v = value(adaptorModel, row, column, m_role);
+ if (!v.isValid()) { // check if the row only has modelData, for example if the row is a QVariantMap
+ v = value(adaptorModel, row, column, QStringLiteral("modelData"));
+ if (v.isValid())
+ v = v.toMap().value(m_role);
+ }
+ // loop through choices, finding first one that fits
+ for (int i = 0; i < m_choices.count(); ++i) {
+ const QQmlDelegateChoice *choice = m_choices.at(i);
+ if (choice->match(row, column, v))
+ return choice->delegate();
+ }
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/types/qqmldelegatecomponent_p.h b/src/qml/types/qqmldelegatecomponent_p.h
new file mode 100644
index 0000000000..c925ed9a60
--- /dev/null
+++ b/src/qml/types/qqmldelegatecomponent_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLDELEGATECOMPONENT_P_H
+#define QQMLDELEGATECOMPONENT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtqmlglobal_p.h>
+#include <qqmlcomponent.h>
+
+QT_REQUIRE_CONFIG(qml_delegate_model);
+
+QT_BEGIN_NAMESPACE
+
+// TODO: consider making QQmlAbstractDelegateComponent public API
+class QQmlAbstractDelegateComponentPrivate;
+class QQmlAdaptorModel;
+class Q_QML_PRIVATE_EXPORT QQmlAbstractDelegateComponent : public QQmlComponent
+{
+ Q_OBJECT
+public:
+ QQmlAbstractDelegateComponent(QObject *parent = nullptr);
+ ~QQmlAbstractDelegateComponent() override;
+
+ virtual QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = 0) const = 0;
+
+signals:
+ void delegateChanged();
+
+protected:
+ QVariant value(QQmlAdaptorModel *adaptorModel,int row, int column, const QString &role) const;
+
+private:
+ Q_DECLARE_PRIVATE(QQmlAbstractDelegateComponent)
+ Q_DISABLE_COPY(QQmlAbstractDelegateComponent)
+};
+
+class Q_QML_PRIVATE_EXPORT QQmlDelegateChoice : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariant roleValue READ roleValue WRITE setRoleValue NOTIFY roleValueChanged)
+ Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged)
+ Q_PROPERTY(int index READ row WRITE setRow NOTIFY indexChanged)
+ Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged)
+ Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_CLASSINFO("DefaultProperty", "delegate")
+public:
+ QVariant roleValue() const;
+ void setRoleValue(const QVariant &roleValue);
+
+ int row() const;
+ void setRow(int r);
+
+ int column() const;
+ void setColumn(int c);
+
+ QQmlComponent *delegate() const;
+ void setDelegate(QQmlComponent *delegate);
+
+ virtual bool match(int row, int column, const QVariant &value) const;
+
+signals:
+ void roleValueChanged();
+ void rowChanged();
+ void indexChanged();
+ void columnChanged();
+ void delegateChanged();
+ void changed();
+
+private:
+ QVariant m_value;
+ int m_row = -1;
+ int m_column = -1;
+ QQmlComponent *m_delegate = nullptr;
+};
+
+class Q_QML_PRIVATE_EXPORT QQmlDelegateChooser : public QQmlAbstractDelegateComponent
+{
+ Q_OBJECT
+ Q_PROPERTY(QString role READ role WRITE setRole NOTIFY roleChanged)
+ Q_PROPERTY(QQmlListProperty<QQmlDelegateChoice> choices READ choices CONSTANT)
+ Q_CLASSINFO("DefaultProperty", "choices")
+
+public:
+ QString role() const { return m_role; }
+ void setRole(const QString &role);
+
+ virtual QQmlListProperty<QQmlDelegateChoice> choices();
+ static void choices_append(QQmlListProperty<QQmlDelegateChoice> *, QQmlDelegateChoice *);
+ static int choices_count(QQmlListProperty<QQmlDelegateChoice> *);
+ static QQmlDelegateChoice *choices_at(QQmlListProperty<QQmlDelegateChoice> *, int);
+ static void choices_clear(QQmlListProperty<QQmlDelegateChoice> *);
+
+ QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = -1) const override;
+
+signals:
+ void roleChanged();
+
+private:
+ QString m_role;
+ QList<QQmlDelegateChoice *> m_choices;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQmlDelegateChoice)
+QML_DECLARE_TYPE(QQmlDelegateChooser)
+
+#endif // QQMLDELEGATECOMPONENT_P_H
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp
index bf2fe3b52a..41970ce626 100644
--- a/src/qml/types/qqmldelegatemodel.cpp
+++ b/src/qml/types/qqmldelegatemodel.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qqmldelegatemodel_p_p.h"
+#include "qqmldelegatecomponent_p.h"
#include <QtQml/qqmlinfo.h>
@@ -200,7 +201,8 @@ QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent)
*/
QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt)
- : m_cacheMetaType(nullptr)
+ : m_delegateChooser(nullptr)
+ , m_cacheMetaType(nullptr)
, m_context(ctxt)
, m_parts(nullptr)
, m_filterGroup(QStringLiteral("items"))
@@ -454,6 +456,8 @@ void QQmlDelegateModel::setModel(const QVariant &model)
The delegate provides a template defining each item instantiated by a view.
The index is exposed as an accessible \c index property. Properties of the
model are also available depending upon the type of \l {qml-data-models}{Data Model}.
+
+ \sa DelegateComponent
*/
QQmlComponent *QQmlDelegateModel::delegate() const
{
@@ -468,22 +472,25 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate)
qmlWarning(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated.");
return;
}
+ if (d->m_delegate == delegate)
+ return;
bool wasValid = d->m_delegate != nullptr;
d->m_delegate.setObject(delegate, this);
d->m_delegateValidated = false;
- if (wasValid && d->m_complete) {
- for (int i = 1; i < d->m_groupCount; ++i) {
- QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.remove(
- 0, d->m_compositor.count(Compositor::Group(i)));
- }
- }
- if (d->m_complete && d->m_delegate) {
- for (int i = 1; i < d->m_groupCount; ++i) {
- QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.insert(
- 0, d->m_compositor.count(Compositor::Group(i)));
+ if (d->m_delegateChooser)
+ QObject::disconnect(d->m_delegateChooserChanged);
+
+ d->m_delegateChooser = nullptr;
+ if (delegate) {
+ QQmlAbstractDelegateComponent *adc =
+ qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
+ if (adc) {
+ d->m_delegateChooser = adc;
+ d->m_delegateChooserChanged = connect(adc, &QQmlAbstractDelegateComponent::delegateChanged,
+ [d](){ d->delegateChanged(); });
}
}
- d->emitChanges();
+ d->delegateChanged(d->m_delegate, wasValid);
}
/*!
@@ -1042,7 +1049,18 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
cacheItem->incubationTask->forceCompletion();
}
} else if (!cacheItem->object) {
- QQmlContext *creationContext = m_delegate->creationContext();
+ QQmlComponent *delegate = nullptr;
+ if (m_delegateChooser) {
+ QQmlAbstractDelegateComponent *chooser = m_delegateChooser;
+ do {
+ delegate = chooser->delegate(&m_adaptorModel, index);
+ chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
+ } while (chooser);
+ }
+ if (!delegate)
+ delegate = m_delegate;
+
+ QQmlContext *creationContext = delegate->creationContext();
cacheItem->scriptRef += 1;
@@ -1067,10 +1085,10 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
}
}
- QQmlComponentPrivate *cp = QQmlComponentPrivate::get(m_delegate);
+ QQmlComponentPrivate *cp = QQmlComponentPrivate::get(delegate);
cp->incubateObject(
cacheItem->incubationTask,
- m_delegate,
+ delegate,
m_context->engine(),
ctxt,
QQmlContextData::get(m_context));
@@ -1568,6 +1586,32 @@ void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet,
emit q->countChanged();
}
+void QQmlDelegateModelPrivate::delegateChanged(bool add, bool remove)
+{
+ Q_Q(QQmlDelegateModel);
+ if (!m_complete)
+ return;
+
+ if (m_transaction) {
+ qmlWarning(q) << QQmlDelegateModel::tr("The delegates of a DelegateModel cannot be changed within onUpdated.");
+ return;
+ }
+
+ if (remove) {
+ for (int i = 1; i < m_groupCount; ++i) {
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(
+ 0, m_compositor.count(Compositor::Group(i)));
+ }
+ }
+ if (add) {
+ for (int i = 1; i < m_groupCount; ++i) {
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(
+ 0, m_compositor.count(Compositor::Group(i)));
+ }
+ }
+ emitChanges();
+}
+
void QQmlDelegateModelPrivate::emitChanges()
{
if (m_transaction || !m_complete || !m_context || !m_context->isValid())
diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h
index 5c66a90798..0ad8939732 100644
--- a/src/qml/types/qqmldelegatemodel_p.h
+++ b/src/qml/types/qqmldelegatemodel_p.h
@@ -67,7 +67,6 @@ QT_REQUIRE_CONFIG(qml_delegate_model);
QT_BEGIN_NAMESPACE
class QQmlChangeSet;
-class QQmlComponent;
class QQuickPackage;
class QQmlV4Function;
class QQmlDelegateModelGroup;
diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h
index 43d288b5c5..2d6fdf228e 100644
--- a/src/qml/types/qqmldelegatemodel_p_p.h
+++ b/src/qml/types/qqmldelegatemodel_p_p.h
@@ -67,6 +67,7 @@ QT_BEGIN_NAMESPACE
typedef QQmlListCompositor Compositor;
class QQmlDelegateModelAttachedMetaObject;
+class QQmlAbstractDelegateComponent;
class Q_QML_PRIVATE_EXPORT QQmlDelegateModelItemMetaType : public QQmlRefCount
{
@@ -303,6 +304,7 @@ public:
void itemsChanged(const QVector<Compositor::Change> &changes);
void emitChanges();
void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) override;
+ void delegateChanged(bool add = true, bool remove = true);
bool insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups);
@@ -319,6 +321,8 @@ public:
QQmlAdaptorModel m_adaptorModel;
QQmlListCompositor m_compositor;
QQmlStrongJSQObjectReference<QQmlComponent> m_delegate;
+ QQmlAbstractDelegateComponent *m_delegateChooser;
+ QMetaObject::Connection m_delegateChooserChanged;
QQmlDelegateModelItemMetaType *m_cacheMetaType;
QPointer<QQmlContext> m_context;
QQmlDelegateModelParts *m_parts;
diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp
index 9c170cb008..30915d96fd 100644
--- a/src/qml/types/qqmlmodelsmodule.cpp
+++ b/src/qml/types/qqmlmodelsmodule.cpp
@@ -44,6 +44,7 @@
#endif
#if QT_CONFIG(qml_delegate_model)
#include <private/qqmldelegatemodel_p.h>
+#include <private/qqmldelegatecomponent_p.h>
#endif
#include <private/qqmlobjectmodel_p.h>
@@ -67,4 +68,13 @@ void QQmlModelsModule::defineModule()
qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel");
}
+void QQmlModelsModule::defineLabsModule()
+{
+ const char uri[] = "Qt.labs.qmlmodels";
+
+ qmlRegisterUncreatableType<QQmlAbstractDelegateComponent>(uri, 1, 0, "AbstractDelegateComponent", QQmlAbstractDelegateComponent::tr("Cannot create instance of abstract class AbstractDelegateComponent."));
+ qmlRegisterType<QQmlDelegateChooser>(uri, 1, 0, "DelegateChooser");
+ qmlRegisterType<QQmlDelegateChoice>(uri, 1, 0, "DelegateChoice");
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/types/qqmlmodelsmodule_p.h
index bac9bea81e..939ecc1500 100644
--- a/src/qml/types/qqmlmodelsmodule_p.h
+++ b/src/qml/types/qqmlmodelsmodule_p.h
@@ -59,6 +59,7 @@ class Q_QML_PRIVATE_EXPORT QQmlModelsModule
{
public:
static void defineModule();
+ static void defineLabsModule();
};
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmltableinstancemodel.cpp b/src/qml/types/qqmltableinstancemodel.cpp
index d667b884fc..33d13e3bad 100644
--- a/src/qml/types/qqmltableinstancemodel.cpp
+++ b/src/qml/types/qqmltableinstancemodel.cpp
@@ -38,7 +38,7 @@
****************************************************************************/
#include "qqmltableinstancemodel_p.h"
-#include "qqmltableinstancemodel_p.h"
+#include "qqmldelegatecomponent_p.h"
#include <QtCore/QTimer>
@@ -107,6 +107,23 @@ QQmlTableInstanceModel::~QQmlTableInstanceModel()
drainReusableItemsPool(0);
}
+QQmlComponent *QQmlTableInstanceModel::resolveDelegate(int index)
+{
+ QQmlComponent *delegate = nullptr;
+ if (m_delegateChooser) {
+ const int row = m_adaptorModel.rowAt(index);
+ const int column = m_adaptorModel.columnAt(index);
+ QQmlAbstractDelegateComponent *chooser = m_delegateChooser;
+ do {
+ delegate = chooser->delegate(&m_adaptorModel, row, column);
+ chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
+ } while (chooser);
+ }
+ if (!delegate)
+ delegate = m_delegate;
+ return delegate;
+}
+
QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(int index)
{
// Check if an item for the given index is already loaded and ready
@@ -114,7 +131,7 @@ QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(int index)
if (modelItem)
return modelItem;
- QQmlComponent *delegate = m_delegate;
+ QQmlComponent *delegate = resolveDelegate(index);
// Check if the pool contains an item that can be reused
modelItem = takeFromReusableItemsPool(delegate);
@@ -478,6 +495,17 @@ QQmlComponent *QQmlTableInstanceModel::delegate() const
void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate)
{
+ if (m_delegate == delegate)
+ return;
+
+ m_delegateChooser = nullptr;
+ if (delegate) {
+ QQmlAbstractDelegateComponent *adc =
+ qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
+ if (adc)
+ m_delegateChooser = adc;
+ }
+
m_delegate = delegate;
}
diff --git a/src/qml/types/qqmltableinstancemodel_p.h b/src/qml/types/qqmltableinstancemodel_p.h
index 93ef4a697f..03761af326 100644
--- a/src/qml/types/qqmltableinstancemodel_p.h
+++ b/src/qml/types/qqmltableinstancemodel_p.h
@@ -57,6 +57,7 @@
QT_BEGIN_NAMESPACE
class QQmlTableInstanceModel;
+class QQmlAbstractDelegateComponent;
class QQmlTableInstanceModelIncubationTask : public QQDMIncubationTask
{
@@ -128,8 +129,11 @@ Q_SIGNALS:
void itemReused(int index, QObject *object);
private:
- QQmlComponent *m_delegate = nullptr;
+ QQmlComponent *resolveDelegate(int index);
+
QQmlAdaptorModel m_adaptorModel;
+ QQmlAbstractDelegateComponent *m_delegateChooser = nullptr;
+ QQmlComponent *m_delegate = nullptr;
QPointer<QQmlContext> m_qmlContext;
QQmlDelegateModelItemMetaType *m_metaType;
diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri
index 6a74d70a0e..492f408271 100644
--- a/src/qml/types/types.pri
+++ b/src/qml/types/types.pri
@@ -34,11 +34,13 @@ qtConfig(qml-list-model) {
qtConfig(qml-delegate-model) {
SOURCES += \
- $$PWD/qqmldelegatemodel.cpp
+ $$PWD/qqmldelegatemodel.cpp \
+ $$PWD/qqmldelegatecomponent.cpp
HEADERS += \
$$PWD/qqmldelegatemodel_p.h \
- $$PWD/qqmldelegatemodel_p_p.h
+ $$PWD/qqmldelegatemodel_p_p.h \
+ $$PWD/qqmldelegatecomponent_p.h
}
qtConfig(qml-animation) {
diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp
index e460c376da..b5caaba727 100644
--- a/src/qml/util/qqmladaptormodel.cpp
+++ b/src/qml/util/qqmladaptormodel.cpp
@@ -1005,6 +1005,11 @@ int QQmlAdaptorModel::columnAt(int index) const
return count <= 0 ? -1 : index / count;
}
+int QQmlAdaptorModel::indexAt(int row, int column) const
+{
+ return column * rowCount() + row;
+}
+
void QQmlAdaptorModel::objectDestroyed(QObject *)
{
setModel(QVariant(), nullptr, nullptr);
diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h
index c4cfc0f466..b5e0881078 100644
--- a/src/qml/util/qqmladaptormodel_p.h
+++ b/src/qml/util/qqmladaptormodel_p.h
@@ -54,7 +54,7 @@
#include <QtCore/qabstractitemmodel.h>
#include "private/qqmllistaccessor_p.h"
-
+#include <private/qqmlglobal_p.h>
#include <private/qqmlguard_p.h>
#include <private/qqmlnullablevalue_p.h>
@@ -123,6 +123,7 @@ public:
int columnCount() const;
int rowAt(int index) const;
int columnAt(int index) const;
+ int indexAt(int row, int column) const;
inline bool adaptsAim() const { return qobject_cast<QAbstractItemModel *>(object()); }
inline QAbstractItemModel *aim() { return static_cast<QAbstractItemModel *>(object()); }
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
index ef6fb4eb7e..4651357403 100644
--- a/src/quick/handlers/qquickhoverhandler.cpp
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -79,7 +79,7 @@ void QQuickHoverHandler::componentComplete()
bool QQuickHoverHandler::wantsPointerEvent(QQuickPointerEvent *event)
{
QQuickEventPoint *point = event->point(0);
- if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(point)) {
+ if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(point) && parentContains(point)) {
// assume this is a mouse event, so there's only one point
setPointId(point->pointId());
return true;
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 83d383d7c0..c2efebf3d8 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -1515,6 +1515,8 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
d->movementEndingTimer.start(MovementEndingTimerInterval, this);
#endif
return;
+ default:
+ return;
}
if (event->source() == Qt::MouseEventNotSynthesized || event->pixelDelta().isNull()) {
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 6b5d595c26..8e2d32a818 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -2566,8 +2566,19 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
from = item->parentItem();
}
bool skip = false;
- QQuickItem * startItem = item;
- QQuickItem * firstFromItem = from;
+
+ QQuickItem *startItem = item;
+ // Protect from endless loop:
+ // If we start on an invisible item we will not find it again.
+ // If there is no other item which can become the focus item, we have a forever loop,
+ // since the protection only works if we encounter the first item again.
+ while (startItem && !startItem->isVisible()) {
+ startItem = startItem->parentItem();
+ }
+ if (!startItem)
+ return item;
+
+ QQuickItem *firstFromItem = from;
QQuickItem *current = item;
qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: startItem:" << startItem;
qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: firstFromItem:" << firstFromItem;
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index bb63bc1ee4..24941ab264 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -331,12 +331,26 @@ public:
Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
struct ChangeListener {
- ChangeListener(QQuickItemChangeListener *l = nullptr, QQuickItemPrivate::ChangeTypes t = nullptr) : listener(l), types(t), gTypes(QQuickGeometryChange::All) {}
- ChangeListener(QQuickItemChangeListener *l, QQuickGeometryChange gt) : listener(l), types(Geometry), gTypes(gt) {}
+ using ChangeTypes = QQuickItemPrivate::ChangeTypes;
+
+ ChangeListener(QQuickItemChangeListener *l = nullptr, ChangeTypes t = nullptr)
+ : listener(l)
+ , types(t)
+ , gTypes(QQuickGeometryChange::All)
+ {}
+
+ ChangeListener(QQuickItemChangeListener *l, QQuickGeometryChange gt)
+ : listener(l)
+ , types(Geometry)
+ , gTypes(gt)
+ {}
+
+ bool operator==(const ChangeListener &other) const
+ { return listener == other.listener && types == other.types; }
+
QQuickItemChangeListener *listener;
- QQuickItemPrivate::ChangeTypes types;
+ ChangeTypes types;
QQuickGeometryChange gTypes; //NOTE: not used for ==
- bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; }
};
struct ExtraData {
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 3925fe95cf..8dce0149f6 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -59,7 +59,6 @@ Q_LOGGING_CATEGORY(lcTableViewDelegateLifecycle, "qt.quick.tableview.lifecycle")
#define Q_TABLEVIEW_ASSERT(cond, output) Q_ASSERT((cond) || [&](){ dumpTable(); qWarning() << "output:" << output; return false;}())
static const Qt::Edge allTableEdges[] = { Qt::LeftEdge, Qt::RightEdge, Qt::TopEdge, Qt::BottomEdge };
-static const int kBufferTimerInterval = 300;
static QLine rectangleEdge(const QRect &rect, Qt::Edge tableEdge)
{
@@ -99,8 +98,6 @@ const QPoint QQuickTableViewPrivate::kDown = QPoint(0, 1);
QQuickTableViewPrivate::QQuickTableViewPrivate()
: QQuickFlickablePrivate()
{
- cacheBufferDelayTimer.setSingleShot(true);
- QObject::connect(&cacheBufferDelayTimer, &QTimer::timeout, [=]{ loadBuffer(); });
}
QQuickTableViewPrivate::~QQuickTableViewPrivate()
@@ -463,19 +460,19 @@ bool QQuickTableViewPrivate::canUnloadTableEdge(Qt::Edge tableEdge, const QRectF
case Qt::LeftEdge:
if (loadedTable.width() <= 1)
return false;
- return loadedTableInnerRect.left() < fillRect.left();
+ return loadedTableInnerRect.left() <= fillRect.left();
case Qt::RightEdge:
if (loadedTable.width() <= 1)
return false;
- return loadedTableInnerRect.right() > fillRect.right();
+ return loadedTableInnerRect.right() >= fillRect.right();
case Qt::TopEdge:
if (loadedTable.height() <= 1)
return false;
- return loadedTableInnerRect.top() < fillRect.top();
+ return loadedTableInnerRect.top() <= fillRect.top();
case Qt::BottomEdge:
if (loadedTable.height() <= 1)
return false;
- return loadedTableInnerRect.bottom() > fillRect.bottom();
+ return loadedTableInnerRect.bottom() >= fillRect.bottom();
}
Q_TABLEVIEW_UNREACHABLE(tableEdge);
return false;
@@ -808,7 +805,7 @@ void QQuickTableViewPrivate::cancelLoadRequest()
loadRequest.markAsDone();
model->cancel(modelIndexAtCell(loadRequest.currentCell()));
- if (tableInvalid) {
+ if (rebuildState == RebuildState::NotStarted) {
// No reason to rollback already loaded edge items
// since we anyway are about to reload all items.
return;
@@ -860,13 +857,77 @@ void QQuickTableViewPrivate::processLoadRequest()
qCDebug(lcTableViewDelegateLifecycle()) << "request completed! Table:" << tableLayoutToString();
}
+void QQuickTableViewPrivate::processRebuildTable()
+{
+ moveToNextRebuildState();
+
+ if (rebuildState == RebuildState::LoadInitalTable) {
+ beginRebuildTable();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::VerifyTable) {
+ if (loadedItems.isEmpty()) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded, meaning empty model or no delegate";
+ rebuildState = RebuildState::Done;
+ return;
+ }
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::LayoutTable) {
+ layoutAfterLoadingInitialTable();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::LoadAndUnloadAfterLayout) {
+ loadAndUnloadVisibleEdges();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::PreloadColumns) {
+ if (loadedTable.right() < tableSize.width() - 1)
+ loadEdge(Qt::RightEdge, QQmlIncubator::AsynchronousIfNested);
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::PreloadRows) {
+ if (loadedTable.bottom() < tableSize.height() - 1)
+ loadEdge(Qt::BottomEdge, QQmlIncubator::AsynchronousIfNested);
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::MovePreloadedItemsToPool) {
+ while (Qt::Edge edge = nextEdgeToUnload(viewportRect))
+ unloadEdge(edge);
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ Q_TABLEVIEW_ASSERT(rebuildState == RebuildState::Done, int(rebuildState));
+}
+
+bool QQuickTableViewPrivate::moveToNextRebuildState()
+{
+ if (loadRequest.isActive()) {
+ // Items are still loading async, which means
+ // that the current state is not yet done.
+ return false;
+ }
+ rebuildState = RebuildState(int(rebuildState) + 1);
+ qCDebug(lcTableViewDelegateLifecycle()) << int(rebuildState);
+ return true;
+}
+
void QQuickTableViewPrivate::beginRebuildTable()
{
Q_Q(QQuickTableView);
- qCDebug(lcTableViewDelegateLifecycle());
-
- tableInvalid = false;
- tableRebuilding = true;
releaseLoadedItems();
loadedTable = QRect();
@@ -882,21 +943,16 @@ void QQuickTableViewPrivate::beginRebuildTable()
loadAndUnloadVisibleEdges();
}
-void QQuickTableViewPrivate::endRebuildTable()
+void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
{
- tableRebuilding = false;
-
- if (rowHeightProvider.isNull() && columnWidthProvider.isNull()) {
- // Since we have no size providers, we need to calculate the size
- // of each row and column based on the size of the delegate items.
+ if (rowHeightProvider.isNull() || columnWidthProvider.isNull()) {
+ // Since we don't have both size providers, we need to calculate the
+ // size of each row and column based on the size of the delegate items.
// This couldn't be done while we were loading the initial rows and
// columns, since during the process, we didn't have all the items
- // available yet for the calculation. So we mark that it needs to be
- // done now, from within updatePolish().
- columnRowPositionsInvalid = true;
+ // available yet for the calculation. So we do it now.
+ relayoutTable();
}
-
- qCDebug(lcTableViewDelegateLifecycle()) << tableLayoutToString();
}
void QQuickTableViewPrivate::loadInitialTopLeftItem()
@@ -958,13 +1014,12 @@ void QQuickTableViewPrivate::loadAndUnloadVisibleEdges()
return;
}
- const QRectF unloadRect = hasBufferedItems ? bufferRect() : viewportRect;
bool tableModified;
do {
tableModified = false;
- if (Qt::Edge edge = nextEdgeToUnload(unloadRect)) {
+ if (Qt::Edge edge = nextEdgeToUnload(viewportRect)) {
tableModified = true;
unloadEdge(edge);
}
@@ -1023,46 +1078,8 @@ void QQuickTableViewPrivate::drainReusePoolAfterLoadRequest()
tableModel->drainReusableItemsPool(maxTime);
}
-void QQuickTableViewPrivate::loadBuffer()
-{
- // Rather than making sure to stop the timer from all locations that can
- // violate the "buffering allowed" state, we just check that we're in the
- // right state here before we start buffering.
- if (cacheBuffer <= 0 || loadRequest.isActive() || loadedItems.isEmpty())
- return;
-
- qCDebug(lcTableViewDelegateLifecycle());
- const QRectF loadRect = bufferRect();
- while (Qt::Edge edge = nextEdgeToLoad(loadRect)) {
- loadEdge(edge, QQmlIncubator::Asynchronous);
- if (loadRequest.isActive())
- break;
- }
-
- hasBufferedItems = true;
-}
-
-void QQuickTableViewPrivate::unloadBuffer()
-{
- if (!hasBufferedItems)
- return;
-
- qCDebug(lcTableViewDelegateLifecycle());
- hasBufferedItems = false;
- cacheBufferDelayTimer.stop();
- if (loadRequest.isActive())
- cancelLoadRequest();
- while (Qt::Edge edge = nextEdgeToUnload(viewportRect))
- unloadEdge(edge);
-}
-
-QRectF QQuickTableViewPrivate::bufferRect()
-{
- return viewportRect.adjusted(-cacheBuffer, -cacheBuffer, cacheBuffer, cacheBuffer);
-}
-
void QQuickTableViewPrivate::invalidateTable() {
- tableInvalid = true;
+ rebuildState = RebuildState::NotStarted;
if (loadRequest.isActive())
cancelLoadRequest();
q_func()->polish();
@@ -1098,41 +1115,18 @@ void QQuickTableViewPrivate::updatePolish()
if (!viewportRect.isValid())
return;
- if (tableInvalid) {
- beginRebuildTable();
- if (loadRequest.isActive())
- return;
+ if (rebuildState != RebuildState::Done) {
+ processRebuildTable();
+ return;
}
- if (tableRebuilding)
- endRebuildTable();
-
- if (loadedItems.isEmpty()) {
- qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded, meaning empty model or no delegate";
+ if (loadedItems.isEmpty())
return;
- }
if (columnRowPositionsInvalid)
relayoutTable();
- if (hasBufferedItems && nextEdgeToLoad(viewportRect)) {
- // We are about to load more edges, so trim down the table as much
- // as possible to avoid loading cells that are outside the viewport.
- unloadBuffer();
- }
-
loadAndUnloadVisibleEdges();
-
- if (loadRequest.isActive())
- return;
-
- if (cacheBuffer > 0) {
- // When polish hasn't been called for a while (which means that the viewport
- // rect hasn't changed), we start buffering items. We delay this operation by
- // using a timer to increase performance (by not loading hidden items) while
- // the user is flicking.
- cacheBufferDelayTimer.start(kBufferTimerInterval);
- }
}
void QQuickTableViewPrivate::createWrapperModel()
@@ -1558,7 +1552,8 @@ qreal QQuickTableView::explicitContentWidth() const
{
Q_D(const QQuickTableView);
- if (d->tableInvalid && d->explicitContentWidth.isNull) {
+ if (d->rebuildState == QQuickTableViewPrivate::RebuildState::NotStarted
+ && d->explicitContentWidth.isNull) {
// The table is pending to be rebuilt. Since we don't
// know the contentWidth before this is done, we do the
// rebuild now, instead of waiting for the polish event.
@@ -1582,7 +1577,8 @@ qreal QQuickTableView::explicitContentHeight() const
{
Q_D(const QQuickTableView);
- if (d->tableInvalid && d->explicitContentHeight.isNull) {
+ if (d->rebuildState == QQuickTableViewPrivate::RebuildState::NotStarted
+ && d->explicitContentHeight.isNull) {
// The table is pending to be rebuilt. Since we don't
// know the contentHeight before this is done, we do the
// rebuild now, instead of waiting for the polish event.
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index 5fb5a7a9fd..53fd936195 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -66,7 +66,6 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcTableViewDelegateLifecycle)
-static const int kDefaultCacheBuffer = 300;
static const qreal kDefaultRowHeight = 50;
static const qreal kDefaultColumnWidth = 50;
@@ -164,6 +163,18 @@ public:
}
};
+ enum class RebuildState {
+ NotStarted = 0,
+ LoadInitalTable,
+ VerifyTable,
+ LayoutTable,
+ LoadAndUnloadAfterLayout,
+ PreloadColumns,
+ PreloadRows,
+ MovePreloadedItemsToPool,
+ Done
+ };
+
public:
QQuickTableViewPrivate();
~QQuickTableViewPrivate() override;
@@ -197,21 +208,16 @@ public:
QSize tableSize;
+ RebuildState rebuildState = RebuildState::NotStarted;
TableEdgeLoadRequest loadRequest;
QPoint contentSizeBenchMarkPoint = QPoint(-1, -1);
QSizeF cellSpacing = QSizeF(0, 0);
QMarginsF tableMargins;
- int cacheBuffer = kDefaultCacheBuffer;
- QTimer cacheBufferDelayTimer;
- bool hasBufferedItems = false;
-
QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable;
bool blockItemCreatedCallback = false;
- bool tableInvalid = false;
- bool tableRebuilding = false;
bool columnRowPositionsInvalid = false;
bool layoutWarningIssued = false;
bool polishing = false;
@@ -294,12 +300,11 @@ public:
void drainReusePoolAfterLoadRequest();
void cancelLoadRequest();
void processLoadRequest();
- void beginRebuildTable();
- void endRebuildTable();
- void loadBuffer();
- void unloadBuffer();
- QRectF bufferRect();
+ void processRebuildTable();
+ bool moveToNextRebuildState();
+ void beginRebuildTable();
+ void layoutAfterLoadingInitialTable();
void invalidateTable();
void invalidateColumnRowPositions();
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index 46af0f28f0..c99e149aa5 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -82,19 +82,26 @@ QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
{
}
+QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::emptyData(glyph_t glyph)
+{
+ GlyphData gd;
+ gd.texture = &s_emptyTexture;
+ QHash<glyph_t, GlyphData>::iterator it = m_glyphsData.insert(glyph, gd);
+ return it.value();
+}
+
QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::glyphData(glyph_t glyph)
{
QHash<glyph_t, GlyphData>::iterator data = m_glyphsData.find(glyph);
if (data == m_glyphsData.end()) {
- GlyphData gd;
- gd.texture = &s_emptyTexture;
+ GlyphData &gd = emptyData(glyph);
gd.path = m_referenceFont.pathForGlyph(glyph);
// need bounding rect in base font size scale
qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution);
QTransform scaleDown;
scaleDown.scale(scaleFactor, scaleFactor);
gd.boundingRect = scaleDown.mapRect(gd.path.boundingRect());
- data = m_glyphsData.insert(glyph, gd);
+ return gd;
}
return data.value();
}
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index 5a465831cb..ba5c4353b2 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -83,6 +83,7 @@ class QSGGlyphNode;
class QSGRootNode;
class QSGSpriteNode;
class QSGRenderNode;
+class QSGRenderContext;
class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx
{
@@ -507,6 +508,7 @@ protected:
uint textureIdForGlyph(glyph_t glyph) const;
GlyphData &glyphData(glyph_t glyph);
+ GlyphData &emptyData(glyph_t glyph);
#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
void saveTexture(GLuint textureId, int width, int height) const;
@@ -514,11 +516,14 @@ protected:
inline bool isCoreProfile() const { return m_coreProfile; }
-private:
+ bool m_doubleGlyphResolution;
+
+protected:
QRawFont m_referenceFont;
+
+private:
int m_glyphCount;
- bool m_doubleGlyphResolution;
bool m_coreProfile;
QList<Texture> m_textures;
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
index ef189ba461..ccc57b0b86 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -39,12 +39,18 @@
#include "qsgdefaultdistancefieldglyphcache_p.h"
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qbuffer.h>
+#include <QtQml/qqmlfile.h>
+
#include <QtGui/private/qdistancefield_p.h>
#include <QtGui/private/qopenglcontext_p.h>
#include <QtQml/private/qqmlglobal_p.h>
#include <qopenglfunctions.h>
#include <qopenglframebufferobject.h>
#include <qmath.h>
+#include "qsgcontext_p.h"
+
#if !defined(QT_OPENGL_ES_2)
#include <QtGui/qopenglfunctions_3_2_core.h>
@@ -59,10 +65,12 @@ DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSI
# define QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING 2
#endif
-QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font)
+QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c,
+ const QRawFont &font)
: QSGDistanceFieldGlyphCache(c, font)
, m_maxTextureSize(0)
, m_maxTextureCount(3)
+ , m_areaAllocator(nullptr)
, m_blitProgram(nullptr)
, m_blitBuffer(QOpenGLBuffer::VertexBuffer)
, m_fboGuard(nullptr)
@@ -81,7 +89,8 @@ QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLCont
qWarning("Buffer creation failed");
}
- m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize()));
+ // Load a pregenerated cache if the font contains one
+ loadPregeneratedCache(font);
}
QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache()
@@ -101,6 +110,9 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph
QList<GlyphPosition> glyphPositions;
QVector<glyph_t> glyphsToRender;
+ if (m_areaAllocator == nullptr)
+ m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize()));
+
for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
glyph_t glyphIndex = *it;
@@ -237,10 +249,23 @@ void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyph
m_unusedGlyphs += glyphs;
}
-void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int width, int height)
+void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+ int width,
+ int height)
+{
+ QByteArray zeroBuf(width * height, 0);
+ createTexture(texInfo, width, height, zeroBuf.constData());
+}
+
+void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+ int width,
+ int height,
+ const void *pixels)
{
- if (useTextureResizeWorkaround() && texInfo->image.isNull())
+ if (useTextureResizeWorkaround() && texInfo->image.isNull()) {
texInfo->image = QDistanceField(width, height);
+ memcpy(texInfo->image.bits(), pixels, width * height);
+ }
while (m_funcs->glGetError() != GL_NO_ERROR) { }
@@ -261,8 +286,7 @@ void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int
const GLenum format = GL_ALPHA;
#endif
- QByteArray zeroBuf(width * height, 0);
- m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, zeroBuf.constData());
+ m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, pixels);
texInfo->size = QSize(width, height);
@@ -520,4 +544,261 @@ int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const
return m_maxTextureSize;
}
+namespace {
+ struct Qtdf {
+ // We need these structs to be tightly packed, but some compilers we use do not
+ // support #pragma pack(1), so we need to hardcode the offsets/sizes in the
+ // file format
+ enum TableSize {
+ HeaderSize = 14,
+ GlyphRecordSize = 46,
+ TextureRecordSize = 17
+ };
+
+ enum Offset {
+ // Header
+ majorVersion = 0,
+ minorVersion = 1,
+ pixelSize = 2,
+ textureSize = 4,
+ flags = 8,
+ headerPadding = 9,
+ numGlyphs = 10,
+
+ // Glyph record
+ glyphIndex = 0,
+ textureOffsetX = 4,
+ textureOffsetY = 8,
+ textureWidth = 12,
+ textureHeight = 16,
+ xMargin = 20,
+ yMargin = 24,
+ boundingRectX = 28,
+ boundingRectY = 32,
+ boundingRectWidth = 36,
+ boundingRectHeight = 40,
+ textureIndex = 44,
+
+ // Texture record
+ allocatedX = 0,
+ allocatedY = 4,
+ allocatedWidth = 8,
+ allocatedHeight = 12,
+ texturePadding = 16
+
+ };
+
+ template <typename T>
+ static inline T fetch(const char *data, Offset offset)
+ {
+ return qFromBigEndian<T>(data + int(offset));
+ }
+ };
+}
+
+bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
+{
+ // The pregenerated data must be loaded first, otherwise the area allocator
+ // will be wrong
+ if (m_areaAllocator != nullptr) {
+ qWarning("Font cache must be loaded before cache is used");
+ return false;
+ }
+
+ static QElapsedTimer timer;
+
+ bool profile = QSG_LOG_TIME_GLYPH().isDebugEnabled();
+ if (profile)
+ timer.start();
+
+ QByteArray qtdfTable = font.fontTable("qtdf");
+ if (qtdfTable.isEmpty())
+ return false;
+
+ typedef QHash<TextureInfo *, QVector<glyph_t> > GlyphTextureHash;
+
+ GlyphTextureHash glyphTextures;
+
+ if (uint(qtdfTable.size()) < Qtdf::HeaderSize) {
+ qWarning("Invalid qtdf table in font '%s'",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ const char *qtdfTableStart = qtdfTable.constData();
+ const char *qtdfTableEnd = qtdfTableStart + qtdfTable.size();
+
+ int padding = 0;
+ int textureCount = 0;
+ {
+ quint8 majorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::majorVersion);
+ quint8 minorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::minorVersion);
+ if (majorVersion != 5 || minorVersion != 12) {
+ qWarning("Invalid version of qtdf table %d.%d in font '%s'",
+ majorVersion,
+ minorVersion,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ qreal pixelSize = qreal(Qtdf::fetch<quint16>(qtdfTableStart, Qtdf::pixelSize));
+ m_maxTextureSize = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::textureSize);
+ m_doubleGlyphResolution = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::flags) == 1;
+ padding = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::headerPadding);
+
+ if (pixelSize <= 0.0) {
+ qWarning("Invalid pixel size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ if (m_maxTextureSize <= 0) {
+ qWarning("Invalid texture size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ int systemMaxTextureSize;
+ m_funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &systemMaxTextureSize);
+
+ if (m_maxTextureSize > systemMaxTextureSize) {
+ qWarning("System maximum texture size is %d. This is lower than the value in '%s', which is %d",
+ systemMaxTextureSize,
+ qPrintable(font.familyName()),
+ m_maxTextureSize);
+ }
+
+ if (padding != QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING) {
+ qWarning("Padding mismatch in '%s'. Font requires %d, but Qt is compiled with %d.",
+ qPrintable(font.familyName()),
+ padding,
+ QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING);
+ }
+
+ m_referenceFont.setPixelSize(pixelSize);
+
+ quint32 glyphCount = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::numGlyphs);
+ m_unusedGlyphs.reserve(glyphCount);
+
+ const char *allocatorData = qtdfTableStart + Qtdf::HeaderSize;
+ {
+ m_areaAllocator = new QSGAreaAllocator(QSize(0, 0));
+ allocatorData = m_areaAllocator->deserialize(allocatorData, qtdfTableEnd - allocatorData);
+ if (allocatorData == nullptr)
+ return false;
+ }
+
+ if (m_areaAllocator->size().height() % m_maxTextureSize != 0) {
+ qWarning("Area allocator size mismatch in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ textureCount = m_areaAllocator->size().height() / m_maxTextureSize;
+ m_maxTextureCount = qMax(m_maxTextureCount, textureCount);
+
+ const char *textureRecord = allocatorData;
+ for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) {
+ if (textureRecord + Qtdf::TextureRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ TextureInfo *tex = textureInfo(i);
+ tex->allocatedArea.setX(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedX));
+ tex->allocatedArea.setY(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedY));
+ tex->allocatedArea.setWidth(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedWidth));
+ tex->allocatedArea.setHeight(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedHeight));
+ tex->padding = Qtdf::fetch<quint8>(textureRecord, Qtdf::texturePadding);
+ }
+
+ const char *glyphRecord = textureRecord;
+ for (quint32 i = 0; i < glyphCount; ++i, glyphRecord += Qtdf::GlyphRecordSize) {
+ if (glyphRecord + Qtdf::GlyphRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ glyph_t glyph = Qtdf::fetch<quint32>(glyphRecord, Qtdf::glyphIndex);
+ m_unusedGlyphs.insert(glyph);
+
+ GlyphData &glyphData = emptyData(glyph);
+
+#define FROM_FIXED_POINT(value) \
+(((qreal)value)/(qreal)65536)
+
+ glyphData.texCoord.x = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetX));
+ glyphData.texCoord.y = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetY));
+ glyphData.texCoord.width = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureWidth));
+ glyphData.texCoord.height = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureHeight));
+ glyphData.texCoord.xMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::xMargin));
+ glyphData.texCoord.yMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::yMargin));
+ glyphData.boundingRect.setX(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectX)));
+ glyphData.boundingRect.setY(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectY)));
+ glyphData.boundingRect.setWidth(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectWidth)));
+ glyphData.boundingRect.setHeight(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectHeight)));
+
+#undef FROM_FIXED_POINT
+
+ int textureIndex = Qtdf::fetch<quint16>(glyphRecord, Qtdf::textureIndex);
+ if (textureIndex < 0 || textureIndex >= textureCount) {
+ qWarning("Invalid texture index %d (texture count == %d) in '%s'",
+ textureIndex,
+ textureCount,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+
+ TextureInfo *texInfo = textureInfo(textureIndex);
+ m_glyphsTexture.insert(glyph, texInfo);
+
+ glyphTextures[texInfo].append(glyph);
+ }
+
+ GLint alignment = 4; // default value
+ m_funcs->glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
+
+ m_funcs->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ const uchar *textureData = reinterpret_cast<const uchar *>(glyphRecord);
+ for (int i = 0; i < textureCount; ++i) {
+
+ TextureInfo *texInfo = textureInfo(i);
+
+ int width = texInfo->allocatedArea.width();
+ int height = texInfo->allocatedArea.height();
+ qint64 size = width * height;
+ if (reinterpret_cast<const char *>(textureData + size) > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ createTexture(texInfo, width, height, textureData);
+
+ QVector<glyph_t> glyphs = glyphTextures.value(texInfo);
+
+ Texture t;
+ t.textureId = texInfo->texture;
+ t.size = texInfo->size;
+
+ setGlyphsTexture(glyphs, t);
+
+ textureData += size;
+ }
+
+ m_funcs->glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
+ }
+
+ if (profile) {
+ quint64 now = timer.elapsed();
+ qCDebug(QSG_LOG_TIME_GLYPH,
+ "distancefield: %d pre-generated glyphs loaded in %dms",
+ m_unusedGlyphs.size(),
+ (int) now);
+ }
+
+ return true;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
index 76c0d20647..a0e4387af9 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
@@ -85,7 +85,10 @@ public:
void setMaxTextureCount(int max) { m_maxTextureCount = max; }
int maxTextureCount() const { return m_maxTextureCount; }
+
private:
+ bool loadPregeneratedCache(const QRawFont &font);
+
struct TextureInfo {
GLuint texture;
QSize size;
@@ -96,6 +99,7 @@ private:
TextureInfo(const QRect &preallocRect = QRect()) : texture(0), allocatedArea(preallocRect) { }
};
+ void createTexture(TextureInfo * texInfo, int width, int height, const void *pixels);
void createTexture(TextureInfo * texInfo, int width, int height);
void resizeTexture(TextureInfo * texInfo, int width, int height);
diff --git a/src/quick/scenegraph/util/qsgareaallocator.cpp b/src/quick/scenegraph/util/qsgareaallocator.cpp
index cd270a1d63..9a8c8e333b 100644
--- a/src/quick/scenegraph/util/qsgareaallocator.cpp
+++ b/src/quick/scenegraph/util/qsgareaallocator.cpp
@@ -42,6 +42,9 @@
#include <QtCore/qglobal.h>
#include <QtCore/qrect.h>
#include <QtCore/qpoint.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qendian.h>
QT_BEGIN_NAMESPACE
@@ -285,4 +288,145 @@ void QSGAreaAllocator::mergeNodeWithNeighbors(QSGAreaAllocatorNode *node)
} // end while(!done)
}
+namespace {
+ struct AreaAllocatorTable
+ {
+ enum TableSize {
+ HeaderSize = 10,
+ NodeSize = 9
+ };
+
+ enum Offset {
+ // Header
+ majorVersion = 0,
+ minorVersion = 1,
+ width = 2,
+ height = 6,
+
+ // Node
+ split = 0,
+ splitType = 4,
+ flags = 8
+ };
+
+ enum Flags {
+ IsOccupied = 1,
+ HasLeft = 2,
+ HasRight = 4
+ };
+
+ template <typename T>
+ static inline T fetch(const char *data, Offset offset)
+ {
+ return qFromBigEndian<T>(data + int(offset));
+ }
+
+ template <typename T>
+ static inline void put(char *data, Offset offset, T value)
+ {
+ qToBigEndian(value, data + int(offset));
+ }
+ };
+}
+
+QByteArray QSGAreaAllocator::serialize()
+{
+ QVarLengthArray<QSGAreaAllocatorNode *> nodesToProcess;
+
+ QStack<QSGAreaAllocatorNode *> nodes;
+ nodes.push(m_root);
+ while (!nodes.isEmpty()) {
+ QSGAreaAllocatorNode *node = nodes.pop();
+
+ nodesToProcess.append(node);
+ if (node->left != nullptr)
+ nodes.push(node->left);
+ if (node->right != nullptr)
+ nodes.push(node->right);
+ }
+
+ QByteArray ret;
+ ret.resize(AreaAllocatorTable::HeaderSize + AreaAllocatorTable::NodeSize * nodesToProcess.size());
+
+ char *data = ret.data();
+ AreaAllocatorTable::put(data, AreaAllocatorTable::majorVersion, quint8(5));
+ AreaAllocatorTable::put(data, AreaAllocatorTable::minorVersion, quint8(12));
+ AreaAllocatorTable::put(data, AreaAllocatorTable::width, quint32(m_size.width()));
+ AreaAllocatorTable::put(data, AreaAllocatorTable::height, quint32(m_size.height()));
+
+ data += AreaAllocatorTable::HeaderSize;
+ for (QSGAreaAllocatorNode *node : nodesToProcess) {
+ AreaAllocatorTable::put(data, AreaAllocatorTable::split, qint32(node->split));
+ AreaAllocatorTable::put(data, AreaAllocatorTable::splitType, quint32(node->splitType));
+
+ quint8 flags =
+ (node->isOccupied ? AreaAllocatorTable::IsOccupied : 0)
+ | (node->left != nullptr ? AreaAllocatorTable::HasLeft : 0)
+ | (node->right != nullptr ? AreaAllocatorTable::HasRight : 0);
+ AreaAllocatorTable::put(data, AreaAllocatorTable::flags, flags);
+ data += AreaAllocatorTable::NodeSize;
+ }
+
+ return ret;
+}
+
+const char *QSGAreaAllocator::deserialize(const char *data, int size)
+{
+ if (uint(size) < AreaAllocatorTable::HeaderSize) {
+ qWarning("QSGAreaAllocator::deserialize: Data not long enough to fit header");
+ return nullptr;
+ }
+
+ const char *end = data + size;
+
+ quint8 majorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::majorVersion);
+ quint8 minorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::minorVersion);
+ if (majorVersion != 5 || minorVersion != 12) {
+ qWarning("Unrecognized version %d.%d of QSGAreaAllocator",
+ majorVersion,
+ minorVersion);
+ return nullptr;
+ }
+
+ m_size = QSize(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::width),
+ AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::height));
+
+ Q_ASSERT(m_root != nullptr);
+ Q_ASSERT(m_root->left == nullptr);
+ Q_ASSERT(m_root->right == nullptr);
+
+ QStack<QSGAreaAllocatorNode *> nodes;
+ nodes.push(m_root);
+
+ data += AreaAllocatorTable::HeaderSize;
+ while (!nodes.isEmpty()) {
+ if (data + AreaAllocatorTable::NodeSize > end) {
+ qWarning("QSGAreaAllocator::deseriable: Data not long enough for nodes");
+ return nullptr;
+ }
+
+ QSGAreaAllocatorNode *node = nodes.pop();
+
+ node->split = AreaAllocatorTable::fetch<qint32>(data, AreaAllocatorTable::split);
+ node->splitType = SplitType(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::splitType));
+
+ quint8 flags = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::flags);
+ node->isOccupied = flags & AreaAllocatorTable::IsOccupied;
+
+ if (flags & AreaAllocatorTable::HasLeft) {
+ node->left = new QSGAreaAllocatorNode(node);
+ nodes.push(node->left);
+ }
+
+ if (flags & AreaAllocatorTable::HasRight) {
+ node->right = new QSGAreaAllocatorNode(node);
+ nodes.push(node->right);
+ }
+
+ data += AreaAllocatorTable::NodeSize;
+ }
+
+ return data;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgareaallocator_p.h b/src/quick/scenegraph/util/qsgareaallocator_p.h
index 8bc05a5a5b..300a8128c0 100644
--- a/src/quick/scenegraph/util/qsgareaallocator_p.h
+++ b/src/quick/scenegraph/util/qsgareaallocator_p.h
@@ -69,6 +69,10 @@ public:
bool deallocate(const QRect &rect);
bool isEmpty() const { return m_root == nullptr; }
QSize size() const { return m_size; }
+
+ QByteArray serialize();
+ const char *deserialize(const char *data, int size);
+
private:
bool allocateInNode(const QSize &size, QPoint &result, const QRect &currentRect, QSGAreaAllocatorNode *node);
bool deallocateInNode(const QPoint &pos, QSGAreaAllocatorNode *node);
diff --git a/src/quick/util/qquicktransitionmanager.cpp b/src/quick/util/qquicktransitionmanager.cpp
index a1367249c6..e51de1a02a 100644
--- a/src/quick/util/qquicktransitionmanager.cpp
+++ b/src/quick/util/qquicktransitionmanager.cpp
@@ -91,10 +91,10 @@ void QQuickTransitionManager::complete()
{
d->applyBindings();
- for (int ii = 0; ii < d->completeList.count(); ++ii) {
- const QQmlProperty &prop = d->completeList.at(ii).property();
- prop.write(d->completeList.at(ii).value());
- }
+ // Explicitly take a copy in case the write action triggers a script that modifies the list.
+ QQuickTransitionManagerPrivate::SimpleActionList completeListCopy = d->completeList;
+ for (const QQuickSimpleAction &action : qAsConst(completeListCopy))
+ action.property().write(action.value());
d->completeList.clear();
@@ -131,7 +131,9 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
{
cancel();
+ // The copy below is ON PURPOSE, because firing actions might involve scripts that modify the list.
QQuickStateOperation::ActionList applyList = list;
+
// Determine which actions are binding changes and disable any current bindings
for (const QQuickStateAction &action : qAsConst(applyList)) {
if (action.toBinding)
@@ -156,8 +158,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
if (transition && !d->bindingsList.isEmpty()) {
// Apply all the property and binding changes
- for (int ii = 0; ii < applyList.size(); ++ii) {
- const QQuickStateAction &action = applyList.at(ii);
+ for (const QQuickStateAction &action : qAsConst(applyList)) {
if (action.toBinding) {
QQmlPropertyPrivate::setBinding(action.toBinding.data(), QQmlPropertyPrivate::None, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else if (!action.event) {
@@ -170,17 +171,15 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
}
}
- // Read all the end values for binding changes
- for (int ii = 0; ii < applyList.size(); ++ii) {
- QQuickStateAction *action = &applyList[ii];
- if (action->event) {
- action->event->saveTargetValues();
+ // Read all the end values for binding changes.
+ for (auto it = applyList.begin(), eit = applyList.end(); it != eit; ++it) {
+ if (it->event) {
+ it->event->saveTargetValues();
continue;
}
- const QQmlProperty &prop = action->property;
- if (action->toBinding || !action->toValue.isValid()) {
- action->toValue = prop.read();
- }
+ const QQmlProperty &prop = it->property;
+ if (it->toBinding || !it->toValue.isValid())
+ it->toValue = prop.read();
}
// Revert back to the original values
@@ -210,29 +209,20 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
delete oldInstance;
// Modify the action list to remove actions handled in the transition
- for (int ii = 0; ii < applyList.count(); ++ii) {
- const QQuickStateAction &action = applyList.at(ii);
-
+ auto isHandledInTransition = [this, touched](const QQuickStateAction &action) {
if (action.event) {
-
- if (action.actionDone) {
- applyList.removeAt(ii);
- --ii;
- }
-
+ return action.actionDone;
} else {
-
if (touched.contains(action.property)) {
if (action.toValue != action.fromValue)
- d->completeList <<
- QQuickSimpleAction(action, QQuickSimpleAction::EndState);
-
- applyList.removeAt(ii);
- --ii;
+ d->completeList << QQuickSimpleAction(action, QQuickSimpleAction::EndState);
+ return true;
}
-
}
- }
+ return false;
+ };
+ auto newEnd = std::remove_if(applyList.begin(), applyList.end(), isHandledInTransition);
+ applyList.erase(newEnd, applyList.end());
}
// Any actions remaining have not been handled by the transition and should
@@ -270,12 +260,9 @@ void QQuickTransitionManager::cancel()
if (d->transitionInstance && d->transitionInstance->isRunning())
d->transitionInstance->stop();
- for(int i = 0; i < d->bindingsList.count(); ++i) {
- QQuickStateAction action = d->bindingsList[i];
+ for (const QQuickStateAction &action : qAsConst(d->bindingsList)) {
if (action.toBinding && action.deletableToBinding) {
QQmlPropertyPrivate::removeBinding(action.property);
- action.toBinding = nullptr;
- action.deletableToBinding = false;
} else if (action.event) {
//### what do we do here?
}
diff --git a/tests/auto/qmltest/listview/data/MultiDelegate.qml b/tests/auto/qmltest/listview/data/MultiDelegate.qml
new file mode 100644
index 0000000000..b9640c18b4
--- /dev/null
+++ b/tests/auto/qmltest/listview/data/MultiDelegate.qml
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQml.Models 2.12
+import Qt.labs.qmlmodels 1.0
+
+ListView {
+ width: 400
+ height: 400
+ model: ListModel {
+ ListElement { dataType: "rect"; color: "red" }
+ ListElement { dataType: "image"; source: "logo.png" }
+ ListElement { dataType: "text"; text: "Hello" }
+ ListElement { dataType: "text"; text: "World" }
+ ListElement { dataType: "rect"; color: "green" }
+ ListElement { dataType: "image"; source: "logo.png" }
+ ListElement { dataType: "rect"; color: "blue" }
+ ListElement { dataType: "" }
+ }
+
+ delegate: DelegateChooser {
+ role: "dataType"
+ DelegateChoice {
+ roleValue: "image"
+ delegate: Image {
+ width: parent.width
+ height: 50
+ fillMode: Image.PreserveAspectFit
+ source: model.source
+ }
+ }
+ DelegateChoice {
+ roleValue: "rect"
+ delegate: Rectangle {
+ width: parent.width
+ height: 50
+ color: model.color
+ }
+ }
+ DelegateChoice {
+ roleValue: "text"
+ delegate: Text {
+ width: parent.width
+ height: 50
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ text: model.text
+ }
+ }
+
+ DelegateChoice {
+ delegate: Item {
+ width: parent.width
+ height: 50
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmltest/listview/data/MultiDelegate2.qml b/tests/auto/qmltest/listview/data/MultiDelegate2.qml
new file mode 100644
index 0000000000..f623204b98
--- /dev/null
+++ b/tests/auto/qmltest/listview/data/MultiDelegate2.qml
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQml.Models 2.12
+import Qt.labs.qmlmodels 1.0
+
+ListView {
+ width: 400
+ height: 400
+ model: 8
+
+ delegate: DelegateChooser {
+ DelegateChoice {
+ index: 0
+ delegate: Item {
+ property string choiceType: "1"
+ width: parent.width
+ height: 50
+ }
+ }
+ DelegateChoice {
+ index: 1
+ delegate: Item {
+ property string choiceType: "2"
+ width: parent.width
+ height: 50
+ }
+ }
+ DelegateChoice {
+ index: 2
+ delegate: Item {
+ property string choiceType: "3"
+ width: parent.width
+ height: 50
+ }
+ }
+ DelegateChoice {
+ index: 5
+ delegate: Item {
+ property string choiceType: "3"
+ width: parent.width
+ height: 50
+ }
+ }
+ DelegateChoice {
+ delegate: Item {
+ property string choiceType: "4"
+ width: parent.width
+ height: 50
+ }
+ }
+ }
+}
diff --git a/tests/auto/qmltest/listview/data/logo.png b/tests/auto/qmltest/listview/data/logo.png
new file mode 100644
index 0000000000..d75936b007
--- /dev/null
+++ b/tests/auto/qmltest/listview/data/logo.png
Binary files differ
diff --git a/tests/auto/qmltest/listview/listview.pro b/tests/auto/qmltest/listview/listview.pro
index a7938e7003..b942729ffa 100644
--- a/tests/auto/qmltest/listview/listview.pro
+++ b/tests/auto/qmltest/listview/listview.pro
@@ -1 +1,2 @@
CONFIG += qmltestcase
+DISTFILES += $$PWD/tst_listview.qml $$files($$PWD/data/*.qml)
diff --git a/tests/auto/qmltest/listview/tst_listview.qml b/tests/auto/qmltest/listview/tst_listview.qml
index f7a34cbce2..5e9bb22e8e 100644
--- a/tests/auto/qmltest/listview/tst_listview.qml
+++ b/tests/auto/qmltest/listview/tst_listview.qml
@@ -50,6 +50,7 @@
import QtQuick 2.1
import QtTest 1.1
+import "data"
Item {
id: top
@@ -204,6 +205,14 @@ Item {
ListElement { component: "data/asynclistviewloader.qml" }
}
+ MultiDelegate {
+ id: multiDelegate
+ }
+
+ MultiDelegate2 {
+ id: multiDelegate2
+ }
+
TestCase {
name: "ListView"
when: windowShown
@@ -363,5 +372,47 @@ Item {
function test_viewWithAction() {
compare(viewWithActionModel.funcResult, "one")
}
+
+ function test_multipleDelegates_data() {
+ return [
+ { y: 25, type: "Rectangle", value: "red" },
+ { y: 75, type: "Image", value: Qt.resolvedUrl("data/logo.png") },
+ { y: 125, type: "Text", value: "Hello" },
+ { y: 175, type: "Text", value: "World" },
+ { y: 225, type: "Rectangle", value: "green" },
+ { y: 275, type: "Image", value: Qt.resolvedUrl("data/logo.png") },
+ { y: 325, type: "Rectangle", value: "blue" },
+ { y: 375, type: "Item", value: "" }
+ ]
+ }
+
+ function test_multipleDelegates(row) {
+ var delegate = multiDelegate.itemAt(10, row.y)
+ verify(delegate.toString().includes(row.type))
+ switch (row.type) {
+ case "Rectangle": verify(Qt.colorEqual(delegate.color, row.value)); break
+ case "Text": compare(delegate.text, row.value); break
+ case "Image": compare(delegate.source, row.value); break
+ case "Item": break
+ }
+ }
+
+ function test_multipleDelegates2_data() {
+ return [
+ { y: 25, type: "1" },
+ { y: 75, type: "2" },
+ { y: 125, type: "3" },
+ { y: 175, type: "4" },
+ { y: 225, type: "4" },
+ { y: 275, type: "3" },
+ { y: 325, type: "4" },
+ { y: 375, type: "4" }
+ ]
+ }
+
+ function test_multipleDelegates2(row) {
+ var delegate = multiDelegate2.itemAt(10, row.y)
+ compare(delegate.choiceType, row.type)
+ }
}
}
diff --git a/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop.qml b/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop.qml
new file mode 100644
index 0000000000..889e480f3b
--- /dev/null
+++ b/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.6
+
+Item {
+ visible: true
+ Item {
+ visible: false
+ Item {
+ objectName: "hiddenChild"
+ activeFocusOnTab: true
+ focus: true
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
index f0f5873ace..7107f4d995 100644
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
@@ -64,6 +64,7 @@ private slots:
void activeFocusOnTab8();
void activeFocusOnTab9();
void activeFocusOnTab10();
+ void activeFocusOnTab_infiniteLoop();
void nextItemInFocusChain();
void nextItemInFocusChain2();
@@ -1023,6 +1024,22 @@ void tst_QQuickItem::activeFocusOnTab10()
delete window;
}
+void tst_QQuickItem::activeFocusOnTab_infiniteLoop()
+{
+ // see QTBUG-68271
+ // create a window where the currently focused item is not visible
+ QScopedPointer<QQuickView>window(new QQuickView());
+ window->setSource(testFileUrl("activeFocusOnTab_infiniteLoop.qml"));
+ window->show();
+ auto *hiddenChild = findItem<QQuickItem>(window->rootObject(), "hiddenChild");
+ QVERIFY(hiddenChild);
+
+ // move the focus - this used to result in an infinite loop
+ auto *item = hiddenChild->nextItemInFocusChain();
+ // focus is moved to the root object since there is no other candidate
+ QCOMPARE(item, window->rootObject());
+}
+
void tst_QQuickItem::nextItemInFocusChain()
{
if (!qt_tab_all_widgets())
diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
index c91085b0ff..09c2850247 100644
--- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
+++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp
@@ -85,6 +85,7 @@ private slots:
void setAndGetModel();
void emptyModel_data();
void emptyModel();
+ void checkPreload();
void checkZeroSizedDelegate();
void checkImplicitSizeDelegate();
void checkColumnWidthWithoutProvider();
@@ -191,6 +192,22 @@ void tst_QQuickTableView::emptyModel()
QCOMPARE(tableViewPrivate->loadedItems.count(), 0);
}
+void tst_QQuickTableView::checkPreload()
+{
+ // Check that the reuse pool is filled up with one extra row and
+ // column (pluss corner) after rebuilding the table.
+ LOAD_TABLEVIEW("plaintableview.qml");
+
+ auto model = TestModelAsVariant(100, 100);
+ tableView->setModel(model);
+
+ WAIT_UNTIL_POLISHED;
+
+ QSize visibleTableSize = tableViewPrivate->loadedTable.size();
+ int expectedPoolSize = visibleTableSize.height() + visibleTableSize.width() + 1;
+ QCOMPARE(tableViewPrivate->tableModel->poolSize(), expectedPoolSize);
+}
+
void tst_QQuickTableView::checkZeroSizedDelegate()
{
// Check that if we assign a delegate with empty width and height, we
@@ -1119,63 +1136,61 @@ void tst_QQuickTableView::checkRowColumnCount()
LOAD_TABLEVIEW("countingtableview.qml");
const char *maxDelegateCountProp = "maxDelegateCount";
+ const qreal delegateWidth = 100;
+ const qreal delegateHeight = 50;
auto model = TestModelAsVariant(100, 100);
tableView->setModel(model);
WAIT_UNTIL_POLISHED;
- const int tableViewCount = tableViewPrivate->loadedItems.count();
+ // We expect that the number of created items after start-up should match
+ //the size of the visible table, pluss one extra preloaded row and column.
+ const QSize visibleTableSize = tableViewPrivate->loadedTable.size();
const int qmlCountAfterInit = view->rootObject()->property(maxDelegateCountProp).toInt();
- QCOMPARE(tableViewCount, qmlCountAfterInit);
+ const int expectedCount = (visibleTableSize.width() + 1) * (visibleTableSize.height() + 1);
+ QCOMPARE(qmlCountAfterInit, expectedCount);
// This test will keep track of the maximum number of delegate items TableView
// had to show at any point while flicking (in countingtableview.qml). Because
// of the geometries chosen for TableView and the delegate, only complete columns
- // will be shown at start-up. But as we flick around, we expect the count to
- // increase with one extra column. This is because TableView sometimes end up
- // showing half a column on the left side, and half a column on the right side.
+ // will be shown at start-up.
const QRect loadedTable = tableViewPrivate->loadedTable;
QVERIFY(loadedTable.height() > loadedTable.width());
QCOMPARE(tableViewPrivate->loadedTableOuterRect.width(), tableView->width());
QCOMPARE(tableViewPrivate->loadedTableOuterRect.height(), tableView->height());
- const int expectedMaxCountAfterFlick = qmlCountAfterInit + loadedTable.height();
+
+ // Flick half an item to the left+up, to force one extra column and row to load before we
+ // start. By doing so, we end up showing the maximum number of rows and columns that will
+ // ever be shown in the view. This will make things less complicated below, when checking
+ // how many items that end up visible while flicking.
+ tableView->setContentX(delegateWidth / 2);
+ tableView->setContentY(delegateHeight / 2);
+ const int qmlCountAfterFirstFlick = view->rootObject()->property(maxDelegateCountProp).toInt();
// Flick a long distance right
tableView->setContentX(tableView->width() * 2);
- tableView->polish();
- WAIT_UNTIL_POLISHED;
-
- const int qmlCountAfterRightFlick = view->rootObject()->property(maxDelegateCountProp).toInt();
- QCOMPARE(qmlCountAfterRightFlick, expectedMaxCountAfterFlick);
+ const int qmlCountAfterLongFlick = view->rootObject()->property(maxDelegateCountProp).toInt();
+ QCOMPARE(qmlCountAfterLongFlick, qmlCountAfterFirstFlick);
// Flick a long distance down
tableView->setContentX(tableView->height() * 2);
- tableView->polish();
-
- WAIT_UNTIL_POLISHED;
const int qmlCountAfterDownFlick = view->rootObject()->property(maxDelegateCountProp).toInt();
- QCOMPARE(qmlCountAfterDownFlick, expectedMaxCountAfterFlick);
+ QCOMPARE(qmlCountAfterDownFlick, qmlCountAfterFirstFlick);
// Flick a long distance left
tableView->setContentX(0);
- tableView->polish();
-
- WAIT_UNTIL_POLISHED;
const int qmlCountAfterLeftFlick = view->rootObject()->property(maxDelegateCountProp).toInt();
- QCOMPARE(qmlCountAfterLeftFlick, expectedMaxCountAfterFlick);
+ QCOMPARE(qmlCountAfterLeftFlick, qmlCountAfterFirstFlick);
// Flick a long distance up
tableView->setContentY(0);
- tableView->polish();
-
- WAIT_UNTIL_POLISHED;
const int qmlCountAfterUpFlick = view->rootObject()->property(maxDelegateCountProp).toInt();
- QCOMPARE(qmlCountAfterUpFlick, expectedMaxCountAfterFlick);
+ QCOMPARE(qmlCountAfterUpFlick, qmlCountAfterFirstFlick);
}
void tst_QQuickTableView::modelSignals()
@@ -1309,19 +1324,29 @@ void tst_QQuickTableView::checkIfDelegatesAreReused()
LOAD_TABLEVIEW("countingtableview.qml");
const qreal delegateWidth = 100;
+ const qreal delegateHeight = 50;
const int pageFlickCount = 4;
auto model = TestModelAsVariant(100, 100);
tableView->setModel(model);
tableView->setReuseItems(reuseItems);
- // Flick half an item to the left, to force one extra column to load before we start.
+ WAIT_UNTIL_POLISHED;
+
+ // Flick half an item to the side, to force one extra row and column to load before we start.
// This will make things less complicated below, when checking how many times the items
// have been reused (all items will then report the same number).
tableView->setContentX(delegateWidth / 2);
- tableView->polish();
+ tableView->setContentY(delegateHeight / 2);
+ QCOMPARE(tableViewPrivate->tableModel->poolSize(), 0);
- WAIT_UNTIL_POLISHED;
+ // Some items have already been pooled and reused after we moved the content view, because
+ // we preload one extra row and column at start-up. So reset the count-properties back to 0
+ // before we continue.
+ for (auto fxItem : tableViewPrivate->loadedItems) {
+ fxItem->item->setProperty("pooledCount", 0);
+ fxItem->item->setProperty("reusedCount", 0);
+ }
const int visibleColumnCount = tableViewPrivate->loadedTable.width();
const int visibleRowCount = tableViewPrivate->loadedTable.height();
@@ -1330,10 +1355,6 @@ void tst_QQuickTableView::checkIfDelegatesAreReused()
for (int column = 1; column <= (visibleColumnCount * pageFlickCount); ++column) {
// Flick columns to the left (and add one pixel to ensure the left column is completely out)
tableView->setContentX((delegateWidth * column) + 1);
- tableView->polish();
-
- WAIT_UNTIL_POLISHED;
-
// Check that the number of delegate items created so far is what we expect.
const int delegatesCreatedCount = view->rootObject()->property(kDelegatesCreatedCountProp).toInt();
int expectedCount = delegateCountAfterInit + (reuseItems ? 0 : visibleRowCount * column);
@@ -1365,7 +1386,6 @@ void tst_QQuickTableView::checkIfDelegatesAreReusedAsymmetricTableSize()
// row at the end of the test.
LOAD_TABLEVIEW("countingtableview.qml");
- const int pageFlickCount = 3;
const int columnCount = 20;
const int rowCount = 2;
const qreal delegateWidth = tableView->width() / columnCount;
@@ -1393,10 +1413,13 @@ void tst_QQuickTableView::checkIfDelegatesAreReusedAsymmetricTableSize()
// times the items have been reused (all items will then report the same number).
tableView->setContentX(delegateWidth * 0.5);
tableView->setContentY(delegateHeight * 0.5);
- tableView->polish();
- WAIT_UNTIL_POLISHED;
+ // Since we have flicked half a delegate to the left, the number of visible
+ // columns is now one more than the column count were when we started the test.
+ const int visibleColumnCount = tableViewPrivate->loadedTable.width();
+ QCOMPARE(visibleColumnCount, columnCount + 1);
+ // We expect no items to have been pooled so far
pooledCount = initialTopLeftItem->property("pooledCount").toInt();
reusedCount = initialTopLeftItem->property("reusedCount").toInt();
QCOMPARE(pooledCount, 0);
@@ -1406,42 +1429,32 @@ void tst_QQuickTableView::checkIfDelegatesAreReusedAsymmetricTableSize()
// Flick one row out of view. This will move one whole row of items into the
// pool without reusing them, since no new row is exposed at the bottom.
tableView->setContentY(delegateHeight + 1);
- tableView->polish();
-
- WAIT_UNTIL_POLISHED;
-
pooledCount = initialTopLeftItem->property("pooledCount").toInt();
reusedCount = initialTopLeftItem->property("reusedCount").toInt();
QCOMPARE(pooledCount, 1);
QCOMPARE(reusedCount, 0);
- QCOMPARE(tableViewPrivate->tableModel->poolSize(), columnCount + 1);
+ QCOMPARE(tableViewPrivate->tableModel->poolSize(), visibleColumnCount);
- const int visibleColumnCount = tableViewPrivate->loadedTable.width();
const int delegateCountAfterInit = view->rootObject()->property(kDelegatesCreatedCountProp).toInt();
// Start flicking in a lot of columns, and check that the created count stays the same
- for (int column = 1; column <= (visibleColumnCount * pageFlickCount); ++column) {
- tableView->setContentX((delegateWidth * column) + 1);
- tableView->polish();
-
- WAIT_UNTIL_POLISHED;
-
+ for (int column = 1; column <= 10; ++column) {
+ tableView->setContentX((delegateWidth * column) + 10);
const int delegatesCreatedCount = view->rootObject()->property(kDelegatesCreatedCountProp).toInt();
+ // Since we reuse items while flicking, the created count should stay the same
QCOMPARE(delegatesCreatedCount, delegateCountAfterInit);
+ // Since we flick out just as many columns as we flick in, the pool size should stay the same
+ QCOMPARE(tableViewPrivate->tableModel->poolSize(), visibleColumnCount);
}
- // Finally, flick one row back into view. The pool should still contain
- // enough items so that we don't have to create any new ones.
- tableView->setContentY(delegateHeight * 2);
- tableView->polish();
-
- WAIT_UNTIL_POLISHED;
-
+ // Finally, flick one row back into view (but without flicking so far that we push the third
+ // row out and into the pool). The pool should still contain the exact amount of items that
+ // we had after we flicked the first row out. And this should be exactly the amount of items
+ // needed to load the row back again. And this also means that the pool count should then return
+ // back to 0.
+ tableView->setContentY(delegateHeight - 1);
const int delegatesCreatedCount = view->rootObject()->property(kDelegatesCreatedCountProp).toInt();
QCOMPARE(delegatesCreatedCount, delegateCountAfterInit);
-
- // After we flicked in a new row, we now expect all items
- // to be visible in the view, effectively making the pool empty.
QCOMPARE(tableViewPrivate->tableModel->poolSize(), 0);
}
diff --git a/tests/manual/tableview/abstracttablemodel/main.cpp b/tests/manual/tableview/abstracttablemodel/main.cpp
index 22dda5ca4a..6e300184be 100644
--- a/tests/manual/tableview/abstracttablemodel/main.cpp
+++ b/tests/manual/tableview/abstracttablemodel/main.cpp
@@ -40,6 +40,7 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QAbstractTableModel>
+#include <QSet>
class TestTableModel : public QAbstractTableModel
{
@@ -56,23 +57,54 @@ public:
int columnCount(const QModelIndex & = QModelIndex()) const override { return m_cols; }
void setColumnCount(int count) { beginResetModel(); m_cols = count; emit columnCountChanged(); endResetModel(); }
- QVariant headerData(int section, Qt::Orientation orientation, int role) const
+ int indexValue(const QModelIndex &index) const
{
- Q_UNUSED(orientation);
- Q_UNUSED(role);
- return QStringLiteral("Column header");
+ return index.row() + (index.column() * rowCount());
}
QVariant data(const QModelIndex &index, int role) const override
{
- if (!index.isValid() || role != Qt::DisplayRole)
+ if (!index.isValid())
return QVariant();
- return QString("[%1-%2]").arg(index.column()).arg(index.row());
+
+ switch (role) {
+ case Qt::DisplayRole:
+ return QString("%1, %2").arg(index.column()).arg(index.row());
+ case Qt::CheckStateRole:
+ return m_checkedCells.contains(indexValue(index));
+ default:
+ return QVariant();
+ }
+
+ return QVariant();
+ }
+
+ bool setData(const QModelIndex &index, const QVariant &value,
+ int role = Qt::EditRole) override
+ {
+ if (role != Qt::CheckStateRole)
+ return false;
+
+ int i = indexValue(index);
+ bool checked = value.toBool();
+ if (checked == m_checkedCells.contains(i))
+ return false;
+
+ if (checked)
+ m_checkedCells.insert(i);
+ else
+ m_checkedCells.remove(i);
+
+ emit dataChanged(index, index, {role});
+ return true;
}
QHash<int, QByteArray> roleNames() const override
{
- return { {Qt::DisplayRole, "display"} };
+ return {
+ {Qt::DisplayRole, "display"},
+ {Qt::CheckStateRole, "checked"}
+ };
}
signals:
@@ -82,6 +114,8 @@ signals:
private:
int m_rows = 0;
int m_cols = 0;
+
+ QSet<int> m_checkedCells;
};
int main(int argc, char *argv[])
diff --git a/tests/manual/tableview/abstracttablemodel/main.qml b/tests/manual/tableview/abstracttablemodel/main.qml
index a9b8da94c6..6e694c71a2 100644
--- a/tests/manual/tableview/abstracttablemodel/main.qml
+++ b/tests/manual/tableview/abstracttablemodel/main.qml
@@ -75,12 +75,21 @@ Window {
Component {
id: tableViewDelegate
Rectangle {
- implicitWidth: column % 3 ? 80 : 50
- implicitHeight: row % 3 ? 80 : 50
+ id: delegate
+ implicitWidth: 100
+ implicitHeight: 50
+ color: checked ? "lightblue" : "white"
Text {
- anchors.centerIn: parent
- text: modelData
+ anchors.fill: parent
+ text: display
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ checked = !checked
+ }
+ }
}
}
}