aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2014-07-26 09:14:44 +0200
committerSimon Hausmann <simon.hausmann@digia.com>2014-07-26 13:21:16 +0200
commit75d8ebb3e6925f500ddeefe2ab491be2ae83264c (patch)
tree6874c91386434f4a1934a9555a3f1d5daf69434c
parentfcb40ff6d71f4561401e6b2bd4d7fc706fff8eee (diff)
parentba8416b80f42c81387170620472194e7a76429b8 (diff)
Merge remote-tracking branch 'origin/5.3' into dev
Conflicts: src/qml/compiler/qv4ssa.cpp src/qml/jsruntime/qv4arrayobject.cpp src/qml/jsruntime/qv4engine.cpp Change-Id: Ie3ef6202b6a3a8521971e1be10c40c6a2db6989c
-rw-r--r--examples/quick/demos/rssnews/doc/src/rssnews.qdoc337
-rw-r--r--src/qml/compiler/qv4ssa.cpp41
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4engine.cpp6
-rw-r--r--src/qml/qml/qqmlcomponent.cpp5
-rw-r--r--src/qmltest/quicktestresult.cpp2
-rw-r--r--src/quick/items/qquickflickable.cpp6
-rw-r--r--src/quick/items/qquickgridview.cpp2
-rw-r--r--src/quick/items/qquickitemview.cpp14
-rw-r--r--src/quick/items/qquickitemview_p_p.h2
-rw-r--r--src/quick/items/qquicklistview.cpp2
-rw-r--r--src/quick/items/qquickpathview.cpp10
-rw-r--r--src/quick/items/qquickpathview_p_p.h1
-rw-r--r--src/quick/items/qquicktext.cpp8
-rw-r--r--src/quick/items/qquicktextedit.cpp6
-rw-r--r--src/quick/items/qquicktextedit_p_p.h5
-rw-r--r--src/quick/items/qquicktextnode.cpp2
-rw-r--r--src/quick/items/qquickwindow.cpp2
-rw-r--r--src/quick/scenegraph/util/qsgatlastexture.cpp3
-rw-r--r--tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml37
-rw-r--r--tests/auto/quick/qquickgridview/tst_qquickgridview.cpp9
-rw-r--r--tests/auto/quick/qquicklistview/data/simplelistview.qml11
-rw-r--r--tests/auto/quick/qquicklistview/tst_qquicklistview.cpp64
-rw-r--r--tests/auto/quick/qquickpathview/data/changePathDuringRefill.qml45
-rw-r--r--tests/auto/quick/qquickpathview/data/incorrectSteal.qml85
-rw-r--r--tests/auto/quick/qquickpathview/tst_qquickpathview.cpp98
26 files changed, 762 insertions, 45 deletions
diff --git a/examples/quick/demos/rssnews/doc/src/rssnews.qdoc b/examples/quick/demos/rssnews/doc/src/rssnews.qdoc
index 12c7c0d19b..b1a2d03cc7 100644
--- a/examples/quick/demos/rssnews/doc/src/rssnews.qdoc
+++ b/examples/quick/demos/rssnews/doc/src/rssnews.qdoc
@@ -29,13 +29,344 @@
\title Qt Quick Demo - RSS News
\ingroup qtquickdemos
\example demos/rssnews
- \brief A QML RSS news reader.
+ \brief A QML RSS news reader that uses XmlListModel and XmlRole to download
+ XML data, ListModel and ListElement to create a category list, and ListView
+ to display the data.
+
\image qtquick-demo-rssnews-small.png
- \e{RSS News} demonstrates various QML and \l{Qt Quick} features such as
- loading XML data and displaying custom components.
+ \e{RSS News} demonstrates the following \l{Qt Quick} features:
+
+ \list
+ \li Using custom types to create screens and screen controls.
+ \li Using list models and list elements to represent data.
+ \li Using XML list models to download XML data.
+ \li Using list views to display data.
+ \li Using the \l Component type to create a footer for the news item
+ list view.
+ \li Using the \l Image type to create a button for closing the app.
+ \endlist
\include examples-run.qdocinc
+ \section1 Using Custom Types
+
+ In the RSS News app, we use the following custom types that are each defined
+ in a separate .qml file:
+
+ \list
+ \li \c BusyIndicator.qml
+ \li \c CategoryDelegate.qml
+ \li \c NewsDelegate.qml
+ \li \c RssFeeds.qml
+ \li \c ScrollBar.qml
+ \endlist
+
+ To use the custom types, we add an import statement to the main QML file,
+ rssnews.qml that imports the folder called \c content where the types are
+ located:
+
+ \quotefromfile demos/rssnews/rssnews.qml
+ \skipto content
+ \printuntil "
+
+ \section1 Creating the Main Window
+
+ In rssnews.qml, we use a \l{Rectangle} type with custom properties to create
+ the app main window:
+
+ \printuntil isPortrait
+
+ We will use the custom properties later for loading XML data and for
+ adjusting the screen layout depending on its orientation.
+
+ \section1 Creating a Category List
+
+ In rssnews.qml, we use the RssFeeds custom type that we specify in
+ RssFeeds.qml to create a list of feed categories:
+
+ \skipto RssFeeds
+ \printuntil }
+
+ In RssFeeds.qml, we use a ListModel type with a ListElement type to
+ create a category list where list elements represent feed categories:
+
+ \quotefromfile demos/rssnews/content/RssFeeds.qml
+ \skipto ListModel
+ \printuntil /^\}/
+
+ List elements are defined like other QML types except that they contain a
+ collection of \e role definitions instead of properties. Roles both define
+ how the data is accessed and include the data itself.
+
+ For each list element, we use the \c name role to specify the category name,
+ the \c feed role to specify the URL to load the data from, and the \c image
+ role to display an image for the category.
+
+ In rssnews.qml, we use a ListView type to display the category list:
+
+ \quotefromfile demos/rssnews/rssnews.qml
+ \skipto ListView
+ \printuntil }
+ \printuntil }
+
+ To lay out the category list horizontally at the top of the window in
+ portrait orientation and vertically on the left side in landscape
+ orientation, we use the \c orientation property. Based on the orientation,
+ we bind either the width or the height of the list to a fixed value
+ (\c itemWidth).
+
+ We use the \c anchors.top property to position the list view at the top of
+ the screen in both orientations.
+
+ We use the \c model property to load XML data from the \c rssFeeds model,
+ and \c CategoryDelegate as the delegate to instantiate each item in the
+ list.
+
+ \section1 Creating List Elements
+
+ In CategoryDelegate.qml, we use the \l Rectangle type with custom properties
+ to create list elements:
+
+ \quotefromfile demos/rssnews/content/CategoryDelegate.qml
+ \skipto Rectangle
+ \printuntil selected
+
+ We set the \c selected property to the \c ListView.isCurrentItem attached
+ property to specify that \c selected is \c true if \c delegate is the
+ current item.
+
+ We use the \l Image type \c source property to display the image, centered
+ in the delegate, specified for the list element by the \c image role in the
+ \c rssFeeds list model:
+
+ \skipto Image
+ \printuntil }
+
+ We use a \l Text type to add titles to list elements:
+
+ \printuntil Behavior
+ \printuntil }
+
+ We use the \c anchors property to position the title at the top of the list
+ element, with a 20-pixel margin. We use \c font properties to adjust font
+ size and text formatting.
+
+ We use the \c color property to brighten the text and to scale it slightly
+ larger when the list item is the current item. By applying a \l Behavior to
+ the property, we animate the actions of selecting and deselecting list
+ items.
+
+ We use a MouseArea type to download XML data when users tap a category list
+ element:
+
+ \skipto MouseArea
+ \printuntil }
+ \printuntil }
+
+ The \c anchors.fill property is set to \c delegate to enable users to tap
+ anywhere within the list element.
+
+ We use the \c onClicked signal handler to load the XML data for the category
+ list. If the tapped category is already current, the \c reload() function
+ is called to reload the data.
+
+ \section1 Downloading XML Data
+
+ In rssnews.qml, we use an XmlListModel type as a data source for ListView
+ elements to display news items in the selected category:
+
+ \quotefromfile demos/rssnews/rssnews.qml
+ \skipto XmlListModel {
+ \printuntil namespaceDeclarations
+
+ We use the \c source property and the \c window.currentFeed custom property
+ to fetch news items for the selected category.
+
+ The \c query property specifies that the XmlListModel generates a model item
+ for each \c <item> in the XML document.
+
+ We use the XmlRole type to specify the model item attributes. Each model
+ item has the \c title, \c description, \c image, \c link, and \c pubDate
+ attributes that match the values of the corresponding \c <item> in the XML
+ document:
+
+ \printuntil pubDate
+ \printuntil }
+
+ We use the \c feedModel model in a ListView type to display the data:
+
+ \skipuntil ScrollBar
+ \skipto ListView
+ \printuntil }
+ \printuntil }
+
+ To list the news items below the category list in portrait orientation and
+ to its right in landscape orientation, we use the \c isPortrait custom
+ property to anchor the top of the news items list to the left of \c window
+ and bottom of \c categories in portrait orientation and to the right of
+ \c categories and bottom of \c window in landscape orientation.
+
+ We use the \c anchors.bottom property to anchor the bottom of the list view
+ to the bottom of the window in both orientations.
+
+ In portrait orientation, we clip the painting of the news items to the
+ bounding rectangle of the list view to avoid graphical artifacts when news
+ items are scrolled over other items. In landscape, this is not required,
+ because the list spans the entire screen vertically.
+
+ We use the \c model property to load XML data from the \c feedModel model,
+ and use \c NewsDelegate as the delegate to instantiate each item in the
+ list.
+
+ In NewsDelegate.qml, we use a \l Column type to lay out the XML data:
+
+ \quotefromfile demos/rssnews/content/NewsDelegate.qml
+ \skipto Column
+ \printuntil spacing
+
+ Within the column, we use a \l Row and another column to position images and
+ title text:
+
+ \skipto Row
+ \printuntil font.bold
+ \printuntil }
+ \printuntil }
+
+ We generate a textual representation of how long ago the item was posted
+ using the \c timeSinceEvent() JavaScript function:
+
+ \printuntil }
+ \printuntil }
+
+ We use the \c onLinkActivated signal handler to open the URL in an external
+ browser when users select the link.
+
+ \section1 Providing Feedback to Users
+
+ In CategoryDelegate.qml, we use the \c BusyIndicator custom type to indicate
+ activity while the XML data is being loaded:
+
+ \quotefromfile demos/rssnews/content/CategoryDelegate.qml
+ \skipto BusyIndicator
+ \printuntil }
+
+ We use the \c scale property to reduce the indicator size to \c 0.8. We bind
+ the \c visible property to the \c isCurrentItem attached property of the
+ \c delegate list view and \c loading property of the main window to display
+ the indicator image when a category list item is the current item and XML
+ data is being loaded.
+
+ We define the \c BusyIndicator type in \c BusyIndicator.qml. We use an
+ \l Image type to display an image and apply a NumberAnimation to its
+ \c rotation property to rotate the image in an infinite loop:
+
+ \quotefromfile demos/rssnews/content/BusyIndicator.qml
+ \skipto Image
+ \printuntil }
+ \printuntil }
+
+ In your apps, you can also use the BusyIndicator type from the
+ \l {Qt Quick Controls} module.
+
+ \section1 Creating Scroll Bars
+
+ In rssnews.qml, we use our own custom \c ScrollBar type to create scroll
+ bars in the category and news item list views. In your apps, you can also
+ use the ScrollView type from the \l {Qt Quick Controls} module.
+
+ First, we create a scroll bar in the category list view. We bind the
+ \c orientation property to the \c isPortrait property and to the
+ \c Horizontal value of the \c Qt::Orientation enum type to display a
+ horizontal scroll bar in portrait orientation and to the \c Vertical value
+ to display a vertical scroll bar in landscape orientation:
+
+ \quotefromfile demos/rssnews/rssnews.qml
+ \skipto ScrollBar
+ \printuntil }
+
+ Same as with the \c categories list view, we adjust the width and height of
+ the scroll bar based on the \c isPortrait property.
+
+ We use the \c scrollArea property to display the scroll bar in the
+ \c categories list view.
+
+ We use the \c anchors.right property to anchor the scroll bar to the right
+ side of the category list.
+
+ \skipto ScrollBar
+ \printuntil }
+
+ Second, we create another scroll bar in the news item list view. We want a
+ vertical scroll bar to appear on the right side of the view regardless of
+ screen orientation, so we can set the \c width property to \c 8 and bind the
+ \c anchors.right property to the \c window.right property. We use the
+ \c anchors.top property to anchor the scroll bar top to the bottom of the
+ category list in portrait orientation and to the top of the news item list
+ in landscape orientation. We use the \c anchors.bottom property to anchor
+ the scroll bar bottom to the list view bottom in both orientations.
+
+ We define the \c ScrollBar type in \c ScrollBar.qml. We use an \l Item type
+ with custom properties to create a container for the scroll bar:
+
+ \quotefromfile demos/rssnews/content/ScrollBar.qml
+ \skipto Item
+ \printuntil opacity
+
+ We use a BorderImage type to display the scroll bar thumb at the x and y
+ position that we calculate by using the \c position() function:
+
+ \skipto BorderImage
+ \printuntil height
+ \printuntil }
+
+ We use the \c size function to calculate the thumb width and height
+ depending on the screen orientation.
+
+ We use \c states to make the scroll bar visible when the users move the
+ scroll area:
+
+ \printuntil }
+ \printuntil }
+
+ We use \c transitions to apply a NumberAnimation to the \c "opacity"
+ property when the state changes from "visible" to the default state:
+
+ \printuntil /^\}/
+
+ \section1 Creating Footers
+
+ In rssnews.qml, we use a \l Component type with a \l Rectangle type to
+ create a footer for the news list view:
+
+ \quotefromfile demos/rssnews/rssnews.qml
+ \skipto Component
+ \printuntil }
+ \printuntil }
+ \printuntil }
+
+ We bind the \c width of the footer to the width of the component and the
+ \c height to the of close button to align them when no news items are
+ displayed.
+
+ \section1 Creating Buttons
+
+ In rssnews.qml, we use an \l Image type to create a simple push button that
+ users can tap to close the app:
+
+ \printuntil Qt.quit()
+ \printuntil }
+ \printuntil }
+ \printuntil }
+
+ We use \c anchors to position the close button in the top right corner of
+ the news list view, with 4-pixel margins. Because the close button overlaps
+ the category list in portrait orientation, we animate the \c opacity
+ property to make the button almost fully transparent when users are
+ scrolling the category list.
+
+ We use the \c onClicked signal handler within a MouseArea to call the
+ \c quit() function when users select the close button.
+
\sa {QML Applications}
*/
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
index 673d4e5db0..8488d6eb2b 100644
--- a/src/qml/compiler/qv4ssa.cpp
+++ b/src/qml/compiler/qv4ssa.cpp
@@ -141,13 +141,31 @@ public:
if (set.blockNumbers)
numberIt = set.blockNumbers->begin();
else
- flagIt = std::distance(set.blockFlags->begin(),
- std::find(set.blockFlags->begin(),
- set.blockFlags->end(),
- true));
+ findNextWithFlags(0);
}
}
+ void findNextWithFlags(size_t start)
+ {
+ flagIt = std::distance(set.blockFlags->begin(),
+ std::find(set.blockFlags->begin() + start,
+ set.blockFlags->end(),
+ true));
+
+ // The ++operator of std::vector<bool>::iterator in libc++ has a bug when using it on an
+ // iterator pointing to the last element. It will not be set to ::end(), but beyond
+ // that. (It will be set to the first multiple of the native word size that is bigger
+ // than size().)
+ //
+ // See http://llvm.org/bugs/show_bug.cgi?id=19663
+ //
+ // As we use the size to for our end() iterator, take the minimum of the size and the
+ // distance for the flagIt:
+ flagIt = qMin(flagIt, set.blockFlags->size());
+
+ Q_ASSERT(flagIt <= set.blockFlags->size());
+ }
+
public:
BasicBlock *operator*() const
{
@@ -175,17 +193,10 @@ public:
const_iterator &operator++()
{
- if (set.blockNumbers) {
- if (numberIt != set.blockNumbers->end())
- ++numberIt;
- } else if (flagIt < set.blockFlags->size()) {
- flagIt = std::distance(set.blockFlags->begin(),
- std::find(set.blockFlags->begin() + flagIt + 1,
- set.blockFlags->end(),
- true));
- if (flagIt > set.blockFlags->size())
- flagIt = set.blockFlags->size();
- }
+ if (set.blockNumbers)
+ ++numberIt;
+ else
+ findNextWithFlags(flagIt + 1);
return *this;
}
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index 838c541900..abe8a44065 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -366,7 +366,7 @@ ReturnedValue ArrayPrototype::method_shift(CallContext *ctx)
ScopedValue result(scope);
- if (!instance->protoHasArray() && !instance->arrayData()->hasAttributes() && instance->arrayData()->length() <= len) {
+ if (!instance->protoHasArray() && !instance->arrayData()->hasAttributes() && instance->arrayData()->length() <= len && instance->arrayData()->type() != ArrayData::Custom) {
result = instance->arrayData()->vtable()->pop_front(instance.getPointer());
} else {
result = instance->getIndexed(0);
@@ -545,7 +545,7 @@ ReturnedValue ArrayPrototype::method_unshift(CallContext *ctx)
uint len = instance->getLength();
- if (!instance->protoHasArray() && !instance->arrayData()->hasAttributes() && instance->arrayData()->length() <= len) {
+ if (!instance->protoHasArray() && !instance->arrayData()->hasAttributes() && instance->arrayData()->length() <= len && instance->arrayData()->type() != ArrayData::Custom) {
instance->arrayData()->vtable()->push_front(instance.getPointer(), ctx->d()->callData->args, ctx->d()->callData->argc);
} else {
ScopedValue v(scope);
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index f2cfc3efd2..7be518916d 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -851,7 +851,11 @@ void ExecutionEngine::markObjects()
ExecutionContext *c = currentContext();
while (c) {
- c->mark(this);
+ Q_ASSERT(c->inUse());
+ if (!c->markBit()) {
+ c->d()->markBit = 1;
+ c->markObjects(c, this);
+ }
c = c->d()->parent;
}
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 40b6a34f07..616f54d174 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -780,6 +780,11 @@ QQmlComponent::QQmlComponent(QQmlComponentPrivate &dd, QObject *parent)
The ownership of the returned object instance is transferred to the caller.
+ If the object being created from this component is a visual item, it must
+ have a visual parent, which can be set by calling
+ QQuickItem::setParentItem(). See \l {Concepts - Visual Parent in Qt Quick}
+ for more details.
+
\sa QQmlEngine::ObjectOwnership
*/
QObject *QQmlComponent::create(QQmlContext *context)
diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp
index ab37be3c95..3329fd72e6 100644
--- a/src/qmltest/quicktestresult.cpp
+++ b/src/qmltest/quicktestresult.cpp
@@ -685,7 +685,7 @@ void QuickTestResult::stopBenchmark()
QObject *QuickTestResult::grabImage(QQuickItem *item)
{
- if (item) {
+ if (item && item->window()) {
QQuickWindow *window = item->window();
QImage grabbed = window->grabWindow();
QRectF rf(item->x(), item->y(), item->width(), item->height());
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index 45566f2e89..ee71ea8a76 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -327,7 +327,7 @@ bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExt
maxDistance = qAbs(maxExtent - data.move.value());
data.flickTarget = maxExtent;
}
- if (maxDistance > 0) {
+ if (maxDistance > 0 || boundsBehavior == QQuickFlickable::DragAndOvershootBounds) {
qreal v = velocity;
if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
if (v < 0)
@@ -1541,6 +1541,10 @@ void QQuickFlickable::geometryChanged(const QRectF &newGeometry,
void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
{
Q_D(QQuickFlickable);
+ d->hData.reset();
+ d->vData.reset();
+ d->hData.velocity = xVelocity;
+ d->vData.velocity = yVelocity;
bool flickedX = d->flickX(xVelocity);
bool flickedY = d->flickY(yVelocity);
d->flickingStarted(flickedX, flickedY);
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index 77ff275619..5b928310cd 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -1616,7 +1616,7 @@ void QQuickGridView::setFlow(Flow flow)
}
setContentX(0);
setContentY(0);
- d->regenerate();
+ d->regenerate(true);
emit flowChanged();
}
}
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index ef696dae96..2a686b4342 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -1783,18 +1783,20 @@ void QQuickItemViewPrivate::refill(qreal from, qreal to)
emit q->countChanged();
}
-void QQuickItemViewPrivate::regenerate()
+void QQuickItemViewPrivate::regenerate(bool orientationChanged)
{
Q_Q(QQuickItemView);
if (q->isComponentComplete()) {
currentChanges.reset();
- delete header;
- header = 0;
- delete footer;
- footer = 0;
+ if (orientationChanged) {
+ delete header;
+ header = 0;
+ delete footer;
+ footer = 0;
+ }
+ clear();
updateHeader();
updateFooter();
- clear();
updateViewport();
setPosition(contentStartOffset());
refill();
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index 3f0f3b3646..7c8656cced 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -184,7 +184,7 @@ public:
virtual void clear();
virtual void updateViewport();
- void regenerate();
+ void regenerate(bool orientationChanged=false);
void layout();
virtual void animationFinished(QAbstractAnimationJob *);
void refill();
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index a1388e3512..e8043804fb 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -2072,7 +2072,7 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
setFlickableDirection(HorizontalFlick);
setContentY(0);
}
- d->regenerate();
+ d->regenerate(true);
emit orientationChanged();
}
}
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index de5ed99640..efce244f7d 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -104,6 +104,7 @@ QQuickPathViewPrivate::QQuickPathViewPrivate()
, stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true)
, autoHighlight(true), highlightUp(false), layoutScheduled(false)
, moving(false), flicking(false), dragging(false), inRequest(false), delegateValidated(false)
+ , inRefill(false)
, dragMargin(0), deceleration(100), maximumFlickVelocity(QML_FLICK_DEFAULTMAXVELOCITY)
, moveOffset(this, &QQuickPathViewPrivate::setAdjustedOffset), flickDuration(0)
, firstIndex(-1), pathItems(-1), requestedIndex(-1), cacheSize(0), requestedZ(0)
@@ -1873,11 +1874,18 @@ void QQuickPathView::refill()
{
Q_D(QQuickPathView);
+ if (d->inRefill) {
+ d->scheduleLayout();
+ return;
+ }
+
d->layoutScheduled = false;
if (!d->isValid() || !isComponentComplete())
return;
+ d->inRefill = true;
+
bool currentVisible = false;
int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
@@ -2010,6 +2018,8 @@ void QQuickPathView::refill()
}
while (d->itemCache.count())
d->releaseItem(d->itemCache.takeLast());
+
+ d->inRefill = false;
}
void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h
index 813f472072..e21f3757e6 100644
--- a/src/quick/items/qquickpathview_p_p.h
+++ b/src/quick/items/qquickpathview_p_p.h
@@ -152,6 +152,7 @@ public:
bool requestedOnPath : 1;
bool inRequest : 1;
bool delegateValidated : 1;
+ bool inRefill : 1;
QElapsedTimer timer;
qint64 lastPosTime;
QPointF lastPos;
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 7a4027890c..71265689e9 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -711,7 +711,8 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
}
bool shouldUseDesignMetrics = renderType != QQuickText::NativeRendering;
-
+ if (!visibleImgTags.isEmpty())
+ visibleImgTags.clear();
layout.setCacheEnabled(true);
QTextOption textOption = layout.textOption();
if (textOption.alignment() != q->effectiveHAlign()
@@ -940,9 +941,10 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
maxHeight = q->heightValid() ? q->height() : FLT_MAX;
// If the width of the item has changed and it's possible the result of wrapping,
- // eliding, or scaling has changed do another layout.
+ // eliding, scaling has changed, or the text is not left aligned do another layout.
if ((lineWidth < qMin(oldWidth, naturalWidth) || (widthExceeded && lineWidth > oldWidth))
- && (singlelineElide || multilineElide || canWrap || horizontalFit)) {
+ && (singlelineElide || multilineElide || canWrap || horizontalFit
+ || q->effectiveHAlign() != QQuickText::AlignLeft)) {
widthExceeded = false;
heightExceeded = false;
continue;
diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp
index b042e30483..fdaef6df8e 100644
--- a/src/quick/items/qquicktextedit.cpp
+++ b/src/quick/items/qquicktextedit.cpp
@@ -1777,8 +1777,12 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
d->updateType = QQuickTextEditPrivate::UpdateNone;
- if (!oldNode) // If we had any text node references, they were deleted along with the root node
+ if (!oldNode) {
+ // If we had any QQuickTextNode node references, they were deleted along with the root node
+ // But here we must delete the Node structures in textNodeMap
+ qDeleteAll(d->textNodeMap);
d->textNodeMap.clear();
+ }
RootNode *rootNode = static_cast<RootNode *>(oldNode);
TextNodeIterator nodeIterator = d->textNodeMap.begin();
diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h
index 25681c89f6..0cf7ee2850 100644
--- a/src/quick/items/qquicktextedit_p_p.h
+++ b/src/quick/items/qquicktextedit_p_p.h
@@ -111,6 +111,11 @@ public:
{
}
+ ~QQuickTextEditPrivate()
+ {
+ qDeleteAll(textNodeMap);
+ }
+
static QQuickTextEditPrivate *get(QQuickTextEdit *item) {
return static_cast<QQuickTextEditPrivate *>(QObjectPrivate::get(item)); }
diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp
index da0d9cd714..02e321dfba 100644
--- a/src/quick/items/qquicktextnode.cpp
+++ b/src/quick/items/qquicktextnode.cpp
@@ -303,6 +303,8 @@ void QQuickTextNode::deleteContent()
while (firstChild() != 0)
delete firstChild();
m_cursorNode = 0;
+ qDeleteAll(m_textures);
+ m_textures.clear();
}
#if 0
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index a7659e4871..37525e6151 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -3335,7 +3335,7 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
\warning The returned texture is not memory managed by the scene graph and
must be explicitly deleted by the caller on the rendering thread.
- This is acheived by deleting the texture from a QSGNode destructor
+ This is achieved by deleting the texture from a QSGNode destructor
or by using deleteLater() in the case where the texture already has affinity
to the rendering thread.
diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp
index ceeaa977d7..c92d79dc2a 100644
--- a/src/quick/scenegraph/util/qsgatlastexture.cpp
+++ b/src/quick/scenegraph/util/qsgatlastexture.cpp
@@ -270,6 +270,9 @@ void Atlas::uploadBgra(Texture *texture)
const QRect &r = texture->atlasSubRect();
QImage image = texture->image();
+ if (image.isNull())
+ return;
+
if (image.format() != QImage::Format_ARGB32_Premultiplied
&& image.format() != QImage::Format_RGB32) {
image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml
index 8847055a70..5103168fd3 100644
--- a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml
+++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml
@@ -165,6 +165,43 @@ Item {
}
}
}
+
+ // unshift
+ msco.stringListProperty = [ "one", "two" ]
+ var unshiftedVal = msco.stringListProperty.unshift("zero")
+ expected = [ "zero", "one", "two" ]
+ if (msco.stringListProperty.toString() != expected.toString()) success = false;
+ expected = 3
+ if (msco.stringListProperty.length != expected) success = false
+ msco.stringListProperty = [ ]
+ msco.stringListProperty.unshift("zero", "one")
+ expected = [ "zero", "one" ]
+ if (msco.stringListProperty.toString() != expected.toString()) success = false;
+ expected = 2
+ if (msco.stringListProperty.length != expected) success = false
+
+ // shift
+ msco.stringListProperty = [ "one", "two", "three" ]
+ var shiftVal = msco.stringListProperty.shift()
+ expected = [ "two", "three" ]
+ if (msco.stringListProperty.toString() != expected.toString()) success = false;
+ expected = "one"
+ if (shiftVal != expected) success = false
+ shiftVal = msco.stringListProperty.shift()
+ expected = [ "three" ]
+ if (msco.stringListProperty.toString() != expected.toString()) success = false;
+ expected = "two"
+ if (shiftVal != expected) success = false
+ shiftVal = msco.stringListProperty.shift()
+ expected = 0
+ if (msco.stringListProperty.length != expected) success = false;
+ expected = "three"
+ if (shiftVal != expected) success = false
+ shiftVal = msco.stringListProperty.shift()
+ expected = 0
+ if (msco.stringListProperty.length != expected) success = false;
+ expected = undefined
+ if (shiftVal != expected) success = false
}
property variant variantList: [ 1, 2, 3, 4, 5 ];
diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
index 863fb69b84..a350074b42 100644
--- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
+++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
@@ -3613,12 +3613,13 @@ void tst_QQuickGridView::resetModel_headerFooter()
model.reset();
- header = findItem<QQuickItem>(contentItem, "header");
- QVERIFY(header);
+ // A reset should not force a new header or footer to be created.
+ QQuickItem *newHeader = findItem<QQuickItem>(contentItem, "header");
+ QVERIFY(newHeader == header);
QCOMPARE(header->y(), -header->height());
- footer = findItem<QQuickItem>(contentItem, "footer");
- QVERIFY(footer);
+ QQuickItem *newFooter = findItem<QQuickItem>(contentItem, "footer");
+ QVERIFY(newFooter == footer);
QCOMPARE(footer->y(), 60.*2);
delete window;
diff --git a/tests/auto/quick/qquicklistview/data/simplelistview.qml b/tests/auto/quick/qquicklistview/data/simplelistview.qml
new file mode 100644
index 0000000000..56a96150c5
--- /dev/null
+++ b/tests/auto/quick/qquicklistview/data/simplelistview.qml
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+
+ListView {
+ width: 400
+ height: 400
+ model: 100
+ delegate: Rectangle {
+ height: 40; width: 400
+ color: index % 2 ? "lightsteelblue" : "lightgray"
+ }
+}
diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
index b909d14301..c8ab62cbbc 100644
--- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
@@ -231,6 +231,9 @@ private slots:
void roundingErrors();
void roundingErrors_data();
+ void QTBUG_38209();
+ void programmaticFlickAtBounds();
+
private:
template <class T> void items(const QUrl &source);
template <class T> void changed(const QUrl &source);
@@ -4081,12 +4084,13 @@ void tst_QQuickListView::resetModel_headerFooter()
model.reset();
- header = findItem<QQuickItem>(contentItem, "header");
- QVERIFY(header);
+ // A reset should not force a new header or footer to be created.
+ QQuickItem *newHeader = findItem<QQuickItem>(contentItem, "header");
+ QVERIFY(newHeader == header);
QCOMPARE(header->y(), -header->height());
- footer = findItem<QQuickItem>(contentItem, "footer");
- QVERIFY(footer);
+ QQuickItem *newFooter = findItem<QQuickItem>(contentItem, "footer");
+ QVERIFY(newFooter == footer);
QCOMPARE(footer->y(), 30.*4);
delete window;
@@ -7362,6 +7366,58 @@ void tst_QQuickListView::roundingErrors_data()
QTest::newRow("pixelAligned=false") << false;
}
+void tst_QQuickListView::QTBUG_38209()
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("simplelistview.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickListView *listview = qobject_cast<QQuickListView *>(window->rootObject());
+ QVERIFY(listview);
+
+ // simulate mouse flick
+ flick(window.data(), QPoint(200, 200), QPoint(200, 50), 100);
+ QTRY_VERIFY(listview->isMoving() == false);
+ qreal contentY = listview->contentY();
+
+ // flick down
+ listview->flick(0, 1000);
+
+ // ensure we move more than just a couple pixels
+ QTRY_VERIFY(contentY - listview->contentY() > qreal(100.0));
+}
+
+void tst_QQuickListView::programmaticFlickAtBounds()
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("simplelistview.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickListView *listview = qobject_cast<QQuickListView *>(window->rootObject());
+ QVERIFY(listview);
+ QSignalSpy spy(listview, SIGNAL(contentYChanged()));
+
+ // flick down
+ listview->flick(0, 1000);
+
+ // verify that there is movement beyond bounds
+ QVERIFY(spy.wait(100));
+
+ // reset, and test with StopAtBounds
+ listview->cancelFlick();
+ listview->returnToBounds();
+ QTRY_COMPARE(listview->contentY(), qreal(0.0));
+ listview->setBoundsBehavior(QQuickFlickable::StopAtBounds);
+
+ // flick down
+ listview->flick(0, 1000);
+
+ // verify that there is no movement beyond bounds
+ QVERIFY(!spy.wait(100));
+}
+
QTEST_MAIN(tst_QQuickListView)
#include "tst_qquicklistview.moc"
diff --git a/tests/auto/quick/qquickpathview/data/changePathDuringRefill.qml b/tests/auto/quick/qquickpathview/data/changePathDuringRefill.qml
new file mode 100644
index 0000000000..f02ab35faf
--- /dev/null
+++ b/tests/auto/quick/qquickpathview/data/changePathDuringRefill.qml
@@ -0,0 +1,45 @@
+import QtQuick 2.3
+
+PathView {
+ id: view
+ objectName: "pathView"
+ width: 100
+ height: delegateHeight * pathItemCount
+ model: ["A", "B", "C"]
+ pathItemCount: 3
+ anchors.centerIn: parent
+
+ property int delegateHeight: 0
+
+ activeFocusOnTab: true
+ Keys.onDownPressed: view.incrementCurrentIndex()
+ Keys.onUpPressed: view.decrementCurrentIndex()
+ preferredHighlightBegin: 0.5
+ preferredHighlightEnd: 0.5
+
+ delegate: Rectangle {
+ objectName: "delegate" + modelData
+ width: view.width
+ height: textItem.height
+ border.color: "red"
+
+ onHeightChanged: {
+ if (index == 0)
+ view.delegateHeight = textItem.height
+ }
+
+ Text {
+ id: textItem
+ text: modelData
+ }
+ }
+
+ path: Path {
+ startX: view.width / 2
+ startY: 0
+ PathLine {
+ x: view.width / 2
+ y: view.pathItemCount * view.delegateHeight
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickpathview/data/incorrectSteal.qml b/tests/auto/quick/qquickpathview/data/incorrectSteal.qml
new file mode 100644
index 0000000000..bcd6923b73
--- /dev/null
+++ b/tests/auto/quick/qquickpathview/data/incorrectSteal.qml
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 Digia Plc and its Subsidiary(-ies) 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.0
+
+Flickable {
+ objectName: "flickable"
+ width: 400; height: 400
+
+ contentHeight: height
+ contentWidth: width * 3
+ contentX: 400
+
+ Row {
+ Rectangle { width: 400; height: 400; color: "green" }
+ Rectangle {
+ width: 400; height: 400; color: "blue"
+ clip: true
+
+ PathView {
+ id: pathView
+ objectName: "pathView"
+ width: parent.width
+ height: 200
+ anchors.verticalCenter: parent.verticalCenter
+
+ dragMargin: 400
+ pathItemCount: 6
+
+ model: 10
+ path: Path {
+ startX: -pathView.width / 2
+ startY: pathView.height / 2
+ PathLine { x: pathView.width + pathView.width / 2; y: pathView.height / 2 }
+ }
+
+ delegate: Rectangle {
+ width: 100; height: 200
+ color: "purple"
+ MouseArea {
+ anchors.fill: parent
+ }
+ }
+ }
+ }
+ Rectangle { width: 400; height: 400; color: "yellow" }
+ }
+}
diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
index 579cb954aa..1960775ad3 100644
--- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
+++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
@@ -47,6 +47,7 @@
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlincubator.h>
#include <QtQuick/private/qquickpathview_p.h>
+#include <QtQuick/private/qquickflickable_p.h>
#include <QtQuick/private/qquickpath_p.h>
#include <QtQuick/private/qquicktext_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
@@ -142,6 +143,8 @@ private slots:
void indexAt_itemAt();
void indexAt_itemAt_data();
void cacheItemCount();
+ void incorrectSteal();
+ void changePathDuringRefill();
};
class TestObject : public QObject
@@ -2120,7 +2123,102 @@ void tst_QQuickPathView::cacheItemCount()
bool b = true;
controller.incubateWhile(&b);
}
+}
+
+static void testCurrentIndexChange(QQuickPathView *pathView, const QStringList &objectNamesInOrder)
+{
+ for (int visualIndex = 0; visualIndex < objectNamesInOrder.size() - 1; ++visualIndex) {
+ QQuickRectangle *delegate = findItem<QQuickRectangle>(pathView, objectNamesInOrder.at(visualIndex));
+ QVERIFY(delegate);
+
+ QQuickRectangle *nextDelegate = findItem<QQuickRectangle>(pathView, objectNamesInOrder.at(visualIndex + 1));
+ QVERIFY(nextDelegate);
+
+ QVERIFY(delegate->y() < nextDelegate->y());
+ }
+}
+
+void tst_QQuickPathView::changePathDuringRefill()
+{
+ QScopedPointer<QQuickView> window(createView());
+
+ window->setSource(testFileUrl("changePathDuringRefill.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowActive(window.data()));
+ QCOMPARE(window.data(), qGuiApp->focusWindow());
+
+ QQuickPathView *pathView = qobject_cast<QQuickPathView*>(window->rootObject());
+ QVERIFY(pathView != 0);
+
+ testCurrentIndexChange(pathView, QStringList() << "delegateC" << "delegateA" << "delegateB");
+
+ pathView->incrementCurrentIndex();
+ /*
+ Decrementing moves delegateA down, resulting in an offset of 1,
+ so incrementing will move it up, resulting in an offset of 2:
+
+ delegateC delegateA
+ delegateA => delegateB
+ delegateB delegateC
+ */
+ QTRY_COMPARE(pathView->offset(), 2.0);
+ testCurrentIndexChange(pathView, QStringList() << "delegateA" << "delegateB" << "delegateC");
+}
+
+void tst_QQuickPathView::incorrectSteal()
+{
+ QScopedPointer<QQuickView> window(createView());
+ QQuickViewTestUtil::moveMouseAway(window.data());
+ window->setSource(testFileUrl("incorrectSteal.qml"));
+ window->show();
+ window->requestActivate();
+ QVERIFY(QTest::qWaitForWindowActive(window.data()));
+ QCOMPARE(window.data(), qGuiApp->focusWindow());
+
+ QQuickPathView *pathview = findItem<QQuickPathView>(window->rootObject(), "pathView");
+ QVERIFY(pathview != 0);
+
+ QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject());
+ QVERIFY(flickable != 0);
+
+ QSignalSpy movingSpy(pathview, SIGNAL(movingChanged()));
+ QSignalSpy moveStartedSpy(pathview, SIGNAL(movementStarted()));
+ QSignalSpy moveEndedSpy(pathview, SIGNAL(movementEnded()));
+
+ QSignalSpy fflickingSpy(flickable, SIGNAL(flickingChanged()));
+ QSignalSpy fflickStartedSpy(flickable, SIGNAL(flickStarted()));
+ QSignalSpy fflickEndedSpy(flickable, SIGNAL(flickEnded()));
+
+ int waitInterval = 5;
+ QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(23,218));
+
+ QTest::mouseMove(window.data(), QPoint(25,218), waitInterval);
+ QTest::mouseMove(window.data(), QPoint(26,218), waitInterval);
+ QTest::mouseMove(window.data(), QPoint(28,219), waitInterval);
+ QTest::mouseMove(window.data(), QPoint(31,219), waitInterval);
+ QTest::mouseMove(window.data(), QPoint(39,219), waitInterval);
+
+ // first move beyond threshold does not trigger drag
+ QVERIFY(!pathview->isMoving());
+ QVERIFY(!pathview->isDragging());
+ QCOMPARE(movingSpy.count(), 0);
+ QCOMPARE(moveStartedSpy.count(), 0);
+ QCOMPARE(moveEndedSpy.count(), 0);
+ QCOMPARE(fflickingSpy.count(), 0);
+ QCOMPARE(fflickStartedSpy.count(), 0);
+ QCOMPARE(fflickEndedSpy.count(), 0);
+
+ // no further moves after the initial move beyond threshold
+ QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(53,219));
+ QTRY_COMPARE(movingSpy.count(), 2);
+ QTRY_COMPARE(moveEndedSpy.count(), 1);
+ QCOMPARE(moveStartedSpy.count(), 1);
+ // Flickable should not handle this
+ QEXPECT_FAIL("", "QTBUG-37859", Abort);
+ QCOMPARE(fflickingSpy.count(), 0);
+ QCOMPARE(fflickStartedSpy.count(), 0);
+ QCOMPARE(fflickEndedSpy.count(), 0);
}
QTEST_MAIN(tst_QQuickPathView)