aboutsummaryrefslogtreecommitdiffstats
path: root/src/declarative/items
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@nokia.com>2011-08-25 11:47:24 +0200
committerGunnar Sletta <gunnar.sletta@nokia.com>2011-08-25 12:52:15 +0200
commit8e6ecb56e5d61ce661422779c9d9cbf22f081a34 (patch)
tree227aeb360800a49de58472bd39bc5f4c596c1b15 /src/declarative/items
parentc9224b6cf5cceb7d5314f7504d44bfe72bc66950 (diff)
parent0d84e957297b4ffa6ab5b0b5bcf8b169d567d298 (diff)
Merge branch 'master' into refactor
Conflicts: src/declarative/items/qsgcanvas.cpp src/declarative/items/qsgitem.cpp src/declarative/items/qsgtextnode.cpp tests/auto/declarative/examples/examples.pro tools/qmlviewer/qmlviewer.pro Change-Id: Icbb0ef5dc79b658c62fd2b2c25a66c9bb3cbeb10
Diffstat (limited to 'src/declarative/items')
-rw-r--r--src/declarative/items/qsganimatedimage.cpp95
-rw-r--r--src/declarative/items/qsgborderimage.cpp249
-rw-r--r--src/declarative/items/qsgcanvas.cpp71
-rw-r--r--src/declarative/items/qsgcanvas_p.h4
-rw-r--r--src/declarative/items/qsgevents.cpp193
-rw-r--r--src/declarative/items/qsgflickable.cpp278
-rw-r--r--src/declarative/items/qsgflipable.cpp54
-rw-r--r--src/declarative/items/qsgfocusscope.cpp16
-rw-r--r--src/declarative/items/qsggridview.cpp495
-rw-r--r--src/declarative/items/qsgimage.cpp4
-rw-r--r--src/declarative/items/qsgitem.cpp1932
-rw-r--r--src/declarative/items/qsgitem.h1
-rw-r--r--src/declarative/items/qsgitemsmodule.cpp1
-rw-r--r--src/declarative/items/qsgitemview.cpp66
-rw-r--r--src/declarative/items/qsgitemview_p.h34
-rw-r--r--src/declarative/items/qsgitemview_p_p.h47
-rw-r--r--src/declarative/items/qsglistview.cpp589
-rw-r--r--src/declarative/items/qsgloader.cpp205
-rw-r--r--src/declarative/items/qsgmousearea.cpp368
-rw-r--r--src/declarative/items/qsgmousearea_p.h2
-rw-r--r--src/declarative/items/qsgpainteditem.cpp9
-rw-r--r--src/declarative/items/qsgpathview.cpp412
-rw-r--r--src/declarative/items/qsgpathview_p.h4
-rw-r--r--src/declarative/items/qsgpathview_p_p.h2
-rw-r--r--src/declarative/items/qsgpincharea.cpp182
-rw-r--r--src/declarative/items/qsgpositioners.cpp755
-rw-r--r--src/declarative/items/qsgpositioners_p.h51
-rw-r--r--src/declarative/items/qsgrectangle.cpp294
-rw-r--r--src/declarative/items/qsgrectangle_p_p.h12
-rw-r--r--src/declarative/items/qsgrepeater.cpp146
-rw-r--r--src/declarative/items/qsgscalegrid.cpp2
-rw-r--r--src/declarative/items/qsgshadereffectsource.cpp21
-rw-r--r--src/declarative/items/qsgtext.cpp455
-rw-r--r--src/declarative/items/qsgtext_p_p.h7
-rw-r--r--src/declarative/items/qsgtextedit.cpp850
-rw-r--r--src/declarative/items/qsgtextedit_p.h9
-rw-r--r--src/declarative/items/qsgtextedit_p_p.h22
-rw-r--r--src/declarative/items/qsgtextinput.cpp832
-rw-r--r--src/declarative/items/qsgtextinput_p.h4
-rw-r--r--src/declarative/items/qsgtextinput_p_p.h39
-rw-r--r--src/declarative/items/qsgtextnode.cpp781
-rw-r--r--src/declarative/items/qsgtextnode_p.h38
-rw-r--r--src/declarative/items/qsgtranslate.cpp22
-rw-r--r--src/declarative/items/qsgvisualitemmodel.cpp729
44 files changed, 9615 insertions, 767 deletions
diff --git a/src/declarative/items/qsganimatedimage.cpp b/src/declarative/items/qsganimatedimage.cpp
index 0a9fdd1783..ea36153ef0 100644
--- a/src/declarative/items/qsganimatedimage.cpp
+++ b/src/declarative/items/qsganimatedimage.cpp
@@ -52,6 +52,75 @@
#include <private/qdeclarativeengine_p.h>
QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass AnimatedImage QSGAnimatedImage
+ \inqmlmodule QtQuick 2
+ \inherits Image
+ \ingroup basic-visual-elements
+
+ The AnimatedImage element extends the features of the \l Image element, providing
+ a way to play animations stored as images containing a series of frames,
+ such as those stored in GIF files.
+
+ Information about the current frame and totla length of the animation can be
+ obtained using the \l currentFrame and \l frameCount properties. You can
+ start, pause and stop the animation by changing the values of the \l playing
+ and \l paused properties.
+
+ The full list of supported formats can be determined with QMovie::supportedFormats().
+
+ \section1 Example Usage
+
+ \beginfloatleft
+ \image animatedimageitem.gif
+ \endfloat
+
+ The following QML shows how to display an animated image and obtain information
+ about its state, such as the current frame and total number of frames.
+ The result is an animated image with a simple progress indicator underneath it.
+
+ \bold Note: Unlike images, animated images are not cached or shared internally.
+
+ \clearfloat
+ \snippet doc/src/snippets/declarative/animatedimage.qml document
+
+ \sa BorderImage, Image
+*/
+
+/*!
+ \qmlproperty url QtQuick2::AnimatedImage::source
+
+ This property holds the URL that refers to the source image.
+
+ AnimatedImage can handle any image format supported by Qt, loaded from any
+ URL scheme supported by Qt.
+
+ \sa QDeclarativeImageProvider
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::AnimatedImage::asynchronous
+
+ Specifies that images on the local filesystem should be loaded
+ asynchronously in a separate thread. The default value is
+ false, causing the user interface thread to block while the
+ image is loaded. Setting \a asynchronous to true is useful where
+ maintaining a responsive user interface is more desirable
+ than having images immediately visible.
+
+ Note that this property is only valid for images read from the
+ local filesystem. Images loaded via a network resource (e.g. HTTP)
+ are always loaded asynchonously.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::AnimatedImage::mirror
+
+ This property holds whether the image should be horizontally inverted
+ (effectively displaying a mirrored image).
+
+ The default value is false.
+*/
QSGAnimatedImage::QSGAnimatedImage(QSGItem *parent)
: QSGImage(*(new QSGAnimatedImagePrivate), parent)
@@ -64,6 +133,14 @@ QSGAnimatedImage::~QSGAnimatedImage()
delete d->_movie;
}
+/*!
+ \qmlproperty bool QtQuick2::AnimatedImage::paused
+ This property holds whether the animated image is paused.
+
+ By default, this property is false. Set it to true when you want to pause
+ the animation.
+*/
+
bool QSGAnimatedImage::isPaused() const
{
Q_D(const QSGAnimatedImage);
@@ -83,6 +160,14 @@ void QSGAnimatedImage::setPaused(bool pause)
d->_movie->setPaused(pause);
}
+/*!
+ \qmlproperty bool QtQuick2::AnimatedImage::playing
+ This property holds whether the animated image is playing.
+
+ By default, this property is true, meaning that the animation
+ will start playing immediately.
+*/
+
bool QSGAnimatedImage::isPlaying() const
{
Q_D(const QSGAnimatedImage);
@@ -105,6 +190,16 @@ void QSGAnimatedImage::setPlaying(bool play)
d->_movie->stop();
}
+/*!
+ \qmlproperty int QtQuick2::AnimatedImage::currentFrame
+ \qmlproperty int QtQuick2::AnimatedImage::frameCount
+
+ currentFrame is the frame that is currently visible. By monitoring this property
+ for changes, you can animate other items at the same time as the image.
+
+ frameCount is the number of frames in the animation. For some animation formats,
+ frameCount is unknown and has a value of zero.
+*/
int QSGAnimatedImage::currentFrame() const
{
Q_D(const QSGAnimatedImage);
diff --git a/src/declarative/items/qsgborderimage.cpp b/src/declarative/items/qsgborderimage.cpp
index 684ea0097b..8d54bce419 100644
--- a/src/declarative/items/qsgborderimage.cpp
+++ b/src/declarative/items/qsgborderimage.cpp
@@ -50,6 +50,119 @@
QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass BorderImage QSGBorderImage
+ \inqmlmodule QtQuick 2
+ \brief The BorderImage element provides an image that can be used as a border.
+ \inherits Item
+ \ingroup qml-basic-visual-elements
+
+ The BorderImage element is used to create borders out of images by scaling or tiling
+ parts of each image.
+
+ A BorderImage element breaks a source image, specified using the \l url property,
+ into 9 regions, as shown below:
+
+ \image declarative-scalegrid.png
+
+ When the image is scaled, regions of the source image are scaled or tiled to
+ create the displayed border image in the following way:
+
+ \list
+ \i The corners (regions 1, 3, 7, and 9) are not scaled at all.
+ \i Regions 2 and 8 are scaled according to
+ \l{BorderImage::horizontalTileMode}{horizontalTileMode}.
+ \i Regions 4 and 6 are scaled according to
+ \l{BorderImage::verticalTileMode}{verticalTileMode}.
+ \i The middle (region 5) is scaled according to both
+ \l{BorderImage::horizontalTileMode}{horizontalTileMode} and
+ \l{BorderImage::verticalTileMode}{verticalTileMode}.
+ \endlist
+
+ The regions of the image are defined using the \l border property group, which
+ describes the distance from each edge of the source image to use as a border.
+
+ \section1 Example Usage
+
+ The following examples show the effects of the different modes on an image.
+ Guide lines are overlaid onto the image to show the different regions of the
+ image as described above.
+
+ \beginfloatleft
+ \image qml-borderimage-normal-image.png
+ \endfloat
+
+ An unscaled image is displayed using an Image element. The \l border property is
+ used to determine the parts of the image that will lie inside the unscaled corner
+ areas and the parts that will be stretched horizontally and vertically.
+
+ \snippet doc/src/snippets/declarative/borderimage/normal-image.qml normal image
+
+ \clearfloat
+ \beginfloatleft
+ \image qml-borderimage-scaled.png
+ \endfloat
+
+ A BorderImage element is used to display the image, and it is given a size that is
+ larger than the original image. Since the \l horizontalTileMode property is set to
+ \l{BorderImage::horizontalTileMode}{BorderImage.Stretch}, the parts of image in
+ regions 2 and 8 are stretched horizontally. Since the \l verticalTileMode property
+ is set to \l{BorderImage::verticalTileMode}{BorderImage.Stretch}, the parts of image
+ in regions 4 and 6 are stretched vertically.
+
+ \snippet doc/src/snippets/declarative/borderimage/borderimage-scaled.qml scaled border image
+
+ \clearfloat
+ \beginfloatleft
+ \image qml-borderimage-tiled.png
+ \endfloat
+
+ Again, a large BorderImage element is used to display the image. With the
+ \l horizontalTileMode property set to \l{BorderImage::horizontalTileMode}{BorderImage.Repeat},
+ the parts of image in regions 2 and 8 are tiled so that they fill the space at the
+ top and bottom of the element. Similarly, the \l verticalTileMode property is set to
+ \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, the parts of image in regions
+ 4 and 6 are tiled so that they fill the space at the left and right of the element.
+
+ \snippet doc/src/snippets/declarative/borderimage/borderimage-tiled.qml tiled border image
+
+ \clearfloat
+ In some situations, the width of regions 2 and 8 may not be an exact multiple of the width
+ of the corresponding regions in the source image. Similarly, the height of regions 4 and 6
+ may not be an exact multiple of the height of the corresponding regions. It can be useful
+ to use \l{BorderImage::horizontalTileMode}{BorderImage.Round} instead of
+ \l{BorderImage::horizontalTileMode}{BorderImage.Repeat} in cases like these.
+
+ The \l{declarative/imageelements/borderimage}{BorderImage example} shows how a BorderImage
+ can be used to simulate a shadow effect on a rectangular item.
+
+ \section1 Quality and Performance
+
+ By default, any scaled regions of the image are rendered without smoothing to improve
+ rendering speed. Setting the \l smooth property improves rendering quality of scaled
+ regions, but may slow down rendering.
+
+ The source image may not be loaded instantaneously, depending on its original location.
+ Loading progress can be monitored with the \l progress property.
+
+ \sa Image, AnimatedImage
+ */
+
+/*!
+ \qmlproperty bool QtQuick2::BorderImage::asynchronous
+
+ Specifies that images on the local filesystem should be loaded
+ asynchronously in a separate thread. The default value is
+ false, causing the user interface thread to block while the
+ image is loaded. Setting \a asynchronous to true is useful where
+ maintaining a responsive user interface is more desirable
+ than having images immediately visible.
+
+ Note that this property is only valid for images read from the
+ local filesystem. Images loaded via a network resource (e.g. HTTP)
+ are always loaded asynchonously.
+*/
QSGBorderImage::QSGBorderImage(QSGItem *parent)
: QSGImageBase(*(new QSGBorderImagePrivate), parent)
{
@@ -62,6 +175,99 @@ QSGBorderImage::~QSGBorderImage()
d->sciReply->deleteLater();
}
+/*!
+ \qmlproperty enumeration QtQuick2::BorderImage::status
+
+ This property describes the status of image loading. It can be one of:
+
+ \list
+ \o BorderImage.Null - no image has been set
+ \o BorderImage.Ready - the image has been loaded
+ \o BorderImage.Loading - the image is currently being loaded
+ \o BorderImage.Error - an error occurred while loading the image
+ \endlist
+
+ \sa progress
+*/
+
+/*!
+ \qmlproperty real QtQuick2::BorderImage::progress
+
+ This property holds the progress of image loading, from 0.0 (nothing loaded)
+ to 1.0 (finished).
+
+ \sa status
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::BorderImage::smooth
+
+ Set this property if you want the image to be smoothly filtered when scaled or
+ transformed. Smooth filtering gives better visual quality, but is slower. If
+ the image is displayed at its natural size, this property has no visual or
+ performance effect.
+
+ By default, this property is set to false.
+
+ \note Generally scaling artifacts are only visible if the image is stationary on
+ the screen. A common pattern when animating an image is to disable smooth
+ filtering at the beginning of the animation and enable it at the conclusion.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::BorderImage::cache
+
+ Specifies whether the image should be cached. The default value is
+ true. Setting \a cache to false is useful when dealing with large images,
+ to make sure that they aren't cached at the expense of small 'ui element' images.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::BorderImage::mirror
+
+ This property holds whether the image should be horizontally inverted
+ (effectively displaying a mirrored image).
+
+ The default value is false.
+*/
+
+/*!
+ \qmlproperty url QtQuick2::BorderImage::source
+
+ This property holds the URL that refers to the source image.
+
+ BorderImage can handle any image format supported by Qt, loaded from any
+ URL scheme supported by Qt.
+
+ This property can also be used to refer to .sci files, which are
+ written in a QML-specific, text-based format that specifies the
+ borders, the image file and the tile rules for a given border image.
+
+ The following .sci file sets the borders to 10 on each side for the
+ image \c picture.png:
+
+ \code
+ border.left: 10
+ border.top: 10
+ border.bottom: 10
+ border.right: 10
+ source: "picture.png"
+ \endcode
+
+ The URL may be absolute, or relative to the URL of the component.
+
+ \sa QDeclarativeImageProvider
+*/
+
+/*!
+ \qmlproperty QSize QtQuick2::BorderImage::sourceSize
+
+ This property holds the actual width and height of the loaded image.
+
+ In BorderImage, this property is read-only.
+
+ \sa Image::sourceSize
+*/
void QSGBorderImage::setSource(const QUrl &url)
{
Q_D(QSGBorderImage);
@@ -160,12 +366,55 @@ void QSGBorderImage::load()
emit statusChanged(d->status);
}
+/*!
+ \qmlproperty int QtQuick2::BorderImage::border.left
+ \qmlproperty int QtQuick2::BorderImage::border.right
+ \qmlproperty int QtQuick2::BorderImage::border.top
+ \qmlproperty int QtQuick2::BorderImage::border.bottom
+
+ The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections,
+ as shown below:
+
+ \image declarative-scalegrid.png
+
+ Each border line (left, right, top, and bottom) specifies an offset in pixels
+ from the respective edge of the source image. By default, each border line has
+ a value of 0.
+
+ For example, the following definition sets the bottom line 10 pixels up from
+ the bottom of the image:
+
+ \qml
+ BorderImage {
+ border.bottom: 10
+ // ...
+ }
+ \endqml
+
+ The border lines can also be specified using a
+ \l {BorderImage::source}{.sci file}.
+*/
+
QSGScaleGrid *QSGBorderImage::border()
{
Q_D(QSGBorderImage);
return d->getScaleGrid();
}
+/*!
+ \qmlproperty enumeration QtQuick2::BorderImage::horizontalTileMode
+ \qmlproperty enumeration QtQuick2::BorderImage::verticalTileMode
+
+ This property describes how to repeat or stretch the middle parts of the border image.
+
+ \list
+ \o BorderImage.Stretch - Scales the image to fit to the available area.
+ \o BorderImage.Repeat - Tile the image until there is no more space. May crop the last image.
+ \o BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped.
+ \endlist
+
+ The default tile mode for each property is BorderImage.Stretch.
+*/
QSGBorderImage::TileMode QSGBorderImage::horizontalTileMode() const
{
Q_D(const QSGBorderImage);
diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp
index 33d661e089..0d4001a393 100644
--- a/src/declarative/items/qsgcanvas.cpp
+++ b/src/declarative/items/qsgcanvas.cpp
@@ -332,7 +332,6 @@ QSGCanvasPrivate::QSGCanvasPrivate()
: rootItem(0)
, activeFocusItem(0)
, mouseGrabberItem(0)
- , hoverItem(0)
, dirtyItemList(0)
, context(0)
, animationRunning(false)
@@ -785,7 +784,6 @@ QSGCanvas::~QSGCanvas()
// manually cleanup for the root item (item destructor only handles these when an item is parented)
QSGItemPrivate *rootItemPrivate = QSGItemPrivate::get(d->rootItem);
rootItemPrivate->removeFromDirtyList();
- rootItemPrivate->canvas = 0;
delete d->rootItem; d->rootItem = 0;
d->cleanupNodes();
@@ -813,17 +811,19 @@ QSGItem *QSGCanvas::mouseGrabberItem() const
}
-void QSGCanvasPrivate::clearHover()
+bool QSGCanvasPrivate::clearHover()
{
Q_Q(QSGCanvas);
- if (!hoverItem)
- return;
+ if (hoverItems.isEmpty())
+ return false;
QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos());
- QSGItem *item = hoverItem;
- hoverItem = 0;
- sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QApplication::keyboardModifiers(), true);
+ bool accepted = false;
+ foreach (QSGItem* item, hoverItems)
+ accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QApplication::keyboardModifiers(), true) || accepted;
+ hoverItems.clear();
+ return accepted;
}
@@ -874,6 +874,9 @@ bool QSGCanvas::event(QEvent *e)
case QSGEvent::SGDragDrop:
d->deliverDragEvent(static_cast<QSGDragEvent *>(e));
break;
+ case QEvent::WindowDeactivate:
+ rootItem()->windowDeactivateEvent();
+ break;
default:
break;
}
@@ -1063,11 +1066,7 @@ void QSGCanvas::mouseMoveEvent(QMouseEvent *event)
bool delivered = d->deliverHoverEvent(d->rootItem, event->pos(), last, event->modifiers(), accepted);
if (!delivered) {
//take care of any exits
- if (d->hoverItem) {
- QSGItem *item = d->hoverItem;
- d->hoverItem = 0;
- accepted = d->sendHoverEvent(QEvent::HoverLeave, item, event->pos(), last, event->modifiers(), accepted);
- }
+ accepted = d->clearHover();
}
event->setAccepted(accepted);
return;
@@ -1105,20 +1104,43 @@ bool QSGCanvasPrivate::deliverHoverEvent(QSGItem *item, const QPointF &scenePos,
if (itemPrivate->hoverEnabled) {
QPointF p = item->mapFromScene(scenePos);
if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
- if (hoverItem == item) {
+ if (!hoverItems.isEmpty() && hoverItems[0] == item) {
//move
accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
} else {
- //exit from previous
- if (hoverItem) {
- QSGItem *item = hoverItem;
- hoverItem = 0;
- accepted = sendHoverEvent(QEvent::HoverLeave, item, scenePos, lastScenePos, modifiers, accepted);
+ QList<QSGItem*> parents;
+ QSGItem* parent = item;
+ parents << item;
+ while ((parent = parent->parentItem()))
+ parents << parent;
+
+ //exit from previous (excepting ancestors)
+ while (!hoverItems.isEmpty() && !parents.contains(hoverItems[0])){
+ sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
+ hoverItems.removeFirst();
}
- //enter new item
- hoverItem = item;
- accepted = sendHoverEvent(QEvent::HoverEnter, item, scenePos, lastScenePos, modifiers, accepted);
+ if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
+ accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
+ } else {
+ //enter any ancestors that also wish to be hovered and aren't
+ int startIdx = -1;
+ if (!hoverItems.isEmpty())
+ startIdx = parents.indexOf(hoverItems[0]);
+ if (startIdx == -1)
+ startIdx = parents.count() - 1;
+
+ for (int i = startIdx; i >= 0; i--) {
+ if (QSGItemPrivate::get(parents[i])->hoverEnabled) {
+ hoverItems.prepend(parents[i]);
+ sendHoverEvent(QEvent::HoverEnter, parents[i], scenePos, lastScenePos, modifiers, accepted);
+ }
+ }
+
+ //enter new item
+ hoverItems.prepend(item);
+ accepted = sendHoverEvent(QEvent::HoverEnter, item, scenePos, lastScenePos, modifiers, accepted);
+ }
}
return true;
}
@@ -1143,7 +1165,7 @@ bool QSGCanvasPrivate::deliverWheelEvent(QSGItem *item, QWheelEvent *event)
QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
for (int ii = children.count() - 1; ii >= 0; --ii) {
QSGItem *child = children.at(ii);
- if (!child->isEnabled())
+ if (!child->isVisible() || !child->isEnabled())
continue;
if (deliverWheelEvent(child, event))
return true;
@@ -1562,6 +1584,7 @@ void QSGCanvasPrivate::updateDirtyNode(QSGItem *item)
if (item->clip()) {
Q_ASSERT(itemPriv->clipNode == 0);
itemPriv->clipNode = new QSGDefaultClipNode(item->boundingRect());
+ itemPriv->clipNode->update();
if (child)
parent->removeChildNode(child);
@@ -1646,7 +1669,7 @@ void QSGCanvasPrivate::updateDirtyNode(QSGItem *item)
}
}
- if ((dirty & QSGItemPrivate::Size || clipEffectivelyChanged) && itemPriv->clipNode) {
+ if ((dirty & QSGItemPrivate::Size) && itemPriv->clipNode) {
itemPriv->clipNode->setRect(item->boundingRect());
itemPriv->clipNode->update();
}
diff --git a/src/declarative/items/qsgcanvas_p.h b/src/declarative/items/qsgcanvas_p.h
index e16d91685d..bf65e95812 100644
--- a/src/declarative/items/qsgcanvas_p.h
+++ b/src/declarative/items/qsgcanvas_p.h
@@ -119,11 +119,11 @@ public:
bool deliverHoverEvent(QSGItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted);
bool sendHoverEvent(QEvent::Type, QSGItem *, const QPointF &scenePos, const QPointF &lastScenePos,
Qt::KeyboardModifiers modifiers, bool accepted);
- void clearHover();
+ bool clearHover();
void deliverDragEvent(QSGDragEvent *);
bool deliverDragEvent(QSGItem *item, QSGDragEvent *);
- QDeclarativeGuard<QSGItem> hoverItem;
+ QList<QSGItem*> hoverItems;
enum FocusOption {
DontChangeFocusProperty = 0x01,
};
diff --git a/src/declarative/items/qsgevents.cpp b/src/declarative/items/qsgevents.cpp
index 0c5d8cac34..2173b4a21f 100644
--- a/src/declarative/items/qsgevents.cpp
+++ b/src/declarative/items/qsgevents.cpp
@@ -43,4 +43,197 @@
QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass KeyEvent QSGKeyEvent
+ \inqmlmodule QtQuick 2
+ \ingroup qml-event-elements
+
+ \brief The KeyEvent object provides information about a key event.
+
+ For example, the following changes the Item's state property when the Enter
+ key is pressed:
+ \qml
+Item {
+ focus: true
+ Keys.onPressed: { if (event.key == Qt.Key_Enter) state = 'ShowDetails'; }
+}
+ \endqml
+*/
+
+/*!
+ \qmlproperty int QtQuick2::KeyEvent::key
+
+ This property holds the code of the key that was pressed or released.
+
+ See \l {Qt::Key}{Qt.Key} for the list of keyboard codes. These codes are
+ independent of the underlying window system. Note that this
+ function does not distinguish between capital and non-capital
+ letters, use the text() function (returning the Unicode text the
+ key generated) for this purpose.
+
+ A value of either 0 or \l {Qt::Key_unknown}{Qt.Key_Unknown} means that the event is not
+ the result of a known key; for example, it may be the result of
+ a compose sequence, a keyboard macro, or due to key event
+ compression.
+*/
+
+/*!
+ \qmlproperty string QtQuick2::KeyEvent::text
+
+ This property holds the Unicode text that the key generated.
+ The text returned can be an empty string in cases where modifier keys,
+ such as Shift, Control, Alt, and Meta, are being pressed or released.
+ In such cases \c key will contain a valid value
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::KeyEvent::isAutoRepeat
+
+ This property holds whether this event comes from an auto-repeating key.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::KeyEvent::count
+
+ This property holds the number of keys involved in this event. If \l KeyEvent::text
+ is not empty, this is simply the length of the string.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::KeyEvent::accepted
+
+ Setting \a accepted to true prevents the key event from being
+ propagated to the item's parent.
+
+ Generally, if the item acts on the key event then it should be accepted
+ so that ancestor items do not also respond to the same event.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::KeyEvent::modifiers
+
+ This property holds the keyboard modifier flags that existed immediately
+ before the event occurred.
+
+ It contains a bitwise combination of:
+ \list
+ \o Qt.NoModifier - No modifier key is pressed.
+ \o Qt.ShiftModifier - A Shift key on the keyboard is pressed.
+ \o Qt.ControlModifier - A Ctrl key on the keyboard is pressed.
+ \o Qt.AltModifier - An Alt key on the keyboard is pressed.
+ \o Qt.MetaModifier - A Meta key on the keyboard is pressed.
+ \o Qt.KeypadModifier - A keypad button is pressed.
+ \endlist
+
+ For example, to react to a Shift key + Enter key combination:
+ \qml
+ Item {
+ focus: true
+ Keys.onPressed: {
+ if ((event.key == Qt.Key_Enter) && (event.modifiers & Qt.ShiftModifier))
+ doSomething();
+ }
+ }
+ \endqml
+*/
+
+
+/*!
+ \qmlclass MouseEvent QSGMouseEvent
+ \inqmlmodule QtQuick 2
+ \ingroup qml-event-elements
+
+ \brief The MouseEvent object provides information about a mouse event.
+
+ The position of the mouse can be found via the \l x and \l y properties.
+ The button that caused the event is available via the \l button property.
+
+ \sa MouseArea
+*/
+
+/*!
+ \internal
+ \class QSGMouseEvent
+*/
+
+/*!
+ \qmlproperty int QtQuick2::MouseEvent::x
+ \qmlproperty int QtQuick2::MouseEvent::y
+
+ These properties hold the coordinates of the position supplied by the mouse event.
+*/
+
+
+/*!
+ \qmlproperty bool QtQuick2::MouseEvent::accepted
+
+ Setting \a accepted to true prevents the mouse event from being
+ propagated to items below this item.
+
+ Generally, if the item acts on the mouse event then it should be accepted
+ so that items lower in the stacking order do not also respond to the same event.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick2::MouseEvent::button
+
+ This property holds the button that caused the event. It can be one of:
+ \list
+ \o Qt.LeftButton
+ \o Qt.RightButton
+ \o Qt.MiddleButton
+ \endlist
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::MouseEvent::wasHeld
+
+ This property is true if the mouse button has been held pressed longer the
+ threshold (800ms).
+*/
+
+/*!
+ \qmlproperty int QtQuick2::MouseEvent::buttons
+
+ This property holds the mouse buttons pressed when the event was generated.
+ For mouse move events, this is all buttons that are pressed down. For mouse
+ press and double click events this includes the button that caused the event.
+ For mouse release events this excludes the button that caused the event.
+
+ It contains a bitwise combination of:
+ \list
+ \o Qt.LeftButton
+ \o Qt.RightButton
+ \o Qt.MiddleButton
+ \endlist
+*/
+
+/*!
+ \qmlproperty int QtQuick2::MouseEvent::modifiers
+
+ This property holds the keyboard modifier flags that existed immediately
+ before the event occurred.
+
+ It contains a bitwise combination of:
+ \list
+ \o Qt.NoModifier - No modifier key is pressed.
+ \o Qt.ShiftModifier - A Shift key on the keyboard is pressed.
+ \o Qt.ControlModifier - A Ctrl key on the keyboard is pressed.
+ \o Qt.AltModifier - An Alt key on the keyboard is pressed.
+ \o Qt.MetaModifier - A Meta key on the keyboard is pressed.
+ \o Qt.KeypadModifier - A keypad button is pressed.
+ \endlist
+
+ For example, to react to a Shift key + Left mouse button click:
+ \qml
+ MouseArea {
+ onClicked: {
+ if ((mouse.button == Qt.LeftButton) && (mouse.modifiers & Qt.ShiftModifier))
+ doSomething();
+ }
+ }
+ \endqml
+*/
+
+
QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgflickable.cpp b/src/declarative/items/qsgflickable.cpp
index 1d8f107abc..e63698eca7 100644
--- a/src/declarative/items/qsgflickable.cpp
+++ b/src/declarative/items/qsgflickable.cpp
@@ -456,6 +456,107 @@ is finished.
touch/mouse button is released then a flick will start.
*/
+/*!
+ \qmlclass Flickable QSGFlickable
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-interaction-elements
+
+ \brief The Flickable item provides a surface that can be "flicked".
+ \inherits Item
+
+ The Flickable item places its children on a surface that can be dragged
+ and flicked, causing the view onto the child items to scroll. This
+ behavior forms the basis of Items that are designed to show large numbers
+ of child items, such as \l ListView and \l GridView.
+
+ In traditional user interfaces, views can be scrolled using standard
+ controls, such as scroll bars and arrow buttons. In some situations, it
+ is also possible to drag the view directly by pressing and holding a
+ mouse button while moving the cursor. In touch-based user interfaces,
+ this dragging action is often complemented with a flicking action, where
+ scrolling continues after the user has stopped touching the view.
+
+ Flickable does not automatically clip its contents. If it is not used as
+ a full-screen item, you should consider setting the \l{Item::}{clip} property
+ to true.
+
+ \section1 Example Usage
+
+ \div {class="float-right"}
+ \inlineimage flickable.gif
+ \enddiv
+
+ The following example shows a small view onto a large image in which the
+ user can drag or flick the image in order to view different parts of it.
+
+ \snippet doc/src/snippets/declarative/flickable.qml document
+
+ \clearfloat
+
+ Items declared as children of a Flickable are automatically parented to the
+ Flickable's \l contentItem. This should be taken into account when
+ operating on the children of the Flickable; it is usually the children of
+ \c contentItem that are relevant. For example, the bound of Items added
+ to the Flickable will be available by \c contentItem.childrenRect
+
+ \section1 Limitations
+
+ \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by
+ \c id. Use \c parent instead.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Flickable::onMovementStarted()
+
+ This handler is called when the view begins moving due to user
+ interaction.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Flickable::onMovementEnded()
+
+ This handler is called when the view stops moving due to user
+ interaction. If a flick was generated, this handler will
+ be triggered once the flick stops. If a flick was not
+ generated, the handler will be triggered when the
+ user stops dragging - i.e. a mouse or touch release.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Flickable::onFlickStarted()
+
+ This handler is called when the view is flicked. A flick
+ starts from the point that the mouse or touch is released,
+ while still in motion.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Flickable::onFlickEnded()
+
+ This handler is called when the view stops moving due to a flick.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Flickable::visibleArea.xPosition
+ \qmlproperty real QtQuick2::Flickable::visibleArea.widthRatio
+ \qmlproperty real QtQuick2::Flickable::visibleArea.yPosition
+ \qmlproperty real QtQuick2::Flickable::visibleArea.heightRatio
+
+ These properties describe the position and size of the currently viewed area.
+ The size is defined as the percentage of the full view currently visible,
+ scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to
+ 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio.
+ However, it is possible for the contents to be dragged outside of the normal
+ range, resulting in the page positions also being outside the normal range.
+
+ These properties are typically used to draw a scrollbar. For example:
+
+ \snippet doc/src/snippets/declarative/flickableScrollbar.qml 0
+ \dots 8
+ \snippet doc/src/snippets/declarative/flickableScrollbar.qml 1
+
+ \sa {declarative/ui-components/scrollbar}{scrollbar example}
+*/
QSGFlickable::QSGFlickable(QSGItem *parent)
: QSGItem(*(new QSGFlickablePrivate), parent)
{
@@ -474,6 +575,14 @@ QSGFlickable::~QSGFlickable()
{
}
+/*!
+ \qmlproperty real QtQuick2::Flickable::contentX
+ \qmlproperty real QtQuick2::Flickable::contentY
+
+ These properties hold the surface coordinate currently at the top-left
+ corner of the Flickable. For example, if you flick an image up 100 pixels,
+ \c contentY will be 100.
+*/
qreal QSGFlickable::contentX() const
{
Q_D(const QSGFlickable);
@@ -510,6 +619,19 @@ void QSGFlickable::setContentY(qreal pos)
}
}
+/*!
+ \qmlproperty bool QtQuick2::Flickable::interactive
+
+ This property describes whether the user can interact with the Flickable.
+ A user cannot drag or flick a Flickable that is not interactive.
+
+ By default, this property is true.
+
+ This property is useful for temporarily disabling flicking. This allows
+ special interaction with Flickable's children; for example, you might want
+ to freeze a flickable map while scrolling through a pop-up dialog that
+ is a child of the Flickable.
+*/
bool QSGFlickable::isInteractive() const
{
Q_D(const QSGFlickable);
@@ -535,6 +657,14 @@ void QSGFlickable::setInteractive(bool interactive)
}
}
+/*!
+ \qmlproperty real QtQuick2::Flickable::horizontalVelocity
+ \qmlproperty real QtQuick2::Flickable::verticalVelocity
+
+ The instantaneous velocity of movement along the x and y axes, in pixels/sec.
+
+ The reported velocity is smoothed to avoid erratic output.
+*/
qreal QSGFlickable::horizontalVelocity() const
{
Q_D(const QSGFlickable);
@@ -547,6 +677,15 @@ qreal QSGFlickable::verticalVelocity() const
return d->vData.smoothVelocity.value();
}
+/*!
+ \qmlproperty bool QtQuick2::Flickable::atXBeginning
+ \qmlproperty bool QtQuick2::Flickable::atXEnd
+ \qmlproperty bool QtQuick2::Flickable::atYBeginning
+ \qmlproperty bool QtQuick2::Flickable::atYEnd
+
+ These properties are true if the flickable view is positioned at the beginning,
+ or end respecively.
+*/
bool QSGFlickable::isAtXEnd() const
{
Q_D(const QSGFlickable);
@@ -576,6 +715,24 @@ void QSGFlickable::ticked()
viewportMoved();
}
+/*!
+ \qmlproperty Item QtQuick2::Flickable::contentItem
+
+ The internal item that contains the Items to be moved in the Flickable.
+
+ Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
+
+ Items created dynamically need to be explicitly parented to the \e contentItem:
+ \code
+ Flickable {
+ id: myFlickable
+ function addItem(file) {
+ var component = Qt.createComponent(file)
+ component.createObject(myFlickable.contentItem);
+ }
+ }
+ \endcode
+*/
QSGItem *QSGFlickable::contentItem()
{
Q_D(QSGFlickable);
@@ -590,6 +747,21 @@ QSGFlickableVisibleArea *QSGFlickable::visibleArea()
return d->visibleArea;
}
+/*!
+ \qmlproperty enumeration QtQuick2::Flickable::flickableDirection
+
+ This property determines which directions the view can be flicked.
+
+ \list
+ \o Flickable.AutoFlickDirection (default) - allows flicking vertically if the
+ \e contentHeight is not equal to the \e height of the Flickable.
+ Allows flicking horizontally if the \e contentWidth is not equal
+ to the \e width of the Flickable.
+ \o Flickable.HorizontalFlick - allows flicking horizontally.
+ \o Flickable.VerticalFlick - allows flicking vertically.
+ \o Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
+ \endlist
+*/
QSGFlickable::FlickableDirection QSGFlickable::flickableDirection() const
{
Q_D(const QSGFlickable);
@@ -1118,6 +1290,27 @@ QDeclarativeListProperty<QSGItem> QSGFlickable::flickableChildren()
return QSGItemPrivate::get(d->contentItem)->children();
}
+/*!
+ \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior
+ This property holds whether the surface may be dragged
+ beyond the Fickable's boundaries, or overshoot the
+ Flickable's boundaries when flicked.
+
+ This enables the feeling that the edges of the view are soft,
+ rather than a hard physical boundary.
+
+ The \c boundsBehavior can be one of:
+
+ \list
+ \o Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
+ of the flickable, and flicks will not overshoot.
+ \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary
+ of the Flickable, but flicks will not overshoot.
+ \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged
+ beyond the boundary of the Flickable, and can overshoot the
+ boundary when flicked.
+ \endlist
+*/
QSGFlickable::BoundsBehavior QSGFlickable::boundsBehavior() const
{
Q_D(const QSGFlickable);
@@ -1133,6 +1326,23 @@ void QSGFlickable::setBoundsBehavior(BoundsBehavior b)
emit boundsBehaviorChanged();
}
+/*!
+ \qmlproperty real QtQuick2::Flickable::contentWidth
+ \qmlproperty real QtQuick2::Flickable::contentHeight
+
+ The dimensions of the content (the surface controlled by Flickable).
+ This should typically be set to the combined size of the items placed in the
+ Flickable.
+
+ The following snippet shows how these properties are used to display
+ an image that is larger than the Flickable item itself:
+
+ \snippet doc/src/snippets/declarative/flickable.qml document
+
+ In some cases, the the content dimensions can be automatically set
+ using the \l {Item::childrenRect.width}{childrenRect.width}
+ and \l {Item::childrenRect.height}{childrenRect.height} properties.
+*/
qreal QSGFlickable::contentWidth() const
{
Q_D(const QSGFlickable);
@@ -1189,6 +1399,19 @@ void QSGFlickable::setContentHeight(qreal h)
d->updateBeginningEnd();
}
+/*!
+ \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center)
+ \preliminary
+
+ Resizes the content to \a width x \a height about \a center.
+
+ This does not scale the contents of the Flickable - it only resizes the \l contentWidth
+ and \l contentHeight.
+
+ Resizing the content may result in the content being positioned outside
+ the bounds of the Flickable. Calling \l returnToBounds() will
+ move the content back within legal bounds.
+*/
void QSGFlickable::resizeContent(qreal w, qreal h, QPointF center)
{
Q_D(QSGFlickable);
@@ -1215,6 +1438,15 @@ void QSGFlickable::resizeContent(qreal w, qreal h, QPointF center)
d->updateBeginningEnd();
}
+/*!
+ \qmlmethod QtQuick2::Flickable::returnToBounds()
+ \preliminary
+
+ Ensures the content is within legal bounds.
+
+ This may be called to ensure that the content is within legal bounds
+ after manually positioning the content.
+*/
void QSGFlickable::returnToBounds()
{
Q_D(QSGFlickable);
@@ -1332,6 +1564,9 @@ bool QSGFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event)
}
return stealThisEvent || d->delayedPressEvent || disabledItem;
+ } else if (d->lastPosTime.isValid()) {
+ d->lastPosTime.invalidate();
+ returnToBounds();
}
if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
d->lastPosTime.invalidate();
@@ -1360,6 +1595,12 @@ bool QSGFlickable::childMouseEventFilter(QSGItem *i, QEvent *e)
return QSGItem::childMouseEventFilter(i, e);
}
+/*!
+ \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity
+ This property holds the maximum velocity that the user can flick the view in pixels/second.
+
+ The default value is platform dependent.
+*/
qreal QSGFlickable::maximumFlickVelocity() const
{
Q_D(const QSGFlickable);
@@ -1375,6 +1616,12 @@ void QSGFlickable::setMaximumFlickVelocity(qreal v)
emit maximumFlickVelocityChanged();
}
+/*!
+ \qmlproperty real QtQuick2::Flickable::flickDeceleration
+ This property holds the rate at which a flick will decelerate.
+
+ The default value is platform dependent.
+*/
qreal QSGFlickable::flickDeceleration() const
{
Q_D(const QSGFlickable);
@@ -1396,6 +1643,14 @@ bool QSGFlickable::isFlicking() const
return d->flickingHorizontally || d->flickingVertically;
}
+/*!
+ \qmlproperty bool QtQuick2::Flickable::flicking
+ \qmlproperty bool QtQuick2::Flickable::flickingHorizontally
+ \qmlproperty bool QtQuick2::Flickable::flickingVertically
+
+ These properties describe whether the view is currently moving horizontally,
+ vertically or in either direction, due to the user flicking the view.
+*/
bool QSGFlickable::isFlickingHorizontally() const
{
Q_D(const QSGFlickable);
@@ -1470,6 +1725,20 @@ void QSGFlickablePrivate::draggingEnding()
}
}
+/*!
+ \qmlproperty int QtQuick2::Flickable::pressDelay
+
+ This property holds the time to delay (ms) delivering a press to
+ children of the Flickable. This can be useful where reacting
+ to a press before a flicking action has undesirable effects.
+
+ If the flickable is dragged/flicked before the delay times out
+ the press event will not be delivered. If the button is released
+ within the timeout, both the press and release will be delivered.
+
+ Note that for nested Flickables with pressDelay set, the pressDelay of
+ inner Flickables is overridden by the outermost Flickable.
+*/
int QSGFlickable::pressDelay() const
{
Q_D(const QSGFlickable);
@@ -1485,6 +1754,15 @@ void QSGFlickable::setPressDelay(int delay)
emit pressDelayChanged();
}
+/*!
+ \qmlproperty bool QtQuick2::Flickable::moving
+ \qmlproperty bool QtQuick2::Flickable::movingHorizontally
+ \qmlproperty bool QtQuick2::Flickable::movingVertically
+
+ These properties describe whether the view is currently moving horizontally,
+ vertically or in either direction, due to the user either dragging or
+ flicking the view.
+*/
bool QSGFlickable::isMoving() const
{
diff --git a/src/declarative/items/qsgflipable.cpp b/src/declarative/items/qsgflipable.cpp
index faceb29f73..ac68b3171c 100644
--- a/src/declarative/items/qsgflipable.cpp
+++ b/src/declarative/items/qsgflipable.cpp
@@ -87,6 +87,47 @@ public:
bool wantBackYFlipped;
};
+/*!
+ \qmlclass Flipable QSGFlipable
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-interaction-elements
+ \brief The Flipable item provides a surface that can be flipped.
+ \inherits Item
+
+ Flipable is an item that can be visibly "flipped" between its front and
+ back sides, like a card. It is used together with \l Rotation, \l State
+ and \l Transition elements to produce a flipping effect.
+
+ The \l front and \l back properties are used to hold the items that are
+ shown respectively on the front and back sides of the flipable item.
+
+ \section1 Example Usage
+
+ The following example shows a Flipable item that flips whenever it is
+ clicked, rotating about the y-axis.
+
+ This flipable item has a \c flipped boolean property that is toggled
+ whenever the MouseArea within the flipable is clicked. When
+ \c flipped is true, the item changes to the "back" state; in this
+ state, the \c angle of the \l Rotation item is changed to 180
+ degrees to produce the flipping effect. When \c flipped is false, the
+ item reverts to the default state, in which the \c angle value is 0.
+
+ \snippet doc/src/snippets/declarative/flipable/flipable.qml 0
+
+ \image flipable.gif
+
+ The \l Transition creates the animation that changes the angle over
+ four seconds. When the item changes between its "back" and
+ default states, the NumberAnimation animates the angle between
+ its old and new values.
+
+ See \l {QML States} for details on state changes and the default
+ state, and \l {QML Animation and Transitions} for more information on how
+ animations work within transitions.
+
+ \sa {declarative/ui-components/flipable}{Flipable example}
+*/
QSGFlipable::QSGFlipable(QSGItem *parent)
: QSGItem(*(new QSGFlipablePrivate), parent)
{
@@ -96,6 +137,13 @@ QSGFlipable::~QSGFlipable()
{
}
+/*!
+ \qmlproperty Item QtQuick2::Flipable::front
+ \qmlproperty Item QtQuick2::Flipable::back
+
+ The front and back sides of the flipable.
+*/
+
QSGItem *QSGFlipable::front()
{
Q_D(const QSGFlipable);
@@ -153,6 +201,12 @@ void QSGFlipable::retransformBack()
d->setBackTransform();
}
+/*!
+ \qmlproperty enumeration QtQuick2::Flipable::side
+
+ The side of the Flipable currently visible. Possible values are \c
+ Flipable.Front and \c Flipable.Back.
+*/
QSGFlipable::Side QSGFlipable::side() const
{
Q_D(const QSGFlipable);
diff --git a/src/declarative/items/qsgfocusscope.cpp b/src/declarative/items/qsgfocusscope.cpp
index 156869120d..2018d5ce7d 100644
--- a/src/declarative/items/qsgfocusscope.cpp
+++ b/src/declarative/items/qsgfocusscope.cpp
@@ -43,7 +43,21 @@
QT_BEGIN_NAMESPACE
-QSGFocusScope::QSGFocusScope(QSGItem *parent)
+/*!
+ \qmlclass FocusScope QSGFocusScope
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-interaction-elements
+
+ \brief The FocusScope object explicitly creates a focus scope.
+ \inherits Item
+
+ Focus scopes assist in keyboard focus handling when building reusable QML
+ components. All the details are covered in the
+ \l {qmlfocus}{keyboard focus documentation}.
+
+ \sa {declarative/keyinteraction/focus}{Keyboard focus example}
+*/
+QSGFocusScope::QSGFocusScope(QSGItem *parent)
: QSGItem(parent)
{
setFlag(ItemIsFocusScope);
diff --git a/src/declarative/items/qsggridview.cpp b/src/declarative/items/qsggridview.cpp
index 26347c1c93..bc4954a168 100644
--- a/src/declarative/items/qsggridview.cpp
+++ b/src/declarative/items/qsggridview.cpp
@@ -176,6 +176,8 @@ public:
virtual qreal headerSize() const;
virtual qreal footerSize() const;
+ virtual bool showHeaderForIndex(int index) const;
+ virtual bool showFooterForIndex(int index) const;
virtual void updateHeader();
virtual void updateFooter();
@@ -602,7 +604,8 @@ void QSGGridViewPrivate::updateHighlight()
{
if ((!currentItem && highlight) || (currentItem && !highlight))
createHighlight();
- if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) {
+ bool strictHighlight = haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange;
+ if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
// auto-update highlight
highlightXAnimator->to = currentItem->item->x();
highlightYAnimator->to = currentItem->item->y();
@@ -637,6 +640,16 @@ qreal QSGGridViewPrivate::footerSize() const
return flow == QSGGridView::LeftToRight? footer->item->height() : footer->item->width();
}
+bool QSGGridViewPrivate::showHeaderForIndex(int index) const
+{
+ return index / columns == 0;
+}
+
+bool QSGGridViewPrivate::showFooterForIndex(int index) const
+{
+ return index / columns == (model->count()-1) / columns;
+}
+
void QSGGridViewPrivate::updateFooter()
{
Q_Q(QSGGridView);
@@ -955,6 +968,80 @@ void QSGGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent,
//----------------------------------------------------------------------------
+/*!
+ \qmlclass GridView QSGGridView
+ \inqmlmodule QtQuick 2
+ \ingroup qml-view-elements
+
+ \inherits Flickable
+ \brief The GridView item provides a grid view of items provided by a model.
+
+ A GridView displays data from models created from built-in QML elements like ListModel
+ and XmlListModel, or custom model classes defined in C++ that inherit from
+ QAbstractListModel.
+
+ A GridView has a \l model, which defines the data to be displayed, and
+ a \l delegate, which defines how the data should be displayed. Items in a
+ GridView are laid out horizontally or vertically. Grid views are inherently flickable
+ as GridView inherits from \l Flickable.
+
+ \section1 Example Usage
+
+ The following example shows the definition of a simple list model defined
+ in a file called \c ContactModel.qml:
+
+ \snippet doc/src/snippets/declarative/gridview/ContactModel.qml 0
+
+ \div {class="float-right"}
+ \inlineimage gridview-simple.png
+ \enddiv
+
+ This model can be referenced as \c ContactModel in other QML files. See \l{QML Modules}
+ for more information about creating reusable components like this.
+
+ Another component can display this model data in a GridView, as in the following
+ example, which creates a \c ContactModel component for its model, and a \l Column element
+ (containing \l Image and \l Text elements) for its delegate.
+
+ \clearfloat
+ \snippet doc/src/snippets/declarative/gridview/gridview.qml import
+ \codeline
+ \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs simple
+
+ \div {class="float-right"}
+ \inlineimage gridview-highlight.png
+ \enddiv
+
+ The view will create a new delegate for each item in the model. Note that the delegate
+ is able to access the model's \c name and \c portrait data directly.
+
+ An improved grid view is shown below. The delegate is visually improved and is moved
+ into a separate \c contactDelegate component.
+
+ \clearfloat
+ \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs advanced
+
+ The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
+ and \c focus is set to \c true to enable keyboard navigation for the grid view.
+ The grid view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
+
+ Delegates are instantiated as needed and may be destroyed at any time.
+ State should \e never be stored in a delegate.
+
+ GridView attaches a number of properties to the root item of the delegate, for example
+ \c {GridView.isCurrentItem}. In the following example, the root delegate item can access
+ this attached property directly as \c GridView.isCurrentItem, while the child
+ \c contactInfo object must refer to this property as \c wrapper.GridView.isCurrentItem.
+
+ \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem
+
+ \note Views do not set the \l{Item::}{clip} property automatically.
+ If the view is not clipped by another item or the screen, it will be necessary
+ to set this property to true in order to clip the items that are partially or
+ fully outside the view.
+
+ \sa {declarative/modelviews/gridview}{GridView example}
+*/
QSGGridView::QSGGridView(QSGItem *parent)
: QSGItemView(*(new QSGGridViewPrivate), parent)
@@ -977,6 +1064,242 @@ void QSGGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
}
}
+/*!
+ \qmlattachedproperty bool QtQuick2::GridView::isCurrentItem
+ This attached property is true if this delegate is the current item; otherwise false.
+
+ It is attached to each instance of the delegate.
+*/
+
+/*!
+ \qmlattachedproperty GridView QtQuick2::GridView::view
+ This attached property holds the view that manages this delegate instance.
+
+ It is attached to each instance of the delegate.
+
+ \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem
+*/
+
+/*!
+ \qmlattachedproperty bool QtQuick2::GridView::delayRemove
+ This attached property holds whether the delegate may be destroyed.
+
+ It is attached to each instance of the delegate.
+
+ It is sometimes necessary to delay the destruction of an item
+ until an animation completes.
+
+ The example below ensures that the animation completes before
+ the item is removed from the grid.
+
+ \snippet doc/src/snippets/declarative/gridview/gridview.qml delayRemove
+*/
+
+/*!
+ \qmlattachedsignal QtQuick2::GridView::onAdd()
+ This attached handler is called immediately after an item is added to the view.
+*/
+
+/*!
+ \qmlattachedsignal QtQuick2::GridView::onRemove()
+ This attached handler is called immediately before an item is removed from the view.
+*/
+
+
+/*!
+ \qmlproperty model QtQuick2::GridView::model
+ This property holds the model providing data for the grid.
+
+ The model provides the set of data that is used to create the items
+ in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
+ or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
+ used, it must be a subclass of \l QAbstractItemModel or a simple list.
+
+ \sa {qmlmodels}{Data Models}
+*/
+
+/*!
+ \qmlproperty Component QtQuick2::GridView::delegate
+
+ The delegate provides a template defining each item instantiated by the view.
+ The index is exposed as an accessible \c index property. Properties of the
+ model are also available depending upon the type of \l {qmlmodels}{Data Model}.
+
+ The number of elements in the delegate has a direct effect on the
+ flicking performance of the view. If at all possible, place functionality
+ that is not needed for the normal display of the delegate in a \l Loader which
+ can load additional elements when needed.
+
+ The GridView will layout the items based on the size of the root item
+ in the delegate.
+
+ \note Delegates are instantiated as needed and may be destroyed at any time.
+ State should \e never be stored in a delegate.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::GridView::currentIndex
+ \qmlproperty Item QtQuick2::GridView::currentItem
+
+ The \c currentIndex property holds the index of the current item, and
+ \c currentItem holds the current item. Setting the currentIndex to -1
+ will clear the highlight and set currentItem to null.
+
+ If highlightFollowsCurrentItem is \c true, setting either of these
+ properties will smoothly scroll the GridView so that the current
+ item becomes visible.
+
+ Note that the position of the current item
+ may only be approximate until it becomes visible in the view.
+*/
+
+
+/*!
+ \qmlproperty Item QtQuick2::GridView::highlightItem
+
+ This holds the highlight item created from the \l highlight component.
+
+ The highlightItem is managed by the view unless
+ \l highlightFollowsCurrentItem is set to false.
+
+ \sa highlight, highlightFollowsCurrentItem
+*/
+
+
+/*!
+ \qmlproperty int QtQuick2::GridView::count
+ This property holds the number of items in the view.
+*/
+
+
+/*!
+ \qmlproperty Component QtQuick2::GridView::highlight
+ This property holds the component to use as the highlight.
+
+ An instance of the highlight component is created for each view.
+ The geometry of the resulting component instance will be managed by the view
+ so as to stay with the current item, unless the highlightFollowsCurrentItem property is false.
+
+ \sa highlightItem, highlightFollowsCurrentItem
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::GridView::highlightFollowsCurrentItem
+ This property sets whether the highlight is managed by the view.
+
+ If this property is true (the default value), the highlight is moved smoothly
+ to follow the current item. Otherwise, the
+ highlight is not moved by the view, and any movement must be implemented
+ by the highlight.
+
+ Here is a highlight with its motion defined by a \l {SpringAnimation} item:
+
+ \snippet doc/src/snippets/declarative/gridview/gridview.qml highlightFollowsCurrentItem
+*/
+
+
+/*!
+ \qmlproperty int QtQuick2::GridView::highlightMoveDuration
+ This property holds the move animation duration of the highlight delegate.
+
+ highlightFollowsCurrentItem must be true for this property
+ to have effect.
+
+ The default value for the duration is 150ms.
+
+ \sa highlightFollowsCurrentItem
+*/
+
+/*!
+ \qmlproperty real QtQuick2::GridView::preferredHighlightBegin
+ \qmlproperty real QtQuick2::GridView::preferredHighlightEnd
+ \qmlproperty enumeration QtQuick2::GridView::highlightRangeMode
+
+ These properties define the preferred range of the highlight (for the current item)
+ within the view. The \c preferredHighlightBegin value must be less than the
+ \c preferredHighlightEnd value.
+
+ These properties affect the position of the current item when the view is scrolled.
+ For example, if the currently selected item should stay in the middle of the
+ view when it is scrolled, set the \c preferredHighlightBegin and
+ \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
+ item would be. If the \c currentItem is changed programmatically, the view will
+ automatically scroll so that the current item is in the middle of the view.
+ Furthermore, the behavior of the current item index will occur whether or not a
+ highlight exists.
+
+ Valid values for \c highlightRangeMode are:
+
+ \list
+ \o GridView.ApplyRange - the view attempts to maintain the highlight within the range.
+ However, the highlight can move outside of the range at the ends of the view or due
+ to mouse interaction.
+ \o GridView.StrictlyEnforceRange - the highlight never moves outside of the range.
+ The current item changes if a keyboard or mouse action would cause the highlight to move
+ outside of the range.
+ \o GridView.NoHighlightRange - this is the default value.
+ \endlist
+*/
+
+
+/*!
+ \qmlproperty enumeration QtQuick2::GridView::layoutDirection
+ This property holds the layout direction of the grid.
+
+ Possible values:
+
+ \list
+ \o Qt.LeftToRight (default) - Items will be laid out starting in the top, left corner. The flow is
+ dependent on the \l GridView::flow property.
+ \o Qt.RightToLeft - Items will be laid out starting in the top, right corner. The flow is dependent
+ on the \l GridView::flow property.
+ \endlist
+
+ \bold Note: If GridView::flow is set to GridView.LeftToRight, this is not to be confused if
+ GridView::layoutDirection is set to Qt.RightToLeft. The GridView.LeftToRight flow value simply
+ indicates that the flow is horizontal.
+*/
+
+
+/*!
+ \qmlproperty enumeration QtQuick2::GridView::effectiveLayoutDirection
+ This property holds the effective layout direction of the grid.
+
+ When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
+ the visual layout direction of the grid will be mirrored. However, the
+ property \l {GridView::layoutDirection}{layoutDirection} will remain unchanged.
+
+ \sa GridView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
+*/
+/*!
+ \qmlproperty bool QtQuick2::GridView::keyNavigationWraps
+ This property holds whether the grid wraps key navigation
+
+ If this is true, key navigation that would move the current item selection
+ past one end of the view instead wraps around and moves the selection to
+ the other end of the view.
+
+ By default, key navigation is not wrapped.
+*/
+/*!
+ \qmlproperty int QtQuick2::GridView::cacheBuffer
+ This property determines whether delegates are retained outside the
+ visible area of the view.
+
+ If non-zero the view will keep as many delegates
+ instantiated as will fit within the buffer specified. For example,
+ if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
+ set to 40, then up to 2 delegates above and 2 delegates below the visible
+ area may be retained.
+
+ Note that cacheBuffer is not a pixel buffer - it only maintains additional
+ instantiated delegates.
+
+ Setting this value can make scrolling the list smoother at the expense
+ of additional memory usage. It is not a substitute for creating efficient
+ delegates; the fewer elements in a delegate, the faster a view may be
+ scrolled.
+*/
void QSGGridView::setHighlightMoveDuration(int duration)
{
Q_D(QSGGridView);
@@ -989,6 +1312,17 @@ void QSGGridView::setHighlightMoveDuration(int duration)
}
}
+/*!
+ \qmlproperty enumeration QtQuick2::GridView::flow
+ This property holds the flow of the grid.
+
+ Possible values:
+
+ \list
+ \o GridView.LeftToRight (default) - Items are laid out from left to right, and the view scrolls vertically
+ \o GridView.TopToBottom - Items are laid out from top to bottom, and the view scrolls horizontally
+ \endlist
+*/
QSGGridView::Flow QSGGridView::flow() const
{
Q_D(const QSGGridView);
@@ -1015,6 +1349,14 @@ void QSGGridView::setFlow(Flow flow)
}
+/*!
+ \qmlproperty int QtQuick2::GridView::cellWidth
+ \qmlproperty int QtQuick2::GridView::cellHeight
+
+ These properties holds the width and height of each cell in the grid.
+
+ The default cell size is 100x100.
+*/
int QSGGridView::cellWidth() const
{
Q_D(const QSGGridView);
@@ -1048,7 +1390,22 @@ void QSGGridView::setCellHeight(int cellHeight)
d->layout();
}
}
-
+/*!
+ \qmlproperty enumeration QtQuick2::GridView::snapMode
+
+ This property determines how the view scrolling will settle following a drag or flick.
+ The possible values are:
+
+ \list
+ \o GridView.NoSnap (default) - the view stops anywhere within the visible area.
+ \o GridView.SnapToRow - the view settles with a row (or column for \c GridView.TopToBottom flow)
+ aligned with the start of the view.
+ \o GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.TopToBottom flow)
+ away from the first visible row at the time the mouse button is released.
+ This mode is particularly useful for moving one page at a time.
+ \endlist
+
+*/
QSGGridView::SnapMode QSGGridView::snapMode() const
{
Q_D(const QSGGridView);
@@ -1065,6 +1422,24 @@ void QSGGridView::setSnapMode(SnapMode mode)
}
+/*!
+ \qmlproperty Component QtQuick2::GridView::footer
+ This property holds the component to use as the footer.
+
+ An instance of the footer component is created for each view. The
+ footer is positioned at the end of the view, after any items.
+
+ \sa header
+*/
+/*!
+ \qmlproperty Component QtQuick2::GridView::header
+ This property holds the component to use as the header.
+
+ An instance of the header component is created for each view. The
+ header is positioned at the beginning of the view, before any items.
+
+ \sa footer
+*/
void QSGGridView::viewportMoved()
{
Q_D(QSGGridView);
@@ -1115,7 +1490,13 @@ void QSGGridView::viewportMoved()
if (pos < viewPos + highlightStart)
pos = viewPos + highlightStart;
- static_cast<FxGridItemSG*>(d->highlight)->setPosition(static_cast<FxGridItemSG*>(d->highlight)->colPos(), qRound(pos));
+ if (pos != d->highlight->position()) {
+ d->highlightXAnimator->stop();
+ d->highlightYAnimator->stop();
+ static_cast<FxGridItemSG*>(d->highlight)->setPosition(static_cast<FxGridItemSG*>(d->highlight)->colPos(), pos);
+ } else {
+ d->updateHighlight();
+ }
// update current index
int idx = d->snapIndex();
@@ -1164,6 +1545,16 @@ void QSGGridView::keyPressEvent(QKeyEvent *event)
event->ignore();
QSGItemView::keyPressEvent(event);
}
+/*!
+ \qmlmethod QtQuick2::GridView::moveCurrentIndexUp()
+
+ Move the currentIndex up one item in the view.
+ The current index will wrap if keyNavigationWraps is true and it
+ is currently at the end. This method has no effect if the \l count is zero.
+
+ \bold Note: methods should only be called after the Component has completed.
+*/
+
void QSGGridView::moveCurrentIndexUp()
{
@@ -1184,6 +1575,15 @@ void QSGGridView::moveCurrentIndexUp()
}
}
+/*!
+ \qmlmethod QtQuick2::GridView::moveCurrentIndexDown()
+
+ Move the currentIndex down one item in the view.
+ The current index will wrap if keyNavigationWraps is true and it
+ is currently at the end. This method has no effect if the \l count is zero.
+
+ \bold Note: methods should only be called after the Component has completed.
+*/
void QSGGridView::moveCurrentIndexDown()
{
Q_D(QSGGridView);
@@ -1203,6 +1603,15 @@ void QSGGridView::moveCurrentIndexDown()
}
}
+/*!
+ \qmlmethod QtQuick2::GridView::moveCurrentIndexLeft()
+
+ Move the currentIndex left one item in the view.
+ The current index will wrap if keyNavigationWraps is true and it
+ is currently at the end. This method has no effect if the \l count is zero.
+
+ \bold Note: methods should only be called after the Component has completed.
+*/
void QSGGridView::moveCurrentIndexLeft()
{
Q_D(QSGGridView);
@@ -1236,6 +1645,16 @@ void QSGGridView::moveCurrentIndexLeft()
}
}
+
+/*!
+ \qmlmethod QtQuick2::GridView::moveCurrentIndexRight()
+
+ Move the currentIndex right one item in the view.
+ The current index will wrap if keyNavigationWraps is true and it
+ is currently at the end. This method has no effect if the \l count is zero.
+
+ \bold Note: methods should only be called after the Component has completed.
+*/
void QSGGridView::moveCurrentIndexRight()
{
Q_D(QSGGridView);
@@ -1273,7 +1692,7 @@ void QSGGridView::moveCurrentIndexRight()
void QSGGridView::itemsInserted(int modelIndex, int count)
{
Q_D(QSGGridView);
- if (!isComponentComplete())
+ if (!isComponentComplete() || !d->model || !d->model->isValid())
return;
int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
@@ -1404,7 +1823,7 @@ void QSGGridView::itemsInserted(int modelIndex, int count)
void QSGGridView::itemsRemoved(int modelIndex, int count)
{
Q_D(QSGGridView);
- if (!isComponentComplete())
+ if (!isComponentComplete() || !d->model || !d->model->isValid())
return;
d->itemCount -= count;
@@ -1486,7 +1905,7 @@ void QSGGridView::itemsRemoved(int modelIndex, int count)
void QSGGridView::itemsMoved(int from, int to, int count)
{
Q_D(QSGGridView);
- if (!isComponentComplete())
+ if (!isComponentComplete() || !d->isValid())
return;
d->updateUnrequestedIndexes();
@@ -1614,7 +2033,71 @@ void QSGGridView::itemsMoved(int from, int to, int count)
d->layout();
}
+/*!
+ \qmlmethod QtQuick2::GridView::positionViewAtIndex(int index, PositionMode mode)
+
+ Positions the view such that the \a index is at the position specified by
+ \a mode:
+
+ \list
+ \o GridView.Beginning - position item at the top (or left for \c GridView.TopToBottom flow) of the view.
+ \o GridView.Center - position item in the center of the view.
+ \o GridView.End - position item at bottom (or right for horizontal orientation) of the view.
+ \o GridView.Visible - if any part of the item is visible then take no action, otherwise
+ bring the item into view.
+ \o GridView.Contain - ensure the entire item is visible. If the item is larger than
+ the view the item is positioned at the top (or left for \c GridView.TopToBottom flow) of the view.
+ \endlist
+
+ If positioning the view at the index would cause empty space to be displayed at
+ the beginning or end of the view, the view will be positioned at the boundary.
+
+ It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
+ at a particular index. This is unreliable since removing items from the start
+ of the view does not cause all other items to be repositioned.
+ The correct way to bring an item into view is with \c positionViewAtIndex.
+
+ \bold Note: methods should only be called after the Component has completed. To position
+ the view at startup, this method should be called by Component.onCompleted. For
+ example, to position the view at the end:
+
+ \code
+ Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning)
+ \endcode
+*/
+
+/*!
+ \qmlmethod QtQuick2::GridView::positionViewAtBeginning()
+ \qmlmethod QtQuick2::GridView::positionViewAtEnd()
+
+ Positions the view at the beginning or end, taking into account any header or footer.
+
+ It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
+ at a particular index. This is unreliable since removing items from the start
+ of the list does not cause all other items to be repositioned, and because
+ the actual start of the view can vary based on the size of the delegates.
+
+ \bold Note: methods should only be called after the Component has completed. To position
+ the view at startup, this method should be called by Component.onCompleted. For
+ example, to position the view at the end on startup:
+
+ \code
+ Component.onCompleted: positionViewAtEnd()
+ \endcode
+*/
+
+/*!
+ \qmlmethod int QtQuick2::GridView::indexAt(int x, int y)
+
+ Returns the index of the visible item containing the point \a x, \a y in content
+ coordinates. If there is no item at the point specified, or the item is
+ not visible -1 is returned.
+
+ If the item is outside the visible area, -1 is returned, regardless of
+ whether an item will exist at that point when scrolled into view.
+ \bold Note: methods should only be called after the Component has completed.
+*/
QSGGridViewAttached *QSGGridView::qmlAttachedProperties(QObject *obj)
{
return new QSGGridViewAttached(obj);
diff --git a/src/declarative/items/qsgimage.cpp b/src/declarative/items/qsgimage.cpp
index dc5c8d11c7..cb12c177cd 100644
--- a/src/declarative/items/qsgimage.cpp
+++ b/src/declarative/items/qsgimage.cpp
@@ -95,7 +95,6 @@ QSGImagePrivate::QSGImagePrivate()
/*!
\qmlclass Image QSGImage
- \since QtQuick 1.0
\inqmlmodule QtQuick 2
\ingroup qml-basic-visual-elements
\brief The Image element displays an image in a declarative user interface
@@ -446,7 +445,6 @@ qreal QSGImage::paintedHeight() const
/*!
\qmlproperty bool QtQuick2::Image::cache
- \since Quick 1.1
Specifies whether the image should be cached. The default value is
true. Setting \a cache to false is useful when dealing with large images,
@@ -455,7 +453,6 @@ qreal QSGImage::paintedHeight() const
/*!
\qmlproperty bool QtQuick2::Image::mirror
- \since Quick 1.1
This property holds whether the image should be horizontally inverted
(effectively displaying a mirrored image).
@@ -466,7 +463,6 @@ qreal QSGImage::paintedHeight() const
/*!
\qmlproperty enumeration QtQuick2::Image::horizontalAlignment
\qmlproperty enumeration QtQuick2::Image::verticalAlignment
- \since Quick 2.0
Sets the horizontal and vertical alignment of the image. By default, the image is top-left aligned.
diff --git a/src/declarative/items/qsgitem.cpp b/src/declarative/items/qsgitem.cpp
index 163cec13ee..a7797630c0 100644
--- a/src/declarative/items/qsgitem.cpp
+++ b/src/declarative/items/qsgitem.cpp
@@ -71,6 +71,168 @@
QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass Transform QSGTransform
+ \inqmlmodule QtQuick 2
+ \ingroup qml-transform-elements
+ \brief The Transform elements provide a way of building advanced transformations on Items.
+
+ The Transform element is a base type which cannot be instantiated directly.
+ The following concrete Transform types are available:
+
+ \list
+ \o \l Rotation
+ \o \l Scale
+ \o \l Translate
+ \endlist
+
+ The Transform elements let you create and control advanced transformations that can be configured
+ independently using specialized properties.
+
+ You can assign any number of Transform elements to an \l Item. Each Transform is applied in order,
+ one at a time.
+*/
+
+/*!
+ \qmlclass Translate QSGTranslate
+ \inqmlmodule QtQuick 2
+ \ingroup qml-transform-elements
+ \brief The Translate object provides a way to move an Item without changing its x or y properties.
+
+ The Translate object provides independent control over position in addition to the Item's x and y properties.
+
+ The following example moves the Y axis of the \l Rectangle elements while still allowing the \l Row element
+ to lay the items out as if they had not been transformed:
+ \qml
+ import QtQuick 1.0
+
+ Row {
+ Rectangle {
+ width: 100; height: 100
+ color: "blue"
+ transform: Translate { y: 20 }
+ }
+ Rectangle {
+ width: 100; height: 100
+ color: "red"
+ transform: Translate { y: -20 }
+ }
+ }
+ \endqml
+
+ \image translate.png
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Translate::x
+
+ The translation along the X axis.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Translate::y
+
+ The translation along the Y axis.
+*/
+
+/*!
+ \qmlclass Scale QSGScale
+ \inqmlmodule QtQuick 2
+ \ingroup qml-transform-elements
+ \brief The Scale element provides a way to scale an Item.
+
+ The Scale element gives more control over scaling than using \l Item's \l{Item::scale}{scale} property. Specifically,
+ it allows a different scale for the x and y axes, and allows the scale to be relative to an
+ arbitrary point.
+
+ The following example scales the X axis of the Rectangle, relative to its interior point 25, 25:
+ \qml
+ Rectangle {
+ width: 100; height: 100
+ color: "blue"
+ transform: Scale { origin.x: 25; origin.y: 25; xScale: 3}
+ }
+ \endqml
+
+ \sa Rotation, Translate
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Scale::origin.x
+ \qmlproperty real QtQuick2::Scale::origin.y
+
+ The point that the item is scaled from (i.e., the point that stays fixed relative to the parent as
+ the rest of the item grows). By default the origin is 0, 0.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Scale::xScale
+
+ The scaling factor for the X axis.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Scale::yScale
+
+ The scaling factor for the Y axis.
+*/
+
+/*!
+ \qmlclass Rotation QSGRotation
+ \inqmlmodule QtQuick 2
+ \ingroup qml-transform-elements
+ \brief The Rotation object provides a way to rotate an Item.
+
+ The Rotation object gives more control over rotation than using \l Item's \l{Item::rotation}{rotation} property.
+ Specifically, it allows (z axis) rotation to be relative to an arbitrary point.
+
+ The following example rotates a Rectangle around its interior point 25, 25:
+ \qml
+ Rectangle {
+ width: 100; height: 100
+ color: "blue"
+ transform: Rotation { origin.x: 25; origin.y: 25; angle: 45}
+ }
+ \endqml
+
+ Rotation also provides a way to specify 3D-like rotations for Items. For these types of
+ rotations you must specify the axis to rotate around in addition to the origin point.
+
+ The following example shows various 3D-like rotations applied to an \l Image.
+ \snippet doc/src/snippets/declarative/rotation.qml 0
+
+ \image axisrotation.png
+
+ \sa {declarative/ui-components/dialcontrol}{Dial Control example}, {declarative/toys/clocks}{Clocks example}
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Rotation::origin.x
+ \qmlproperty real QtQuick2::Rotation::origin.y
+
+ The origin point of the rotation (i.e., the point that stays fixed relative to the parent as
+ the rest of the item rotates). By default the origin is 0, 0.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Rotation::axis.x
+ \qmlproperty real QtQuick2::Rotation::axis.y
+ \qmlproperty real QtQuick2::Rotation::axis.z
+
+ The axis to rotate around. For simple (2D) rotation around a point, you do not need to specify an axis,
+ as the default axis is the z axis (\c{ axis { x: 0; y: 0; z: 1 } }).
+
+ For a typical 3D-like rotation you will usually specify both the origin and the axis.
+
+ \image 3d-rotation-axis.png
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Rotation::angle
+
+ The angle to rotate, in degrees clockwise.
+*/
+
QSGTransformPrivate::QSGTransformPrivate()
{
}
@@ -104,7 +266,7 @@ void QSGTransform::update()
}
}
-QSGContents::QSGContents(QSGItem *item)
+QSGContents::QSGContents(QSGItem *item)
: m_item(item), m_x(0), m_y(0), m_width(0), m_height(0)
{
//### optimize
@@ -283,6 +445,69 @@ void QSGItemKeyFilter::componentComplete()
{
if (m_next) m_next->componentComplete();
}
+/*!
+ \qmlclass KeyNavigation QSGKeyNavigationAttached
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-interaction-elements
+ \brief The KeyNavigation attached property supports key navigation by arrow keys.
+
+ Key-based user interfaces commonly allow the use of arrow keys to navigate between
+ focusable items. The KeyNavigation attached property enables this behavior by providing a
+ convenient way to specify the item that should gain focus when an arrow or tab key is pressed.
+
+ The following example provides key navigation for a 2x2 grid of items:
+
+ \snippet doc/src/snippets/declarative/keynavigation.qml 0
+
+ The top-left item initially receives focus by setting \l {Item::}{focus} to
+ \c true. When an arrow key is pressed, the focus will move to the
+ appropriate item, as defined by the value that has been set for
+ the KeyNavigation \l left, \l right, \l up or \l down properties.
+
+ Note that if a KeyNavigation attached property receives the key press and release
+ events for a requested arrow or tab key, the event is accepted and does not
+ propagate any further.
+
+ By default, KeyNavigation receives key events after the item to which it is attached.
+ If the item accepts the key event, the KeyNavigation attached property will not
+ receive an event for that key. Setting the \l priority property to
+ \c KeyNavigation.BeforeItem allows the event to be used for key navigation
+ before the item, rather than after.
+
+ If item to which the focus is switching is not enabled or visible, an attempt will
+ be made to skip this item and focus on the next. This is possible if there are
+ a chain of items with the same KeyNavigation handler. If multiple items in a row are not enabled
+ or visible, they will also be skipped.
+
+ KeyNavigation will implicitly set the other direction to return focus to this item. So if you set
+ \l left to another item, \l right will be set on that item's KeyNavigation to set focus back to this
+ item. However, if that item's KeyNavigation has had right explicitly set then no change will occur.
+ This means that the above example could have been written, with the same behaviour, without specifing
+ KeyNavigation.right or KeyNavigation.down for any of the items.
+
+ \sa {Keys}{Keys attached property}
+*/
+
+/*!
+ \qmlproperty Item QtQuick2::KeyNavigation::left
+ \qmlproperty Item QtQuick2::KeyNavigation::right
+ \qmlproperty Item QtQuick2::KeyNavigation::up
+ \qmlproperty Item QtQuick2::KeyNavigation::down
+ \qmlproperty Item QtQuick2::KeyNavigation::tab
+ \qmlproperty Item QtQuick2::KeyNavigation::backtab
+
+ These properties hold the item to assign focus to
+ when the left, right, up or down cursor keys, or the
+ tab key are pressed.
+*/
+
+/*!
+ \qmlproperty Item QtQuick2::KeyNavigation::tab
+ \qmlproperty Item QtQuick2::KeyNavigation::backtab
+
+ These properties hold the item to assign focus to
+ when the Tab key or Shift+Tab key combination (Backtab) are pressed.
+*/
QSGKeyNavigationAttached::QSGKeyNavigationAttached(QObject *parent)
: QObject(*(new QSGKeyNavigationAttachedPrivate), parent),
@@ -429,6 +654,21 @@ void QSGKeyNavigationAttached::setBacktab(QSGItem *i)
emit backtabChanged();
}
+/*!
+ \qmlproperty enumeration QtQuick2::KeyNavigation::priority
+
+ This property determines whether the keys are processed before
+ or after the attached item's own key handling.
+
+ \list
+ \o KeyNavigation.BeforeItem - process the key events before normal
+ item key processing. If the event is used for key navigation, it will be accepted and will not
+ be passed on to the item.
+ \o KeyNavigation.AfterItem (default) - process the key events after normal item key
+ handling. If the item accepts the key event it will not be
+ handled by the KeyNavigation attached property handler.
+ \endlist
+*/
QSGKeyNavigationAttached::Priority QSGKeyNavigationAttached::priority() const
{
return m_processPost ? AfterItem : BeforeItem;
@@ -617,6 +857,393 @@ bool QSGKeysAttachedPrivate::isConnected(const char *signalName)
return isSignalConnected(signalIndex(signalName));
}
+/*!
+ \qmlclass Keys QSGKeysAttached
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-interaction-elements
+ \brief The Keys attached property provides key handling to Items.
+
+ All visual primitives support key handling via the Keys
+ attached property. Keys can be handled via the onPressed
+ and onReleased signal properties.
+
+ The signal properties have a \l KeyEvent parameter, named
+ \e event which contains details of the event. If a key is
+ handled \e event.accepted should be set to true to prevent the
+ event from propagating up the item hierarchy.
+
+ \section1 Example Usage
+
+ The following example shows how the general onPressed handler can
+ be used to test for a certain key; in this case, the left cursor
+ key:
+
+ \snippet doc/src/snippets/declarative/keys/keys-pressed.qml key item
+
+ Some keys may alternatively be handled via specific signal properties,
+ for example \e onSelectPressed. These handlers automatically set
+ \e event.accepted to true.
+
+ \snippet doc/src/snippets/declarative/keys/keys-handler.qml key item
+
+ See \l{Qt::Key}{Qt.Key} for the list of keyboard codes.
+
+ \section1 Key Handling Priorities
+
+ The Keys attached property can be configured to handle key events
+ before or after the item it is attached to. This makes it possible
+ to intercept events in order to override an item's default behavior,
+ or act as a fallback for keys not handled by the item.
+
+ If \l priority is Keys.BeforeItem (default) the order of key event processing is:
+
+ \list 1
+ \o Items specified in \c forwardTo
+ \o specific key handlers, e.g. onReturnPressed
+ \o onKeyPress, onKeyRelease handlers
+ \o Item specific key handling, e.g. TextInput key handling
+ \o parent item
+ \endlist
+
+ If priority is Keys.AfterItem the order of key event processing is:
+
+ \list 1
+ \o Item specific key handling, e.g. TextInput key handling
+ \o Items specified in \c forwardTo
+ \o specific key handlers, e.g. onReturnPressed
+ \o onKeyPress, onKeyRelease handlers
+ \o parent item
+ \endlist
+
+ If the event is accepted during any of the above steps, key
+ propagation stops.
+
+ \sa KeyEvent, {KeyNavigation}{KeyNavigation attached property}
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::Keys::enabled
+
+ This flags enables key handling if true (default); otherwise
+ no key handlers will be called.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick2::Keys::priority
+
+ This property determines whether the keys are processed before
+ or after the attached item's own key handling.
+
+ \list
+ \o Keys.BeforeItem (default) - process the key events before normal
+ item key processing. If the event is accepted it will not
+ be passed on to the item.
+ \o Keys.AfterItem - process the key events after normal item key
+ handling. If the item accepts the key event it will not be
+ handled by the Keys attached property handler.
+ \endlist
+*/
+
+/*!
+ \qmlproperty list<Object> QtQuick2::Keys::forwardTo
+
+ This property provides a way to forward key presses, key releases, and keyboard input
+ coming from input methods to other items. This can be useful when you want
+ one item to handle some keys (e.g. the up and down arrow keys), and another item to
+ handle other keys (e.g. the left and right arrow keys). Once an item that has been
+ forwarded keys accepts the event it is no longer forwarded to items later in the
+ list.
+
+ This example forwards key events to two lists:
+ \qml
+ Item {
+ ListView {
+ id: list1
+ // ...
+ }
+ ListView {
+ id: list2
+ // ...
+ }
+ Keys.forwardTo: [list1, list2]
+ focus: true
+ }
+ \endqml
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onPressed(KeyEvent event)
+
+ This handler is called when a key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onReleased(KeyEvent event)
+
+ This handler is called when a key has been released. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onDigit0Pressed(KeyEvent event)
+
+ This handler is called when the digit '0' has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onDigit1Pressed(KeyEvent event)
+
+ This handler is called when the digit '1' has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onDigit2Pressed(KeyEvent event)
+
+ This handler is called when the digit '2' has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onDigit3Pressed(KeyEvent event)
+
+ This handler is called when the digit '3' has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onDigit4Pressed(KeyEvent event)
+
+ This handler is called when the digit '4' has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onDigit5Pressed(KeyEvent event)
+
+ This handler is called when the digit '5' has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onDigit6Pressed(KeyEvent event)
+
+ This handler is called when the digit '6' has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onDigit7Pressed(KeyEvent event)
+
+ This handler is called when the digit '7' has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onDigit8Pressed(KeyEvent event)
+
+ This handler is called when the digit '8' has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onDigit9Pressed(KeyEvent event)
+
+ This handler is called when the digit '9' has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onLeftPressed(KeyEvent event)
+
+ This handler is called when the Left arrow has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onRightPressed(KeyEvent event)
+
+ This handler is called when the Right arrow has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onUpPressed(KeyEvent event)
+
+ This handler is called when the Up arrow has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onDownPressed(KeyEvent event)
+
+ This handler is called when the Down arrow has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onTabPressed(KeyEvent event)
+
+ This handler is called when the Tab key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onBacktabPressed(KeyEvent event)
+
+ This handler is called when the Shift+Tab key combination (Backtab) has
+ been pressed. The \a event parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onAsteriskPressed(KeyEvent event)
+
+ This handler is called when the Asterisk '*' has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onEscapePressed(KeyEvent event)
+
+ This handler is called when the Escape key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onReturnPressed(KeyEvent event)
+
+ This handler is called when the Return key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onEnterPressed(KeyEvent event)
+
+ This handler is called when the Enter key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onDeletePressed(KeyEvent event)
+
+ This handler is called when the Delete key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onSpacePressed(KeyEvent event)
+
+ This handler is called when the Space key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onBackPressed(KeyEvent event)
+
+ This handler is called when the Back key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onCancelPressed(KeyEvent event)
+
+ This handler is called when the Cancel key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onSelectPressed(KeyEvent event)
+
+ This handler is called when the Select key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onYesPressed(KeyEvent event)
+
+ This handler is called when the Yes key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onNoPressed(KeyEvent event)
+
+ This handler is called when the No key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onContext1Pressed(KeyEvent event)
+
+ This handler is called when the Context1 key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onContext2Pressed(KeyEvent event)
+
+ This handler is called when the Context2 key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onContext3Pressed(KeyEvent event)
+
+ This handler is called when the Context3 key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onContext4Pressed(KeyEvent event)
+
+ This handler is called when the Context4 key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onCallPressed(KeyEvent event)
+
+ This handler is called when the Call key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onHangupPressed(KeyEvent event)
+
+ This handler is called when the Hangup key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onFlipPressed(KeyEvent event)
+
+ This handler is called when the Flip key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onMenuPressed(KeyEvent event)
+
+ This handler is called when the Menu key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onVolumeUpPressed(KeyEvent event)
+
+ This handler is called when the VolumeUp key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Keys::onVolumeDownPressed(KeyEvent event)
+
+ This handler is called when the VolumeDown key has been pressed. The \a event
+ parameter provides information about the event.
+*/
+
QSGKeysAttached::QSGKeysAttached(QObject *parent)
: QObject(*(new QSGKeysAttachedPrivate), parent),
QSGItemKeyFilter(qobject_cast<QSGItem*>(parent))
@@ -759,7 +1386,7 @@ QVariant QSGKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const
if (d->item) {
for (int ii = 0; ii < d->targets.count(); ++ii) {
QSGItem *i = d->targets.at(ii);
- if (i && i->isVisible() && (i->flags() & QSGItem::ItemAcceptsInputMethod) && i == d->imeItem) {
+ if (i && i->isVisible() && (i->flags() & QSGItem::ItemAcceptsInputMethod) && i == d->imeItem) {
//### how robust is i == d->imeItem check?
QVariant v = i->inputMethodQuery(query);
if (v.userType() == QVariant::RectF)
@@ -776,6 +1403,69 @@ QSGKeysAttached *QSGKeysAttached::qmlAttachedProperties(QObject *obj)
return new QSGKeysAttached(obj);
}
+/*!
+ \qmlclass LayoutMirroring QSGLayoutMirroringAttached
+ \inqmlmodule QtQuick 2
+ \ingroup qml-utility-elements
+ \brief The LayoutMirroring attached property is used to mirror layout behavior.
+
+ The LayoutMirroring attached property is used to horizontally mirror \l {anchor-layout}{Item anchors},
+ \l{Using QML Positioner and Repeater Items}{positioner} elements (such as \l Row and \l Grid)
+ and views (such as \l GridView and horizontal \l ListView). Mirroring is a visual change: left
+ anchors become right anchors, and positioner elements like \l Grid and \l Row reverse the
+ horizontal layout of child items.
+
+ Mirroring is enabled for an item by setting the \l enabled property to true. By default, this
+ only affects the item itself; setting the \l childrenInherit property to true propagates the mirroring
+ behavior to all child elements as well. If the \c LayoutMirroring attached property has not been defined
+ for an item, mirroring is not enabled.
+
+ The following example shows mirroring in action. The \l Row below is specified as being anchored
+ to the left of its parent. However, since mirroring has been enabled, the anchor is horizontally
+ reversed and it is now anchored to the right. Also, since items in a \l Row are positioned
+ from left to right by default, they are now positioned from right to left instead, as demonstrated
+ by the numbering and opacity of the items:
+
+ \snippet doc/src/snippets/declarative/layoutmirroring.qml 0
+
+ \image layoutmirroring.png
+
+ Layout mirroring is useful when it is necessary to support both left-to-right and right-to-left
+ layout versions of an application to target different language areas. The \l childrenInherit
+ property allows layout mirroring to be applied without manually setting layout configurations
+ for every item in an application. Keep in mind, however, that mirroring does not affect any
+ positioning that is defined by the \l Item \l {Item::}{x} coordinate value, so even with
+ mirroring enabled, it will often be necessary to apply some layout fixes to support the
+ desired layout direction. Also, it may be necessary to disable the mirroring of individual
+ child items (by setting \l {enabled}{LayoutMirroring.enabled} to false for such items) if
+ mirroring is not the desired behavior, or if the child item already implements mirroring in
+ some custom way.
+
+ See \l {QML Right-to-left User Interfaces} for further details on using \c LayoutMirroring and
+ other related features to implement right-to-left support for an application.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::LayoutMirroring::enabled
+
+ This property holds whether the item's layout is mirrored horizontally. Setting this to true
+ horizontally reverses \l {anchor-layout}{anchor} settings such that left anchors become right,
+ and right anchors become left. For \l{Using QML Positioner and Repeater Items}{positioner} elements
+ (such as \l Row and \l Grid) and view elements (such as \l {GridView}{GridView} and \l {ListView}{ListView})
+ this also mirrors the horizontal layout direction of the item.
+
+ The default value is false.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::LayoutMirroring::childrenInherit
+
+ This property holds whether the \l {enabled}{LayoutMirroring.enabled} value for this item
+ is inherited by its children.
+
+ The default value is false.
+*/
+
QSGLayoutMirroringAttached::QSGLayoutMirroringAttached(QObject *parent) : QObject(parent), itemPrivate(0)
{
@@ -880,6 +1570,137 @@ void QSGItemPrivate::setLayoutMirror(bool mirror)
}
}
+/*!
+ \class QSGItem
+ \brief The QSGItem class provides the most basic of all visual items in QML.
+
+ All visual items in Qt Declarative inherit from QSGItem. Although QSGItem
+ has no visual appearance, it defines all the properties that are
+ common across visual items - such as the x and y position, the
+ width and height, \l {anchor-layout}{anchoring} and key handling.
+
+ You can subclass QSGItem to provide your own custom visual item that inherits
+ these features. Note that, because it does not draw anything, QSGItem sets the
+ QGraphicsItem::ItemHasNoContents flag. If you subclass QSGItem to create a visual
+ item, you will need to unset this flag.
+
+*/
+
+/*!
+ \qmlclass Item QSGItem
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-visual-elements
+ \brief The Item is the most basic of all visual items in QML.
+
+ All visual items in Qt Declarative inherit from Item. Although Item
+ has no visual appearance, it defines all the properties that are
+ common across visual items - such as the x and y position, the
+ width and height, \l {anchor-layout}{anchoring} and key handling.
+
+ Item is also useful for grouping items together.
+
+ \qml
+ Item {
+ Image {
+ source: "tile.png"
+ }
+ Image {
+ x: 80
+ width: 100
+ height: 100
+ source: "tile.png"
+ }
+ Image {
+ x: 190
+ width: 100
+ height: 100
+ fillMode: Image.Tile
+ source: "tile.png"
+ }
+ }
+ \endqml
+
+
+ \section1 Key Handling
+
+ Key handling is available to all Item-based visual elements via the \l {Keys}{Keys}
+ attached property. The \e Keys attached property provides basic handlers such
+ as \l {Keys::onPressed}{onPressed} and \l {Keys::onReleased}{onReleased},
+ as well as handlers for specific keys, such as
+ \l {Keys::onCancelPressed}{onCancelPressed}. The example below
+ assigns \l {qmlfocus}{focus} to the item and handles
+ the Left key via the general \e onPressed handler and the Select key via the
+ onSelectPressed handler:
+
+ \qml
+ Item {
+ focus: true
+ Keys.onPressed: {
+ if (event.key == Qt.Key_Left) {
+ console.log("move left");
+ event.accepted = true;
+ }
+ }
+ Keys.onSelectPressed: console.log("Selected");
+ }
+ \endqml
+
+ See the \l {Keys}{Keys} attached property for detailed documentation.
+
+ \section1 Layout Mirroring
+
+ Item layouts can be mirrored using the \l {LayoutMirroring}{LayoutMirroring} attached property.
+
+*/
+
+/*!
+ \fn void QSGItem::childrenRectChanged(const QRectF &)
+ \internal
+*/
+
+/*!
+ \fn void QSGItem::baselineOffsetChanged(qreal)
+ \internal
+*/
+
+/*!
+ \fn void QSGItem::stateChanged(const QString &state)
+ \internal
+*/
+
+/*!
+ \fn void QSGItem::parentChanged(QSGItem *)
+ \internal
+*/
+
+/*!
+ \fn void QSGItem::smoothChanged(bool)
+ \internal
+*/
+
+/*!
+ \fn void QSGItem::clipChanged(bool)
+ \internal
+*/
+
+/*! \fn void QSGItem::transformOriginChanged(TransformOrigin)
+ \internal
+*/
+
+/*!
+ \fn void QSGItem::focusChanged(bool)
+ \internal
+*/
+
+/*!
+ \fn void QSGItem::activeFocusChanged(bool)
+ \internal
+*/
+/*!
+ \fn QSGItem::QSGItem(QSGItem *parent)
+
+ Constructs a QSGItem with the given \a parent.
+*/
QSGItem::QSGItem(QSGItem* parent)
: QObject(*(new QSGItemPrivate), parent)
{
@@ -887,6 +1708,8 @@ QSGItem::QSGItem(QSGItem* parent)
d->init(parent);
}
+/*! \internal
+*/
QSGItem::QSGItem(QSGItemPrivate &dd, QSGItem *parent)
: QObject(dd, parent)
{
@@ -894,15 +1717,37 @@ QSGItem::QSGItem(QSGItemPrivate &dd, QSGItem *parent)
d->init(parent);
}
+#ifndef QT_NO_DEBUG
+static int qt_item_count = 0;
+
+static void qt_print_item_count()
+{
+ qDebug("Number of leaked items: %i", qt_item_count);
+ qt_item_count = -1;
+}
+#endif
+
+/*!
+ Destroys the QSGItem.
+*/
QSGItem::~QSGItem()
{
+#ifndef QT_NO_DEBUG
+ --qt_item_count;
+ if (qt_item_count < 0)
+ qDebug("Item destroyed after qt_print_item_count() was called.");
+#endif
+
Q_D(QSGItem);
+ if (d->parentItem)
+ setParentItem(0);
+ else if (d->canvas && d->itemNodeInstance)
+ QSGCanvasPrivate::get(d->canvas)->cleanup(d->itemNodeInstance); // cleanup root
// XXX todo - optimize
- setParentItem(0);
while (!d->childItems.isEmpty())
d->childItems.first()->setParentItem(0);
-
+
for (int ii = 0; ii < d->changeListeners.count(); ++ii) {
QSGAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate();
if (anchor)
@@ -915,7 +1760,7 @@ QSGItem::~QSGItem()
if (anchor && anchor->item && anchor->item->parent() != this) //child will be deleted anyway
anchor->updateOnComplete();
}
-
+
for (int ii = 0; ii < d->changeListeners.count(); ++ii) {
const QSGItemPrivate::ChangeListener &change = d->changeListeners.at(ii);
if (change.types & QSGItemPrivate::Destroyed)
@@ -928,6 +1773,38 @@ QSGItem::~QSGItem()
delete d->_contents; d->_contents = 0;
}
+/*!
+ \qmlproperty enumeration QtQuick2::Item::transformOrigin
+ This property holds the origin point around which scale and rotation transform.
+
+ Nine transform origins are available, as shown in the image below.
+
+ \image declarative-transformorigin.png
+
+ This example rotates an image around its bottom-right corner.
+ \qml
+ Image {
+ source: "myimage.png"
+ transformOrigin: Item.BottomRight
+ rotation: 45
+ }
+ \endqml
+
+ The default transform origin is \c Item.Center.
+
+ To set an arbitrary transform origin point use the \l Scale or \l Rotation
+ transform elements.
+*/
+
+/*!
+ \qmlproperty Item QtQuick2::Item::parent
+ This property holds the parent of the item.
+*/
+
+/*!
+ \property QSGItem::parent
+ This property holds the parent of the item.
+*/
void QSGItem::setParentItem(QSGItem *parentItem)
{
Q_D(QSGItem);
@@ -952,10 +1829,10 @@ void QSGItem::setParentItem(QSGItem *parentItem)
scopeItem = oldParentItem;
while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem();
scopeFocusedItem = d->subFocusItem;
- }
+ }
- if (scopeFocusedItem)
- QSGCanvasPrivate::get(d->canvas)->clearFocusInScope(scopeItem, scopeFocusedItem,
+ if (scopeFocusedItem)
+ QSGCanvasPrivate::get(d->canvas)->clearFocusInScope(scopeItem, scopeFocusedItem,
QSGCanvasPrivate::DontChangeFocusProperty);
op->removeChild(this);
@@ -965,16 +1842,13 @@ void QSGItem::setParentItem(QSGItem *parentItem)
QSGCanvas *parentCanvas = parentItem?QSGItemPrivate::get(parentItem)->canvas:0;
if (d->canvas != parentCanvas) {
- if (d->canvas && d->itemNodeInstance)
- QSGCanvasPrivate::get(d->canvas)->cleanup(d->itemNodeInstance);
-
QSGItemPrivate::InitializationState initState;
initState.clear();
d->initCanvas(&initState, parentCanvas);
}
d->dirty(QSGItemPrivate::ParentChanged);
-
+
if (d->parentItem)
QSGItemPrivate::get(d->parentItem)->addChild(this);
@@ -991,7 +1865,7 @@ void QSGItem::setParentItem(QSGItem *parentItem)
QSGItemPrivate::get(scopeFocusedItem)->focus = false;
emit scopeFocusedItem->focusChanged(false);
} else {
- QSGCanvasPrivate::get(d->canvas)->setFocusInScope(scopeItem, scopeFocusedItem,
+ QSGCanvasPrivate::get(d->canvas)->setFocusInScope(scopeItem, scopeFocusedItem,
QSGCanvasPrivate::DontChangeFocusProperty);
}
}
@@ -1015,7 +1889,7 @@ void QSGItem::stackBefore(const QSGItem *sibling)
int myIndex = parentPrivate->childItems.indexOf(this);
int siblingIndex = parentPrivate->childItems.indexOf(const_cast<QSGItem *>(sibling));
-
+
Q_ASSERT(myIndex != -1 && siblingIndex != -1);
if (myIndex == siblingIndex - 1)
@@ -1029,7 +1903,7 @@ void QSGItem::stackBefore(const QSGItem *sibling)
parentPrivate->dirty(QSGItemPrivate::ChildrenStackingChanged);
- for (int ii = qMin(siblingIndex, myIndex); ii < parentPrivate->childItems.count(); ++ii)
+ for (int ii = qMin(siblingIndex, myIndex); ii < parentPrivate->childItems.count(); ++ii)
QSGItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged();
}
@@ -1045,7 +1919,7 @@ void QSGItem::stackAfter(const QSGItem *sibling)
int myIndex = parentPrivate->childItems.indexOf(this);
int siblingIndex = parentPrivate->childItems.indexOf(const_cast<QSGItem *>(sibling));
-
+
Q_ASSERT(myIndex != -1 && siblingIndex != -1);
if (myIndex == siblingIndex + 1)
@@ -1059,7 +1933,7 @@ void QSGItem::stackAfter(const QSGItem *sibling)
parentPrivate->dirty(QSGItemPrivate::ChildrenStackingChanged);
- for (int ii = qMin(myIndex, siblingIndex + 1); ii < parentPrivate->childItems.count(); ++ii)
+ for (int ii = qMin(myIndex, siblingIndex + 1); ii < parentPrivate->childItems.count(); ++ii)
QSGItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged();
}
@@ -1077,8 +1951,8 @@ QSGEngine *QSGItem::sceneGraphEngine() const
return canvas()->sceneGraphEngine();
}
-QSGCanvas *QSGItem::canvas() const
-{
+QSGCanvas *QSGItem::canvas() const
+{
Q_D(const QSGItem);
return d->canvas;
}
@@ -1128,13 +2002,13 @@ void QSGItemPrivate::removeChild(QSGItem *child)
emit q->childrenChanged();
}
-void QSGItemPrivate::InitializationState::clear()
-{
- focusScope = 0;
+void QSGItemPrivate::InitializationState::clear()
+{
+ focusScope = 0;
}
-void QSGItemPrivate::InitializationState::clear(QSGItem *fs)
-{
+void QSGItemPrivate::InitializationState::clear(QSGItem *fs)
+{
focusScope = fs;
}
@@ -1160,14 +2034,17 @@ void QSGItemPrivate::initCanvas(InitializationState *state, QSGCanvas *c)
c->itemsToPolish.remove(q);
if (c->mouseGrabberItem == q)
c->mouseGrabberItem = 0;
+ if ( hoverEnabled )
+ c->hoverItems.removeAll(q);
+ if (itemNodeInstance)
+ c->cleanup(itemNodeInstance);
}
canvas = c;
- if (canvas && polishScheduled)
+ if (canvas && polishScheduled)
QSGCanvasPrivate::get(canvas)->itemsToPolish.insert(q);
- // XXX todo - why aren't these added to the destroy list?
itemNodeInstance = 0;
opacityNode = 0;
clipNode = 0;
@@ -1248,6 +2125,55 @@ void QSGItemPrivate::itemToParentTransform(QTransform &t) const
}
}
+
+/*!
+ \qmlproperty real QtQuick2::Item::childrenRect.x
+ \qmlproperty real QtQuick2::Item::childrenRect.y
+ \qmlproperty real QtQuick2::Item::childrenRect.width
+ \qmlproperty real QtQuick2::Item::childrenRect.height
+
+ The childrenRect properties allow an item access to the geometry of its
+ children. This property is useful if you have an item that needs to be
+ sized to fit its children.
+*/
+
+
+/*!
+ \qmlproperty list<Item> QtQuick2::Item::children
+ \qmlproperty list<Object> QtQuick2::Item::resources
+
+ The children property contains the list of visual children of this item.
+ The resources property contains non-visual resources that you want to
+ reference by name.
+
+ Generally you can rely on Item's default property to handle all this for
+ you, but it can come in handy in some cases.
+
+ \qml
+ Item {
+ children: [
+ Text {},
+ Rectangle {}
+ ]
+ resources: [
+ Component {
+ id: myComponent
+ Text {}
+ }
+ ]
+ }
+ \endqml
+*/
+
+/*!
+ Returns true if construction of the QML component is complete; otherwise
+ returns false.
+
+ It is often desirable to delay some processing until the component is
+ completed.
+
+ \sa componentComplete()
+*/
bool QSGItem::isComponentComplete() const
{
Q_D(const QSGItem);
@@ -1255,11 +2181,11 @@ bool QSGItem::isComponentComplete() const
}
QSGItemPrivate::QSGItemPrivate()
-: _anchors(0), _contents(0), baselineOffset(0), _anchorLines(0), _stateGroup(0), origin(QSGItem::Center),
-
- flags(0), widthValid(false), heightValid(false), componentComplete(true),
+: _anchors(0), _contents(0), baselineOffset(0), _anchorLines(0), _stateGroup(0), origin(QSGItem::Center),
+
+ flags(0), widthValid(false), heightValid(false), componentComplete(true),
keepMouse(false), hoverEnabled(false), smooth(false), focus(false), activeFocus(false), notifiedFocus(false),
- notifiedActiveFocus(false), filtersChildMouseEvents(false), explicitVisible(true),
+ notifiedActiveFocus(false), filtersChildMouseEvents(false), explicitVisible(true),
effectiveVisible(true), explicitEnable(true), effectiveEnable(true), polishScheduled(false),
inheritedLayoutMirror(false), effectiveLayoutMirror(false), isMirrorImplicit(true),
inheritMirrorFromParent(false), inheritMirrorFromItem(false), childrenDoNotOverlap(false),
@@ -1268,12 +2194,12 @@ QSGItemPrivate::QSGItemPrivate()
subFocusItem(0),
- x(0), y(0), width(0), height(0), implicitWidth(0), implicitHeight(0),
+ x(0), y(0), width(0), height(0), implicitWidth(0), implicitHeight(0),
z(0), scale(1), rotation(0), opacity(1),
attachedLayoutDirection(0), acceptedMouseButtons(0),
imHints(Qt::ImhNone),
-
+
keyHandler(0),
dirtyAttributes(0), nextDirtyItem(0), prevDirtyItem(0),
@@ -1285,6 +2211,15 @@ QSGItemPrivate::QSGItemPrivate()
void QSGItemPrivate::init(QSGItem *parent)
{
+#ifndef QT_NO_DEBUG
+ ++qt_item_count;
+ static bool atexit_registered = false;
+ if (!atexit_registered) {
+ atexit(qt_print_item_count);
+ atexit_registered = true;
+ }
+#endif
+
Q_Q(QSGItem);
baselineOffset.invalidate();
@@ -1305,7 +2240,7 @@ void QSGItemPrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObjec
// This test is measurably (albeit only slightly) faster than qobject_cast<>()
const QMetaObject *mo = o->metaObject();
while (mo && mo != &QSGItem::staticMetaObject) {
- if (mo == &QGraphicsObject::staticMetaObject)
+ if (mo == &QGraphicsObject::staticMetaObject)
qWarning("Cannot add a QtQuick 1.0 item (%s) into a QtQuick 2.0 scene!", o->metaObject()->className());
mo = mo->d.superdata;
}
@@ -1319,6 +2254,40 @@ void QSGItemPrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObjec
}
}
+/*!
+ \qmlproperty list<Object> QtQuick2::Item::data
+ \default
+
+ The data property allows you to freely mix visual children and resources
+ in an item. If you assign a visual item to the data list it becomes
+ a child and if you assign any other object type, it is added as a resource.
+
+ So you can write:
+ \qml
+ Item {
+ Text {}
+ Rectangle {}
+ Timer {}
+ }
+ \endqml
+
+ instead of:
+ \qml
+ Item {
+ children: [
+ Text {},
+ Rectangle {}
+ ]
+ resources: [
+ Timer {}
+ ]
+ }
+ \endqml
+
+ data is a behind-the-scenes property: you should never need to explicitly
+ specify it.
+ */
+
int QSGItemPrivate::data_count(QDeclarativeListProperty<QObject> *prop)
{
Q_UNUSED(prop);
@@ -1483,6 +2452,227 @@ void QSGItemPrivate::transform_clear(QDeclarativeListProperty<QSGTransform> *pro
p->dirty(QSGItemPrivate::Transform);
}
+/*!
+ \property QSGItem::childrenRect
+ \brief The geometry of an item's children.
+
+ This property holds the (collective) position and size of the item's children.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Item::x
+ \qmlproperty real QtQuick2::Item::y
+ \qmlproperty real QtQuick2::Item::width
+ \qmlproperty real QtQuick2::Item::height
+
+ Defines the item's position and size relative to its parent.
+
+ \qml
+ Item { x: 100; y: 100; width: 100; height: 100 }
+ \endqml
+ */
+
+/*!
+ \qmlproperty real QtQuick2::Item::z
+
+ Sets the stacking order of sibling items. By default the stacking order is 0.
+
+ Items with a higher stacking value are drawn on top of siblings with a
+ lower stacking order. Items with the same stacking value are drawn
+ bottom up in the order they appear. Items with a negative stacking
+ value are drawn under their parent's content.
+
+ The following example shows the various effects of stacking order.
+
+ \table
+ \row
+ \o \image declarative-item_stacking1.png
+ \o Same \c z - later children above earlier children:
+ \qml
+ Item {
+ Rectangle {
+ color: "red"
+ width: 100; height: 100
+ }
+ Rectangle {
+ color: "blue"
+ x: 50; y: 50; width: 100; height: 100
+ }
+ }
+ \endqml
+ \row
+ \o \image declarative-item_stacking2.png
+ \o Higher \c z on top:
+ \qml
+ Item {
+ Rectangle {
+ z: 1
+ color: "red"
+ width: 100; height: 100
+ }
+ Rectangle {
+ color: "blue"
+ x: 50; y: 50; width: 100; height: 100
+ }
+ }
+ \endqml
+ \row
+ \o \image declarative-item_stacking3.png
+ \o Same \c z - children above parents:
+ \qml
+ Item {
+ Rectangle {
+ color: "red"
+ width: 100; height: 100
+ Rectangle {
+ color: "blue"
+ x: 50; y: 50; width: 100; height: 100
+ }
+ }
+ }
+ \endqml
+ \row
+ \o \image declarative-item_stacking4.png
+ \o Lower \c z below:
+ \qml
+ Item {
+ Rectangle {
+ color: "red"
+ width: 100; height: 100
+ Rectangle {
+ z: -1
+ color: "blue"
+ x: 50; y: 50; width: 100; height: 100
+ }
+ }
+ }
+ \endqml
+ \endtable
+ */
+
+/*!
+ \qmlproperty bool QtQuick2::Item::visible
+
+ This property holds whether the item is visible. By default this is true.
+
+ Setting this property directly affects the \c visible value of child
+ items. When set to \c false, the \c visible values of all child items also
+ become \c false. When set to \c true, the \c visible values of child items
+ are returned to \c true, unless they have explicitly been set to \c false.
+
+ (Because of this flow-on behavior, using the \c visible property may not
+ have the intended effect if a property binding should only respond to
+ explicit property changes. In such cases it may be better to use the
+ \l opacity property instead.)
+
+ Setting this property to \c false automatically causes \l focus to be set
+ to \c false, and this item will longer receive mouse and keyboard events.
+ (In contrast, setting the \l opacity to 0 does not affect the \l focus
+ property and the receiving of key events.)
+
+ \note This property's value is only affected by changes to this property or
+ the parent's \c visible property. It does not change, for example, if this
+ item moves off-screen, or if the \l opacity changes to 0.
+*/
+
+
+/*!
+ \qmlproperty AnchorLine QtQuick2::Item::anchors.top
+ \qmlproperty AnchorLine QtQuick2::Item::anchors.bottom
+ \qmlproperty AnchorLine QtQuick2::Item::anchors.left
+ \qmlproperty AnchorLine QtQuick2::Item::anchors.right
+ \qmlproperty AnchorLine QtQuick2::Item::anchors.horizontalCenter
+ \qmlproperty AnchorLine QtQuick2::Item::anchors.verticalCenter
+ \qmlproperty AnchorLine QtQuick2::Item::anchors.baseline
+
+ \qmlproperty Item QtQuick2::Item::anchors.fill
+ \qmlproperty Item QtQuick2::Item::anchors.centerIn
+
+ \qmlproperty real QtQuick2::Item::anchors.margins
+ \qmlproperty real QtQuick2::Item::anchors.topMargin
+ \qmlproperty real QtQuick2::Item::anchors.bottomMargin
+ \qmlproperty real QtQuick2::Item::anchors.leftMargin
+ \qmlproperty real QtQuick2::Item::anchors.rightMargin
+ \qmlproperty real QtQuick2::Item::anchors.horizontalCenterOffset
+ \qmlproperty real QtQuick2::Item::anchors.verticalCenterOffset
+ \qmlproperty real QtQuick2::Item::anchors.baselineOffset
+
+ \qmlproperty bool QtQuick2::Item::anchors.mirrored
+
+ Anchors provide a way to position an item by specifying its
+ relationship with other items.
+
+ Margins apply to top, bottom, left, right, and fill anchors.
+ The \c anchors.margins property can be used to set all of the various margins at once, to the same value.
+ Note that margins are anchor-specific and are not applied if an item does not
+ use anchors.
+
+ Offsets apply for horizontal center, vertical center, and baseline anchors.
+
+ \table
+ \row
+ \o \image declarative-anchors_example.png
+ \o Text anchored to Image, horizontally centered and vertically below, with a margin.
+ \qml
+ Item {
+ Image {
+ id: pic
+ // ...
+ }
+ Text {
+ id: label
+ anchors.horizontalCenter: pic.horizontalCenter
+ anchors.top: pic.bottom
+ anchors.topMargin: 5
+ // ...
+ }
+ }
+ \endqml
+ \row
+ \o \image declarative-anchors_example2.png
+ \o
+ Left of Text anchored to right of Image, with a margin. The y
+ property of both defaults to 0.
+
+ \qml
+ Item {
+ Image {
+ id: pic
+ // ...
+ }
+ Text {
+ id: label
+ anchors.left: pic.right
+ anchors.leftMargin: 5
+ // ...
+ }
+ }
+ \endqml
+ \endtable
+
+ \c anchors.fill provides a convenient way for one item to have the
+ same geometry as another item, and is equivalent to connecting all
+ four directional anchors.
+
+ To clear an anchor value, set it to \c undefined.
+
+ \c anchors.mirrored returns true it the layout has been \l {LayoutMirroring}{mirrored}.
+
+ \note You can only anchor an item to siblings or a parent.
+
+ For more information see \l {anchor-layout}{Anchor Layouts}.
+*/
+
+/*!
+ \property QSGItem::baselineOffset
+ \brief The position of the item's baseline in local coordinates.
+
+ The baseline of a \l Text item is the imaginary line on which the text
+ sits. Controls containing text usually set their baseline to the
+ baseline of their text.
+
+ For non-text items, a default baseline offset of 0 is used.
+*/
QSGAnchors *QSGItemPrivate::anchors() const
{
if (!_anchors) {
@@ -1494,7 +2684,7 @@ QSGAnchors *QSGItemPrivate::anchors() const
return _anchors;
}
-QSGItemPrivate::AnchorLines *QSGItemPrivate::anchorLines() const
+QSGItemPrivate::AnchorLines *QSGItemPrivate::anchorLines() const
{
Q_Q(const QSGItem);
if (!_anchorLines) _anchorLines =
@@ -1553,6 +2743,12 @@ void QSGItem::setClip(bool c)
emit clipChanged(c);
}
+
+/*!
+ This function is called to handle this item's changes in
+ geometry from \a oldGeometry to \a newGeometry. If the two
+ geometries are the same, it doesn't do anything.
+ */
void QSGItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QSGItem);
@@ -1708,6 +2904,13 @@ bool QSGItem::childMouseEventFilter(QSGItem *, QEvent *)
return false;
}
+void QSGItem::windowDeactivateEvent()
+{
+ foreach (QSGItem* item, childItems()) {
+ item->windowDeactivateEvent();
+ }
+}
+
Qt::InputMethodHints QSGItem::inputMethodHints() const
{
Q_D(const QSGItem);
@@ -1866,7 +3069,7 @@ QTransform QSGItem::itemTransform(QSGItem *other, bool *ok) const
{
Q_D(const QSGItem);
- // XXX todo - we need to be able to handle common parents better and detect
+ // XXX todo - we need to be able to handle common parents better and detect
// invalid cases
if (ok) *ok = true;
@@ -1912,7 +3115,7 @@ void QSGItem::forceActiveFocus()
while (parent) {
if (parent->flags() & QSGItem::ItemIsFocusScope) {
parent->setFocus(true);
- }
+ }
parent = parent->parentItem();
}
}
@@ -1942,11 +3145,11 @@ QDeclarativeListProperty<QObject> QSGItemPrivate::resources()
QDeclarativeListProperty<QSGItem> QSGItemPrivate::children()
{
- return QDeclarativeListProperty<QSGItem>(q_func(), 0, QSGItemPrivate::children_append,
+ return QDeclarativeListProperty<QSGItem>(q_func(), 0, QSGItemPrivate::children_append,
QSGItemPrivate::children_count,
QSGItemPrivate::children_at,
QSGItemPrivate::children_clear);
-
+
}
QDeclarativeListProperty<QDeclarativeState> QSGItemPrivate::states()
@@ -2069,7 +3272,7 @@ void QSGItemPrivate::transformChanged()
void QSGItemPrivate::deliverKeyEvent(QKeyEvent *e)
{
Q_Q(QSGItem);
-
+
Q_ASSERT(e->isAccepted());
if (keyHandler) {
if (e->type() == QEvent::KeyPress)
@@ -2237,7 +3440,7 @@ QSGItem::TransformOrigin QSGItem::transformOrigin() const
void QSGItem::setTransformOrigin(TransformOrigin origin)
{
Q_D(QSGItem);
- if (origin == d->origin)
+ if (origin == d->origin)
return;
d->origin = origin;
@@ -2267,19 +3470,146 @@ void QSGItem::setZ(qreal v)
d->z = v;
d->dirty(QSGItemPrivate::ZValue);
- if (d->parentItem)
+ if (d->parentItem)
QSGItemPrivate::get(d->parentItem)->dirty(QSGItemPrivate::ChildrenStackingChanged);
emit zChanged();
}
-qreal QSGItem::rotation() const
-{
+
+/*!
+ \qmlproperty real QtQuick2::Item::rotation
+ This property holds the rotation of the item in degrees clockwise.
+
+ This specifies how many degrees to rotate the item around its transformOrigin.
+ The default rotation is 0 degrees (i.e. not rotated at all).
+
+ \table
+ \row
+ \o \image declarative-rotation.png
+ \o
+ \qml
+ Rectangle {
+ color: "blue"
+ width: 100; height: 100
+ Rectangle {
+ color: "red"
+ x: 25; y: 25; width: 50; height: 50
+ rotation: 30
+ }
+ }
+ \endqml
+ \endtable
+
+ \sa transform, Rotation
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Item::scale
+ This property holds the scale of the item.
+
+ A scale of less than 1 means the item will be displayed smaller than
+ normal, and a scale of greater than 1 means the item will be
+ displayed larger than normal. A negative scale means the item will
+ be mirrored.
+
+ By default, items are displayed at a scale of 1 (i.e. at their
+ normal size).
+
+ Scaling is from the item's transformOrigin.
+
+ \table
+ \row
+ \o \image declarative-scale.png
+ \o
+ \qml
+ Rectangle {
+ color: "blue"
+ width: 100; height: 100
+ Rectangle {
+ color: "green"
+ width: 25; height: 25
+ }
+ Rectangle {
+ color: "red"
+ x: 25; y: 25; width: 50; height: 50
+ scale: 1.4
+ }
+ }
+ \endqml
+ \endtable
+
+ \sa transform, Scale
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Item::opacity
+
+ This property holds the opacity of the item. Opacity is specified as a
+ number between 0 (fully transparent) and 1 (fully opaque). The default is 1.
+
+ When this property is set, the specified opacity is also applied
+ individually to child items. In almost all cases this is what you want,
+ but in some cases it may produce undesired results. For example in the
+ second set of rectangles below, the red rectangle has specified an opacity
+ of 0.5, which affects the opacity of its blue child rectangle even though
+ the child has not specified an opacity.
+
+ \table
+ \row
+ \o \image declarative-item_opacity1.png
+ \o
+ \qml
+ Item {
+ Rectangle {
+ color: "red"
+ width: 100; height: 100
+ Rectangle {
+ color: "blue"
+ x: 50; y: 50; width: 100; height: 100
+ }
+ }
+ }
+ \endqml
+ \row
+ \o \image declarative-item_opacity2.png
+ \o
+ \qml
+ Item {
+ Rectangle {
+ opacity: 0.5
+ color: "red"
+ width: 100; height: 100
+ Rectangle {
+ color: "blue"
+ x: 50; y: 50; width: 100; height: 100
+ }
+ }
+ }
+ \endqml
+ \endtable
+
+ If an item's opacity is set to 0, the item will no longer receive mouse
+ events, but will continue to receive key events and will retain the keyboard
+ \l focus if it has been set. (In contrast, setting the \l visible property
+ to \c false stops both mouse and keyboard events, and also removes focus
+ from the item.)
+*/
+
+/*!
+ Returns a value indicating whether mouse input should
+ remain with this item exclusively.
+
+ \sa setKeepMouseGrab()
+ */
+
+qreal QSGItem::rotation() const
+{
Q_D(const QSGItem);
return d->rotation;
}
-void QSGItem::setRotation(qreal r)
+void QSGItem::setRotation(qreal r)
{
Q_D(QSGItem);
if (d->rotation == r)
@@ -2294,13 +3624,13 @@ void QSGItem::setRotation(qreal r)
emit rotationChanged();
}
-qreal QSGItem::scale() const
-{
+qreal QSGItem::scale() const
+{
Q_D(const QSGItem);
return d->scale;
}
-void QSGItem::setScale(qreal s)
+void QSGItem::setScale(qreal s)
{
Q_D(QSGItem);
if (d->scale == s)
@@ -2326,7 +3656,7 @@ void QSGItem::setOpacity(qreal o)
return;
d->opacity = o;
-
+
d->dirty(QSGItemPrivate::OpacityValue);
d->itemChange(ItemOpacityHasChanged, o);
@@ -2400,7 +3730,7 @@ void QSGItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
q->ungrabMouse();
}
- for (int ii = 0; ii < childItems.count(); ++ii)
+ for (int ii = 0; ii < childItems.count(); ++ii)
QSGItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible);
for(int ii = 0; ii < changeListeners.count(); ++ii) {
@@ -2444,7 +3774,7 @@ void QSGItemPrivate::setEffectiveEnableRecur(bool newEffectiveEnable)
q->ungrabMouse();
}
- for (int ii = 0; ii < childItems.count(); ++ii)
+ for (int ii = 0; ii < childItems.count(); ++ii)
QSGItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur(newEffectiveEnable);
emit q->enabledChanged();
@@ -2625,12 +3955,37 @@ void QSGItemPrivate::itemChange(QSGItem::ItemChange change, const QSGItem::ItemC
}
}
+/*!
+ \property QSGItem::smooth
+ \brief whether the item is smoothly transformed.
+
+ This property is provided purely for the purpose of optimization. Turning
+ smooth transforms off is faster, but looks worse; turning smooth
+ transformations on is slower, but looks better.
+
+ By default smooth transformations are off.
+*/
+
+/*!
+ Returns true if the item should be drawn with antialiasing and
+ smooth pixmap filtering, false otherwise.
+
+ The default is false.
+
+ \sa setSmooth()
+*/
bool QSGItem::smooth() const
{
Q_D(const QSGItem);
return d->smooth;
}
+/*!
+ Sets whether the item should be drawn with antialiasing and
+ smooth pixmap filtering to \a smooth.
+
+ \sa smooth()
+*/
void QSGItem::setSmooth(bool smooth)
{
Q_D(QSGItem);
@@ -2643,14 +3998,14 @@ void QSGItem::setSmooth(bool smooth)
emit smoothChanged(smooth);
}
-QSGItem::Flags QSGItem::flags() const
-{
+QSGItem::Flags QSGItem::flags() const
+{
Q_D(const QSGItem);
return (QSGItem::Flags)d->flags;
}
void QSGItem::setFlag(Flag flag, bool enabled)
-{
+{
Q_D(QSGItem);
if (enabled)
setFlags((Flags)(d->flags | (quint32)flag));
@@ -2658,8 +4013,8 @@ void QSGItem::setFlag(Flag flag, bool enabled)
setFlags((Flags)(d->flags & ~(quint32)flag));
}
-void QSGItem::setFlags(Flags flags)
-{
+void QSGItem::setFlags(Flags flags)
+{
Q_D(QSGItem);
if ((flags & ItemIsFocusScope) != (d->flags & ItemIsFocusScope)) {
@@ -2669,10 +4024,10 @@ void QSGItem::setFlags(Flags flags)
} else if (d->flags & ItemIsFocusScope) {
qWarning("QSGItem: Cannot unset FocusScope flag.");
flags |= ItemIsFocusScope;
- }
+ }
}
- if ((flags & ItemClipsChildrenToShape ) != (d->flags & ItemClipsChildrenToShape))
+ if ((flags & ItemClipsChildrenToShape ) != (d->flags & ItemClipsChildrenToShape))
d->dirty(QSGItemPrivate::Clip);
d->flags = flags;
@@ -2786,13 +4141,54 @@ qreal QSGItemPrivate::getImplicitWidth() const
{
return implicitWidth;
}
-
+/*!
+ Returns the width of the item that is implied by other properties that determine the content.
+*/
qreal QSGItem::implicitWidth() const
{
Q_D(const QSGItem);
return d->getImplicitWidth();
}
+/*!
+ \qmlproperty real QtQuick2::Item::implicitWidth
+ \qmlproperty real QtQuick2::Item::implicitHeight
+
+ Defines the natural width or height of the Item if no \l width or \l height is specified.
+
+ The default implicit size for most items is 0x0, however some elements have an inherent
+ implicit size which cannot be overridden, e.g. Image, Text.
+
+ Setting the implicit size is useful for defining components that have a preferred size
+ based on their content, for example:
+
+ \qml
+ // Label.qml
+ import QtQuick 1.1
+
+ Item {
+ property alias icon: image.source
+ property alias label: text.text
+ implicitWidth: text.implicitWidth + image.implicitWidth
+ implicitHeight: Math.max(text.implicitHeight, image.implicitHeight)
+ Image { id: image }
+ Text {
+ id: text
+ wrapMode: Text.Wrap
+ anchors.left: image.right; anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+ \endqml
+
+ \bold Note: using implicitWidth of Text or TextEdit and setting the width explicitly
+ incurs a performance penalty as the text must be laid out twice.
+*/
+
+/*!
+ Sets the implied width of the item to \a w.
+ This is the width implied by other properties that determine the content.
+*/
void QSGItem::setImplicitWidth(qreal w)
{
Q_D(QSGItem);
@@ -2802,11 +4198,11 @@ void QSGItem::setImplicitWidth(qreal w)
if (changed)
d->implicitWidthChanged();
return;
- }
+ }
qreal oldWidth = d->width;
d->width = w;
-
+
d->dirty(QSGItemPrivate::Size);
geometryChanged(QRectF(x(), y(), width(), height()),
@@ -2816,6 +4212,9 @@ void QSGItem::setImplicitWidth(qreal w)
d->implicitWidthChanged();
}
+/*!
+ Returns whether the width property has been set explicitly.
+*/
bool QSGItem::widthValid() const
{
Q_D(const QSGItem);
@@ -2865,12 +4264,20 @@ qreal QSGItemPrivate::getImplicitHeight() const
return implicitHeight;
}
+/*!
+ Returns the height of the item that is implied by other properties that determine the content.
+*/
qreal QSGItem::implicitHeight() const
{
Q_D(const QSGItem);
return d->getImplicitHeight();
}
+
+/*!
+ Sets the implied height of the item to \a h.
+ This is the height implied by other properties that determine the content.
+*/
void QSGItem::setImplicitHeight(qreal h)
{
Q_D(QSGItem);
@@ -2894,6 +4301,9 @@ void QSGItem::setImplicitHeight(qreal h)
d->implicitHeightChanged();
}
+/*!
+ Returns whether the height property has been set explicitly.
+*/
bool QSGItem::heightValid() const
{
Q_D(const QSGItem);
@@ -2963,19 +4373,19 @@ QSGItem *QSGItem::scopedFocusItem() const
Q_D(const QSGItem);
if (!isFocusScope())
return 0;
- else
+ else
return d->subFocusItem;
}
-Qt::MouseButtons QSGItem::acceptedMouseButtons() const
-{
+Qt::MouseButtons QSGItem::acceptedMouseButtons() const
+{
Q_D(const QSGItem);
return d->acceptedMouseButtons;
}
-void QSGItem::setAcceptedMouseButtons(Qt::MouseButtons buttons)
-{
+void QSGItem::setAcceptedMouseButtons(Qt::MouseButtons buttons)
+{
Q_D(QSGItem);
d->acceptedMouseButtons = buttons;
}
@@ -2986,13 +4396,13 @@ bool QSGItem::filtersChildMouseEvents() const
return d->filtersChildMouseEvents;
}
-void QSGItem::setFiltersChildMouseEvents(bool filter)
-{
+void QSGItem::setFiltersChildMouseEvents(bool filter)
+{
Q_D(QSGItem);
d->filtersChildMouseEvents = filter;
}
-bool QSGItem::isUnderMouse() const
+bool QSGItem::isUnderMouse() const
{
Q_D(const QSGItem);
if (!d->canvas)
@@ -3001,17 +4411,17 @@ bool QSGItem::isUnderMouse() const
QPoint cursorPos = QCursor::pos();
if (QRectF(0, 0, width(), height()).contains(mapFromScene(cursorPos))) // ### refactor: d->canvas->mapFromGlobal(cursorPos))))
return true;
- return false;
+ return false;
}
-bool QSGItem::acceptHoverEvents() const
-{
+bool QSGItem::acceptHoverEvents() const
+{
Q_D(const QSGItem);
return d->hoverEnabled;
}
-void QSGItem::setAcceptHoverEvents(bool enabled)
-{
+void QSGItem::setAcceptHoverEvents(bool enabled)
+{
Q_D(QSGItem);
d->hoverEnabled = enabled;
}
@@ -3031,7 +4441,7 @@ void QSGItem::grabMouse()
oldGrabber->mouseUngrabEvent();
}
-void QSGItem::ungrabMouse()
+void QSGItem::ungrabMouse()
{
Q_D(QSGItem);
if (!d->canvas)
@@ -3052,22 +4462,60 @@ bool QSGItem::keepMouseGrab() const
return d->keepMouse;
}
+/*!
+ The flag indicating whether the mouse should remain
+ with this item is set to \a keep.
+
+ This is useful for items that wish to grab and keep mouse
+ interaction following a predefined gesture. For example,
+ an item that is interested in horizontal mouse movement
+ may set keepMouseGrab to true once a threshold has been
+ exceeded. Once keepMouseGrab has been set to true, filtering
+ items will not react to mouse events.
+
+ If the item does not indicate that it wishes to retain mouse grab,
+ a filtering item may steal the grab. For example, Flickable may attempt
+ to steal a mouse grab if it detects that the user has begun to
+ move the viewport.
+
+ \sa keepMouseGrab()
+ */
void QSGItem::setKeepMouseGrab(bool keep)
{
Q_D(QSGItem);
d->keepMouse = keep;
}
-QPointF QSGItem::mapToItem(const QSGItem *item, const QPointF &point) const
-{
+/*!
+ \qmlmethod object QtQuick2::Item::mapFromItem(Item item, real x, real y)
+
+ Maps the point (\a x, \a y), which is in \a item's coordinate system, to
+ this item's coordinate system, and returns an object with \c x and \c y
+ properties matching the mapped cooordinate.
+
+ If \a item is a \c null value, this maps the point from the coordinate
+ system of the root QML view.
+*/
+/*!
+ \qmlmethod object QtQuick2::Item::mapToItem(Item item, real x, real y)
+
+ Maps the point (\a x, \a y), which is in this item's coordinate system, to
+ \a item's coordinate system, and returns an object with \c x and \c y
+ properties matching the mapped cooordinate.
+
+ If \a item is a \c null value, this maps \a x and \a y to the coordinate
+ system of the root QML view.
+*/
+QPointF QSGItem::mapToItem(const QSGItem *item, const QPointF &point) const
+{
QPointF p = mapToScene(point);
if (item)
p = item->mapFromScene(p);
return p;
}
-QPointF QSGItem::mapToScene(const QPointF &point) const
-{
+QPointF QSGItem::mapToScene(const QPointF &point) const
+{
Q_D(const QSGItem);
return d->itemToCanvasTransform().map(point);
}
@@ -3081,26 +4529,26 @@ QRectF QSGItem::mapRectToItem(const QSGItem *item, const QRectF &rect) const
return t.mapRect(rect);
}
-QRectF QSGItem::mapRectToScene(const QRectF &rect) const
-{
+QRectF QSGItem::mapRectToScene(const QRectF &rect) const
+{
Q_D(const QSGItem);
return d->itemToCanvasTransform().mapRect(rect);
}
-QPointF QSGItem::mapFromItem(const QSGItem *item, const QPointF &point) const
-{
+QPointF QSGItem::mapFromItem(const QSGItem *item, const QPointF &point) const
+{
QPointF p = item?item->mapToScene(point):point;
return mapFromScene(p);
}
-QPointF QSGItem::mapFromScene(const QPointF &point) const
-{
+QPointF QSGItem::mapFromScene(const QPointF &point) const
+{
Q_D(const QSGItem);
return d->canvasToItemTransform().map(point);
}
-QRectF QSGItem::mapRectFromItem(const QSGItem *item, const QRectF &rect) const
-{
+QRectF QSGItem::mapRectFromItem(const QSGItem *item, const QRectF &rect) const
+{
Q_D(const QSGItem);
QTransform t = item?QSGItemPrivate::get(item)->itemToCanvasTransform():QTransform();
t *= d->canvasToItemTransform();
@@ -3113,6 +4561,318 @@ QRectF QSGItem::mapRectFromScene(const QRectF &rect) const
return d->canvasToItemTransform().mapRect(rect);
}
+
+/*!
+ \qmlmethod QtQuick2::Item::forceActiveFocus()
+
+ Forces active focus on the item.
+
+ This method sets focus on the item and makes sure that all the focus scopes
+ higher in the object hierarchy are also given the focus.
+*/
+
+/*!
+ Forces active focus on the item.
+
+ This method sets focus on the item and makes sure that all the focus scopes
+ higher in the object hierarchy are also given the focus.
+*/
+
+/*!
+ \qmlmethod QtQuick2::Item::childAt(real x, real y)
+
+ Returns the visible child item at point (\a x, \a y), which is in this
+ item's coordinate system, or \c null if there is no such item.
+*/
+
+/*!
+ Returns the visible child item at point (\a x, \a y), which is in this
+ item's coordinate system, or 0 if there is no such item.
+*/
+
+/*!
+ \qmlproperty list<State> QtQuick2::Item::states
+ This property holds a list of states defined by the item.
+
+ \qml
+ Item {
+ states: [
+ State {
+ // ...
+ },
+ State {
+ // ...
+ }
+ // ...
+ ]
+ }
+ \endqml
+
+ \sa {qmlstate}{States}
+*/
+/*!
+ \qmlproperty list<Transition> QtQuick2::Item::transitions
+ This property holds a list of transitions defined by the item.
+
+ \qml
+ Item {
+ transitions: [
+ Transition {
+ // ...
+ },
+ Transition {
+ // ...
+ }
+ // ...
+ ]
+ }
+ \endqml
+
+ \sa {QML Animation and Transitions}{Transitions}
+*/
+/*
+ \qmlproperty list<Filter> QtQuick2::Item::filter
+ This property holds a list of graphical filters to be applied to the item.
+
+ \l {Filter}{Filters} include things like \l {Blur}{blurring}
+ the item, or giving it a \l Reflection. Some
+ filters may not be available on all canvases; if a filter is not
+ available on a certain canvas, it will simply not be applied for
+ that canvas (but the QML will still be considered valid).
+
+ \qml
+ Item {
+ filter: [
+ Blur {
+ // ...
+ },
+ Reflection {
+ // ...
+ }
+ // ...
+ ]
+ }
+ \endqml
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::Item::clip
+ This property holds whether clipping is enabled. The default clip value is \c false.
+
+ If clipping is enabled, an item will clip its own painting, as well
+ as the painting of its children, to its bounding rectangle.
+
+ Non-rectangular clipping regions are not supported for performance reasons.
+*/
+
+/*!
+ \property QSGItem::clip
+ This property holds whether clipping is enabled. The default clip value is \c false.
+
+ If clipping is enabled, an item will clip its own painting, as well
+ as the painting of its children, to its bounding rectangle. If you set
+ clipping during an item's paint operation, remember to re-set it to
+ prevent clipping the rest of your scene.
+
+ Non-rectangular clipping regions are not supported for performance reasons.
+*/
+
+/*!
+ \qmlproperty string QtQuick2::Item::state
+
+ This property holds the name of the current state of the item.
+
+ This property is often used in scripts to change between states. For
+ example:
+
+ \js
+ function toggle() {
+ if (button.state == 'On')
+ button.state = 'Off';
+ else
+ button.state = 'On';
+ }
+ \endjs
+
+ If the item is in its base state (i.e. no explicit state has been
+ set), \c state will be a blank string. Likewise, you can return an
+ item to its base state by setting its current state to \c ''.
+
+ \sa {qmlstates}{States}
+*/
+
+/*!
+ \qmlproperty list<Transform> QtQuick2::Item::transform
+ This property holds the list of transformations to apply.
+
+ For more information see \l Transform.
+*/
+
+/*!
+ \enum QSGItem::TransformOrigin
+
+ Controls the point about which simple transforms like scale apply.
+
+ \value TopLeft The top-left corner of the item.
+ \value Top The center point of the top of the item.
+ \value TopRight The top-right corner of the item.
+ \value Left The left most point of the vertical middle.
+ \value Center The center of the item.
+ \value Right The right most point of the vertical middle.
+ \value BottomLeft The bottom-left corner of the item.
+ \value Bottom The center point of the bottom of the item.
+ \value BottomRight The bottom-right corner of the item.
+*/
+
+
+/*!
+ \qmlproperty bool QtQuick2::Item::activeFocus
+
+ This property indicates whether the item has active focus.
+
+ An item with active focus will receive keyboard input,
+ or is a FocusScope ancestor of the item that will receive keyboard input.
+
+ Usually, activeFocus is gained by setting focus on an item and its enclosing
+ FocusScopes. In the following example \c input will have activeFocus.
+ \qml
+ Rectangle {
+ FocusScope {
+ focus: true
+ TextInput {
+ id: input
+ focus: true
+ }
+ }
+ }
+ \endqml
+
+ \sa focus, {qmlfocus}{Keyboard Focus}
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::Item::focus
+ This property indicates whether the item has focus within the enclosing focus scope. If true, this item
+ will gain active focus when the enclosing focus scope gains active focus.
+ In the following example, \c input will be given active focus when \c scope gains active focus.
+ \qml
+ Rectangle {
+ FocusScope {
+ id: scope
+ TextInput {
+ id: input
+ focus: true
+ }
+ }
+ }
+ \endqml
+
+ For the purposes of this property, the scene as a whole is assumed to act like a focus scope.
+ On a practical level, that means the following QML will give active focus to \c input on startup.
+
+ \qml
+ Rectangle {
+ TextInput {
+ id: input
+ focus: true
+ }
+ }
+ \endqml
+
+ \sa activeFocus, {qmlfocus}{Keyboard Focus}
+*/
+
+
+/*!
+ \property QSGItem::anchors
+ \internal
+*/
+
+/*!
+ \property QSGItem::left
+ \internal
+*/
+
+/*!
+ \property QSGItem::right
+ \internal
+*/
+
+/*!
+ \property QSGItem::horizontalCenter
+ \internal
+*/
+
+/*!
+ \property QSGItem::top
+ \internal
+*/
+
+/*!
+ \property QSGItem::bottom
+ \internal
+*/
+
+/*!
+ \property QSGItem::verticalCenter
+ \internal
+*/
+
+/*!
+ \property QSGItem::focus
+ \internal
+*/
+
+/*!
+ \property QSGItem::transform
+ \internal
+*/
+
+/*!
+ \property QSGItem::transformOrigin
+ \internal
+*/
+
+/*!
+ \property QSGItem::activeFocus
+ \internal
+*/
+
+/*!
+ \property QSGItem::baseline
+ \internal
+*/
+
+/*!
+ \property QSGItem::data
+ \internal
+*/
+
+/*!
+ \property QSGItem::resources
+ \internal
+*/
+
+/*!
+ \property QSGItem::state
+ \internal
+*/
+
+/*!
+ \property QSGItem::states
+ \internal
+*/
+
+/*!
+ \property QSGItem::transformOriginPoint
+ \internal
+*/
+
+/*!
+ \property QSGItem::transitions
+ \internal
+*/
+
bool QSGItem::event(QEvent *ev)
{
return QObject::event(ev);
diff --git a/src/declarative/items/qsgitem.h b/src/declarative/items/qsgitem.h
index f8e52cf848..02a7a33ed8 100644
--- a/src/declarative/items/qsgitem.h
+++ b/src/declarative/items/qsgitem.h
@@ -375,6 +375,7 @@ protected:
virtual void dragExitEvent(QSGDragEvent *event);
virtual void dragDropEvent(QSGDragEvent *event);
virtual bool childMouseEventFilter(QSGItem *, QEvent *);
+ virtual void windowDeactivateEvent();
virtual void geometryChanged(const QRectF &newGeometry,
const QRectF &oldGeometry);
diff --git a/src/declarative/items/qsgitemsmodule.cpp b/src/declarative/items/qsgitemsmodule.cpp
index 62a4bd06cb..9359906f25 100644
--- a/src/declarative/items/qsgitemsmodule.cpp
+++ b/src/declarative/items/qsgitemsmodule.cpp
@@ -126,6 +126,7 @@ static void qt_sgitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QDeclarativePathPercent>(uri,major,minor,"PathPercent");
qmlRegisterType<QDeclarativePathQuad>(uri,major,minor,"PathQuad");
qmlRegisterType<QSGPathView>(uri,major,minor,"PathView");
+ qmlRegisterUncreatableType<QSGBasePositioner>(uri,major,minor,"Positioner","Positioner is an abstract type that is only available as an attached property.");
#ifndef QT_NO_VALIDATOR
qmlRegisterType<QIntValidator>(uri,major,minor,"IntValidator");
qmlRegisterType<QDoubleValidator>(uri,major,minor,"DoubleValidator");
diff --git a/src/declarative/items/qsgitemview.cpp b/src/declarative/items/qsgitemview.cpp
index bcb4a8a97c..5543f12f6d 100644
--- a/src/declarative/items/qsgitemview.cpp
+++ b/src/declarative/items/qsgitemview.cpp
@@ -7,28 +7,29 @@
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying ** this package.
-**
** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
+** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
**
**
**
@@ -723,7 +724,6 @@ void QSGItemView::trackedPositionChanged()
qreal trackedPos = d->trackedItem->position();
qreal trackedSize = d->trackedItem->size();
if (d->trackedItem != d->currentItem) {
- trackedPos -= d->currentItem->sectionSize();
trackedSize += d->currentItem->sectionSize();
}
qreal viewPos;
@@ -756,16 +756,32 @@ void QSGItemView::trackedPositionChanged()
pos = d->startPosition();
}
} else {
- if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
- pos = qMax(trackedPos, d->currentItem->position());
- } else if (d->trackedItem->endPosition() >= viewPos + d->size()
- && d->currentItem->endPosition() >= viewPos + d->size()) {
- if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
- pos = d->trackedItem->endPosition() - d->size();
+ qreal trackedEndPos = d->trackedItem->endPosition();
+ qreal toItemPos = d->currentItem->position();
+ qreal toItemEndPos = d->currentItem->endPosition();
+
+ if (d->header && d->showHeaderForIndex(d->currentIndex)) {
+ trackedPos -= d->headerSize();
+ trackedEndPos -= d->headerSize();
+ toItemPos -= d->headerSize();
+ toItemEndPos -= d->headerSize();
+ } else if (d->footer && d->showFooterForIndex(d->currentIndex)) {
+ trackedPos += d->footerSize();
+ trackedEndPos += d->footerSize();
+ toItemPos += d->footerSize();
+ toItemEndPos += d->footerSize();
+ }
+
+ if (trackedPos < viewPos && toItemPos < viewPos) {
+ pos = qMax(trackedPos, toItemPos);
+ } else if (trackedEndPos >= viewPos + d->size()
+ && toItemEndPos >= viewPos + d->size()) {
+ if (trackedEndPos <= toItemEndPos) {
+ pos = trackedEndPos - d->size();
if (trackedSize > d->size())
pos = trackedPos;
} else {
- pos = d->currentItem->endPosition() - d->size();
+ pos = toItemEndPos - d->size();
if (d->currentItem->size() > d->size())
pos = d->currentItem->position();
}
@@ -1082,7 +1098,7 @@ int QSGItemViewPrivate::mapFromModel(int modelIndex) const
for (int i = 0; i < visibleItems.count(); ++i) {
FxViewItem *item = visibleItems.at(i);
if (item->index == modelIndex)
- return i + visibleIndex;
+ return i;
if (item->index > modelIndex)
return -1;
}
diff --git a/src/declarative/items/qsgitemview_p.h b/src/declarative/items/qsgitemview_p.h
index 4c5b91fcde..b784015f66 100644
--- a/src/declarative/items/qsgitemview_p.h
+++ b/src/declarative/items/qsgitemview_p.h
@@ -7,29 +7,29 @@
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
+** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
**
**
**
diff --git a/src/declarative/items/qsgitemview_p_p.h b/src/declarative/items/qsgitemview_p_p.h
index 3113a8b754..f20f4cca3e 100644
--- a/src/declarative/items/qsgitemview_p_p.h
+++ b/src/declarative/items/qsgitemview_p_p.h
@@ -7,29 +7,29 @@
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
+** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
**
**
**
@@ -95,10 +95,10 @@ public:
void adjustMoveParameters(int *from, int *to, int *count) const;
virtual void init();
- virtual void updateCurrent(int modelIndex);
virtual void clear();
- virtual void regenerate();
virtual void updateViewport();
+
+ void regenerate();
void layout();
void refill();
void refill(qreal from, qreal to, bool doBuffer = false);
@@ -111,6 +111,7 @@ public:
QSGItem *createHighlightItem();
QSGItem *createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault = false);
+ void updateCurrent(int modelIndex);
void updateTrackedItem();
void updateUnrequestedIndexes();
void updateUnrequestedPositions();
@@ -176,6 +177,8 @@ protected:
virtual qreal headerSize() const = 0;
virtual qreal footerSize() const = 0;
+ virtual bool showHeaderForIndex(int index) const = 0;
+ virtual bool showFooterForIndex(int index) const = 0;
virtual void updateHeader() = 0;
virtual void updateFooter() = 0;
@@ -191,14 +194,14 @@ protected:
virtual void visibleItemsChanged() = 0;
virtual FxViewItem *newViewItem(int index, QSGItem *item) = 0;
- virtual void initializeViewItem(FxViewItem *) {}
virtual void repositionPackageItemAt(QSGItem *item, int index) = 0;
virtual void layoutVisibleItems() = 0;
-
- virtual void updateSections() {}
virtual void changedVisibleIndex(int newIndex) = 0;
+
+ virtual void initializeViewItem(FxViewItem *) {}
virtual void initializeCurrentItem() {}
+ virtual void updateSections() {}
virtual void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
};
diff --git a/src/declarative/items/qsglistview.cpp b/src/declarative/items/qsglistview.cpp
index 641dd35104..3be4a4bd58 100644
--- a/src/declarative/items/qsglistview.cpp
+++ b/src/declarative/items/qsglistview.cpp
@@ -223,6 +223,8 @@ public:
virtual qreal headerSize() const;
virtual qreal footerSize() const;
+ virtual bool showHeaderForIndex(int index) const;
+ virtual bool showFooterForIndex(int index) const;
virtual void updateHeader();
virtual void updateFooter();
@@ -434,6 +436,7 @@ qreal QSGListViewPrivate::snapPosAt(qreal pos)
FxViewItem *QSGListViewPrivate::snapItemAt(qreal pos)
{
FxViewItem *snapItem = 0;
+ qreal prevItemSize = 0;
for (int i = 0; i < visibleItems.count(); ++i) {
FxViewItem *item = visibleItems.at(i);
if (item->index == -1)
@@ -441,8 +444,9 @@ FxViewItem *QSGListViewPrivate::snapItemAt(qreal pos)
qreal itemTop = item->position();
if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
return item;
- if (itemTop+item->size()/2 >= pos && itemTop-item->size()/2 < pos)
+ if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
snapItem = item;
+ prevItemSize = item->size();
}
return snapItem;
}
@@ -455,6 +459,7 @@ void QSGListViewPrivate::changedVisibleIndex(int newIndex)
void QSGListViewPrivate::init()
{
+ QSGItemViewPrivate::init();
::memset(sectionCache, 0, sizeof(QSGItem*) * sectionCacheSize);
}
@@ -498,6 +503,8 @@ FxViewItem *QSGListViewPrivate::newViewItem(int modelIndex, QSGItem *item)
void QSGListViewPrivate::initializeViewItem(FxViewItem *item)
{
+ QSGItemViewPrivate::initializeViewItem(item);
+
QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item);
itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
@@ -600,6 +607,8 @@ bool QSGListViewPrivate::removeNonVisibleItems(int bufferFrom, int bufferTo)
while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() <= bufferFrom) {
if (item->attached->delayRemove())
break;
+ if (item->size() == 0)
+ break;
// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
if (item->index != -1)
visibleIndex++;
@@ -726,7 +735,8 @@ void QSGListViewPrivate::updateHighlight()
{
if ((!currentItem && highlight) || (currentItem && !highlight))
createHighlight();
- if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) {
+ bool strictHighlight = haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange;
+ if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
// auto-update highlight
FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
highlightPosAnimator->to = isRightToLeft()
@@ -814,6 +824,8 @@ void QSGListViewPrivate::createSection(FxListItemSG *listItem)
void QSGListViewPrivate::updateSections()
{
+ QSGItemViewPrivate::updateSections();
+
if (sectionCriteria && !visibleItems.isEmpty()) {
QString prevSection;
if (visibleIndex > 0)
@@ -870,6 +882,8 @@ void QSGListViewPrivate::updateCurrentSection()
void QSGListViewPrivate::initializeCurrentItem()
{
+ QSGItemViewPrivate::initializeCurrentItem();
+
if (currentItem) {
FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
@@ -913,6 +927,16 @@ qreal QSGListViewPrivate::footerSize() const
return footer ? footer->size() : 0.0;
}
+bool QSGListViewPrivate::showHeaderForIndex(int index) const
+{
+ return index == 0;
+}
+
+bool QSGListViewPrivate::showFooterForIndex(int index) const
+{
+ return index == model->count()-1;
+}
+
void QSGListViewPrivate::updateFooter()
{
Q_Q(QSGListView);
@@ -1241,6 +1265,68 @@ void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent,
//----------------------------------------------------------------------------
+/*!
+ \qmlclass ListView QSGListView
+ \inqmlmodule QtQuick 2
+ \ingroup qml-view-elements
+ \inherits Flickable
+ \brief The ListView item provides a list view of items provided by a model.
+
+ A ListView displays data from models created from built-in QML elements like ListModel
+ and XmlListModel, or custom model classes defined in C++ that inherit from
+ QAbstractListModel.
+
+ A ListView has a \l model, which defines the data to be displayed, and
+ a \l delegate, which defines how the data should be displayed. Items in a
+ ListView are laid out horizontally or vertically. List views are inherently
+ flickable because ListView inherits from \l Flickable.
+
+ \section1 Example Usage
+
+ The following example shows the definition of a simple list model defined
+ in a file called \c ContactModel.qml:
+
+ \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0
+
+ Another component can display this model data in a ListView, like this:
+
+ \snippet doc/src/snippets/declarative/listview/listview.qml import
+ \codeline
+ \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple
+
+ \image listview-simple.png
+
+ Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
+ for its delegate. The view will create a new \l Text component for each item in the model. Notice
+ the delegate is able to access the model's \c name and \c number data directly.
+
+ An improved list view is shown below. The delegate is visually improved and is moved
+ into a separate \c contactDelegate component.
+
+ \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced
+ \image listview-highlight.png
+
+ The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
+ and \c focus is set to \c true to enable keyboard navigation for the list view.
+ The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
+
+ Delegates are instantiated as needed and may be destroyed at any time.
+ State should \e never be stored in a delegate.
+
+ ListView attaches a number of properties to the root item of the delegate, for example
+ \c {ListView.isCurrentItem}. In the following example, the root delegate item can access
+ this attached property directly as \c ListView.isCurrentItem, while the child
+ \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
+
+ \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
+
+ \note Views do not enable \e clip automatically. If the view
+ is not clipped by another item or the screen, it will be necessary
+ to set \e {clip: true} in order to have the out of view items clipped
+ nicely.
+
+ \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples}
+*/
QSGListView::QSGListView(QSGItem *parent)
: QSGItemView(*(new QSGListViewPrivate), parent)
{
@@ -1250,6 +1336,203 @@ QSGListView::~QSGListView()
{
}
+/*!
+ \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem
+ This attached property is true if this delegate is the current item; otherwise false.
+
+ It is attached to each instance of the delegate.
+
+ This property may be used to adjust the appearance of the current item, for example:
+
+ \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
+*/
+
+/*!
+ \qmlattachedproperty ListView QtQuick2::ListView::view
+ This attached property holds the view that manages this delegate instance.
+
+ It is attached to each instance of the delegate.
+*/
+
+/*!
+ \qmlattachedproperty string QtQuick2::ListView::previousSection
+ This attached property holds the section of the previous element.
+
+ It is attached to each instance of the delegate.
+
+ The section is evaluated using the \l {ListView::section.property}{section} properties.
+*/
+
+/*!
+ \qmlattachedproperty string QtQuick2::ListView::nextSection
+ This attached property holds the section of the next element.
+
+ It is attached to each instance of the delegate.
+
+ The section is evaluated using the \l {ListView::section.property}{section} properties.
+*/
+
+/*!
+ \qmlattachedproperty string QtQuick2::ListView::section
+ This attached property holds the section of this element.
+
+ It is attached to each instance of the delegate.
+
+ The section is evaluated using the \l {ListView::section.property}{section} properties.
+*/
+
+/*!
+ \qmlattachedproperty bool QtQuick2::ListView::delayRemove
+ This attached property holds whether the delegate may be destroyed.
+
+ It is attached to each instance of the delegate.
+
+ It is sometimes necessary to delay the destruction of an item
+ until an animation completes.
+
+ The example delegate below ensures that the animation completes before
+ the item is removed from the list.
+
+ \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove
+*/
+
+/*!
+ \qmlattachedsignal QtQuick2::ListView::onAdd()
+ This attached handler is called immediately after an item is added to the view.
+*/
+
+/*!
+ \qmlattachedsignal QtQuick2::ListView::onRemove()
+ This attached handler is called immediately before an item is removed from the view.
+*/
+
+/*!
+ \qmlproperty model QtQuick2::ListView::model
+ This property holds the model providing data for the list.
+
+ The model provides the set of data that is used to create the items
+ in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
+ or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
+ used, it must be a subclass of \l QAbstractItemModel or a simple list.
+
+ \sa {qmlmodels}{Data Models}
+*/
+
+/*!
+ \qmlproperty Component QtQuick2::ListView::delegate
+
+ The delegate provides a template defining each item instantiated by the view.
+ The index is exposed as an accessible \c index property. Properties of the
+ model are also available depending upon the type of \l {qmlmodels}{Data Model}.
+
+ The number of elements in the delegate has a direct effect on the
+ flicking performance of the view. If at all possible, place functionality
+ that is not needed for the normal display of the delegate in a \l Loader which
+ can load additional elements when needed.
+
+ The ListView will lay out the items based on the size of the root item
+ in the delegate.
+
+ It is recommended that the delagate's size be a whole number to avoid sub-pixel
+ alignment of items.
+
+ \note Delegates are instantiated as needed and may be destroyed at any time.
+ State should \e never be stored in a delegate.
+*/
+/*!
+ \qmlproperty int QtQuick2::ListView::currentIndex
+ \qmlproperty Item QtQuick2::ListView::currentItem
+
+ The \c currentIndex property holds the index of the current item, and
+ \c currentItem holds the current item. Setting the currentIndex to -1
+ will clear the highlight and set currentItem to null.
+
+ If highlightFollowsCurrentItem is \c true, setting either of these
+ properties will smoothly scroll the ListView so that the current
+ item becomes visible.
+
+ Note that the position of the current item
+ may only be approximate until it becomes visible in the view.
+*/
+
+/*!
+ \qmlproperty Item QtQuick2::ListView::highlightItem
+
+ This holds the highlight item created from the \l highlight component.
+
+ The \c highlightItem is managed by the view unless
+ \l highlightFollowsCurrentItem is set to false.
+
+ \sa highlight, highlightFollowsCurrentItem
+*/
+
+/*!
+ \qmlproperty int QtQuick2::ListView::count
+ This property holds the number of items in the view.
+*/
+
+/*!
+ \qmlproperty Component QtQuick2::ListView::highlight
+ This property holds the component to use as the highlight.
+
+ An instance of the highlight component is created for each list.
+ The geometry of the resulting component instance is managed by the list
+ so as to stay with the current item, unless the highlightFollowsCurrentItem
+ property is false.
+
+ \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples}
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem
+ This property holds whether the highlight is managed by the view.
+
+ If this property is true (the default value), the highlight is moved smoothly
+ to follow the current item. Otherwise, the
+ highlight is not moved by the view, and any movement must be implemented
+ by the highlight.
+
+ Here is a highlight with its motion defined by a \l {SpringAnimation} item:
+
+ \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem
+
+ Note that the highlight animation also affects the way that the view
+ is scrolled. This is because the view moves to maintain the
+ highlight within the preferred highlight range (or visible viewport).
+
+ \sa highlight, highlightMoveSpeed
+*/
+//###Possibly rename these properties, since they are very useful even without a highlight?
+/*!
+ \qmlproperty real QtQuick2::ListView::preferredHighlightBegin
+ \qmlproperty real QtQuick2::ListView::preferredHighlightEnd
+ \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode
+
+ These properties define the preferred range of the highlight (for the current item)
+ within the view. The \c preferredHighlightBegin value must be less than the
+ \c preferredHighlightEnd value.
+
+ These properties affect the position of the current item when the list is scrolled.
+ For example, if the currently selected item should stay in the middle of the
+ list when the view is scrolled, set the \c preferredHighlightBegin and
+ \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
+ item would be. If the \c currentItem is changed programmatically, the list will
+ automatically scroll so that the current item is in the middle of the view.
+ Furthermore, the behavior of the current item index will occur whether or not a
+ highlight exists.
+
+ Valid values for \c highlightRangeMode are:
+
+ \list
+ \o ListView.ApplyRange - the view attempts to maintain the highlight within the range.
+ However, the highlight can move outside of the range at the ends of the list or due
+ to mouse interaction.
+ \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
+ The current item changes if a keyboard or mouse action would cause the highlight to move
+ outside of the range.
+ \o ListView.NoHighlightRange - this is the default value.
+ \endlist
+*/
void QSGListView::setHighlightFollowsCurrentItem(bool autoHighlight)
{
Q_D(QSGListView);
@@ -1264,6 +1547,13 @@ void QSGListView::setHighlightFollowsCurrentItem(bool autoHighlight)
}
}
+/*!
+ \qmlproperty real QtQuick2::ListView::spacing
+
+ This property holds the spacing between items.
+
+ The default value is 0.
+*/
qreal QSGListView::spacing() const
{
Q_D(const QSGListView);
@@ -1280,6 +1570,27 @@ void QSGListView::setSpacing(qreal spacing)
}
}
+/*!
+ \qmlproperty enumeration QtQuick2::ListView::orientation
+ This property holds the orientation of the list.
+
+ Possible values:
+
+ \list
+ \o ListView.Horizontal - Items are laid out horizontally
+ \o ListView.Vertical (default) - Items are laid out vertically
+ \endlist
+
+ \table
+ \row
+ \o Horizontal orientation:
+ \image ListViewHorizontal.png
+
+ \row
+ \o Vertical orientation:
+ \image listview-highlight.png
+ \endtable
+*/
QSGListView::Orientation QSGListView::orientation() const
{
Q_D(const QSGListView);
@@ -1305,6 +1616,116 @@ void QSGListView::setOrientation(QSGListView::Orientation orientation)
}
}
+/*!
+ \qmlproperty enumeration QtQuick2::ListView::layoutDirection
+ This property holds the layout direction of the horizontal list.
+
+ Possible values:
+
+ \list
+ \o Qt.LeftToRight (default) - Items will be laid out from left to right.
+ \o Qt.RightToLeft - Items will be laid out from right to let.
+ \endlist
+
+ \sa ListView::effectiveLayoutDirection
+*/
+
+
+/*!
+ \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection
+ This property holds the effective layout direction of the horizontal list.
+
+ When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
+ the visual layout direction of the horizontal list will be mirrored. However, the
+ property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
+
+ \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::ListView::keyNavigationWraps
+ This property holds whether the list wraps key navigation.
+
+ If this is true, key navigation that would move the current item selection
+ past the end of the list instead wraps around and moves the selection to
+ the start of the list, and vice-versa.
+
+ By default, key navigation is not wrapped.
+*/
+
+
+/*!
+ \qmlproperty int QtQuick2::ListView::cacheBuffer
+ This property determines whether delegates are retained outside the
+ visible area of the view.
+
+ If this value is non-zero, the view keeps as many delegates
+ instantiated as it can fit within the buffer specified. For example,
+ if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
+ set to 40, then up to 2 delegates above and 2 delegates below the visible
+ area may be retained.
+
+ Note that cacheBuffer is not a pixel buffer - it only maintains additional
+ instantiated delegates.
+
+ Setting this value can improve the smoothness of scrolling behavior at the expense
+ of additional memory usage. It is not a substitute for creating efficient
+ delegates; the fewer elements in a delegate, the faster a view can be
+ scrolled.
+*/
+
+
+/*!
+ \qmlproperty string QtQuick2::ListView::section.property
+ \qmlproperty enumeration QtQuick2::ListView::section.criteria
+ \qmlproperty Component QtQuick2::ListView::section.delegate
+
+ These properties hold the expression to be evaluated for the \l section attached property.
+
+ The \l section attached property enables a ListView to be visually
+ separated into different parts. These properties determine how sections
+ are created.
+
+ \c section.property holds the name of the property that is the basis
+ of each section.
+
+ \c section.criteria holds the criteria for forming each section based on
+ \c section.property. This value can be one of:
+
+ \list
+ \o ViewSection.FullString (default) - sections are created based on the
+ \c section.property value.
+ \o ViewSection.FirstCharacter - sections are created based on the first
+ character of the \c section.property value (for example, 'A', 'B', 'C'
+ sections, etc. for an address book)
+ \endlist
+
+ \c section.delegate holds the delegate component for each section.
+
+ Each item in the list has attached properties named \c ListView.section,
+ \c ListView.previousSection and \c ListView.nextSection. These may be
+ used to place a section header for related items.
+
+ For example, here is a ListView that displays a list of animals, separated
+ into sections. Each item in the ListView is placed in a different section
+ depending on the "size" property of the model item. The \c sectionHeading
+ delegate component provides the light blue bar that marks the beginning of
+ each section.
+
+
+ \snippet examples/declarative/modelviews/listview/sections.qml 0
+
+ \image qml-listview-sections-example.png
+
+ \note Adding sections to a ListView does not automatically re-order the
+ list items by the section criteria.
+ If the model is not ordered by section, then it is possible that
+ the sections created will not be unique; each boundary between
+ differing sections will result in a section header being created
+ even if that section exists elsewhere.
+
+ \sa {declarative/modelviews/listview}{ListView examples}
+*/
QSGViewSection *QSGListView::sectionCriteria()
{
Q_D(QSGListView);
@@ -1315,12 +1736,35 @@ QSGViewSection *QSGListView::sectionCriteria()
return d->sectionCriteria;
}
+/*!
+ \qmlproperty string QtQuick2::ListView::currentSection
+ This property holds the section that is currently at the beginning of the view.
+*/
QString QSGListView::currentSection() const
{
Q_D(const QSGListView);
return d->currentSection;
}
+/*!
+ \qmlproperty real QtQuick2::ListView::highlightMoveSpeed
+ \qmlproperty int QtQuick2::ListView::highlightMoveDuration
+ \qmlproperty real QtQuick2::ListView::highlightResizeSpeed
+ \qmlproperty int QtQuick2::ListView::highlightResizeDuration
+
+ These properties hold the move and resize animation speed of the highlight delegate.
+
+ \l highlightFollowsCurrentItem must be true for these properties
+ to have effect.
+
+ The default value for the speed properties is 400 pixels/second.
+ The default value for the duration properties is -1, i.e. the
+ highlight will take as much time as necessary to move at the set speed.
+
+ These properties have the same characteristics as a SmoothedAnimation.
+
+ \sa highlightFollowsCurrentItem
+*/
qreal QSGListView::highlightMoveSpeed() const
{
Q_D(const QSGListView);
@@ -1382,6 +1826,27 @@ void QSGListView::setHighlightResizeDuration(int duration)
}
}
+/*!
+ \qmlproperty enumeration QtQuick2::ListView::snapMode
+
+ This property determines how the view scrolling will settle following a drag or flick.
+ The possible values are:
+
+ \list
+ \o ListView.NoSnap (default) - the view stops anywhere within the visible area.
+ \o ListView.SnapToItem - the view settles with an item aligned with the start of
+ the view.
+ \o ListView.SnapOneItem - the view settles no more than one item away from the first
+ visible item at the time the mouse button is released. This mode is particularly
+ useful for moving one page at a time.
+ \endlist
+
+ \c snapMode does not affect the \l currentIndex. To update the
+ \l currentIndex as the list is moved, set \l highlightRangeMode
+ to \c ListView.StrictlyEnforceRange.
+
+ \sa highlightRangeMode
+*/
QSGListView::SnapMode QSGListView::snapMode() const
{
Q_D(const QSGListView);
@@ -1397,6 +1862,28 @@ void QSGListView::setSnapMode(SnapMode mode)
}
}
+
+/*!
+ \qmlproperty Component QtQuick2::ListView::footer
+ This property holds the component to use as the footer.
+
+ An instance of the footer component is created for each view. The
+ footer is positioned at the end of the view, after any items.
+
+ \sa header
+*/
+
+
+/*!
+ \qmlproperty Component QtQuick2::ListView::header
+ This property holds the component to use as the header.
+
+ An instance of the header component is created for each view. The
+ header is positioned at the beginning of the view, before any items.
+
+ \sa footer
+*/
+
void QSGListView::viewportMoved()
{
Q_D(QSGListView);
@@ -1432,8 +1919,12 @@ void QSGListView::viewportMoved()
pos = viewPos + highlightEnd - d->highlight->size();
if (pos < viewPos + highlightStart)
pos = viewPos + highlightStart;
- d->highlightPosAnimator->stop();
- static_cast<FxListItemSG*>(d->highlight)->setPosition(qRound(pos));
+ if (pos != d->highlight->position()) {
+ d->highlightPosAnimator->stop();
+ static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
+ } else {
+ d->updateHighlight();
+ }
// update current index
if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
@@ -1527,6 +2018,15 @@ void QSGListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGe
}
+/*!
+ \qmlmethod QtQuick2::ListView::incrementCurrentIndex()
+
+ Increments the current index. The current index will wrap
+ if keyNavigationWraps is true and it is currently at the end.
+ This method has no effect if the \l count is zero.
+
+ \bold Note: methods should only be called after the Component has completed.
+*/
void QSGListView::incrementCurrentIndex()
{
Q_D(QSGListView);
@@ -1538,6 +2038,15 @@ void QSGListView::incrementCurrentIndex()
}
}
+/*!
+ \qmlmethod QtQuick2::ListView::decrementCurrentIndex()
+
+ Decrements the current index. The current index will wrap
+ if keyNavigationWraps is true and it is currently at the beginning.
+ This method has no effect if the \l count is zero.
+
+ \bold Note: methods should only be called after the Component has completed.
+*/
void QSGListView::decrementCurrentIndex()
{
Q_D(QSGListView);
@@ -1566,7 +2075,7 @@ void QSGListView::updateSections()
void QSGListView::itemsInserted(int modelIndex, int count)
{
Q_D(QSGListView);
- if (!isComponentComplete())
+ if (!isComponentComplete() || !d->model || !d->model->isValid())
return;
d->updateUnrequestedIndexes();
d->moveReason = QSGListViewPrivate::Other;
@@ -1706,7 +2215,7 @@ void QSGListView::itemsInserted(int modelIndex, int count)
void QSGListView::itemsRemoved(int modelIndex, int count)
{
Q_D(QSGListView);
- if (!isComponentComplete())
+ if (!isComponentComplete() || !d->model || !d->model->isValid())
return;
d->moveReason = QSGListViewPrivate::Other;
d->updateUnrequestedIndexes();
@@ -1803,7 +2312,7 @@ void QSGListView::itemsRemoved(int modelIndex, int count)
void QSGListView::itemsMoved(int from, int to, int count)
{
Q_D(QSGListView);
- if (!isComponentComplete())
+ if (!isComponentComplete() || !d->isValid())
return;
d->updateUnrequestedIndexes();
@@ -1926,6 +2435,72 @@ void QSGListView::itemsMoved(int from, int to, int count)
d->layout();
}
+/*!
+ \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode)
+
+ Positions the view such that the \a index is at the position specified by
+ \a mode:
+
+ \list
+ \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
+ \o ListView.Center - position item in the center of the view.
+ \o ListView.End - position item at bottom (or right for horizontal orientation) of the view.
+ \o ListView.Visible - if any part of the item is visible then take no action, otherwise
+ bring the item into view.
+ \o ListView.Contain - ensure the entire item is visible. If the item is larger than
+ the view the item is positioned at the top (or left for horizontal orientation) of the view.
+ \endlist
+
+ If positioning the view at \a index would cause empty space to be displayed at
+ the beginning or end of the view, the view will be positioned at the boundary.
+
+ It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
+ at a particular index. This is unreliable since removing items from the start
+ of the list does not cause all other items to be repositioned, and because
+ the actual start of the view can vary based on the size of the delegates.
+ The correct way to bring an item into view is with \c positionViewAtIndex.
+
+ \bold Note: methods should only be called after the Component has completed. To position
+ the view at startup, this method should be called by Component.onCompleted. For
+ example, to position the view at the end:
+
+ \code
+ Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
+ \endcode
+*/
+
+/*!
+ \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
+ \qmlmethod QtQuick2::ListView::positionViewAtEnd()
+
+ Positions the view at the beginning or end, taking into account any header or footer.
+
+ It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
+ at a particular index. This is unreliable since removing items from the start
+ of the list does not cause all other items to be repositioned, and because
+ the actual start of the view can vary based on the size of the delegates.
+
+ \bold Note: methods should only be called after the Component has completed. To position
+ the view at startup, this method should be called by Component.onCompleted. For
+ example, to position the view at the end on startup:
+
+ \code
+ Component.onCompleted: positionViewAtEnd()
+ \endcode
+*/
+
+/*!
+ \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
+
+ Returns the index of the visible item containing the point \a x, \a y in content
+ coordinates. If there is no item at the point specified, or the item is
+ not visible -1 is returned.
+
+ If the item is outside the visible area, -1 is returned, regardless of
+ whether an item will exist at that point when scrolled into view.
+
+ \bold Note: methods should only be called after the Component has completed.
+*/
QSGListViewAttached *QSGListView::qmlAttachedProperties(QObject *obj)
{
diff --git a/src/declarative/items/qsgloader.cpp b/src/declarative/items/qsgloader.cpp
index ccf16896fb..999c174ee9 100644
--- a/src/declarative/items/qsgloader.cpp
+++ b/src/declarative/items/qsgloader.cpp
@@ -103,6 +103,115 @@ void QSGLoaderPrivate::initResize()
_q_updateSize();
}
+/*!
+ \qmlclass Loader QSGLoader
+ \inqmlmodule QtQuick 2
+ \ingroup qml-utility-elements
+ \inherits Item
+
+ \brief The Loader item allows dynamically loading an Item-based
+ subtree from a URL or Component.
+
+ Loader is used to dynamically load visual QML components. It can load a
+ QML file (using the \l source property) or a \l Component object (using
+ the \l sourceComponent property). It is useful for delaying the creation
+ of a component until it is required: for example, when a component should
+ be created on demand, or when a component should not be created
+ unnecessarily for performance reasons.
+
+ Here is a Loader that loads "Page1.qml" as a component when the
+ \l MouseArea is clicked:
+
+ \snippet doc/src/snippets/declarative/loader/simple.qml 0
+
+ The loaded item can be accessed using the \l item property.
+
+ If the \l source or \l sourceComponent changes, any previously instantiated
+ items are destroyed. Setting \l source to an empty string or setting
+ \l sourceComponent to \c undefined destroys the currently loaded item,
+ freeing resources and leaving the Loader empty.
+
+ \section2 Loader sizing behavior
+
+ Loader is like any other visual item and must be positioned and sized
+ accordingly to become visible.
+
+ \list
+ \o If an explicit size is not specified for the Loader, the Loader
+ is automatically resized to the size of the loaded item once the
+ component is loaded.
+ \o If the size of the Loader is specified explicitly by setting
+ the width, height or by anchoring, the loaded item will be resized
+ to the size of the Loader.
+ \endlist
+
+ In both scenarios the size of the item and the Loader are identical.
+ This ensures that anchoring to the Loader is equivalent to anchoring
+ to the loaded item.
+
+ \table
+ \row
+ \o sizeloader.qml
+ \o sizeitem.qml
+ \row
+ \o \snippet doc/src/snippets/declarative/loader/sizeloader.qml 0
+ \o \snippet doc/src/snippets/declarative/loader/sizeitem.qml 0
+ \row
+ \o The red rectangle will be sized to the size of the root item.
+ \o The red rectangle will be 50x50, centered in the root item.
+ \endtable
+
+
+ \section2 Receiving signals from loaded items
+
+ Any signals emitted from the loaded item can be received using the
+ \l Connections element. For example, the following \c application.qml
+ loads \c MyItem.qml, and is able to receive the \c message signal from
+ the loaded item through a \l Connections object:
+
+ \table
+ \row
+ \o application.qml
+ \o MyItem.qml
+ \row
+ \o \snippet doc/src/snippets/declarative/loader/connections.qml 0
+ \o \snippet doc/src/snippets/declarative/loader/MyItem.qml 0
+ \endtable
+
+ Alternatively, since \c MyItem.qml is loaded within the scope of the
+ Loader, it could also directly call any function defined in the Loader or
+ its parent \l Item.
+
+
+ \section2 Focus and key events
+
+ Loader is a focus scope. Its \l {Item::}{focus} property must be set to
+ \c true for any of its children to get the \e {active focus}. (See
+ \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page}
+ for more details.) Any key events received in the loaded item should likely
+ also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader.
+
+ For example, the following \c application.qml loads \c KeyReader.qml when
+ the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is
+ set to \c true for the Loader as well as the \l Item in the dynamically
+ loaded object:
+
+ \table
+ \row
+ \o application.qml
+ \o KeyReader.qml
+ \row
+ \o \snippet doc/src/snippets/declarative/loader/focus.qml 0
+ \o \snippet doc/src/snippets/declarative/loader/KeyReader.qml 0
+ \endtable
+
+ Once \c KeyReader.qml is loaded, it accepts key events and sets
+ \c event.accepted to \c true so that the event is not propagated to the
+ parent \l Rectangle.
+
+ \sa {dynamic-object-creation}{Dynamic Object Creation}
+*/
+
QSGLoader::QSGLoader(QSGItem *parent)
: QSGImplicitSizeItem(*(new QSGLoaderPrivate), parent)
{
@@ -118,6 +227,19 @@ QSGLoader::~QSGLoader()
}
}
+/*!
+ \qmlproperty url QtQuick2::Loader::source
+ This property holds the URL of the QML component to instantiate.
+
+ Note the QML component must be an \l{Item}-based component. The loader
+ cannot load non-visual components.
+
+ To unload the currently loaded item, set this property to an empty string,
+ or set \l sourceComponent to \c undefined. Setting \c source to a
+ new URL will also cause the item created by the previous URL to be unloaded.
+
+ \sa sourceComponent, status, progress
+*/
QUrl QSGLoader::source() const
{
Q_D(const QSGLoader);
@@ -148,6 +270,28 @@ void QSGLoader::setSource(const QUrl &url)
d->load();
}
+/*!
+ \qmlproperty Component QtQuick2::Loader::sourceComponent
+ This property holds the \l{Component} to instantiate.
+
+ \qml
+ Item {
+ Component {
+ id: redSquare
+ Rectangle { color: "red"; width: 10; height: 10 }
+ }
+
+ Loader { sourceComponent: redSquare }
+ Loader { sourceComponent: redSquare; x: 10 }
+ }
+ \endqml
+
+ To unload the currently loaded item, set this property to an empty string
+ or \c undefined.
+
+ \sa source, progress
+*/
+
QDeclarativeComponent *QSGLoader::sourceComponent() const
{
Q_D(const QSGLoader);
@@ -270,6 +414,46 @@ void QSGLoaderPrivate::_q_sourceLoaded()
}
}
+/*!
+ \qmlproperty enumeration QtQuick2::Loader::status
+
+ This property holds the status of QML loading. It can be one of:
+ \list
+ \o Loader.Null - no QML source has been set
+ \o Loader.Ready - the QML source has been loaded
+ \o Loader.Loading - the QML source is currently being loaded
+ \o Loader.Error - an error occurred while loading the QML source
+ \endlist
+
+ Use this status to provide an update or respond to the status change in some way.
+ For example, you could:
+
+ \list
+ \o Trigger a state change:
+ \qml
+ State { name: 'loaded'; when: loader.status == Loader.Ready }
+ \endqml
+
+ \o Implement an \c onStatusChanged signal handler:
+ \qml
+ Loader {
+ id: loader
+ onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
+ }
+ \endqml
+
+ \o Bind to the status value:
+ \qml
+ Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' }
+ \endqml
+ \endlist
+
+ Note that if the source is a local file, the status will initially be Ready (or Error). While
+ there will be no onStatusChanged signal in that case, the onLoaded will still be invoked.
+
+ \sa progress
+*/
+
QSGLoader::Status QSGLoader::status() const
{
Q_D(const QSGLoader);
@@ -290,6 +474,23 @@ void QSGLoader::componentComplete()
d->load();
}
+/*!
+ \qmlsignal QtQuick2::Loader::onLoaded()
+
+ This handler is called when the \l status becomes \c Loader.Ready, or on successful
+ initial load.
+*/
+
+
+/*!
+\qmlproperty real QtQuick2::Loader::progress
+
+This property holds the progress of loading QML data from the network, from
+0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so
+this value will rapidly change from 0 to 1.
+
+\sa status
+*/
qreal QSGLoader::progress() const
{
Q_D(const QSGLoader);
@@ -328,6 +529,10 @@ void QSGLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
updatingSize = false;
}
+/*!
+ \qmlproperty Item QtQuick2::Loader::item
+ This property holds the top-level item that is currently loaded.
+*/
QSGItem *QSGLoader::item() const
{
Q_D(const QSGLoader);
diff --git a/src/declarative/items/qsgmousearea.cpp b/src/declarative/items/qsgmousearea.cpp
index b35c14624a..7d86d2e248 100644
--- a/src/declarative/items/qsgmousearea.cpp
+++ b/src/declarative/items/qsgmousearea.cpp
@@ -85,7 +85,7 @@ void QSGDrag::resetTarget()
}
/*!
- \qmlproperty Item MouseArea::drag.dropItem
+ \qmlproperty Item QtQuick2::MouseArea::drag.dropItem
This property holds the item an active drag will be dropped on if released
at the current position.
@@ -115,7 +115,7 @@ void QSGDrag::setGrabItem(QSGItem *item)
}
/*!
- \qmlproperty variant MouseArea::drag.data
+ \qmlproperty variant QtQuick2::MouseArea::drag.data
This property holds the data sent to recipients of drag events generated
by a MouseArea.
@@ -234,7 +234,7 @@ void QSGDrag::setFilterChildren(bool filter)
}
/*!
- \qmlproperty stringlist MouseArea::drag.keys
+ \qmlproperty stringlist QtQuick2::MouseArea::drag.keys
This property holds a list of keys drag recipients can use to identify the
source or data type of a drag event.
@@ -272,7 +272,7 @@ void QSGMouseAreaPrivate::init()
q->setFiltersChildMouseEvents(true);
}
-void QSGMouseAreaPrivate::saveEvent(QGraphicsSceneMouseEvent *event)
+void QSGMouseAreaPrivate::saveEvent(QGraphicsSceneMouseEvent *event)
{
lastPos = event->pos();
lastScenePos = event->scenePos();
@@ -287,14 +287,14 @@ void QSGMouseAreaPrivate::forwardEvent(QGraphicsSceneMouseEvent* event)
event->setPos(q->mapFromScene(event->scenePos()));
}
-bool QSGMouseAreaPrivate::isPressAndHoldConnected()
+bool QSGMouseAreaPrivate::isPressAndHoldConnected()
{
Q_Q(QSGMouseArea);
static int idx = QObjectPrivate::get(q)->signalIndex("pressAndHold(QSGMouseEvent*)");
return QObjectPrivate::get(q)->isSignalConnected(idx);
}
-bool QSGMouseAreaPrivate::isDoubleClickConnected()
+bool QSGMouseAreaPrivate::isDoubleClickConnected()
{
Q_Q(QSGMouseArea);
static int idx = QObjectPrivate::get(q)->signalIndex("doubleClicked(QSGMouseEvent*)");
@@ -373,7 +373,65 @@ bool QSGMouseAreaPrivate::propagateHelper(QSGMouseEvent *ev, QSGItem *item,const
}
-/*
+/*!
+ \qmlclass MouseArea QSGMouseArea
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-interaction-elements
+ \brief The MouseArea item enables simple mouse handling.
+ \inherits Item
+
+ A MouseArea is an invisible item that is typically used in conjunction with
+ a visible item in order to provide mouse handling for that item.
+ By effectively acting as a proxy, the logic for mouse handling can be
+ contained within a MouseArea item.
+
+ For basic key handling, see the \l{Keys}{Keys attached property}.
+
+ The \l enabled property is used to enable and disable mouse handling for
+ the proxied item. When disabled, the mouse area becomes transparent to
+ mouse events.
+
+ The \l pressed read-only property indicates whether or not the user is
+ holding down a mouse button over the mouse area. This property is often
+ used in bindings between properties in a user interface. The containsMouse
+ read-only property indicates the presence of the mouse cursor over the
+ mouse area but, by default, only when a mouse button is held down; see below
+ for further details.
+
+ Information about the mouse position and button clicks are provided via
+ signals for which event handler properties are defined. The most commonly
+ used involved handling mouse presses and clicks: onClicked, onDoubleClicked,
+ onPressed, onReleased and onPressAndHold.
+
+ By default, MouseArea items only report mouse clicks and not changes to the
+ position of the mouse cursor. Setting the hoverEnabled property ensures that
+ handlers defined for onPositionChanged, onEntered and onExited are used and
+ that the containsMouse property is updated even when no mouse buttons are
+ pressed.
+
+ \section1 Example Usage
+
+ \div {class="float-right"}
+ \inlineimage qml-mousearea-snippet.png
+ \enddiv
+
+ The following example uses a MouseArea in a \l Rectangle that changes
+ the \l Rectangle color to red when clicked:
+
+ \snippet doc/src/snippets/declarative/mousearea/mousearea.qml import
+ \codeline
+ \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro
+
+ \clearfloat
+ Many MouseArea signals pass a \l{MouseEvent}{mouse} parameter that contains
+ additional information about the mouse event, such as the position, button,
+ and any key modifiers.
+
+ Here is an extension of the previous example that produces a different
+ color when the area is right clicked:
+
+ \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro-extended
+
Behavioral Change in QtQuick 2.0
From QtQuick 2.0, the signals clicked, doubleClicked and pressAndHold have a different interaction
@@ -385,7 +443,149 @@ bool QSGMouseAreaPrivate::propagateHelper(QSGMouseEvent *ev, QSGItem *item,const
Note that to get the same behavior as a QtQuick 1.0 MouseArea{} with regard to absorbing all mouse events, you will
now need to add empty signal handlers for these three signals.
- */
+
+ \sa MouseEvent, {declarative/touchinteraction/mousearea}{MouseArea example}
+*/
+
+/*!
+ \qmlsignal QtQuick2::MouseArea::onEntered()
+
+ This handler is called when the mouse enters the mouse area.
+
+ By default the onEntered handler is only called while a button is
+ pressed. Setting hoverEnabled to true enables handling of
+ onEntered when no mouse button is pressed.
+
+ \sa hoverEnabled
+*/
+
+/*!
+ \qmlsignal QtQuick2::MouseArea::onExited()
+
+ This handler is called when the mouse exits the mouse area.
+
+ By default the onExited handler is only called while a button is
+ pressed. Setting hoverEnabled to true enables handling of
+ onExited when no mouse button is pressed.
+
+ The example below shows a fairly typical relationship between
+ two MouseAreas, with \c mouseArea2 on top of \c mouseArea1. Moving the
+ mouse into \c mouseArea2 from \c mouseArea1 will cause \c onExited
+ to be called for \c mouseArea1.
+ \qml
+ Rectangle {
+ width: 400; height: 400
+ MouseArea {
+ id: mouseArea1
+ anchors.fill: parent
+ hoverEnabled: true
+ }
+ MouseArea {
+ id: mouseArea2
+ width: 100; height: 100
+ anchors.centerIn: parent
+ hoverEnabled: true
+ }
+ }
+ \endqml
+
+ If instead you give the two mouseAreas a parent-child relationship,
+ moving the mouse into \c mouseArea2 from \c mouseArea1 will \b not
+ cause \c onExited to be called for \c mouseArea1. Instead, they will
+ both be considered to be simultaneously hovered.
+
+ \sa hoverEnabled
+*/
+
+/*!
+ \qmlsignal QtQuick2::MouseArea::onPositionChanged(MouseEvent mouse)
+
+ This handler is called when the mouse position changes.
+
+ The \l {MouseEvent}{mouse} parameter provides information about the mouse, including the x and y
+ position, and any buttons currently pressed.
+
+ The \e accepted property of the MouseEvent parameter is ignored in this handler.
+
+ By default the onPositionChanged handler is only called while a button is
+ pressed. Setting hoverEnabled to true enables handling of
+ onPositionChanged when no mouse button is pressed.
+*/
+
+/*!
+ \qmlsignal QtQuick2::MouseArea::onClicked(MouseEvent mouse)
+
+ This handler is called when there is a click. A click is defined as a press followed by a release,
+ both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and
+ releasing is also considered a click).
+
+ The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
+ position of the release of the click, and whether the click was held.
+
+ The \e accepted property of the MouseEvent parameter is ignored in this handler.
+*/
+
+/*!
+ \qmlsignal QtQuick2::MouseArea::onPressed(MouseEvent mouse)
+
+ This handler is called when there is a press.
+ The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y
+ position and which button was pressed.
+
+ The \e accepted property of the MouseEvent parameter determines whether this MouseArea
+ will handle the press and all future mouse events until release. The default is to accept
+ the event and not allow other MouseArea beneath this one to handle the event. If \e accepted
+ is set to false, no further events will be sent to this MouseArea until the button is next
+ pressed.
+*/
+
+/*!
+ \qmlsignal QtQuick2::MouseArea::onReleased(MouseEvent mouse)
+
+ This handler is called when there is a release.
+ The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
+ position of the release of the click, and whether the click was held.
+
+ The \e accepted property of the MouseEvent parameter is ignored in this handler.
+
+ \sa onCanceled
+*/
+
+/*!
+ \qmlsignal QtQuick2::MouseArea::onPressAndHold(MouseEvent mouse)
+
+ This handler is called when there is a long press (currently 800ms).
+ The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y
+ position of the press, and which button is pressed.
+
+ The \e accepted property of the MouseEvent parameter is ignored in this handler.
+*/
+
+/*!
+ \qmlsignal QtQuick2::MouseArea::onDoubleClicked(MouseEvent mouse)
+
+ This handler is called when there is a double-click (a press followed by a release followed by a press).
+ The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y
+ position of the release of the click, and whether the click was held.
+
+ If the \e accepted property of the \l {MouseEvent}{mouse} parameter is set to false
+ in the handler, the onPressed/onReleased/onClicked handlers will be called for the second
+ click; otherwise they are suppressed. The accepted property defaults to true.
+*/
+
+/*!
+ \qmlsignal QtQuick2::MouseArea::onCanceled()
+
+ This handler is called when mouse events have been canceled, either because an event was not accepted, or
+ because another element stole the mouse event handling.
+
+ This signal is for advanced use: it is useful when there is more than one MouseArea
+ that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter
+ case, if you execute some logic on the pressed signal and then start dragging, the
+ \l Flickable will steal the mouse handling from the MouseArea. In these cases, to reset
+ the logic when the MouseArea has lost the mouse handling to the \l Flickable,
+ \c onCanceled should be used in addition to onReleased.
+*/
QSGMouseArea::QSGMouseArea(QSGItem *parent)
: QSGItem(*(new QSGMouseAreaPrivate), parent)
{
@@ -397,6 +597,25 @@ QSGMouseArea::~QSGMouseArea()
{
}
+/*!
+ \qmlproperty real QtQuick2::MouseArea::mouseX
+ \qmlproperty real QtQuick2::MouseArea::mouseY
+ These properties hold the coordinates of the mouse cursor.
+
+ If the hoverEnabled property is false then these properties will only be valid
+ while a button is pressed, and will remain valid as long as the button is held
+ down even if the mouse is moved outside the area.
+
+ By default, this property is false.
+
+ If hoverEnabled is true then these properties will be valid when:
+ \list
+ \i no button is pressed, but the mouse is within the MouseArea (containsMouse is true).
+ \i a button is pressed and held, even if it has since moved out of the area.
+ \endlist
+
+ The coordinates are relative to the MouseArea.
+*/
qreal QSGMouseArea::mouseX() const
{
Q_D(const QSGMouseArea);
@@ -409,6 +628,12 @@ qreal QSGMouseArea::mouseY() const
return d->lastPos.y();
}
+/*!
+ \qmlproperty bool QtQuick2::MouseArea::enabled
+ This property holds whether the item accepts mouse events.
+
+ By default, this property is true.
+*/
bool QSGMouseArea::isEnabled() const
{
Q_D(const QSGMouseArea);
@@ -424,6 +649,22 @@ void QSGMouseArea::setEnabled(bool a)
}
}
+/*!
+ \qmlproperty bool QtQuick2::MouseArea::preventStealing
+ This property holds whether the mouse events may be stolen from this
+ MouseArea.
+
+ If a MouseArea is placed within an item that filters child mouse
+ events, such as Flickable, the mouse
+ events may be stolen from the MouseArea if a gesture is recognized
+ by the parent element, e.g. a flick gesture. If preventStealing is
+ set to true, no element will steal the mouse events.
+
+ Note that setting preventStealing to true once an element has started
+ stealing events will have no effect until the next press event.
+
+ By default this property is false.
+*/
bool QSGMouseArea::preventStealing() const
{
Q_D(const QSGMouseArea);
@@ -440,6 +681,23 @@ void QSGMouseArea::setPreventStealing(bool prevent)
}
}
+/*!
+ \qmlproperty MouseButtons QtQuick2::MouseArea::pressedButtons
+ This property holds the mouse buttons currently pressed.
+
+ It contains a bitwise combination of:
+ \list
+ \o Qt.LeftButton
+ \o Qt.RightButton
+ \o Qt.MiddleButton
+ \endlist
+
+ The code below displays "right" when the right mouse buttons is pressed:
+
+ \snippet doc/src/snippets/declarative/mousearea/mousearea.qml mousebuttons
+
+ \sa acceptedButtons
+*/
Qt::MouseButtons QSGMouseArea::pressedButtons() const
{
Q_D(const QSGMouseArea);
@@ -682,7 +940,7 @@ void QSGMouseArea::hoverLeaveEvent(QHoverEvent *event)
setHovered(false);
}
-void QSGMouseArea::mouseUngrabEvent()
+void QSGMouseArea::ungrabMouse()
{
Q_D(QSGMouseArea);
if (d->pressed) {
@@ -700,6 +958,11 @@ void QSGMouseArea::mouseUngrabEvent()
}
}
+void QSGMouseArea::mouseUngrabEvent()
+{
+ ungrabMouse();
+}
+
bool QSGMouseArea::sendMouseEvent(QGraphicsSceneMouseEvent *event)
{
Q_D(QSGMouseArea);
@@ -794,6 +1057,12 @@ void QSGMouseArea::timerEvent(QTimerEvent *event)
}
}
+void QSGMouseArea::windowDeactivateEvent()
+{
+ ungrabMouse();
+ QSGItem::windowDeactivateEvent();
+}
+
void QSGMouseArea::geometryChanged(const QRectF &newGeometry,
const QRectF &oldGeometry)
{
@@ -821,6 +1090,17 @@ void QSGMouseArea::itemChange(ItemChange change, const ItemChangeData &value)
QSGItem::itemChange(change, value);
}
+/*!
+ \qmlproperty bool QtQuick2::MouseArea::hoverEnabled
+ This property holds whether hover events are handled.
+
+ By default, mouse events are only handled in response to a button event, or when a button is
+ pressed. Hover enables handling of all mouse events even when no mouse button is
+ pressed.
+
+ This property affects the containsMouse property and the onEntered, onExited and
+ onPositionChanged signals.
+*/
bool QSGMouseArea::hoverEnabled() const
{
return acceptHoverEvents();
@@ -834,16 +1114,26 @@ void QSGMouseArea::setHoverEnabled(bool h)
setAcceptHoverEvents(h);
emit hoverEnabledChanged();
- if (d->hovered != isUnderMouse())
- setHovered(!d->hovered);
}
+
+/*!
+ \qmlproperty bool QtQuick2::MouseArea::containsMouse
+ This property holds whether the mouse is currently inside the mouse area.
+
+ \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change.
+ In addition, if hoverEnabled is false, containsMouse will only be valid when the mouse is pressed.
+*/
bool QSGMouseArea::hovered() const
{
Q_D(const QSGMouseArea);
return d->hovered;
}
+/*!
+ \qmlproperty bool QtQuick2::MouseArea::pressed
+ This property holds whether the mouse area is currently pressed.
+*/
bool QSGMouseArea::pressed() const
{
Q_D(const QSGMouseArea);
@@ -859,7 +1149,26 @@ void QSGMouseArea::setHovered(bool h)
d->hovered ? emit entered() : emit exited();
}
}
+/*!
+ \qmlproperty QtQuick2::Qt::MouseButtons MouseArea::acceptedButtons
+ This property holds the mouse buttons that the mouse area reacts to.
+ The available buttons are:
+ \list
+ \o Qt.LeftButton
+ \o Qt.RightButton
+ \o Qt.MiddleButton
+ \endlist
+
+ To accept more than one button the flags can be combined with the
+ "|" (or) operator:
+
+ \code
+ MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton }
+ \endcode
+
+ The default value is \c Qt.LeftButton.
+*/
Qt::MouseButtons QSGMouseArea::acceptedButtons() const
{
return acceptedMouseButtons();
@@ -907,6 +1216,43 @@ bool QSGMouseArea::setPressed(bool p)
return false;
}
+/*!
+ \qmlproperty Item QtQuick2::MouseArea::drag.target
+ \qmlproperty bool QtQuick2::MouseArea::drag.active
+ \qmlproperty enumeration QtQuick2::MouseArea::drag.axis
+ \qmlproperty real QtQuick2::MouseArea::drag.minimumX
+ \qmlproperty real QtQuick2::MouseArea::drag.maximumX
+ \qmlproperty real QtQuick2::MouseArea::drag.minimumY
+ \qmlproperty real QtQuick2::MouseArea::drag.maximumY
+ \qmlproperty bool QtQuick2::MouseArea::drag.filterChildren
+
+ \c drag provides a convenient way to make an item draggable.
+
+ \list
+ \i \c drag.target specifies the id of the item to drag.
+ \i \c drag.active specifies if the target item is currently being dragged.
+ \i \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XandYAxis)
+ \i \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes.
+ \endlist
+
+ The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity
+ of the rectangle is reduced when it is dragged to the right.
+
+ \snippet doc/src/snippets/declarative/mousearea/mousearea.qml drag
+
+ \note Items cannot be dragged if they are anchored for the requested
+ \c drag.axis. For example, if \c anchors.left or \c anchors.right was set
+ for \c rect in the above example, it cannot be dragged along the X-axis.
+ This can be avoided by settng the anchor value to \c undefined in
+ an \l onPressed handler.
+
+ If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas. This
+ enables a parent MouseArea to handle drags, for example, while descendants handle clicks:
+
+ \snippet doc/src/snippets/declarative/mousearea/mouseareadragfilter.qml dragfilter
+
+*/
+
QSGDrag *QSGMouseArea::drag()
{
Q_D(QSGMouseArea);
diff --git a/src/declarative/items/qsgmousearea_p.h b/src/declarative/items/qsgmousearea_p.h
index aac829f6a9..ff3863c3bb 100644
--- a/src/declarative/items/qsgmousearea_p.h
+++ b/src/declarative/items/qsgmousearea_p.h
@@ -223,6 +223,7 @@ protected:
virtual void hoverLeaveEvent(QHoverEvent *event);
virtual bool childMouseEventFilter(QSGItem *i, QEvent *e);
virtual void timerEvent(QTimerEvent *event);
+ virtual void windowDeactivateEvent();
virtual void geometryChanged(const QRectF &newGeometry,
const QRectF &oldGeometry);
@@ -231,6 +232,7 @@ protected:
private:
void handlePress();
void handleRelease();
+ void ungrabMouse();
private:
Q_DISABLE_COPY(QSGMouseArea)
diff --git a/src/declarative/items/qsgpainteditem.cpp b/src/declarative/items/qsgpainteditem.cpp
index d4543c94f4..95aa2b4fb2 100644
--- a/src/declarative/items/qsgpainteditem.cpp
+++ b/src/declarative/items/qsgpainteditem.cpp
@@ -150,15 +150,10 @@ void QSGPaintedItem::update(const QRect &rect)
Q_D(QSGPaintedItem);
d->contentsDirty = true;
- QRect srect(qCeil(rect.x()*d->contentsScale),
- qCeil(rect.y()*d->contentsScale),
- qCeil(rect.width()*d->contentsScale),
- qCeil(rect.height()*d->contentsScale));
-
- if (srect.isNull() && !d->dirtyRect.isNull())
+ if (rect.isNull() && !d->dirtyRect.isNull())
d->dirtyRect = contentsBoundingRect().toAlignedRect();
else
- d->dirtyRect |= (contentsBoundingRect() & srect).toAlignedRect();
+ d->dirtyRect |= (contentsBoundingRect() & rect).toAlignedRect();
QSGItem::update();
}
diff --git a/src/declarative/items/qsgpathview.cpp b/src/declarative/items/qsgpathview.cpp
index 0bc45fd037..2c3e3cee71 100644
--- a/src/declarative/items/qsgpathview.cpp
+++ b/src/declarative/items/qsgpathview.cpp
@@ -111,7 +111,7 @@ void QSGPathViewPrivate::init()
q, movementEndingIdx, Qt::DirectConnection);
}
-QSGItem *QSGPathViewPrivate::getItem(int modelIndex)
+QSGItem *QSGPathViewPrivate::getItem(int modelIndex, bool onPath)
{
Q_Q(QSGPathView);
requestedIndex = modelIndex;
@@ -128,7 +128,7 @@ QSGItem *QSGPathViewPrivate::getItem(int modelIndex)
qPathViewAttachedType = 0;
if (att) {
att->m_view = q;
- att->setOnPath(true);
+ att->setOnPath(onPath);
}
item->setParentItem(q);
QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
@@ -158,6 +158,10 @@ QSGPathViewAttached *QSGPathViewPrivate::attached(QSGItem *item)
void QSGPathViewPrivate::clear()
{
+ if (currentItem) {
+ releaseItem(currentItem);
+ currentItem = 0;
+ }
for (int i=0; i<items.count(); i++){
QSGItem *p = items[i];
releaseItem(p);
@@ -345,6 +349,71 @@ void QSGPathViewPrivate::regenerate()
q->refill();
}
+/*!
+ \qmlclass PathView QSGPathView
+ \inqmlmodule QtQuick 2
+ \ingroup qml-view-elements
+ \brief The PathView element lays out model-provided items on a path.
+ \inherits Item
+
+ A PathView displays data from models created from built-in QML elements like ListModel
+ and XmlListModel, or custom model classes defined in C++ that inherit from
+ QAbstractListModel.
+
+ The view has a \l model, which defines the data to be displayed, and
+ a \l delegate, which defines how the data should be displayed.
+ The \l delegate is instantiated for each item on the \l path.
+ The items may be flicked to move them along the path.
+
+ For example, if there is a simple list model defined in a file \c ContactModel.qml like this:
+
+ \snippet doc/src/snippets/declarative/pathview/ContactModel.qml 0
+
+ This data can be represented as a PathView, like this:
+
+ \snippet doc/src/snippets/declarative/pathview/pathview.qml 0
+
+ \image pathview.gif
+
+ (Note the above example uses PathAttribute to scale and modify the
+ opacity of the items as they rotate. This additional code can be seen in the
+ PathAttribute documentation.)
+
+ PathView does not automatically handle keyboard navigation. This is because
+ the keys to use for navigation will depend upon the shape of the path. Navigation
+ can be added quite simply by setting \c focus to \c true and calling
+ \l decrementCurrentIndex() or \l incrementCurrentIndex(), for example to navigate
+ using the left and right arrow keys:
+
+ \qml
+ PathView {
+ // ...
+ focus: true
+ Keys.onLeftPressed: decrementCurrentIndex()
+ Keys.onRightPressed: incrementCurrentIndex()
+ }
+ \endqml
+
+ The path view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
+
+ Delegates are instantiated as needed and may be destroyed at any time.
+ State should \e never be stored in a delegate.
+
+ PathView attaches a number of properties to the root item of the delegate, for example
+ \c {PathView.isCurrentItem}. In the following example, the root delegate item can access
+ this attached property directly as \c PathView.isCurrentItem, while the child
+ \c nameText object must refer to this property as \c wrapper.PathView.isCurrentItem.
+
+ \snippet doc/src/snippets/declarative/pathview/pathview.qml 1
+
+ \bold Note that views do not enable \e clip automatically. If the view
+ is not clipped by another item or the screen, it will be necessary
+ to set \e {clip: true} in order to have the out of view items clipped
+ nicely.
+
+ \sa Path, {declarative/modelviews/pathview}{PathView example}
+*/
+
QSGPathView::QSGPathView(QSGItem *parent)
: QSGItem(*(new QSGPathViewPrivate), parent)
{
@@ -362,6 +431,54 @@ QSGPathView::~QSGPathView()
delete d->model;
}
+/*!
+ \qmlattachedproperty PathView QtQuick2::PathView::view
+ This attached property holds the view that manages this delegate instance.
+
+ It is attached to each instance of the delegate.
+*/
+
+/*!
+ \qmlattachedproperty bool QtQuick2::PathView::onPath
+ This attached property holds whether the item is currently on the path.
+
+ If a pathItemCount has been set, it is possible that some items may
+ be instantiated, but not considered to be currently on the path.
+ Usually, these items would be set invisible, for example:
+
+ \qml
+ Component {
+ Rectangle {
+ visible: PathView.onPath
+ // ...
+ }
+ }
+ \endqml
+
+ It is attached to each instance of the delegate.
+*/
+
+/*!
+ \qmlattachedproperty bool QtQuick2::PathView::isCurrentItem
+ This attached property is true if this delegate is the current item; otherwise false.
+
+ It is attached to each instance of the delegate.
+
+ This property may be used to adjust the appearance of the current item.
+
+ \snippet doc/src/snippets/declarative/pathview/pathview.qml 1
+*/
+
+/*!
+ \qmlproperty model QtQuick2::PathView::model
+ This property holds the model providing data for the view.
+
+ The model provides a set of data that is used to create the items for the view.
+ For large or dynamic datasets the model is usually provided by a C++ model object.
+ Models can also be created directly in QML, using the ListModel element.
+
+ \sa {qmlmodels}{Data Models}
+*/
QVariant QSGPathView::model() const
{
Q_D(const QSGPathView);
@@ -426,12 +543,21 @@ void QSGPathView::setModel(const QVariant &model)
emit modelChanged();
}
+/*!
+ \qmlproperty int QtQuick2::PathView::count
+ This property holds the number of items in the model.
+*/
int QSGPathView::count() const
{
Q_D(const QSGPathView);
return d->model ? d->modelCount : 0;
}
+/*!
+ \qmlproperty Path QtQuick2::PathView::path
+ This property holds the path used to lay out the items.
+ For more information see the \l Path documentation.
+*/
QDeclarativePath *QSGPathView::path() const
{
Q_D(const QSGPathView);
@@ -458,6 +584,10 @@ void QSGPathView::setPath(QDeclarativePath *path)
emit pathChanged();
}
+/*!
+ \qmlproperty int QtQuick2::PathView::currentIndex
+ This property holds the index of the current item.
+*/
int QSGPathView::currentIndex() const
{
Q_D(const QSGPathView);
@@ -470,28 +600,31 @@ void QSGPathView::setCurrentIndex(int idx)
if (d->model && d->modelCount)
idx = qAbs(idx % d->modelCount);
if (d->model && idx != d->currentIndex) {
- if (d->modelCount) {
- int itemIndex = (d->currentIndex - d->firstIndex + d->modelCount) % d->modelCount;
- if (itemIndex < d->items.count()) {
- if (QSGItem *item = d->items.at(itemIndex)) {
- if (QSGPathViewAttached *att = d->attached(item))
- att->setIsCurrentItem(false);
- }
- }
+ if (d->currentItem) {
+ if (QSGPathViewAttached *att = d->attached(d->currentItem))
+ att->setIsCurrentItem(false);
+ d->releaseItem(d->currentItem);
}
d->currentItem = 0;
d->moveReason = QSGPathViewPrivate::SetIndex;
d->currentIndex = idx;
if (d->modelCount) {
- if (d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange)
- d->snapToCurrent();
int itemIndex = (idx - d->firstIndex + d->modelCount) % d->modelCount;
if (itemIndex < d->items.count()) {
- d->currentItem = d->items.at(itemIndex);
+ d->currentItem = d->model->item(d->currentIndex, true);
d->currentItem->setFocus(true);
if (QSGPathViewAttached *att = d->attached(d->currentItem))
att->setIsCurrentItem(true);
+ } else {
+ d->currentItem = d->getItem(d->currentIndex, false);
+ d->updateItem(d->currentItem, d->currentIndex < d->firstIndex ? 0.0 : 1.0);
+ if (QSGPathViewAttached *att = d->attached(d->currentItem))
+ att->setIsCurrentItem(true);
+ if (d->model->completePending())
+ d->model->completeItem();
}
+ if (d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange)
+ d->snapToCurrent();
d->currentItemOffset = d->positionOfIndex(d->currentIndex);
d->updateHighlight();
}
@@ -499,6 +632,19 @@ void QSGPathView::setCurrentIndex(int idx)
}
}
+QSGItem *QSGPathView::currentItem() const
+{
+ Q_D(const QSGPathView);
+ return d->currentItem;
+}
+
+/*!
+ \qmlmethod QtQuick2::PathView::incrementCurrentIndex()
+
+ Increments the current index.
+
+ \bold Note: methods should only be called after the Component has completed.
+*/
void QSGPathView::incrementCurrentIndex()
{
Q_D(QSGPathView);
@@ -506,6 +652,13 @@ void QSGPathView::incrementCurrentIndex()
setCurrentIndex(currentIndex()+1);
}
+/*!
+ \qmlmethod QtQuick2::PathView::decrementCurrentIndex()
+
+ Decrements the current index.
+
+ \bold Note: methods should only be called after the Component has completed.
+*/
void QSGPathView::decrementCurrentIndex()
{
Q_D(QSGPathView);
@@ -518,6 +671,12 @@ void QSGPathView::decrementCurrentIndex()
}
}
+/*!
+ \qmlproperty real QtQuick2::PathView::offset
+
+ The offset specifies how far along the path the items are from their initial positions.
+ This is a real number that ranges from 0.0 to the count of items in the model.
+*/
qreal QSGPathView::offset() const
{
Q_D(const QSGPathView);
@@ -552,6 +711,30 @@ void QSGPathViewPrivate::setAdjustedOffset(qreal o)
setOffset(o+offsetAdj);
}
+/*!
+ \qmlproperty Component QtQuick2::PathView::highlight
+ This property holds the component to use as the highlight.
+
+ An instance of the highlight component will be created for each view.
+ The geometry of the resultant component instance will be managed by the view
+ so as to stay with the current item.
+
+ The below example demonstrates how to make a simple highlight. Note the use
+ of the \l{PathView::onPath}{PathView.onPath} attached property to ensure that
+ the highlight is hidden when flicked away from the path.
+
+ \qml
+ Component {
+ Rectangle {
+ visible: PathView.onPath
+ // ...
+ }
+ }
+ \endqml
+
+ \sa highlightItem, highlightRangeMode
+*/
+
QDeclarativeComponent *QSGPathView::highlight() const
{
Q_D(const QSGPathView);
@@ -569,12 +752,54 @@ void QSGPathView::setHighlight(QDeclarativeComponent *highlight)
}
}
+/*!
+ \qmlproperty Item QtQuick2::PathView::highlightItem
+
+ \c highlightItem holds the highlight item, which was created
+ from the \l highlight component.
+
+ \sa highlight
+*/
QSGItem *QSGPathView::highlightItem()
{
Q_D(const QSGPathView);
return d->highlightItem;
}
-
+/*!
+ \qmlproperty real QtQuick2::PathView::preferredHighlightBegin
+ \qmlproperty real QtQuick2::PathView::preferredHighlightEnd
+ \qmlproperty enumeration QtQuick2::PathView::highlightRangeMode
+
+ These properties set the preferred range of the highlight (current item)
+ within the view. The preferred values must be in the range 0.0-1.0.
+
+ If highlightRangeMode is set to \e PathView.NoHighlightRange
+
+ If highlightRangeMode is set to \e PathView.ApplyRange the view will
+ attempt to maintain the highlight within the range, however
+ the highlight can move outside of the range at the ends of the path
+ or due to a mouse interaction.
+
+ If highlightRangeMode is set to \e PathView.StrictlyEnforceRange the highlight will never
+ move outside of the range. This means that the current item will change
+ if a keyboard or mouse action would cause the highlight to move
+ outside of the range.
+
+ Note that this is the correct way to influence where the
+ current item ends up when the view moves. For example, if you want the
+ currently selected item to be in the middle of the path, then set the
+ highlight range to be 0.5,0.5 and highlightRangeMode to PathView.StrictlyEnforceRange.
+ Then, when the path scrolls,
+ the currently selected item will be the item at that position. This also applies to
+ when the currently selected item changes - it will scroll to within the preferred
+ highlight range. Furthermore, the behaviour of the current item index will occur
+ whether or not a highlight exists.
+
+ The default value is \e PathView.StrictlyEnforceRange.
+
+ Note that a valid range requires preferredHighlightEnd to be greater
+ than or equal to preferredHighlightBegin.
+*/
qreal QSGPathView::preferredHighlightBegin() const
{
Q_D(const QSGPathView);
@@ -625,6 +850,15 @@ void QSGPathView::setHighlightRangeMode(HighlightRangeMode mode)
emit highlightRangeModeChanged();
}
+/*!
+ \qmlproperty int QtQuick2::PathView::highlightMoveDuration
+ This property holds the move animation duration of the highlight delegate.
+
+ If the highlightRangeMode is StrictlyEnforceRange then this property
+ determines the speed that the items move along the path.
+
+ The default value for the duration is 300ms.
+*/
int QSGPathView::highlightMoveDuration() const
{
Q_D(const QSGPathView);
@@ -640,6 +874,14 @@ void QSGPathView::setHighlightMoveDuration(int duration)
emit highlightMoveDurationChanged();
}
+/*!
+ \qmlproperty real QtQuick2::PathView::dragMargin
+ This property holds the maximum distance from the path that initiate mouse dragging.
+
+ By default the path can only be dragged by clicking on an item. If
+ dragMargin is greater than zero, a drag can be initiated by clicking
+ within dragMargin pixels of the path.
+*/
qreal QSGPathView::dragMargin() const
{
Q_D(const QSGPathView);
@@ -655,6 +897,12 @@ void QSGPathView::setDragMargin(qreal dragMargin)
emit dragMarginChanged();
}
+/*!
+ \qmlproperty real QtQuick2::PathView::flickDeceleration
+ This property holds the rate at which a flick will decelerate.
+
+ The default is 100.
+*/
qreal QSGPathView::flickDeceleration() const
{
Q_D(const QSGPathView);
@@ -670,6 +918,14 @@ void QSGPathView::setFlickDeceleration(qreal dec)
emit flickDecelerationChanged();
}
+/*!
+ \qmlproperty bool QtQuick2::PathView::interactive
+
+ A user cannot drag or flick a PathView that is not interactive.
+
+ This property is useful for temporarily disabling flicking. This allows
+ special interaction with PathView's children.
+*/
bool QSGPathView::isInteractive() const
{
Q_D(const QSGPathView);
@@ -687,18 +943,79 @@ void QSGPathView::setInteractive(bool interactive)
}
}
+/*!
+ \qmlproperty bool QtQuick2::PathView::moving
+
+ This property holds whether the view is currently moving
+ due to the user either dragging or flicking the view.
+*/
bool QSGPathView::isMoving() const
{
Q_D(const QSGPathView);
return d->moving;
}
+/*!
+ \qmlproperty bool QtQuick2::PathView::flicking
+
+ This property holds whether the view is currently moving
+ due to the user flicking the view.
+*/
bool QSGPathView::isFlicking() const
{
Q_D(const QSGPathView);
return d->flicking;
}
+/*!
+ \qmlsignal QtQuick2::PathView::onMovementStarted()
+
+ This handler is called when the view begins moving due to user
+ interaction.
+*/
+
+/*!
+ \qmlsignal QtQuick2::PathView::onMovementEnded()
+
+ This handler is called when the view stops moving due to user
+ interaction. If a flick was generated, this handler will
+ be triggered once the flick stops. If a flick was not
+ generated, the handler will be triggered when the
+ user stops dragging - i.e. a mouse or touch release.
+*/
+
+/*!
+ \qmlsignal QtQuick2::PathView::onFlickStarted()
+
+ This handler is called when the view is flicked. A flick
+ starts from the point that the mouse or touch is released,
+ while still in motion.
+*/
+
+/*!
+ \qmlsignal QtQuick2::PathView::onFlickEnded()
+
+ This handler is called when the view stops moving due to a flick.
+*/
+
+/*!
+ \qmlproperty Component QtQuick2::PathView::delegate
+
+ The delegate provides a template defining each item instantiated by the view.
+ The index is exposed as an accessible \c index property. Properties of the
+ model are also available depending upon the type of \l {qmlmodels}{Data Model}.
+
+ The number of elements in the delegate has a direct effect on the
+ flicking performance of the view when pathItemCount is specified. If at all possible, place functionality
+ that is not needed for the normal display of the delegate in a \l Loader which
+ can load additional elements when needed.
+
+ Note that the PathView will layout the items based on the size of the root
+ item in the delegate.
+
+ Here is an example delegate:
+ \snippet doc/src/snippets/declarative/pathview/pathview.qml 1
+*/
QDeclarativeComponent *QSGPathView::delegate() const
{
Q_D(const QSGPathView);
@@ -730,6 +1047,10 @@ void QSGPathView::setDelegate(QDeclarativeComponent *delegate)
}
}
+/*!
+ \qmlproperty int QtQuick2::PathView::pathItemCount
+ This property holds the number of items visible on the path at any one time.
+*/
int QSGPathView::pathItemCount() const
{
Q_D(const QSGPathView);
@@ -976,6 +1297,7 @@ bool QSGPathView::sendMouseEvent(QGraphicsSceneMouseEvent *event)
return d->stealMouse;
} else if (d->lastPosTime.isValid()) {
d->lastPosTime.invalidate();
+ d->fixOffset();
}
if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease)
d->stealMouse = false;
@@ -1000,6 +1322,18 @@ bool QSGPathView::childMouseEventFilter(QSGItem *i, QEvent *e)
return QSGItem::childMouseEventFilter(i, e);
}
+void QSGPathView::mouseUngrabEvent()
+{
+ Q_D(QSGPathView);
+ if (d->stealMouse) {
+ // if our mouse grab has been removed (probably by a Flickable),
+ // fix our state
+ d->stealMouse = false;
+ setKeepMouseGrab(false);
+ d->lastPosTime.invalidate();
+ }
+}
+
void QSGPathView::updatePolish()
{
QSGItem::updatePolish();
@@ -1079,12 +1413,8 @@ void QSGPathView::refill()
if (d->model->completePending())
item->setZ(idx+1);
if (d->currentIndex == idx) {
- item->setFocus(true);
- if (QSGPathViewAttached *att = d->attached(item))
- att->setIsCurrentItem(true);
currentVisible = true;
d->currentItemOffset = pos;
- d->currentItem = item;
}
if (d->items.count() == 0)
d->firstIndex = idx;
@@ -1108,12 +1438,8 @@ void QSGPathView::refill()
if (d->model->completePending())
item->setZ(idx+1);
if (d->currentIndex == idx) {
- item->setFocus(true);
- if (QSGPathViewAttached *att = d->attached(item))
- att->setIsCurrentItem(true);
currentVisible = true;
d->currentItemOffset = pos;
- d->currentItem = item;
}
d->items.prepend(item);
d->updateItem(item, pos);
@@ -1128,8 +1454,25 @@ void QSGPathView::refill()
}
}
- if (!currentVisible)
+ if (!currentVisible) {
d->currentItemOffset = 1.0;
+ if (d->currentItem) {
+ if (QSGPathViewAttached *att = d->attached(d->currentItem))
+ att->setOnPath(false);
+ } else if (d->currentIndex >= 0 && d->currentIndex < d->modelCount) {
+ d->currentItem = d->getItem(d->currentIndex, false);
+ d->updateItem(d->currentItem, d->currentIndex < d->firstIndex ? 0.0 : 1.0);
+ if (QSGPathViewAttached *att = d->attached(d->currentItem))
+ att->setIsCurrentItem(true);
+ if (d->model->completePending())
+ d->model->completeItem();
+ }
+ } else if (!d->currentItem) {
+ d->currentItem = d->model->item(d->currentIndex, true);
+ d->currentItem->setFocus(true);
+ if (QSGPathViewAttached *att = d->attached(d->currentItem))
+ att->setIsCurrentItem(true);
+ }
if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
d->updateItem(d->highlightItem, d->highlightRangeStart);
@@ -1193,6 +1536,8 @@ void QSGPathView::itemsRemoved(int modelIndex, int count)
if (d->currentItem) {
if (QSGPathViewAttached *att = d->attached(d->currentItem))
att->setIsCurrentItem(true);
+ d->releaseItem(d->currentItem);
+ d->currentItem = 0;
}
currentChanged = true;
}
@@ -1332,21 +1677,26 @@ void QSGPathViewPrivate::updateCurrent()
int idx = calcCurrentIndex();
if (model && idx != currentIndex) {
- int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount;
- if (itemIndex < items.count()) {
- if (QSGItem *item = items.at(itemIndex)) {
- if (QSGPathViewAttached *att = attached(item))
- att->setIsCurrentItem(false);
- }
+ if (currentItem) {
+ if (QSGPathViewAttached *att = attached(currentItem))
+ att->setIsCurrentItem(false);
+ releaseItem(currentItem);
}
currentIndex = idx;
currentItem = 0;
- itemIndex = (idx - firstIndex + modelCount) % modelCount;
+ int itemIndex = (idx - firstIndex + modelCount) % modelCount;
if (itemIndex < items.count()) {
- currentItem = items.at(itemIndex);
+ currentItem = model->item(currentIndex, true);
currentItem->setFocus(true);
if (QSGPathViewAttached *att = attached(currentItem))
att->setIsCurrentItem(true);
+ } else if (currentIndex >= 0 && currentIndex < modelCount) {
+ currentItem = getItem(currentIndex, false);
+ updateItem(currentItem, currentIndex < firstIndex ? 0.0 : 1.0);
+ if (QSGPathViewAttached *att = attached(currentItem))
+ att->setIsCurrentItem(true);
+ if (model->completePending())
+ model->completeItem();
}
emit q->currentIndexChanged();
}
diff --git a/src/declarative/items/qsgpathview_p.h b/src/declarative/items/qsgpathview_p.h
index 31f2f6a113..a271e323cd 100644
--- a/src/declarative/items/qsgpathview_p.h
+++ b/src/declarative/items/qsgpathview_p.h
@@ -62,6 +62,7 @@ class Q_AUTOTEST_EXPORT QSGPathView : public QSGItem
Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(QDeclarativePath *path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
+ Q_PROPERTY(QSGItem *currentItem READ currentItem NOTIFY currentIndexChanged)
Q_PROPERTY(qreal offset READ offset WRITE setOffset NOTIFY offsetChanged)
Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged)
@@ -98,6 +99,8 @@ public:
int currentIndex() const;
void setCurrentIndex(int idx);
+ QSGItem *currentItem() const;
+
qreal offset() const;
void setOffset(qreal offset);
@@ -176,6 +179,7 @@ protected:
void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
bool sendMouseEvent(QGraphicsSceneMouseEvent *event);
bool childMouseEventFilter(QSGItem *, QEvent *);
+ void mouseUngrabEvent();
void componentComplete();
private Q_SLOTS:
diff --git a/src/declarative/items/qsgpathview_p_p.h b/src/declarative/items/qsgpathview_p_p.h
index a133e80abc..1575c6c1ca 100644
--- a/src/declarative/items/qsgpathview_p_p.h
+++ b/src/declarative/items/qsgpathview_p_p.h
@@ -111,7 +111,7 @@ public:
}
}
- QSGItem *getItem(int modelIndex);
+ QSGItem *getItem(int modelIndex, bool onPath = true);
void releaseItem(QSGItem *item);
QSGPathViewAttached *attached(QSGItem *item);
void clear();
diff --git a/src/declarative/items/qsgpincharea.cpp b/src/declarative/items/qsgpincharea.cpp
index f86c18dfcf..54c32e5025 100644
--- a/src/declarative/items/qsgpincharea.cpp
+++ b/src/declarative/items/qsgpincharea.cpp
@@ -50,6 +50,102 @@
QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass PinchEvent QSGPinchEvent
+ \inqmlmodule QtQuick 2
+ \ingroup qml-event-elements
+ \brief The PinchEvent object provides information about a pinch event.
+
+ \bold {The PinchEvent element was added in QtQuick 1.1}
+
+ The \c center, \c startCenter, \c previousCenter properties provide the center position between the two touch points.
+
+ The \c scale and \c previousScale properties provide the scale factor.
+
+ The \c angle, \c previousAngle and \c rotation properties provide the angle between the two points and the amount of rotation.
+
+ The \c point1, \c point2, \c startPoint1, \c startPoint2 properties provide the positions of the touch points.
+
+ The \c accepted property may be set to false in the \c onPinchStarted handler if the gesture should not
+ be handled.
+
+ \sa PinchArea
+*/
+
+/*!
+ \qmlproperty QPointF QtQuick2::PinchEvent::center
+ \qmlproperty QPointF QtQuick2::PinchEvent::startCenter
+ \qmlproperty QPointF QtQuick2::PinchEvent::previousCenter
+
+ These properties hold the position of the center point between the two touch points.
+
+ \list
+ \o \c center is the current center point
+ \o \c previousCenter is the center point of the previous event.
+ \o \c startCenter is the center point when the gesture began
+ \endlist
+*/
+
+/*!
+ \qmlproperty real QtQuick2::PinchEvent::scale
+ \qmlproperty real QtQuick2::PinchEvent::previousScale
+
+ These properties hold the scale factor determined by the change in distance between the two touch points.
+
+ \list
+ \o \c scale is the current scale factor.
+ \o \c previousScale is the scale factor of the previous event.
+ \endlist
+
+ When a pinch gesture is started, the scale is 1.0.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::PinchEvent::angle
+ \qmlproperty real QtQuick2::PinchEvent::previousAngle
+ \qmlproperty real QtQuick2::PinchEvent::rotation
+
+ These properties hold the angle between the two touch points.
+
+ \list
+ \o \c angle is the current angle between the two points in the range -180 to 180.
+ \o \c previousAngle is the angle of the previous event.
+ \o \c rotation is the total rotation since the pinch gesture started.
+ \endlist
+
+ When a pinch gesture is started, the rotation is 0.0.
+*/
+
+/*!
+ \qmlproperty QPointF QtQuick2::PinchEvent::point1
+ \qmlproperty QPointF QtQuick2::PinchEvent::startPoint1
+ \qmlproperty QPointF QtQuick2::PinchEvent::point2
+ \qmlproperty QPointF QtQuick2::PinchEvent::startPoint2
+
+ These properties provide the actual touch points generating the pinch.
+
+ \list
+ \o \c point1 and \c point2 hold the current positions of the points.
+ \o \c startPoint1 and \c startPoint2 hold the positions of the points when the second point was touched.
+ \endlist
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::PinchEvent::accepted
+
+ Setting this property to false in the \c PinchArea::onPinchStarted handler
+ will result in no further pinch events being generated, and the gesture
+ ignored.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::PinchEvent::pointCount
+
+ Holds the number of points currently touched. The PinchArea will not react
+ until two touch points have initited a gesture, but will remain active until
+ all touch points have been released.
+*/
+
QSGPinch::QSGPinch()
: m_target(0), m_minScale(1.0), m_maxScale(1.0)
, m_minRotation(0.0), m_maxRotation(0.0)
@@ -63,6 +159,87 @@ QSGPinchAreaPrivate::~QSGPinchAreaPrivate()
delete pinch;
}
+/*!
+ \qmlclass PinchArea QSGPinchArea
+ \inqmlmodule QtQuick 2
+ \brief The PinchArea item enables simple pinch gesture handling.
+ \inherits Item
+
+ \bold {The PinchArea element was added in QtQuick 1.1}
+
+ A PinchArea is an invisible item that is typically used in conjunction with
+ a visible item in order to provide pinch gesture handling for that item.
+
+ The \l enabled property is used to enable and disable pinch handling for
+ the proxied item. When disabled, the pinch area becomes transparent to
+ mouse/touch events.
+
+ PinchArea can be used in two ways:
+
+ \list
+ \o setting a \c pinch.target to provide automatic interaction with an element
+ \o using the onPinchStarted, onPinchUpdated and onPinchFinished handlers
+ \endlist
+
+ \sa PinchEvent
+*/
+
+/*!
+ \qmlsignal QtQuick2::PinchArea::onPinchStarted()
+
+ This handler is called when the pinch area detects that a pinch gesture has started.
+
+ The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture,
+ including the scale, center and angle of the pinch.
+
+ To ignore this gesture set the \c pinch.accepted property to false. The gesture
+ will be cancelled and no further events will be sent.
+*/
+
+/*!
+ \qmlsignal QtQuick2::PinchArea::onPinchUpdated()
+
+ This handler is called when the pinch area detects that a pinch gesture has changed.
+
+ The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture,
+ including the scale, center and angle of the pinch.
+*/
+
+/*!
+ \qmlsignal QtQuick2::PinchArea::onPinchFinished()
+
+ This handler is called when the pinch area detects that a pinch gesture has finished.
+
+ The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture,
+ including the scale, center and angle of the pinch.
+*/
+
+
+/*!
+ \qmlproperty Item QtQuick2::PinchArea::pinch.target
+ \qmlproperty bool QtQuick2::PinchArea::pinch.active
+ \qmlproperty real QtQuick2::PinchArea::pinch.minimumScale
+ \qmlproperty real QtQuick2::PinchArea::pinch.maximumScale
+ \qmlproperty real QtQuick2::PinchArea::pinch.minimumRotation
+ \qmlproperty real QtQuick2::PinchArea::pinch.maximumRotation
+ \qmlproperty enumeration QtQuick2::PinchArea::pinch.dragAxis
+ \qmlproperty real QtQuick2::PinchArea::pinch.minimumX
+ \qmlproperty real QtQuick2::PinchArea::pinch.maximumX
+ \qmlproperty real QtQuick2::PinchArea::pinch.minimumY
+ \qmlproperty real QtQuick2::PinchArea::pinch.maximumY
+
+ \c pinch provides a convenient way to make an item react to pinch gestures.
+
+ \list
+ \i \c pinch.target specifies the id of the item to drag.
+ \i \c pinch.active specifies if the target item is currently being dragged.
+ \i \c pinch.minimumScale and \c pinch.maximumScale limit the range of the Item::scale property.
+ \i \c pinch.minimumRotation and \c pinch.maximumRotation limit the range of the Item::rotation property.
+ \i \c pinch.dragAxis specifies whether dragging in not allowed (\c Pinch.NoDrag), can be done horizontally (\c Pinch.XAxis), vertically (\c Pinch.YAxis), or both (\c Pinch.XandYAxis)
+ \i \c pinch.minimum and \c pinch.maximum limit how far the target can be dragged along the corresponding axes.
+ \endlist
+*/
+
QSGPinchArea::QSGPinchArea(QSGItem *parent)
: QSGItem(*(new QSGPinchAreaPrivate), parent)
{
@@ -73,7 +250,12 @@ QSGPinchArea::QSGPinchArea(QSGItem *parent)
QSGPinchArea::~QSGPinchArea()
{
}
+/*!
+ \qmlproperty bool QtQuick2::PinchArea::enabled
+ This property holds whether the item accepts pinch gestures.
+ This property defaults to true.
+*/
bool QSGPinchArea::isEnabled() const
{
Q_D(const QSGPinchArea);
diff --git a/src/declarative/items/qsgpositioners.cpp b/src/declarative/items/qsgpositioners.cpp
index da3c7fea04..fca0aa1877 100644
--- a/src/declarative/items/qsgpositioners.cpp
+++ b/src/declarative/items/qsgpositioners.cpp
@@ -50,6 +50,7 @@
#include <private/qdeclarativestate_p.h>
#include <private/qdeclarativestategroup_p.h>
#include <private/qdeclarativestateoperations_p.h>
+#include <private/qdeclarativetransition_p.h>
QT_BEGIN_NAMESPACE
@@ -77,6 +78,22 @@ QSGBasePositioner::QSGBasePositioner(PositionerType at, QSGItem *parent)
Q_D(QSGBasePositioner);
d->init(at);
}
+/*!
+ \internal
+ \class QSGBasePositioner
+ \brief The QSGBasePositioner class provides a base for QSGGraphics layouts.
+
+ To create a QSGGraphics Positioner, simply subclass QSGBasePositioner and implement
+ doLayout(), which is automatically called when the layout might need
+ updating. In doLayout() use the setX and setY functions from QSGBasePositioner, and the
+ base class will apply the positions along with the appropriate transitions. The items to
+ position are provided in order as the protected member positionedItems.
+
+ You also need to set a PositionerType, to declare whether you are positioning the x, y or both
+ for the child items. Depending on the chosen type, only x or y changes will be applied.
+
+ Note that the subclass is responsible for adding the spacing in between items.
+*/
QSGBasePositioner::QSGBasePositioner(QSGBasePositionerPrivate &dd, PositionerType at, QSGItem *parent)
: QSGImplicitSizeItem(dd, parent)
@@ -213,7 +230,8 @@ void QSGBasePositioner::prePositioning()
}
QSizeF contentSize(0,0);
doPositioning(&contentSize);
- if(d->addTransition || d->moveTransition)
+ updateAttachedProperties();
+ if (!d->addActions.isEmpty() || !d->moveActions.isEmpty())
finishApplyTransitions();
d->doingPositioning = false;
//Set implicit size to the size of its children
@@ -226,12 +244,12 @@ void QSGBasePositioner::positionX(int x, const PositionedItem &target)
Q_D(QSGBasePositioner);
if(d->type == Horizontal || d->type == Both){
if (target.isNew) {
- if (!d->addTransition)
+ if (!d->addTransition || !d->addTransition->enabled())
target.item->setX(x);
else
d->addActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x));
} else if (x != target.item->x()) {
- if (!d->moveTransition)
+ if (!d->moveTransition || !d->moveTransition->enabled())
target.item->setX(x);
else
d->moveActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x));
@@ -244,12 +262,12 @@ void QSGBasePositioner::positionY(int y, const PositionedItem &target)
Q_D(QSGBasePositioner);
if(d->type == Vertical || d->type == Both){
if (target.isNew) {
- if (!d->addTransition)
+ if (!d->addTransition || !d->addTransition->enabled())
target.item->setY(y);
else
d->addActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y));
} else if (y != target.item->y()) {
- if (!d->moveTransition)
+ if (!d->moveTransition || !d->moveTransition->enabled())
target.item->setY(y);
else
d->moveActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y));
@@ -268,6 +286,233 @@ void QSGBasePositioner::finishApplyTransitions()
d->moveActions.clear();
}
+QSGPositionerAttached *QSGBasePositioner::qmlAttachedProperties(QObject *obj)
+{
+ return new QSGPositionerAttached(obj);
+}
+
+void QSGBasePositioner::updateAttachedProperties(QSGPositionerAttached *specificProperty, QSGItem *specificPropertyOwner) const
+{
+ // If this function is deemed too expensive or shows up in profiles, it could
+ // be changed to run only when there are attached properties present. This
+ // could be a flag in the positioner that is set by the attached property
+ // constructor.
+ QSGPositionerAttached *prevLastProperty = 0;
+ QSGPositionerAttached *lastProperty = 0;
+
+ int visibleItemIndex = 0;
+ for (int ii = 0; ii < positionedItems.count(); ++ii) {
+ const PositionedItem &child = positionedItems.at(ii);
+ if (!child.item)
+ continue;
+
+ QSGPositionerAttached *property = 0;
+
+ if (specificProperty) {
+ if (specificPropertyOwner == child.item) {
+ property = specificProperty;
+ }
+ } else {
+ property = static_cast<QSGPositionerAttached *>(qmlAttachedPropertiesObject<QSGBasePositioner>(child.item, false));
+ }
+
+ if (child.isVisible) {
+ if (property) {
+ property->setIndex(visibleItemIndex);
+ property->setIsFirstItem(visibleItemIndex == 0);
+
+ if (property->isLastItem())
+ prevLastProperty = property;
+ }
+
+ lastProperty = property;
+ ++visibleItemIndex;
+ } else if (property) {
+ property->setIndex(-1);
+ property->setIsFirstItem(false);
+ property->setIsLastItem(false);
+ }
+ }
+
+ if (prevLastProperty && prevLastProperty != lastProperty)
+ prevLastProperty->setIsLastItem(false);
+ if (lastProperty)
+ lastProperty->setIsLastItem(true);
+}
+
+/*!
+ \qmlclass Positioner QSGPositionerAttached
+ \inqmlmodule QtQuick 2
+ \ingroup qml-positioning-elements
+ \brief The Positioner type provides attached properties that contain details on where an item exists in a positioner.
+
+ Positioner items (such as Column, Row, Flow and Grid) provide automatic layout
+ for child items. Attaching this property allows a child item to determine
+ where it exists within the positioner.
+*/
+
+QSGPositionerAttached::QSGPositionerAttached(QObject *parent) : QObject(parent), m_index(-1), m_isFirstItem(false), m_isLastItem(false)
+{
+ QSGItem *attachedItem = qobject_cast<QSGItem *>(parent);
+ if (attachedItem) {
+ QSGBasePositioner *positioner = qobject_cast<QSGBasePositioner *>(attachedItem->parent());
+ if (positioner) {
+ positioner->updateAttachedProperties(this, attachedItem);
+ }
+ }
+}
+
+/*!
+ \qmlattachedproperty Item QtQuick2::Positioner::index
+
+ This property allows the item to determine
+ its index within the positioner.
+*/
+void QSGPositionerAttached::setIndex(int index)
+{
+ if (m_index == index)
+ return;
+ m_index = index;
+ emit indexChanged();
+}
+
+/*!
+ \qmlattachedproperty Item QtQuick2::Positioner::isFirstItem
+ \qmlattachedproperty Item QtQuick2::Positioner::isLastItem
+
+ These properties allow the item to determine if it
+ is the first or last item in the positioner, respectively.
+*/
+void QSGPositionerAttached::setIsFirstItem(bool isFirstItem)
+{
+ if (m_isFirstItem == isFirstItem)
+ return;
+ m_isFirstItem = isFirstItem;
+ emit isFirstItemChanged();
+}
+
+void QSGPositionerAttached::setIsLastItem(bool isLastItem)
+{
+ if (m_isLastItem == isLastItem)
+ return;
+ m_isLastItem = isLastItem;
+ emit isLastItemChanged();
+}
+
+/*!
+ \qmlclass Column QSGColumn
+ \inqmlmodule QtQuick 2
+ \ingroup qml-positioning-elements
+ \brief The Column item arranges its children vertically.
+ \inherits Item
+
+ The Column item positions its child items so that they are vertically
+ aligned and not overlapping.
+
+ Spacing between items can be added using the \l spacing property.
+ Transitions can be used for cases where items managed by a Column are
+ added or moved. These are stored in the \l add and \l move properties
+ respectively.
+
+ See \l{Using QML Positioner and Repeater Items} for more details about this item and other
+ related items.
+
+ \section1 Example Usage
+
+ The following example positions differently shaped rectangles using a Column
+ item.
+
+ \image verticalpositioner_example.png
+
+ \snippet doc/src/snippets/declarative/column/vertical-positioner.qml document
+
+ \section1 Using Transitions
+
+ Transitions can be used to animate items that are added to, moved within,
+ or removed from a Column item. The \l add and \l move properties can be set to
+ the transitions that will be applied when items are added to, removed from,
+ or re-positioned within a Column item.
+
+ The use of transitions with positioners is described in more detail in the
+ \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML
+ Positioner and Repeater Items} document.
+
+ \image verticalpositioner_transition.gif
+
+ \qml
+ Column {
+ spacing: 2
+ add: Transition {
+ // Define an animation for adding a new item...
+ }
+ move: Transition {
+ // Define an animation for moving items within the column...
+ }
+ // ...
+ }
+ \endqml
+
+ \section1 Limitations
+
+ Note that the positioner assumes that the x and y positions of its children
+ will not change. If you manually change the x or y properties in script, bind
+ the x or y properties, use anchors on a child of a positioner, or have the
+ height of a child depend on the position of a child, then the
+ positioner may exhibit strange behavior. If you need to perform any of these
+ actions, consider positioning the items without the use of a Column.
+
+ Items with a width or height of 0 will not be positioned.
+
+ \sa Row, Grid, Flow, Positioner, {declarative/positioners}{Positioners example}
+*/
+/*!
+ \qmlproperty Transition QtQuick2::Column::add
+
+ This property holds the transition to be applied when adding an
+ item to the positioner. The transition will only be applied to the
+ added item(s). Positioner transitions will only affect the
+ position (x, y) of items.
+
+ For a positioner, adding an item can mean that either the object
+ has been created or reparented, and thus is now a child or the
+ positioner, or that the object has had its opacity increased from
+ zero, and thus is now visible.
+
+ \sa move
+*/
+/*!
+ \qmlproperty Transition QtQuick2::Column::move
+
+ This property holds the transition to apply when moving an item
+ within the positioner. Positioner transitions will only affect
+ the position (x, y) of items.
+
+ This transition can be performed when other items are added or removed
+ from the positioner, or when items resize themselves.
+
+ \image positioner-move.gif
+
+ \qml
+ Column {
+ move: Transition {
+ NumberAnimation {
+ properties: "y"
+ duration: 1000
+ }
+ }
+ }
+ \endqml
+
+ \sa add, {declarative/positioners}{Positioners example}
+*/
+/*!
+ \qmlproperty int QtQuick2::Column::spacing
+
+ The spacing is the amount in pixels left empty between adjacent
+ items. The default spacing is 0.
+
+ \sa Grid::spacing
+*/
QSGColumn::QSGColumn(QSGItem *parent)
: QSGBasePositioner(Vertical, parent)
{
@@ -288,10 +533,11 @@ void QSGColumn::doPositioning(QSizeF *contentSize)
contentSize->setWidth(qMax(contentSize->width(), child.item->width()));
voffset += child.item->height();
- if (ii != positionedItems.count() - 1)
- voffset += spacing();
+ voffset += spacing();
}
+ if (voffset != 0)//If we positioned any items, undo the spacing from the last item
+ voffset -= spacing();
contentSize->setHeight(voffset);
}
@@ -318,11 +564,119 @@ void QSGColumn::reportConflictingAnchors()
qmlInfo(this) << "Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column";
}
}
+/*!
+ \qmlclass Row QSGRow
+ \inqmlmodule QtQuick 2
+ \ingroup qml-positioning-elements
+ \brief The Row item arranges its children horizontally.
+ \inherits Item
+
+ The Row item positions its child items so that they are horizontally
+ aligned and not overlapping.
+
+ Use \l spacing to set the spacing between items in a Row, and use the
+ \l add and \l move properties to set the transitions that should be applied
+ when items are added to, removed from, or re-positioned within the Row.
+
+ See \l{Using QML Positioner and Repeater Items} for more details about this item and other
+ related items.
+
+ \section1 Example Usage
+
+ The following example lays out differently shaped rectangles using a Row.
+
+ \image horizontalpositioner_example.png
+
+ \snippet doc/src/snippets/declarative/row/row.qml document
+
+ \section1 Using Transitions
+
+ Transitions can be used to animate items that are added to, moved within,
+ or removed from a Grid item. The \l add and \l move properties can be set to
+ the transitions that will be applied when items are added to, removed from,
+ or re-positioned within a Row item.
+
+ \section1 Limitations
+
+ Note that the positioner assumes that the x and y positions of its children
+ will not change. If you manually change the x or y properties in script, bind
+ the x or y properties, use anchors on a child of a positioner, or have the
+ width of a child depend on the position of a child, then the
+ positioner may exhibit strange behaviour. If you need to perform any of these
+ actions, consider positioning the items without the use of a Row.
+
+ Items with a width or height of 0 will not be positioned.
+
+ \sa Column, Grid, Flow, Positioner, {declarative/positioners}{Positioners example}
+*/
+/*!
+ \qmlproperty Transition QtQuick2::Row::add
+
+ This property holds the transition to be applied when adding an
+ item to the positioner. The transition will only be applied to the
+ added item(s). Positioner transitions will only affect the
+ position (x, y) of items.
+
+ For a positioner, adding an item can mean that either the object
+ has been created or reparented, and thus is now a child or the
+ positioner, or that the object has had its opacity increased from
+ zero, and thus is now visible.
+
+ \sa move
+*/
+/*!
+ \qmlproperty Transition QtQuick2::Row::move
+
+ This property holds the transition to be applied when moving an
+ item within the positioner. Positioner transitions will only affect
+ the position (x, y) of items.
+
+ This transition can be performed when other items are added or removed
+ from the positioner, or when items resize themselves.
+
+ \qml
+ Row {
+ id: positioner
+ move: Transition {
+ NumberAnimation {
+ properties: "x"
+ duration: 1000
+ }
+ }
+ }
+ \endqml
+
+ \sa add, {declarative/positioners}{Positioners example}
+*/
+/*!
+ \qmlproperty int QtQuick2::Row::spacing
+
+ The spacing is the amount in pixels left empty between adjacent
+ items. The default spacing is 0.
+
+ \sa Grid::spacing
+*/
QSGRow::QSGRow(QSGItem *parent)
: QSGBasePositioner(Horizontal, parent)
{
}
+/*!
+ \qmlproperty enumeration QtQuick2::Row::layoutDirection
+
+ This property holds the layoutDirection of the row.
+
+ Possible values:
+
+ \list
+ \o Qt.LeftToRight (default) - Items are laid out from left to right. If the width of the row is explicitly set,
+ the left anchor remains to the left of the row.
+ \o Qt.RightToLeft - Items are laid out from right to left. If the width of the row is explicitly set,
+ the right anchor remains to the right of the row.
+ \endlist
+
+ \sa Grid::layoutDirection, Flow::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example}
+*/
Qt::LayoutDirection QSGRow::layoutDirection() const
{
@@ -344,6 +698,16 @@ void QSGRow::setLayoutDirection(Qt::LayoutDirection layoutDirection)
emit effectiveLayoutDirectionChanged();
}
}
+/*!
+ \qmlproperty enumeration QtQuick2::Row::effectiveLayoutDirection
+ This property holds the effective layout direction of the row positioner.
+
+ When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
+ the visual layout direction of the row positioner will be mirrored. However, the
+ property \l {Row::layoutDirection}{layoutDirection} will remain unchanged.
+
+ \sa Row::layoutDirection, {LayoutMirroring}{LayoutMirroring}
+*/
Qt::LayoutDirection QSGRow::effectiveLayoutDirection() const
{
@@ -371,10 +735,11 @@ void QSGRow::doPositioning(QSizeF *contentSize)
contentSize->setHeight(qMax(contentSize->height(), child.item->height()));
hoffset += child.item->width();
- if (ii != positionedItems.count() - 1)
- hoffset += spacing();
+ hoffset += spacing();
}
+ if (hoffset != 0)//If we positioned any items, undo the extra spacing from the last item
+ hoffset -= spacing();
contentSize->setWidth(hoffset);
if (d->isLeftToRight())
@@ -421,11 +786,138 @@ void QSGRow::reportConflictingAnchors()
qmlInfo(this) << "Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row";
}
+/*!
+ \qmlclass Grid QSGGrid
+ \inqmlmodule QtQuick 2
+ \ingroup qml-positioning-elements
+ \brief The Grid item positions its children in a grid.
+ \inherits Item
+
+ The Grid item positions its child items so that they are
+ aligned in a grid and are not overlapping.
+
+ The grid positioner calculates a grid of rectangular cells of sufficient
+ size to hold all items, placing the items in the cells, from left to right
+ and top to bottom. Each item is positioned in the top-left corner of its
+ cell with position (0, 0).
+
+ A Grid defaults to four columns, and as many rows as are necessary to
+ fit all child items. The number of rows and columns can be constrained
+ by setting the \l rows and \l columns properties.
+
+ Spacing can be added between child items by setting the \l spacing
+ property. The amount of spacing applied will be the same in the
+ horizontal and vertical directions.
+
+ See \l{Using QML Positioner and Repeater Items} for more details about this item and other
+ related items.
+
+ \section1 Example Usage
+
+ The following example demonstrates this.
+
+ \image gridLayout_example.png
+
+ \snippet doc/src/snippets/declarative/grid/grid.qml document
+
+ \section1 Using Transitions
+
+ Transitions can be used to animate items that are added to, moved within,
+ or removed from a Grid item. The \l add and \l move properties can be set to
+ the transitions that will be applied when items are added to, removed from,
+ or re-positioned within a Grid item.
+
+ \section1 Limitations
+
+ Note that the positioner assumes that the x and y positions of its children
+ will not change. If you manually change the x or y properties in script, bind
+ the x or y properties, use anchors on a child of a positioner, or have the
+ width or height of a child depend on the position of a child, then the
+ positioner may exhibit strange behaviour. If you need to perform any of these
+ actions, consider positioning the items without the use of a Grid.
+
+ Items with a width or height of 0 will not be positioned.
+
+ \sa Flow, Row, Column, Positioner, {declarative/positioners}{Positioners example}
+*/
+/*!
+ \qmlproperty Transition QtQuick2::Grid::add
+
+ This property holds the transition to be applied when adding an
+ item to the positioner. The transition will only be applied to the
+ added item(s). Positioner transitions will only affect the
+ position (x, y) of items.
+
+ For a positioner, adding an item can mean that either the object
+ has been created or reparented, and thus is now a child or the
+ positioner, or that the object has had its opacity increased from
+ zero, and thus is now visible.
+
+ \sa move
+*/
+/*!
+ \qmlproperty Transition QtQuick2::Grid::move
+
+ This property holds the transition to be applied when moving an
+ item within the positioner. Positioner transitions will only affect
+ the position (x, y) of items.
+
+ This transition can be performed when other items are added or removed
+ from the positioner, or when items resize themselves.
+
+ \qml
+ Grid {
+ move: Transition {
+ NumberAnimation {
+ properties: "x,y"
+ duration: 1000
+ }
+ }
+ }
+ \endqml
+
+ \sa add, {declarative/positioners}{Positioners example}
+*/
+/*!
+ \qmlproperty int QtQuick2::Grid::spacing
+
+ The spacing is the amount in pixels left empty between adjacent
+ items. The default spacing is 0.
+
+ The below example places a Grid containing a red, a blue and a
+ green rectangle on a gray background. The area the grid positioner
+ occupies is colored white. The positioner on the left has the
+ no spacing (the default), and the positioner on the right has
+ a spacing of 6.
+
+ \inlineimage qml-grid-no-spacing.png
+ \inlineimage qml-grid-spacing.png
+
+ \sa rows, columns
+*/
QSGGrid::QSGGrid(QSGItem *parent) :
- QSGBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_flow(LeftToRight)
+ QSGBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_rowSpacing(-1), m_columnSpacing(-1), m_flow(LeftToRight)
{
}
+/*!
+ \qmlproperty int QtQuick2::Grid::columns
+
+ This property holds the number of columns in the grid. The default
+ number of columns is 4.
+
+ If the grid does not have enough items to fill the specified
+ number of columns, some columns will be of zero width.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::Grid::rows
+ This property holds the number of rows in the grid.
+
+ If the grid does not have enough items to fill the specified
+ number of rows, some rows will be of zero width.
+*/
+
void QSGGrid::setColumns(const int columns)
{
if (columns == m_columns)
@@ -444,6 +936,19 @@ void QSGGrid::setRows(const int rows)
emit rowsChanged();
}
+/*!
+ \qmlproperty enumeration QtQuick2::Grid::flow
+ This property holds the flow of the layout.
+
+ Possible values are:
+
+ \list
+ \o Grid.LeftToRight (default) - Items are positioned next to
+ each other in the \l layoutDirection, then wrapped to the next line.
+ \o Grid.TopToBottom - Items are positioned next to each
+ other from top to bottom, then wrapped to the next column.
+ \endlist
+*/
QSGGrid::Flow QSGGrid::flow() const
{
return m_flow;
@@ -458,6 +963,58 @@ void QSGGrid::setFlow(Flow flow)
}
}
+/*!
+ \qmlproperty int QtQuick2::Grid::rowSpacing
+
+ This property holds the spacing in pixels between rows.
+
+ \sa columnSpacing
+ \since QtQuick2.0
+*/
+void QSGGrid::setRowSpacing(const int rowSpacing)
+{
+ if (rowSpacing == m_rowSpacing)
+ return;
+ m_rowSpacing = rowSpacing;
+ prePositioning();
+ emit rowSpacingChanged();
+}
+
+/*!
+ \qmlproperty int QtQuick2::Grid::columnSpacing
+
+ This property holds the spacing in pixels between columns.
+
+ \sa rowSpacing
+ \since QtQuick2.0
+*/
+void QSGGrid::setColumnSpacing(const int columnSpacing)
+{
+ if (columnSpacing == m_columnSpacing)
+ return;
+ m_columnSpacing = columnSpacing;
+ prePositioning();
+ emit columnSpacingChanged();
+}
+
+/*!
+ \qmlproperty enumeration QtQuick2::Grid::layoutDirection
+
+ This property holds the layout direction of the layout.
+
+ Possible values are:
+
+ \list
+ \o Qt.LeftToRight (default) - Items are positioned from the top to bottom,
+ and left to right. The flow direction is dependent on the
+ \l Grid::flow property.
+ \o Qt.RightToLeft - Items are positioned from the top to bottom,
+ and right to left. The flow direction is dependent on the
+ \l Grid::flow property.
+ \endlist
+
+ \sa Flow::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example}
+*/
Qt::LayoutDirection QSGGrid::layoutDirection() const
{
return QSGBasePositionerPrivate::getLayoutDirection(this);
@@ -479,6 +1036,16 @@ void QSGGrid::setLayoutDirection(Qt::LayoutDirection layoutDirection)
}
}
+/*!
+ \qmlproperty enumeration QtQuick2::Grid::effectiveLayoutDirection
+ This property holds the effective layout direction of the grid positioner.
+
+ When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
+ the visual layout direction of the grid positioner will be mirrored. However, the
+ property \l {Grid::layoutDirection}{layoutDirection} will remain unchanged.
+
+ \sa Grid::layoutDirection, {LayoutMirroring}{LayoutMirroring}
+*/
Qt::LayoutDirection QSGGrid::effectiveLayoutDirection() const
{
return QSGBasePositionerPrivate::getEffectiveLayoutDirection(this);
@@ -550,17 +1117,25 @@ void QSGGrid::doPositioning(QSizeF *contentSize)
}
}
+ int columnSpacing = m_columnSpacing;
+ if (columnSpacing == -1)
+ columnSpacing = spacing();
+
+ int rowSpacing = m_rowSpacing;
+ if (rowSpacing == -1)
+ rowSpacing = spacing();
+
int widthSum = 0;
for (int j=0; j < maxColWidth.size(); j++){
if (j)
- widthSum += spacing();
+ widthSum += columnSpacing;
widthSum += maxColWidth[j];
}
int heightSum = 0;
for (int i=0; i < maxRowHeight.size(); i++){
if (i)
- heightSum += spacing();
+ heightSum += rowSpacing;
heightSum += maxRowHeight[i];
}
@@ -591,13 +1166,13 @@ void QSGGrid::doPositioning(QSizeF *contentSize)
if (m_flow == LeftToRight) {
if (d->isLeftToRight())
- xoffset += maxColWidth[curCol]+spacing();
+ xoffset += maxColWidth[curCol]+columnSpacing;
else
- xoffset -= maxColWidth[curCol]+spacing();
+ xoffset -= maxColWidth[curCol]+columnSpacing;
curCol++;
curCol%=c;
if (!curCol){
- yoffset += maxRowHeight[curRow]+spacing();
+ yoffset += maxRowHeight[curRow]+rowSpacing;
if (d->isLeftToRight())
xoffset = 0;
else
@@ -607,14 +1182,14 @@ void QSGGrid::doPositioning(QSizeF *contentSize)
break;
}
} else {
- yoffset+=maxRowHeight[curRow]+spacing();
+ yoffset+=maxRowHeight[curRow]+rowSpacing;
curRow++;
curRow%=r;
if (!curRow){
if (d->isLeftToRight())
- xoffset += maxColWidth[curCol]+spacing();
+ xoffset += maxColWidth[curCol]+columnSpacing;
else
- xoffset -= maxColWidth[curCol]+spacing();
+ xoffset -= maxColWidth[curCol]+columnSpacing;
yoffset=0;
curCol++;
if (curCol>=c)
@@ -641,6 +1216,105 @@ void QSGGrid::reportConflictingAnchors()
qmlInfo(this) << "Cannot specify anchors for items inside Grid";
}
+/*!
+ \qmlclass Flow QSGFlow
+ \inqmlmodule QtQuick 2
+ \ingroup qml-positioning-elements
+ \brief The Flow item arranges its children side by side, wrapping as necessary.
+ \inherits Item
+
+ The Flow item positions its child items like words on a page, wrapping them
+ to create rows or columns of items that do not overlap.
+
+ Spacing between items can be added using the \l spacing property.
+ Transitions can be used for cases where items managed by a Column are
+ added or moved. These are stored in the \l add and \l move properties
+ respectively.
+
+ See \l{Using QML Positioner and Repeater Items} for more details about this item and other
+ related items.
+
+ \section1 Example Usage
+
+ The following example positions \l Text items within a parent item using
+ a Flow item.
+
+ \image qml-flow-snippet.png
+
+ \snippet doc/src/snippets/declarative/flow.qml flow item
+
+ \section1 Using Transitions
+
+ Transitions can be used to animate items that are added to, moved within,
+ or removed from a Flow item. The \l add and \l move properties can be set to
+ the transitions that will be applied when items are added to, removed from,
+ or re-positioned within a Flow item.
+
+ The use of transitions with positioners is described in more detail in the
+ \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML
+ Positioner and Repeater Items} document.
+
+ \section1 Limitations
+
+ Note that the positioner assumes that the x and y positions of its children
+ will not change. If you manually change the x or y properties in script, bind
+ the x or y properties, use anchors on a child of a positioner, or have the
+ width or height of a child depend on the position of a child, then the
+ positioner may exhibit strange behaviour. If you need to perform any of these
+ actions, consider positioning the items without the use of a Flow.
+
+ Items with a width or height of 0 will not be positioned.
+
+ \sa Column, Row, Grid, Positioner, {declarative/positioners}{Positioners example}
+*/
+/*!
+ \qmlproperty Transition QtQuick2::Flow::add
+
+ This property holds the transition to be applied when adding an
+ item to the positioner. The transition will only be applied to the
+ added item(s). Positioner transitions will only affect the
+ position (x, y) of items.
+
+ For a positioner, adding an item can mean that either the object
+ has been created or reparented, and thus is now a child or the
+ positioner, or that the object has had its opacity increased from
+ zero, and thus is now visible.
+
+ \sa move
+*/
+/*!
+ \qmlproperty Transition QtQuick2::Flow::move
+
+ This property holds the transition to be applied when moving an
+ item within the positioner. Positioner transitions will only affect
+ the position (x, y) of items.
+
+ This transition can be performed when other items are added or removed
+ from the positioner, or when items resize themselves.
+
+ \qml
+ Flow {
+ id: positioner
+ move: Transition {
+ NumberAnimation {
+ properties: "x,y"
+ ease: "easeOutBounce"
+ }
+ }
+ }
+ \endqml
+
+ \sa add, {declarative/positioners}{Positioners example}
+*/
+/*!
+ \qmlproperty int QtQuick2::Flow::spacing
+
+ spacing is the amount in pixels left empty between each adjacent
+ item, and defaults to 0.
+
+ \sa Grid::spacing
+*/
+
class QSGFlowPrivate : public QSGBasePositionerPrivate
{
Q_DECLARE_PUBLIC(QSGFlow)
@@ -661,6 +1335,21 @@ QSGFlow::QSGFlow(QSGItem *parent)
d->addItemChangeListener(d, QSGItemPrivate::Geometry);
}
+/*!
+ \qmlproperty enumeration QtQuick2::Flow::flow
+ This property holds the flow of the layout.
+
+ Possible values are:
+
+ \list
+ \o Flow.LeftToRight (default) - Items are positioned next to
+ to each other according to the \l layoutDirection until the width of the Flow
+ is exceeded, then wrapped to the next line.
+ \o Flow.TopToBottom - Items are positioned next to each
+ other from top to bottom until the height of the Flow is exceeded,
+ then wrapped to the next column.
+ \endlist
+*/
QSGFlow::Flow QSGFlow::flow() const
{
Q_D(const QSGFlow);
@@ -677,6 +1366,25 @@ void QSGFlow::setFlow(Flow flow)
}
}
+/*!
+ \qmlproperty enumeration QtQuick2::Flow::layoutDirection
+
+ This property holds the layout direction of the layout.
+
+ Possible values are:
+
+ \list
+ \o Qt.LeftToRight (default) - Items are positioned from the top to bottom,
+ and left to right. The flow direction is dependent on the
+ \l Flow::flow property.
+ \o Qt.RightToLeft - Items are positioned from the top to bottom,
+ and right to left. The flow direction is dependent on the
+ \l Flow::flow property.
+ \endlist
+
+ \sa Grid::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example}
+*/
+
Qt::LayoutDirection QSGFlow::layoutDirection() const
{
Q_D(const QSGFlow);
@@ -694,6 +1402,17 @@ void QSGFlow::setLayoutDirection(Qt::LayoutDirection layoutDirection)
}
}
+/*!
+ \qmlproperty enumeration QtQuick2::Flow::effectiveLayoutDirection
+ This property holds the effective layout direction of the flow positioner.
+
+ When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
+ the visual layout direction of the grid positioner will be mirrored. However, the
+ property \l {Flow::layoutDirection}{layoutDirection} will remain unchanged.
+
+ \sa Flow::layoutDirection, {LayoutMirroring}{LayoutMirroring}
+*/
+
Qt::LayoutDirection QSGFlow::effectiveLayoutDirection() const
{
return QSGBasePositionerPrivate::getEffectiveLayoutDirection(this);
diff --git a/src/declarative/items/qsgpositioners_p.h b/src/declarative/items/qsgpositioners_p.h
index a23c9d446c..7200b6da8d 100644
--- a/src/declarative/items/qsgpositioners_p.h
+++ b/src/declarative/items/qsgpositioners_p.h
@@ -59,6 +59,37 @@ QT_MODULE(Declarative)
class QSGBasePositionerPrivate;
+class QSGPositionerAttached : public QObject
+{
+ Q_OBJECT
+
+public:
+ QSGPositionerAttached(QObject *parent);
+
+ Q_PROPERTY(int index READ index NOTIFY indexChanged)
+ Q_PROPERTY(bool isFirstItem READ isFirstItem NOTIFY isFirstItemChanged)
+ Q_PROPERTY(bool isLastItem READ isLastItem NOTIFY isLastItemChanged)
+
+ int index() const { return m_index; }
+ void setIndex(int index);
+
+ bool isFirstItem() const { return m_isFirstItem; }
+ void setIsFirstItem(bool isFirstItem);
+
+ bool isLastItem() const { return m_isLastItem; }
+ void setIsLastItem(bool isLastItem);
+
+Q_SIGNALS:
+ void indexChanged();
+ void isFirstItemChanged();
+ void isLastItemChanged();
+
+private:
+ int m_index;
+ bool m_isFirstItem;
+ bool m_isLastItem;
+};
+
class Q_DECLARATIVE_PRIVATE_EXPORT QSGBasePositioner : public QSGImplicitSizeItem
{
Q_OBJECT
@@ -80,6 +111,10 @@ public:
QDeclarativeTransition *add() const;
void setAdd(QDeclarativeTransition *);
+ static QSGPositionerAttached *qmlAttachedProperties(QObject *obj);
+
+ void updateAttachedProperties(QSGPositionerAttached *specificProperty = 0, QSGItem *specificPropertyOwner = 0) const;
+
protected:
QSGBasePositioner(QSGBasePositionerPrivate &dd, PositionerType at, QSGItem *parent);
virtual void componentComplete();
@@ -120,6 +155,7 @@ class Q_AUTOTEST_EXPORT QSGColumn : public QSGBasePositioner
Q_OBJECT
public:
QSGColumn(QSGItem *parent=0);
+
protected:
virtual void doPositioning(QSizeF *contentSize);
virtual void reportConflictingAnchors();
@@ -155,6 +191,8 @@ class Q_AUTOTEST_EXPORT QSGGrid : public QSGBasePositioner
Q_OBJECT
Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged)
+ Q_PROPERTY(int rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
+ Q_PROPERTY(int columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged)
Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged)
Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged)
@@ -168,6 +206,12 @@ public:
int columns() const {return m_columns;}
void setColumns(const int columns);
+ int rowSpacing() const { return m_rowSpacing; }
+ void setRowSpacing(int);
+
+ int columnSpacing() const { return m_columnSpacing; }
+ void setColumnSpacing(int);
+
Q_ENUMS(Flow)
enum Flow { LeftToRight, TopToBottom };
Flow flow() const;
@@ -183,6 +227,8 @@ Q_SIGNALS:
void flowChanged();
void layoutDirectionChanged();
void effectiveLayoutDirectionChanged();
+ void rowSpacingChanged();
+ void columnSpacingChanged();
protected:
virtual void doPositioning(QSizeF *contentSize);
@@ -191,6 +237,8 @@ protected:
private:
int m_rows;
int m_columns;
+ int m_rowSpacing;
+ int m_columnSpacing;
Flow m_flow;
Q_DISABLE_COPY(QSGGrid)
};
@@ -237,6 +285,9 @@ QML_DECLARE_TYPE(QSGRow)
QML_DECLARE_TYPE(QSGGrid)
QML_DECLARE_TYPE(QSGFlow)
+QML_DECLARE_TYPE(QSGBasePositioner)
+QML_DECLARE_TYPEINFO(QSGBasePositioner, QML_HAS_ATTACHED_PROPERTIES)
+
QT_END_HEADER
#endif // QSGPOSITIONERS_P_H
diff --git a/src/declarative/items/qsgrectangle.cpp b/src/declarative/items/qsgrectangle.cpp
index cb648e25d7..25f0eda5d5 100644
--- a/src/declarative/items/qsgrectangle.cpp
+++ b/src/declarative/items/qsgrectangle.cpp
@@ -52,6 +52,24 @@
QT_BEGIN_NAMESPACE
// XXX todo - should we change rectangle to draw entirely within its width/height?
+/*!
+ \internal
+ \class QSGPen
+ \brief The QSGPen class provides a pen used for drawing rectangle borders on a QSGView.
+
+ By default, the pen is invalid and nothing is drawn. You must either set a color (then the default
+ width is 1) or a width (then the default color is black).
+
+ A width of 1 indicates is a single-pixel line on the border of the item being painted.
+
+ Example:
+ \qml
+ Rectangle {
+ border.width: 2
+ border.color: "red"
+ }
+ \endqml
+*/
QSGPen::QSGPen(QObject *parent)
: QObject(parent)
@@ -63,7 +81,7 @@ QSGPen::QSGPen(QObject *parent)
}
qreal QSGPen::width() const
-{
+{
return m_width;
}
@@ -77,9 +95,9 @@ void QSGPen::setWidth(qreal w)
emit penChanged();
}
-QColor QSGPen::color() const
-{
- return m_color;
+QColor QSGPen::color() const
+{
+ return m_color;
}
void QSGPen::setColor(const QColor &c)
@@ -104,33 +122,53 @@ void QSGPen::setAligned(bool aligned)
}
bool QSGPen::isValid() const
-{
+{
return m_valid;
}
-QSGGradientStop::QSGGradientStop(QObject *parent)
+/*!
+ \qmlclass GradientStop QSGGradientStop
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-visual-elements
+ \brief The GradientStop item defines the color at a position in a Gradient.
+
+ \sa Gradient
+*/
+
+/*!
+ \qmlproperty real QtQuick2::GradientStop::position
+ \qmlproperty color QtQuick2::GradientStop::color
+
+ The position and color properties describe the color used at a given
+ position in a gradient, as represented by a gradient stop.
+
+ The default position is 0.0; the default color is black.
+
+ \sa Gradient
+*/
+QSGGradientStop::QSGGradientStop(QObject *parent)
: QObject(parent)
{
}
-qreal QSGGradientStop::position() const
+qreal QSGGradientStop::position() const
{
- return m_position;
+ return m_position;
}
-void QSGGradientStop::setPosition(qreal position)
+void QSGGradientStop::setPosition(qreal position)
{
- m_position = position; updateGradient();
+ m_position = position; updateGradient();
}
-QColor QSGGradientStop::color() const
+QColor QSGGradientStop::color() const
{
- return m_color;
+ return m_color;
}
-void QSGGradientStop::setColor(const QColor &color)
+void QSGGradientStop::setColor(const QColor &color)
{
- m_color = color; updateGradient();
+ m_color = color; updateGradient();
}
void QSGGradientStop::updateGradient()
@@ -139,19 +177,80 @@ void QSGGradientStop::updateGradient()
grad->doUpdate();
}
-QSGGradient::QSGGradient(QObject *parent)
-: QObject(parent), m_gradient(0)
+/*!
+ \qmlclass Gradient QSGGradient
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-visual-elements
+ \brief The Gradient item defines a gradient fill.
+
+ A gradient is defined by two or more colors, which will be blended seamlessly.
+
+ The colors are specified as a set of GradientStop child items, each of
+ which defines a position on the gradient from 0.0 to 1.0 and a color.
+ The position of each GradientStop is defined by setting its
+ \l{GradientStop::}{position} property; its color is defined using its
+ \l{GradientStop::}{color} property.
+
+ A gradient without any gradient stops is rendered as a solid white fill.
+
+ Note that this item is not a visual representation of a gradient. To display a
+ gradient, use a visual element (like \l Rectangle) which supports the use
+ of gradients.
+
+ \section1 Example Usage
+
+ \div {class="float-right"}
+ \inlineimage qml-gradient.png
+ \enddiv
+
+ The following example declares a \l Rectangle item with a gradient starting
+ with red, blending to yellow at one third of the height of the rectangle,
+ and ending with green:
+
+ \snippet doc/src/snippets/declarative/gradient.qml code
+
+ \clearfloat
+ \section1 Performance and Limitations
+
+ Calculating gradients can be computationally expensive compared to the use
+ of solid color fills or images. Consider using gradients for static items
+ in a user interface.
+
+ In Qt 4.7, only vertical, linear gradients can be applied to items. If you
+ need to apply different orientations of gradients, a combination of rotation
+ and clipping will need to be applied to the relevant items. This can
+ introduce additional performance requirements for your application.
+
+ The use of animations involving gradient stops may not give the desired
+ result. An alternative way to animate gradients is to use pre-generated
+ images or SVG drawings containing gradients.
+
+ \sa GradientStop
+*/
+
+/*!
+ \qmlproperty list<GradientStop> QtQuick2::Gradient::stops
+ \default
+
+ This property holds the gradient stops describing the gradient.
+
+ By default, this property contains an empty list.
+
+ To set the gradient stops, define them as children of the Gradient element.
+*/
+QSGGradient::QSGGradient(QObject *parent)
+: QObject(parent), m_gradient(0)
{
}
-QSGGradient::~QSGGradient()
+QSGGradient::~QSGGradient()
{
- delete m_gradient;
+ delete m_gradient;
}
-QDeclarativeListProperty<QSGGradientStop> QSGGradient::stops()
+QDeclarativeListProperty<QSGGradientStop> QSGGradient::stops()
{
- return QDeclarativeListProperty<QSGGradientStop>(this, m_stops);
+ return QDeclarativeListProperty<QSGGradientStop>(this, m_stops);
}
const QGradient *QSGGradient::gradient() const
@@ -177,6 +276,51 @@ void QSGGradient::doUpdate()
int QSGRectanglePrivate::doUpdateSlotIdx = -1;
+/*!
+ \qmlclass Rectangle QSGRectangle
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-visual-elements
+ \brief The Rectangle item provides a filled rectangle with an optional border.
+ \inherits Item
+
+ Rectangle items are used to fill areas with solid color or gradients, and are
+ often used to hold other items.
+
+ \section1 Appearance
+
+ Each Rectangle item is painted using either a solid fill color, specified using
+ the \l color property, or a gradient, defined using a Gradient element and set
+ using the \l gradient property. If both a color and a gradient are specified,
+ the gradient is used.
+
+ You can add an optional border to a rectangle with its own color and thickness
+ by settting the \l border.color and \l border.width properties.
+
+ You can also create rounded rectangles using the \l radius property. Since this
+ introduces curved edges to the corners of a rectangle, it may be appropriate to
+ set the \l smooth property to improve its appearance.
+
+ \section1 Example Usage
+
+ \div {class="float-right"}
+ \inlineimage declarative-rect.png
+ \enddiv
+
+ The following example shows the effects of some of the common properties on a
+ Rectangle item, which in this case is used to create a square:
+
+ \snippet doc/src/snippets/declarative/rectangle/rectangle.qml document
+
+ \clearfloat
+ \section1 Performance
+
+ Using the \l smooth property improves the appearance of a rounded rectangle at
+ the cost of rendering performance. You should consider unsetting this property
+ for rectangles in motion, and only set it when they are stationary.
+
+ \sa Image
+*/
+
QSGRectangle::QSGRectangle(QSGItem *parent)
: QSGItem(*(new QSGRectanglePrivate), parent)
{
@@ -186,17 +330,77 @@ QSGRectangle::QSGRectangle(QSGItem *parent)
void QSGRectangle::doUpdate()
{
Q_D(QSGRectangle);
- const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
- d->setPaintMargin((pw+1)/2);
+ qreal penMargin = 0;
+ qreal penOffset = 0;
+ if (d->pen && d->pen->isValid()) {
+ if (d->pen->aligned()) {
+ const int pw = qRound(d->pen->width());
+ penMargin = qreal(0.5) * pw;
+ penOffset = (pw & 1) * qreal(0.5);
+ } else {
+ penMargin = qreal(0.5) * d->pen->width();
+ }
+ }
+ if (penMargin != d->penMargin || penOffset != d->penOffset) {
+ d->penMargin = penMargin;
+ d->penOffset = penOffset;
+ d->dirty(QSGItemPrivate::Size); // update clip
+ }
update();
}
+/*!
+ \qmlproperty int QtQuick2::Rectangle::border.width
+ \qmlproperty color QtQuick2::Rectangle::border.color
+
+ The width and color used to draw the border of the rectangle.
+
+ A width of 1 creates a thin line. For no line, use a width of 0 or a transparent color.
+
+ \note The width of the rectangle's border does not affect the geometry of the
+ rectangle itself or its position relative to other items if anchors are used.
+
+ If \c border.width is an odd number, the rectangle is painted at a half-pixel offset to retain
+ border smoothness. Also, the border is rendered evenly on either side of the
+ rectangle's boundaries, and the spare pixel is rendered to the right and below the
+ rectangle (as documented for QRect rendering). This can cause unintended effects if
+ \c border.width is 1 and the rectangle is \l{Item::clip}{clipped} by a parent item:
+
+ \div {class="float-right"}
+ \inlineimage rect-border-width.png
+ \enddiv
+
+ \snippet doc/src/snippets/declarative/rectangle/rect-border-width.qml 0
+
+ \clearfloat
+ Here, the innermost rectangle's border is clipped on the bottom and right edges by its
+ parent. To avoid this, the border width can be set to two instead of one.
+*/
QSGPen *QSGRectangle::border()
{
Q_D(QSGRectangle);
return d->getPen();
}
+/*!
+ \qmlproperty Gradient QtQuick2::Rectangle::gradient
+
+ The gradient to use to fill the rectangle.
+
+ This property allows for the construction of simple vertical gradients.
+ Other gradients may by formed by adding rotation to the rectangle.
+
+ \div {class="float-left"}
+ \inlineimage declarative-rect_gradient.png
+ \enddiv
+
+ \snippet doc/src/snippets/declarative/rectangle/rectangle-gradient.qml rectangles
+ \clearfloat
+
+ If both a gradient and a color are specified, the gradient will be used.
+
+ \sa Gradient, color
+*/
QSGGradient *QSGRectangle::gradient() const
{
Q_D(const QSGRectangle);
@@ -221,6 +425,14 @@ void QSGRectangle::setGradient(QSGGradient *gradient)
update();
}
+/*!
+ \qmlproperty real QtQuick2::Rectangle::radius
+ This property holds the corner radius used to draw a rounded rectangle.
+
+ If radius is non-zero, the rectangle will be painted as a rounded rectangle, otherwise it will be
+ painted as a normal rectangle. The same radius is used by all 4 corners; there is currently
+ no way to specify different radii for different corners.
+*/
qreal QSGRectangle::radius() const
{
Q_D(const QSGRectangle);
@@ -238,6 +450,26 @@ void QSGRectangle::setRadius(qreal radius)
emit radiusChanged();
}
+/*!
+ \qmlproperty color QtQuick2::Rectangle::color
+ This property holds the color used to fill the rectangle.
+
+ The default color is white.
+
+ \div {class="float-right"}
+ \inlineimage rect-color.png
+ \enddiv
+
+ The following example shows rectangles with colors specified
+ using hexadecimal and named color notation:
+
+ \snippet doc/src/snippets/declarative/rectangle/rectangle-colors.qml rectangles
+
+ \clearfloat
+ If both a gradient and a color are specified, the gradient will be used.
+
+ \sa gradient
+*/
QColor QSGRectangle::color() const
{
Q_D(const QSGRectangle);
@@ -297,11 +529,27 @@ QSGNode *QSGRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *da
return rectangle;
}
+/*!
+ \qmlproperty bool QtQuick2::Rectangle::smooth
+
+ Set this property if you want the item to be smoothly scaled or
+ transformed. Smooth filtering gives better visual quality, but is slower. If
+ the item is displayed at its natural size, this property has no visual or
+ performance effect.
+
+ \note Generally scaling artifacts are only visible if the item is stationary on
+ the screen. A common pattern when animating an item is to disable smooth
+ filtering at the beginning of the animation and reenable it at the conclusion.
+
+ \image rect-smooth.png
+ On this image, smooth is turned off on the top half and on on the bottom half.
+*/
QRectF QSGRectangle::boundingRect() const
{
Q_D(const QSGRectangle);
- return QRectF(-d->paintmargin, -d->paintmargin, width()+d->paintmargin*2, height()+d->paintmargin*2);
+ return QRectF(d->penOffset - d->penMargin, d->penOffset - d->penMargin,
+ d->width + 2 * d->penMargin, d->height + 2 * d->penMargin);
}
QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgrectangle_p_p.h b/src/declarative/items/qsgrectangle_p_p.h
index 6e1beb7bdb..234a029aaf 100644
--- a/src/declarative/items/qsgrectangle_p_p.h
+++ b/src/declarative/items/qsgrectangle_p_p.h
@@ -66,7 +66,7 @@ class QSGRectanglePrivate : public QSGItemPrivate
public:
QSGRectanglePrivate() :
- color(Qt::white), gradient(0), pen(0), radius(0), paintmargin(0)
+ color(Qt::white), gradient(0), pen(0), radius(0), penMargin(0), penOffset(0)
{
}
@@ -79,7 +79,8 @@ public:
QSGGradient *gradient;
QSGPen *pen;
qreal radius;
- qreal paintmargin;
+ qreal penMargin;
+ qreal penOffset;
static int doUpdateSlotIdx;
QSGPen *getPen() {
@@ -95,13 +96,6 @@ public:
}
return pen;
}
-
- void setPaintMargin(qreal margin)
- {
- if (margin == paintmargin)
- return;
- paintmargin = margin;
- }
};
QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgrepeater.cpp b/src/declarative/items/qsgrepeater.cpp
index cdada2d59d..bdb3a3d98a 100644
--- a/src/declarative/items/qsgrepeater.cpp
+++ b/src/declarative/items/qsgrepeater.cpp
@@ -60,6 +60,90 @@ QSGRepeaterPrivate::~QSGRepeaterPrivate()
delete model;
}
+/*!
+ \qmlclass Repeater QSGRepeater
+ \inqmlmodule QtQuick 2
+ \ingroup qml-utility-elements
+ \inherits Item
+
+ \brief The Repeater element allows you to repeat an Item-based component using a model.
+
+ The Repeater element is used to create a large number of
+ similar items. Like other view elements, a Repeater has a \l model and a \l delegate:
+ for each entry in the model, the delegate is instantiated
+ in a context seeded with data from the model. A Repeater item is usually
+ enclosed in a positioner element such as \l Row or \l Column to visually
+ position the multiple delegate items created by the Repeater.
+
+ The following Repeater creates three instances of a \l Rectangle item within
+ a \l Row:
+
+ \snippet doc/src/snippets/declarative/repeaters/repeater.qml import
+ \codeline
+ \snippet doc/src/snippets/declarative/repeaters/repeater.qml simple
+
+ \image repeater-simple.png
+
+ A Repeater's \l model can be any of the supported \l {qmlmodels}{data models}.
+ Additionally, like delegates for other views, a Repeater delegate can access
+ its index within the repeater, as well as the model data relevant to the
+ delegate. See the \l delegate property documentation for details.
+
+ Items instantiated by the Repeater are inserted, in order, as
+ children of the Repeater's parent. The insertion starts immediately after
+ the repeater's position in its parent stacking list. This allows
+ a Repeater to be used inside a layout. For example, the following Repeater's
+ items are stacked between a red rectangle and a blue rectangle:
+
+ \snippet doc/src/snippets/declarative/repeaters/repeater.qml layout
+
+ \image repeater.png
+
+
+ \note A Repeater item owns all items it instantiates. Removing or dynamically destroying
+ an item created by a Repeater results in unpredictable behavior.
+
+
+ \section2 Considerations when using Repeater
+
+ The Repeater element creates all of its delegate items when the repeater is first
+ created. This can be inefficient if there are a large number of delegate items and
+ not all of the items are required to be visible at the same time. If this is the case,
+ consider using other view elements like ListView (which only creates delegate items
+ when they are scrolled into view) or use the \l {Dynamic Object Creation} methods to
+ create items as they are required.
+
+ Also, note that Repeater is \l {Item}-based, and can only repeat \l {Item}-derived objects.
+ For example, it cannot be used to repeat QtObjects:
+ \badcode
+ Item {
+ //XXX does not work! Can't repeat QtObject as it doesn't derive from Item.
+ Repeater {
+ model: 10
+ QtObject {}
+ }
+ }
+ \endcode
+ */
+
+/*!
+ \qmlsignal QtQuick2::Repeater::onItemAdded(int index, Item item)
+
+ This handler is called when an item is added to the repeater. The \a index
+ parameter holds the index at which the item has been inserted within the
+ repeater, and the \a item parameter holds the \l Item that has been added.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Repeater::onItemRemoved(int index, Item item)
+
+ This handler is called when an item is removed from the repeater. The \a index
+ parameter holds the index at which the item was removed from the repeater,
+ and the \a item parameter holds the \l Item that was removed.
+
+ Do not keep a reference to \a item if it was created by this repeater, as
+ in these cases it will be deleted shortly after the handler is called.
+*/
QSGRepeater::QSGRepeater(QSGItem *parent)
: QSGItem(*(new QSGRepeaterPrivate), parent)
{
@@ -69,6 +153,24 @@ QSGRepeater::~QSGRepeater()
{
}
+/*!
+ \qmlproperty any QtQuick2::Repeater::model
+
+ The model providing data for the repeater.
+
+ This property can be set to any of the supported \l {qmlmodels}{data models}:
+
+ \list
+ \o A number that indicates the number of delegates to be created by the repeater
+ \o A model (e.g. a ListModel item, or a QAbstractItemModel subclass)
+ \o A string list
+ \o An object list
+ \endlist
+
+ The type of model affects the properties that are exposed to the \l delegate.
+
+ \sa {qmlmodels}{Data Models}
+*/
QVariant QSGRepeater::model() const
{
Q_D(const QSGRepeater);
@@ -124,6 +226,39 @@ void QSGRepeater::setModel(const QVariant &model)
emit countChanged();
}
+/*!
+ \qmlproperty Component QtQuick2::Repeater::delegate
+ \default
+
+ The delegate provides a template defining each item instantiated by the repeater.
+
+ Delegates are exposed to a read-only \c index property that indicates the index
+ of the delegate within the repeater. For example, the following \l Text delegate
+ displays the index of each repeated item:
+
+ \table
+ \row
+ \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml index
+ \o \image repeater-index.png
+ \endtable
+
+ If the \l model is a \l{QStringList-based model}{string list} or
+ \l{QObjectList-based model}{object list}, the delegate is also exposed to
+ a read-only \c modelData property that holds the string or object data. For
+ example:
+
+ \table
+ \row
+ \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml modeldata
+ \o \image repeater-modeldata.png
+ \endtable
+
+ If the \l model is a model object (such as a \l ListModel) the delegate
+ can access all model roles as named properties, in the same way that delegates
+ do for view classes like ListView.
+
+ \sa {QML Data Models}
+ */
QDeclarativeComponent *QSGRepeater::delegate() const
{
Q_D(const QSGRepeater);
@@ -153,6 +288,11 @@ void QSGRepeater::setDelegate(QDeclarativeComponent *delegate)
}
}
+/*!
+ \qmlproperty int QtQuick2::Repeater::count
+
+ This property holds the number of items in the repeater.
+*/
int QSGRepeater::count() const
{
Q_D(const QSGRepeater);
@@ -161,6 +301,12 @@ int QSGRepeater::count() const
return 0;
}
+/*!
+ \qmlmethod Item QtQuick2::Repeater::itemAt(index)
+
+ Returns the \l Item that has been created at the given \a index, or \c null
+ if no item exists at \a index.
+*/
QSGItem *QSGRepeater::itemAt(int index) const
{
Q_D(const QSGRepeater);
diff --git a/src/declarative/items/qsgscalegrid.cpp b/src/declarative/items/qsgscalegrid.cpp
index a9eb080d86..e1b76c4158 100644
--- a/src/declarative/items/qsgscalegrid.cpp
+++ b/src/declarative/items/qsgscalegrid.cpp
@@ -164,6 +164,8 @@ QSGGridScaledImage::QSGGridScaledImage(QIODevice *data)
_l = l; _r = r; _t = t; _b = b;
_pix = imgFile;
+ if (_pix.startsWith(QLatin1Char('"')) && _pix.endsWith(QLatin1Char('"')))
+ _pix = _pix.mid(1, _pix.size() - 2); // remove leading/trailing quotes.
}
QSGBorderImage::TileMode QSGGridScaledImage::stringToRule(const QString &s)
diff --git a/src/declarative/items/qsgshadereffectsource.cpp b/src/declarative/items/qsgshadereffectsource.cpp
index f9dbfe58ec..ef6dd64532 100644
--- a/src/declarative/items/qsgshadereffectsource.cpp
+++ b/src/declarative/items/qsgshadereffectsource.cpp
@@ -619,8 +619,12 @@ void QSGShaderEffectSource::setSourceRect(const QRectF &rect)
/*!
\qmlproperty size ShaderEffectSource::textureSize
- This property holds the size of the texture. If it is empty, which is the
- default, the size of the source rectangle is used.
+ This property holds the requested size of the texture. If it is empty,
+ which is the default, the size of the source rectangle is used.
+
+ \note Some platforms have a limit on how small framebuffer objects can be,
+ which means the actual texture size might be larger than the requested
+ size.
*/
QSize QSGShaderEffectSource::textureSize() const
@@ -812,7 +816,7 @@ static void get_wrap_mode(QSGShaderEffectSource::WrapMode mode, QSGTexture::Wrap
QSGNode *QSGShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
- if (!m_sourceItem) {
+ if (!m_sourceItem || m_sourceItem->width() == 0 || m_sourceItem->height() == 0) {
delete oldNode;
return 0;
}
@@ -820,13 +824,22 @@ QSGNode *QSGShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNod
QSGShaderEffectTexture *tex = qobject_cast<QSGShaderEffectTexture *>(m_texture);
tex->setLive(m_live);
tex->setItem(QSGItemPrivate::get(m_sourceItem)->itemNode());
- QRectF sourceRect = m_sourceRect.isNull()
+ QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0
? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
: m_sourceRect;
tex->setRect(sourceRect);
QSize textureSize = m_textureSize.isEmpty()
? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())))
: m_textureSize;
+ Q_ASSERT(!textureSize.isEmpty());
+ QSGItemPrivate *d = static_cast<QSGItemPrivate *>(QObjectPrivate::get(this));
+ const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize();
+ // Keep power-of-two by doubling the size.
+ while (textureSize.width() < minTextureSize.width())
+ textureSize.rwidth() *= 2;
+ while (textureSize.height() < minTextureSize.height())
+ textureSize.rheight() *= 2;
+
tex->setSize(textureSize);
tex->setRecursive(m_recursive);
tex->setFormat(GLenum(m_format));
diff --git a/src/declarative/items/qsgtext.cpp b/src/declarative/items/qsgtext.cpp
index cc6a4b406d..bac0be969b 100644
--- a/src/declarative/items/qsgtext.cpp
+++ b/src/declarative/items/qsgtext.cpp
@@ -91,6 +91,7 @@ private:
static QSet<QUrl> errors;
};
+DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
QString QSGTextPrivate::elideChar = QString(0x2026);
@@ -106,7 +107,12 @@ QSGTextPrivate::QSGTextPrivate()
richText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false),
requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
layoutTextElided(false), richTextAsImage(false), textureImageCacheDirty(false), naturalWidth(0),
- doc(0), layoutThread(0), nodeType(NodeIsNull)
+ doc(0), nodeType(NodeIsNull)
+
+#if defined(Q_OS_MAC)
+ , layoutThread(0)
+#endif
+
{
cacheAllTextAsImage = enableImageCache();
}
@@ -290,7 +296,9 @@ void QSGTextPrivate::updateSize()
int dy = q->height();
QSize size(0, 0);
+#if defined(Q_OS_MAC)
layoutThread = QThread::currentThread();
+#endif
//setup instance of QTextLayout for all cases other than richtext
if (!richText) {
@@ -312,6 +320,8 @@ void QSGTextPrivate::updateSize()
QTextOption option;
option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
option.setWrapMode(QTextOption::WrapMode(wrapMode));
+ if (!cacheAllTextAsImage && !richTextAsImage && !qmlDisableDistanceField())
+ option.setUseDesignMetrics(true);
doc->setDefaultTextOption(option);
if (requireImplicitWidth && q->widthValid()) {
doc->setTextWidth(-1);
@@ -375,6 +385,8 @@ QRect QSGTextPrivate::setupTextLayout()
QTextOption textOption = layout.textOption();
textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
+ if (!cacheAllTextAsImage && !richTextAsImage && !qmlDisableDistanceField())
+ textOption.setUseDesignMetrics(true);
layout.setTextOption(textOption);
bool elideText = false;
@@ -574,7 +586,7 @@ void QSGTextPrivate::invalidateImageCache()
{
Q_Q(QSGText);
- if(richTextAsImage || cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && style != QSGText::Normal)){//If actually using the image cache
+ if(richTextAsImage || cacheAllTextAsImage || (qmlDisableDistanceField() && style != QSGText::Normal)){//If actually using the image cache
if (imageCacheDirty)
return;
@@ -694,6 +706,49 @@ QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleS
return img;
}
+/*!
+ \qmlclass Text QSGText
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-visual-elements
+ \brief The Text item allows you to add formatted text to a scene.
+ \inherits Item
+
+ Text items can display both plain and rich text. For example, red text with
+ a specific font and size can be defined like this:
+
+ \qml
+ Text {
+ text: "Hello World!"
+ font.family: "Helvetica"
+ font.pointSize: 24
+ color: "red"
+ }
+ \endqml
+
+ Rich text is defined using HTML-style markup:
+
+ \qml
+ Text {
+ text: "<b>Hello</b> <i>World!</i>"
+ }
+ \endqml
+
+ \image declarative-text.png
+
+ If height and width are not explicitly set, Text will attempt to determine how
+ much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
+ prefer width to height (all text will be placed on a single line).
+
+ The \l elide property can alternatively be used to fit a single line of
+ plain text to a set width.
+
+ Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
+ HTML img tags that load remote images, the text is reloaded.
+
+ Text provides read-only text. For editable text, see \l TextEdit.
+
+ \sa {declarative/text/fonts}{Fonts example}
+*/
QSGText::QSGText(QSGItem *parent)
: QSGImplicitSizeItem(*(new QSGTextPrivate), parent)
{
@@ -705,6 +760,149 @@ QSGText::~QSGText()
{
}
+/*!
+ \qmlproperty bool QtQuick2::Text::clip
+ This property holds whether the text is clipped.
+
+ Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
+
+ If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::Text::smooth
+
+ This property holds whether the text is smoothly scaled or transformed.
+
+ Smooth filtering gives better visual quality, but is slower. If
+ the item is displayed at its natural size, this property has no visual or
+ performance effect.
+
+ \note Generally scaling artifacts are only visible if the item is stationary on
+ the screen. A common pattern when animating an item is to disable smooth
+ filtering at the beginning of the animation and reenable it at the conclusion.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Text::onLinkActivated(string link)
+
+ This handler is called when the user clicks on a link embedded in the text.
+ The link must be in rich text or HTML format and the
+ \a link string provides access to the particular link.
+
+ \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0
+
+ The example code will display the text
+ "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}."
+
+ Clicking on the highlighted link will output
+ \tt{http://qt.nokia.com link activated} to the console.
+*/
+
+/*!
+ \qmlproperty string QtQuick2::Text::font.family
+
+ Sets the family name of the font.
+
+ The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
+ If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
+ If the family isn't available a family will be set using the font matching algorithm.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::Text::font.bold
+
+ Sets whether the font weight is bold.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick2::Text::font.weight
+
+ Sets the font's weight.
+
+ The weight can be one of:
+ \list
+ \o Font.Light
+ \o Font.Normal - the default
+ \o Font.DemiBold
+ \o Font.Bold
+ \o Font.Black
+ \endlist
+
+ \qml
+ Text { text: "Hello"; font.weight: Font.DemiBold }
+ \endqml
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::Text::font.italic
+
+ Sets whether the font has an italic style.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::Text::font.underline
+
+ Sets whether the text is underlined.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::Text::font.strikeout
+
+ Sets whether the font has a strikeout style.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Text::font.pointSize
+
+ Sets the font size in points. The point size must be greater than zero.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::Text::font.pixelSize
+
+ Sets the font size in pixels.
+
+ Using this function makes the font device dependent.
+ Use \c pointSize to set the size of the font in a device independent manner.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Text::font.letterSpacing
+
+ Sets the letter spacing for the font.
+
+ Letter spacing changes the default spacing between individual letters in the font.
+ A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::Text::font.wordSpacing
+
+ Sets the word spacing for the font.
+
+ Word spacing changes the default spacing between individual words.
+ A positive value increases the word spacing by a corresponding amount of pixels,
+ while a negative value decreases the inter-word spacing accordingly.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick2::Text::font.capitalization
+
+ Sets the capitalization for the text.
+
+ \list
+ \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
+ \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
+ \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
+ \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
+ \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
+ \endlist
+
+ \qml
+ Text { text: "Hello"; font.capitalization: Font.AllLowercase }
+ \endqml
+*/
QFont QSGText::font() const
{
Q_D(const QSGText);
@@ -720,8 +918,6 @@ void QSGText::setFont(const QFont &font)
d->sourceFont = font;
QFont oldFont = d->font;
d->font = font;
- if (QSGDistanceFieldGlyphCache::distanceFieldEnabled())
- d->font.setHintingPreference(QFont::PreferNoHinting);
if (d->font.pointSizeF() != -1) {
// 0.5pt resolution
@@ -735,6 +931,14 @@ void QSGText::setFont(const QFont &font)
emit fontChanged(d->sourceFont);
}
+/*!
+ \qmlproperty string QtQuick2::Text::text
+
+ The text to display. Text supports both plain and rich text strings.
+
+ The item will try to automatically determine whether the text should
+ be treated as rich text. This determination is made using Qt::mightBeRichText().
+*/
QString QSGText::text() const
{
Q_D(const QSGText);
@@ -764,6 +968,27 @@ void QSGText::setText(const QString &n)
emit textChanged(d->text);
}
+/*!
+ \qmlproperty color QtQuick2::Text::color
+
+ The text color.
+
+ An example of green text defined using hexadecimal notation:
+ \qml
+ Text {
+ color: "#00FF00"
+ text: "green text"
+ }
+ \endqml
+
+ An example of steel blue text defined using an SVG color name:
+ \qml
+ Text {
+ color: "steelblue"
+ text: "blue text"
+ }
+ \endqml
+*/
QColor QSGText::color() const
{
Q_D(const QSGText);
@@ -780,7 +1005,30 @@ void QSGText::setColor(const QColor &color)
d->invalidateImageCache();
emit colorChanged(d->color);
}
+/*!
+ \qmlproperty enumeration QtQuick2::Text::style
+
+ Set an additional text style.
+
+ Supported text styles are:
+ \list
+ \o Text.Normal - the default
+ \o Text.Outline
+ \o Text.Raised
+ \o Text.Sunken
+ \endlist
+
+ \qml
+ Row {
+ Text { font.pointSize: 24; text: "Normal" }
+ Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
+ Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
+ Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
+ }
+ \endqml
+ \image declarative-textstyle.png
+*/
QSGText::TextStyle QSGText::style() const
{
Q_D(const QSGText);
@@ -801,6 +1049,21 @@ void QSGText::setStyle(QSGText::TextStyle style)
emit styleChanged(d->style);
}
+/*!
+ \qmlproperty color QtQuick2::Text::styleColor
+
+ Defines the secondary color used by text styles.
+
+ \c styleColor is used as the outline color for outlined text, and as the
+ shadow color for raised or sunken text. If no style has been set, it is not
+ used at all.
+
+ \qml
+ Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
+ \endqml
+
+ \sa style
+ */
QColor QSGText::styleColor() const
{
Q_D(const QSGText);
@@ -818,6 +1081,30 @@ void QSGText::setStyleColor(const QColor &color)
emit styleColorChanged(d->styleColor);
}
+/*!
+ \qmlproperty enumeration QtQuick2::Text::horizontalAlignment
+ \qmlproperty enumeration QtQuick2::Text::verticalAlignment
+ \qmlproperty enumeration QtQuick2::Text::effectiveHorizontalAlignment
+
+ Sets the horizontal and vertical alignment of the text within the Text items
+ width and height. By default, the text is vertically aligned to the top. Horizontal
+ alignment follows the natural alignment of the text, for example text that is read
+ from left to right will be aligned to the left.
+
+ The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
+ \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
+ and \c Text.AlignVCenter.
+
+ Note that for a single line of text, the size of the text is the area of the text. In this common case,
+ all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
+ need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
+ that of the parent.
+
+ When using the attached property LayoutMirroring::enabled to mirror application
+ layouts, the horizontal alignment of text will also be mirrored. However, the property
+ \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
+ of Text, use the read-only property \c effectiveHorizontalAlignment.
+*/
QSGText::HAlignment QSGText::hAlign() const
{
Q_D(const QSGText);
@@ -917,6 +1204,19 @@ void QSGText::setVAlign(VAlignment align)
emit verticalAlignmentChanged(align);
}
+/*!
+ \qmlproperty enumeration QtQuick2::Text::wrapMode
+
+ Set this property to wrap the text to the Text item's width. The text will only
+ wrap if an explicit width has been set. wrapMode can be one of:
+
+ \list
+ \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width.
+ \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width.
+ \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
+ \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
+ \endlist
+*/
QSGText::WrapMode QSGText::wrapMode() const
{
Q_D(const QSGText);
@@ -935,18 +1235,48 @@ void QSGText::setWrapMode(WrapMode mode)
emit wrapModeChanged();
}
+/*!
+ \qmlproperty int QtQuick2::Text::lineCount
+
+ Returns the number of lines visible in the text item.
+
+ This property is not supported for rich text.
+
+ \sa maximumLineCount
+*/
int QSGText::lineCount() const
{
Q_D(const QSGText);
return d->lineCount;
}
+/*!
+ \qmlproperty bool QtQuick2::Text::truncated
+
+ Returns true if the text has been truncated due to \l maximumLineCount
+ or \l elide.
+
+ This property is not supported for rich text.
+
+ \sa maximumLineCount, elide
+*/
bool QSGText::truncated() const
{
Q_D(const QSGText);
return d->truncated;
}
+/*!
+ \qmlproperty int QtQuick2::Text::maximumLineCount
+
+ Set this property to limit the number of lines that the text item will show.
+ If elide is set to Text.ElideRight, the text will be elided appropriately.
+ By default, this is the value of the largest possible integer.
+
+ This property is not supported for rich text.
+
+ \sa lineCount, elide
+*/
int QSGText::maximumLineCount() const
{
Q_D(const QSGText);
@@ -976,6 +1306,61 @@ void QSGText::resetMaximumLineCount()
}
}
+/*!
+ \qmlproperty enumeration QtQuick2::Text::textFormat
+
+ The way the text property should be displayed.
+
+ Supported text formats are:
+
+ \list
+ \o Text.AutoText (default)
+ \o Text.PlainText
+ \o Text.RichText
+ \o Text.StyledText
+ \endlist
+
+ If the text format is \c Text.AutoText the text element
+ will automatically determine whether the text should be treated as
+ rich text. This determination is made using Qt::mightBeRichText().
+
+ Text.StyledText is an optimized format supporting some basic text
+ styling markup, in the style of html 3.2:
+
+ \code
+ <font size="4" color="#ff0000">font size and color</font>
+ <b>bold</b>
+ <i>italic</i>
+ <br>
+ &gt; &lt; &amp;
+ \endcode
+
+ \c Text.StyledText parser is strict, requiring tags to be correctly nested.
+
+ \table
+ \row
+ \o
+ \qml
+Column {
+ Text {
+ font.pointSize: 24
+ text: "<b>Hello</b> <i>World!</i>"
+ }
+ Text {
+ font.pointSize: 24
+ textFormat: Text.RichText
+ text: "<b>Hello</b> <i>World!</i>"
+ }
+ Text {
+ font.pointSize: 24
+ textFormat: Text.PlainText
+ text: "<b>Hello</b> <i>World!</i>"
+ }
+}
+ \endqml
+ \o \image declarative-textformat.png
+ \endtable
+*/
QSGText::TextFormat QSGText::textFormat() const
{
Q_D(const QSGText);
@@ -1002,6 +1387,31 @@ void QSGText::setTextFormat(TextFormat format)
emit textFormatChanged(d->format);
}
+/*!
+ \qmlproperty enumeration QtQuick2::Text::elide
+
+ Set this property to elide parts of the text fit to the Text item's width.
+ The text will only elide if an explicit width has been set.
+
+ This property cannot be used with rich text.
+
+ Eliding can be:
+ \list
+ \o Text.ElideNone - the default
+ \o Text.ElideLeft
+ \o Text.ElideMiddle
+ \o Text.ElideRight
+ \endlist
+
+ If this property is set to Text.ElideRight, it can be used with multiline
+ text. The text will only elide if maximumLineCount has been set.
+
+ If the text is a multi-length string, and the mode is not \c Text.ElideNone,
+ the first string that fits will be used, otherwise the last will be elided.
+
+ Multi-length strings are ordered from longest to shortest, separated by the
+ Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
+*/
QSGText::TextElideMode QSGText::elideMode() const
{
Q_D(const QSGText);
@@ -1079,11 +1489,13 @@ QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
QRectF bounds = boundingRect();
// We need to make sure the layout is done in the current thread
+#if defined(Q_OS_MAC)
if (d->layoutThread != QThread::currentThread())
d->updateLayout();
+#endif
// XXX todo - some styled text can be done by the QSGTextNode
- if (d->richTextAsImage || d->cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && d->style != Normal)) {
+ if (d->richTextAsImage || d->cacheAllTextAsImage || (qmlDisableDistanceField() && d->style != Normal)) {
bool wasDirty = d->textureImageCacheDirty;
d->textureImageCacheDirty = false;
@@ -1156,18 +1568,39 @@ bool QSGText::event(QEvent *e)
}
}
+/*!
+ \qmlproperty real QtQuick2::Text::paintedWidth
+
+ Returns the width of the text, including width past the width
+ which is covered due to insufficient wrapping if WrapMode is set.
+*/
qreal QSGText::paintedWidth() const
{
Q_D(const QSGText);
return d->paintedSize.width();
}
+/*!
+ \qmlproperty real QtQuick2::Text::paintedHeight
+
+ Returns the height of the text, including height past the height
+ which is covered due to there being more text than fits in the set height.
+*/
qreal QSGText::paintedHeight() const
{
Q_D(const QSGText);
return d->paintedSize.height();
}
+/*!
+ \qmlproperty real QtQuick2::Text::lineHeight
+
+ Sets the line height for the text.
+ The value can be in pixels or a multiplier depending on lineHeightMode.
+
+ The default value is a multiplier of 1.0.
+ The line height must be a positive value.
+*/
qreal QSGText::lineHeight() const
{
Q_D(const QSGText);
@@ -1186,6 +1619,18 @@ void QSGText::setLineHeight(qreal lineHeight)
emit lineHeightChanged(lineHeight);
}
+/*!
+ \qmlproperty enumeration QtQuick2::Text::lineHeightMode
+
+ This property determines how the line height is specified.
+ The possible values are:
+
+ \list
+ \o Text.ProportionalHeight (default) - this sets the spacing proportional to the
+ line (as a multiplier). For example, set to 2 for double spacing.
+ \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels).
+ \endlist
+*/
QSGText::LineHeightMode QSGText::lineHeightMode() const
{
Q_D(const QSGText);
diff --git a/src/declarative/items/qsgtext_p_p.h b/src/declarative/items/qsgtext_p_p.h
index 050e3984ab..40c986142c 100644
--- a/src/declarative/items/qsgtext_p_p.h
+++ b/src/declarative/items/qsgtext_p_p.h
@@ -134,7 +134,6 @@ public:
QPixmap textLayoutImage(bool drawStyle);
void drawTextLayout(QPainter *p, const QPointF &pos, bool drawStyle);
QTextLayout layout;
- QThread *layoutThread;
static QPixmap drawOutline(const QPixmap &source, const QPixmap &styleSource);
static QPixmap drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset);
@@ -146,9 +145,13 @@ public:
enum NodeType {
NodeIsNull,
NodeIsTexture,
- NodeIsText,
+ NodeIsText
};
NodeType nodeType;
+
+#if defined(Q_OS_MAC)
+ QThread *layoutThread;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgtextedit.cpp b/src/declarative/items/qsgtextedit.cpp
index 4ba5bec5e4..1c0ed62f0d 100644
--- a/src/declarative/items/qsgtextedit.cpp
+++ b/src/declarative/items/qsgtextedit.cpp
@@ -43,6 +43,8 @@
#include "qsgtextedit_p_p.h"
#include "qsgevents_p_p.h"
#include "qsgcanvas.h"
+#include "qsgtextnode_p.h"
+#include "qsgsimplerectnode.h"
#include <QtDeclarative/qdeclarativeinfo.h>
#include <QtWidgets/qapplication.h>
@@ -55,13 +57,68 @@
#include <private/qtextcontrol_p.h>
#include <private/qtextengine_p.h>
#include <private/qwidget_p.h>
+#include <private/qsgdistancefieldglyphcache_p.h>
+#include <private/qsgtexture_p.h>
+#include <private/qsgadaptationlayer_p.h>
QT_BEGIN_NAMESPACE
+DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
+
QWidgetPrivate *qt_widget_private(QWidget *widget);
+/*!
+ \qmlclass TextEdit QSGTextEdit
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-visual-elements
+ \brief The TextEdit item displays multiple lines of editable formatted text.
+ \inherits Item
+
+ The TextEdit item displays a block of editable, formatted text.
+
+ It can display both plain and rich text. For example:
+
+ \qml
+TextEdit {
+ width: 240
+ text: "<b>Hello</b> <i>World!</i>"
+ font.family: "Helvetica"
+ font.pointSize: 20
+ color: "blue"
+ focus: true
+}
+ \endqml
+
+ \image declarative-textedit.gif
+
+ Setting \l {Item::focus}{focus} to \c true enables the TextEdit item to receive keyboard focus.
+
+ Note that the TextEdit does not implement scrolling, following the cursor, or other behaviors specific
+ to a look-and-feel. For example, to add flickable scrolling that follows the cursor:
+ \snippet snippets/declarative/texteditor.qml 0
+
+ A particular look-and-feel might use smooth scrolling (eg. using SmoothedFollow), might have a visible
+ scrollbar, or a scrollbar that fades in to show location, etc.
+
+ Clipboard support is provided by the cut(), copy(), and paste() functions, and the selection can
+ be handled in a traditional "mouse" mechanism by setting selectByMouse, or handled completely
+ from QML by manipulating selectionStart and selectionEnd, or using selectAll() or selectWord().
+
+ You can translate between cursor positions (characters from the start of the document) and pixel
+ points using positionAt() and positionToRectangle().
+
+ \sa Text, TextInput, {declarative/text/textselection}{Text Selection example}
+*/
+
+/*!
+ \qmlsignal QtQuick2::TextEdit::onLinkActivated(string link)
+
+ This handler is called when the user clicks on a link embedded in the text.
+ The link must be in rich text or HTML format and the
+ \a link string provides access to the particular link.
+*/
QSGTextEdit::QSGTextEdit(QSGItem *parent)
-: QSGImplicitSizePaintedItem(*(new QSGTextEditPrivate), parent)
+: QSGImplicitSizeItem(*(new QSGTextEditPrivate), parent)
{
Q_D(QSGTextEdit);
d->init();
@@ -79,6 +136,119 @@ QString QSGTextEdit::text() const
return d->document->toPlainText();
}
+/*!
+ \qmlproperty string QtQuick2::TextEdit::font.family
+
+ Sets the family name of the font.
+
+ The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
+ If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
+ If the family isn't available a family will be set using the font matching algorithm.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::font.bold
+
+ Sets whether the font weight is bold.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick2::TextEdit::font.weight
+
+ Sets the font's weight.
+
+ The weight can be one of:
+ \list
+ \o Font.Light
+ \o Font.Normal - the default
+ \o Font.DemiBold
+ \o Font.Bold
+ \o Font.Black
+ \endlist
+
+ \qml
+ TextEdit { text: "Hello"; font.weight: Font.DemiBold }
+ \endqml
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::font.italic
+
+ Sets whether the font has an italic style.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::font.underline
+
+ Sets whether the text is underlined.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::font.strikeout
+
+ Sets whether the font has a strikeout style.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::TextEdit::font.pointSize
+
+ Sets the font size in points. The point size must be greater than zero.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::TextEdit::font.pixelSize
+
+ Sets the font size in pixels.
+
+ Using this function makes the font device dependent. Use
+ \l{TextEdit::font.pointSize} to set the size of the font in a
+ device independent manner.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::TextEdit::font.letterSpacing
+
+ Sets the letter spacing for the font.
+
+ Letter spacing changes the default spacing between individual letters in the font.
+ A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::TextEdit::font.wordSpacing
+
+ Sets the word spacing for the font.
+
+ Word spacing changes the default spacing between individual words.
+ A positive value increases the word spacing by a corresponding amount of pixels,
+ while a negative value decreases the inter-word spacing accordingly.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick2::TextEdit::font.capitalization
+
+ Sets the capitalization for the text.
+
+ \list
+ \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
+ \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
+ \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
+ \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
+ \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
+ \endlist
+
+ \qml
+ TextEdit { text: "Hello"; font.capitalization: Font.AllLowercase }
+ \endqml
+*/
+
+/*!
+ \qmlproperty string QtQuick2::TextEdit::text
+
+ The text to display. If the text format is AutoText the text edit will
+ automatically determine whether the text should be treated as
+ rich text. This determination is made using Qt::mightBeRichText().
+*/
void QSGTextEdit::setText(const QString &text)
{
Q_D(QSGTextEdit);
@@ -92,12 +262,52 @@ void QSGTextEdit::setText(const QString &text)
#else
d->control->setPlainText(text);
#endif
+ d->isComplexRichText = QSGTextNode::isComplexRichText(d->document);
} else {
d->control->setPlainText(text);
}
q_textChanged();
}
+/*!
+ \qmlproperty enumeration QtQuick2::TextEdit::textFormat
+
+ The way the text property should be displayed.
+
+ \list
+ \o TextEdit.AutoText
+ \o TextEdit.PlainText
+ \o TextEdit.RichText
+ \endlist
+
+ The default is TextEdit.AutoText. If the text format is TextEdit.AutoText the text edit
+ will automatically determine whether the text should be treated as
+ rich text. This determination is made using Qt::mightBeRichText().
+
+ \table
+ \row
+ \o
+ \qml
+Column {
+ TextEdit {
+ font.pointSize: 24
+ text: "<b>Hello</b> <i>World!</i>"
+ }
+ TextEdit {
+ font.pointSize: 24
+ textFormat: TextEdit.RichText
+ text: "<b>Hello</b> <i>World!</i>"
+ }
+ TextEdit {
+ font.pointSize: 24
+ textFormat: TextEdit.PlainText
+ text: "<b>Hello</b> <i>World!</i>"
+ }
+}
+ \endqml
+ \o \image declarative-textformat.png
+ \endtable
+*/
QSGTextEdit::TextFormat QSGTextEdit::textFormat() const
{
Q_D(const QSGTextEdit);
@@ -122,6 +332,7 @@ void QSGTextEdit::setTextFormat(TextFormat format)
d->control->setPlainText(d->text);
#endif
updateSize();
+ d->isComplexRichText = QSGTextNode::isComplexRichText(d->document);
}
d->format = format;
d->control->setAcceptRichText(d->format != PlainText);
@@ -156,11 +367,26 @@ void QSGTextEdit::setFont(const QFont &font)
moveCursorDelegate();
}
updateSize();
- update();
+ updateDocument();
}
emit fontChanged(d->sourceFont);
}
+/*!
+ \qmlproperty color QtQuick2::TextEdit::color
+
+ The text color.
+
+ \qml
+ // green text using hexadecimal notation
+ TextEdit { color: "#00FF00" }
+ \endqml
+
+ \qml
+ // steelblue text using SVG color name
+ TextEdit { color: "steelblue" }
+ \endqml
+*/
QColor QSGTextEdit::color() const
{
Q_D(const QSGTextEdit);
@@ -177,10 +403,15 @@ void QSGTextEdit::setColor(const QColor &color)
QPalette pal = d->control->palette();
pal.setColor(QPalette::Text, color);
d->control->setPalette(pal);
- update();
+ updateDocument();
emit colorChanged(d->color);
}
+/*!
+ \qmlproperty color QtQuick2::TextEdit::selectionColor
+
+ The text highlight color, used behind selections.
+*/
QColor QSGTextEdit::selectionColor() const
{
Q_D(const QSGTextEdit);
@@ -197,10 +428,15 @@ void QSGTextEdit::setSelectionColor(const QColor &color)
QPalette pal = d->control->palette();
pal.setColor(QPalette::Highlight, color);
d->control->setPalette(pal);
- update();
+ updateDocument();
emit selectionColorChanged(d->selectionColor);
}
+/*!
+ \qmlproperty color QtQuick2::TextEdit::selectedTextColor
+
+ The selected text color, used in selections.
+*/
QColor QSGTextEdit::selectedTextColor() const
{
Q_D(const QSGTextEdit);
@@ -217,10 +453,40 @@ void QSGTextEdit::setSelectedTextColor(const QColor &color)
QPalette pal = d->control->palette();
pal.setColor(QPalette::HighlightedText, color);
d->control->setPalette(pal);
- update();
+ updateDocument();
emit selectedTextColorChanged(d->selectedTextColor);
}
+/*!
+ \qmlproperty enumeration QtQuick2::TextEdit::horizontalAlignment
+ \qmlproperty enumeration QtQuick2::TextEdit::verticalAlignment
+ \qmlproperty enumeration QtQuick2::TextEdit::effectiveHorizontalAlignment
+
+ Sets the horizontal and vertical alignment of the text within the TextEdit item's
+ width and height. By default, the text alignment follows the natural alignment
+ of the text, for example text that is read from left to right will be aligned to
+ the left.
+
+ Valid values for \c horizontalAlignment are:
+ \list
+ \o TextEdit.AlignLeft (default)
+ \o TextEdit.AlignRight
+ \o TextEdit.AlignHCenter
+ \o TextEdit.AlignJustify
+ \endlist
+
+ Valid values for \c verticalAlignment are:
+ \list
+ \o TextEdit.AlignTop (default)
+ \o TextEdit.AlignBottom
+ \o TextEdit.AlignVCenter
+ \endlist
+
+ When using the attached property LayoutMirroring::enabled to mirror application
+ layouts, the horizontal alignment of text will also be mirrored. However, the property
+ \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
+ of TextEdit, use the read-only property \c effectiveHorizontalAlignment.
+*/
QSGTextEdit::HAlignment QSGTextEdit::hAlign() const
{
Q_D(const QSGTextEdit);
@@ -320,7 +586,21 @@ void QSGTextEdit::setVAlign(QSGTextEdit::VAlignment alignment)
moveCursorDelegate();
emit verticalAlignmentChanged(d->vAlign);
}
+/*!
+ \qmlproperty enumeration QtQuick2::TextEdit::wrapMode
+
+ Set this property to wrap the text to the TextEdit item's width.
+ The text will only wrap if an explicit width has been set.
+
+ \list
+ \o TextEdit.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
+ \o TextEdit.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
+ \o TextEdit.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
+ \o TextEdit.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
+ \endlist
+ The default is TextEdit.NoWrap. If you set a width, consider using TextEdit.Wrap.
+*/
QSGTextEdit::WrapMode QSGTextEdit::wrapMode() const
{
Q_D(const QSGTextEdit);
@@ -338,24 +618,48 @@ void QSGTextEdit::setWrapMode(WrapMode mode)
emit wrapModeChanged();
}
+/*!
+ \qmlproperty int QtQuick2::TextEdit::lineCount
+
+ Returns the total number of lines in the textEdit item.
+*/
int QSGTextEdit::lineCount() const
{
Q_D(const QSGTextEdit);
return d->lineCount;
}
+/*!
+ \qmlproperty real QtQuick2::TextEdit::paintedWidth
+
+ Returns the width of the text, including the width past the width
+ which is covered due to insufficient wrapping if \l wrapMode is set.
+*/
qreal QSGTextEdit::paintedWidth() const
{
Q_D(const QSGTextEdit);
return d->paintedSize.width();
}
+/*!
+ \qmlproperty real QtQuick2::TextEdit::paintedHeight
+
+ Returns the height of the text, including the height past the height
+ that is covered if the text does not fit within the set height.
+*/
qreal QSGTextEdit::paintedHeight() const
{
Q_D(const QSGTextEdit);
return d->paintedSize.height();
}
+/*!
+ \qmlmethod rectangle QtQuick2::TextEdit::positionToRectangle(position)
+
+ Returns the rectangle at the given \a position in the text. The x, y,
+ and height properties correspond to the cursor that would describe
+ that position.
+*/
QRectF QSGTextEdit::positionToRectangle(int pos) const
{
Q_D(const QSGTextEdit);
@@ -365,6 +669,14 @@ QRectF QSGTextEdit::positionToRectangle(int pos) const
}
+/*!
+ \qmlmethod int QtQuick2::TextEdit::positionAt(int x, int y)
+
+ Returns the text position closest to pixel position (\a x, \a y).
+
+ Position 0 is before the first character, position 1 is after the first character
+ but before the second, and so on until position \l {text}.length, which is after all characters.
+*/
int QSGTextEdit::positionAt(int x, int y) const
{
Q_D(const QSGTextEdit);
@@ -388,6 +700,43 @@ int QSGTextEdit::positionAt(int x, int y) const
return r;
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::moveCursorSelection(int position, SelectionMode mode = TextEdit.SelectCharacters)
+
+ Moves the cursor to \a position and updates the selection according to the optional \a mode
+ parameter. (To only move the cursor, set the \l cursorPosition property.)
+
+ When this method is called it additionally sets either the
+ selectionStart or the selectionEnd (whichever was at the previous cursor position)
+ to the specified position. This allows you to easily extend and contract the selected
+ text range.
+
+ The selection mode specifies whether the selection is updated on a per character or a per word
+ basis. If not specified the selection mode will default to TextEdit.SelectCharacters.
+
+ \list
+ \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
+ the previous cursor position) to the specified position.
+ \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all
+ words between the specified postion and the previous cursor position. Words partially in the
+ range are included.
+ \endlist
+
+ For example, take this sequence of calls:
+
+ \code
+ cursorPosition = 5
+ moveCursorSelection(9, TextEdit.SelectCharacters)
+ moveCursorSelection(7, TextEdit.SelectCharacters)
+ \endcode
+
+ This moves the cursor to position 5, extend the selection end from 5 to 9
+ and then retract the selection end from 9 to 7, leaving the text from position 5 to 7
+ selected (the 6th and 7th characters).
+
+ The same sequence with TextEdit.SelectWords will extend the selection start to a word boundary
+ before or on position 5 and extend the selection end to a word boundary on or past position 9.
+*/
void QSGTextEdit::moveCursorSelection(int pos)
{
//Note that this is the same as setCursorPosition but with the KeepAnchor flag set
@@ -448,6 +797,13 @@ void QSGTextEdit::moveCursorSelection(int pos, SelectionMode mode)
d->control->setTextCursor(cursor);
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::cursorVisible
+ If true the text edit shows a cursor.
+
+ This property is set and unset when the text edit gets active focus, but it can also
+ be set directly (useful, for example, if a KeyProxy might forward keys to it).
+*/
bool QSGTextEdit::isCursorVisible() const
{
Q_D(const QSGTextEdit);
@@ -467,6 +823,10 @@ void QSGTextEdit::setCursorVisible(bool on)
emit cursorVisibleChanged(d->cursorVisible);
}
+/*!
+ \qmlproperty int QtQuick2::TextEdit::cursorPosition
+ The position of the cursor in the TextEdit.
+*/
int QSGTextEdit::cursorPosition() const
{
Q_D(const QSGTextEdit);
@@ -485,6 +845,19 @@ void QSGTextEdit::setCursorPosition(int pos)
d->control->setTextCursor(cursor);
}
+/*!
+ \qmlproperty Component QtQuick2::TextEdit::cursorDelegate
+ The delegate for the cursor in the TextEdit.
+
+ If you set a cursorDelegate for a TextEdit, this delegate will be used for
+ drawing the cursor instead of the standard cursor. An instance of the
+ delegate will be created and managed by the text edit when a cursor is
+ needed, and the x and y properties of delegate instance will be set so as
+ to be one pixel before the top left of the current character.
+
+ Note that the root item of the delegate component must be a QDeclarativeItem or
+ QDeclarativeItem derived item.
+*/
QDeclarativeComponent* QSGTextEdit::cursorDelegate() const
{
Q_D(const QSGTextEdit);
@@ -497,7 +870,7 @@ void QSGTextEdit::setCursorDelegate(QDeclarativeComponent* c)
if(d->cursorComponent){
if(d->cursor){
d->control->setCursorWidth(-1);
- update(cursorRectangle());
+ updateCursor();
delete d->cursor;
d->cursor = 0;
}
@@ -522,7 +895,7 @@ void QSGTextEdit::loadCursorDelegate()
d->cursor = qobject_cast<QSGItem*>(d->cursorComponent->create(qmlContext(this)));
if(d->cursor){
d->control->setCursorWidth(0);
- update(cursorRectangle());
+ updateCursor();
QDeclarative_setParent_noEvent(d->cursor, this);
d->cursor->setParentItem(this);
d->cursor->setHeight(QFontMetrics(d->font).height());
@@ -532,24 +905,64 @@ void QSGTextEdit::loadCursorDelegate()
}
}
+/*!
+ \qmlproperty int QtQuick2::TextEdit::selectionStart
+
+ The cursor position before the first character in the current selection.
+
+ This property is read-only. To change the selection, use select(start,end),
+ selectAll(), or selectWord().
+
+ \sa selectionEnd, cursorPosition, selectedText
+*/
int QSGTextEdit::selectionStart() const
{
Q_D(const QSGTextEdit);
return d->control->textCursor().selectionStart();
}
+/*!
+ \qmlproperty int QtQuick2::TextEdit::selectionEnd
+
+ The cursor position after the last character in the current selection.
+
+ This property is read-only. To change the selection, use select(start,end),
+ selectAll(), or selectWord().
+
+ \sa selectionStart, cursorPosition, selectedText
+*/
int QSGTextEdit::selectionEnd() const
{
Q_D(const QSGTextEdit);
return d->control->textCursor().selectionEnd();
}
+/*!
+ \qmlproperty string QtQuick2::TextEdit::selectedText
+
+ This read-only property provides the text currently selected in the
+ text edit.
+
+ It is equivalent to the following snippet, but is faster and easier
+ to use.
+ \code
+ //myTextEdit is the id of the TextEdit
+ myTextEdit.text.toString().substring(myTextEdit.selectionStart,
+ myTextEdit.selectionEnd);
+ \endcode
+*/
QString QSGTextEdit::selectedText() const
{
Q_D(const QSGTextEdit);
return d->control->textCursor().selectedText();
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::activeFocusOnPress
+
+ Whether the TextEdit should gain active focus on a mouse press. By default this is
+ set to true.
+*/
bool QSGTextEdit::focusOnPress() const
{
Q_D(const QSGTextEdit);
@@ -565,6 +978,12 @@ void QSGTextEdit::setFocusOnPress(bool on)
emit activeFocusOnPressChanged(d->focusOnPress);
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::persistentSelection
+
+ Whether the TextEdit should keep the selection visible when it loses active focus to another
+ item in the scene. By default this is set to true;
+*/
bool QSGTextEdit::persistentSelection() const
{
Q_D(const QSGTextEdit);
@@ -580,6 +999,11 @@ void QSGTextEdit::setPersistentSelection(bool on)
emit persistentSelectionChanged(d->persistentSelection);
}
+/*
+ \qmlproperty real QtQuick2::TextEdit::textMargin
+
+ The margin, in pixels, around the text in the TextEdit.
+*/
qreal QSGTextEdit::textMargin() const
{
Q_D(const QSGTextEdit);
@@ -601,21 +1025,40 @@ void QSGTextEdit::geometryChanged(const QRectF &newGeometry,
{
if (newGeometry.width() != oldGeometry.width())
updateSize();
- QSGPaintedItem::geometryChanged(newGeometry, oldGeometry);
+ QSGImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
}
+/*!
+ Ensures any delayed caching or data loading the class
+ needs to performed is complete.
+*/
void QSGTextEdit::componentComplete()
{
Q_D(QSGTextEdit);
- QSGPaintedItem::componentComplete();
+ QSGImplicitSizeItem::componentComplete();
+
+ if (d->richText) {
+ d->isComplexRichText = QSGTextNode::isComplexRichText(d->document);
+ }
+
if (d->dirty) {
d->determineHorizontalAlignment();
d->updateDefaultTextOption();
updateSize();
d->dirty = false;
}
+
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::selectByMouse
+ Defaults to false.
+
+ If true, the user can use the mouse to select text in some
+ platform-specific way. Note that for some platforms this may
+ not be an appropriate interaction (eg. may conflict with how
+ the text needs to behave inside a Flickable.
+*/
bool QSGTextEdit::selectByMouse() const
{
Q_D(const QSGTextEdit);
@@ -636,6 +1079,18 @@ void QSGTextEdit::setSelectByMouse(bool on)
}
}
+/*!
+ \qmlproperty enum QtQuick2::TextEdit::mouseSelectionMode
+
+ Specifies how text should be selected using a mouse.
+
+ \list
+ \o TextEdit.SelectCharacters - The selection is updated with individual characters. (Default)
+ \o TextEdit.SelectWords - The selection is updated with whole words.
+ \endlist
+
+ This property only applies when \l selectByMouse is true.
+*/
QSGTextEdit::SelectionMode QSGTextEdit::mouseSelectionMode() const
{
Q_D(const QSGTextEdit);
@@ -652,6 +1107,14 @@ void QSGTextEdit::setMouseSelectionMode(SelectionMode mode)
}
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::readOnly
+
+ Whether the user can interact with the TextEdit item. If this
+ property is set to true the text cannot be edited by user interaction.
+
+ By default this property is false.
+*/
void QSGTextEdit::setReadOnly(bool r)
{
Q_D(QSGTextEdit);
@@ -677,18 +1140,32 @@ bool QSGTextEdit::isReadOnly() const
return !(d->control->textInteractionFlags() & Qt::TextEditable);
}
+/*!
+ Sets how the text edit should interact with user input to the given
+ \a flags.
+*/
void QSGTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
{
Q_D(QSGTextEdit);
d->control->setTextInteractionFlags(flags);
}
+/*!
+ Returns the flags specifying how the text edit should interact
+ with user input.
+*/
Qt::TextInteractionFlags QSGTextEdit::textInteractionFlags() const
{
Q_D(const QSGTextEdit);
return d->control->textInteractionFlags();
}
+/*!
+ \qmlproperty rectangle QtQuick2::TextEdit::cursorRectangle
+
+ The rectangle where the text cursor is rendered
+ within the text edit. Read-only.
+*/
QRect QSGTextEdit::cursorRectangle() const
{
Q_D(const QSGTextEdit);
@@ -702,7 +1179,7 @@ bool QSGTextEdit::event(QEvent *event)
d->control->processEvent(event, QPointF(0, -d->yoff));
return event->isAccepted();
}
- return QSGPaintedItem::event(event);
+ return QSGImplicitSizeItem::event(event);
}
/*!
@@ -714,7 +1191,7 @@ void QSGTextEdit::keyPressEvent(QKeyEvent *event)
Q_D(QSGTextEdit);
d->control->processEvent(event, QPointF(0, -d->yoff));
if (!event->isAccepted())
- QSGPaintedItem::keyPressEvent(event);
+ QSGImplicitSizeItem::keyPressEvent(event);
}
/*!
@@ -726,9 +1203,14 @@ void QSGTextEdit::keyReleaseEvent(QKeyEvent *event)
Q_D(QSGTextEdit);
d->control->processEvent(event, QPointF(0, -d->yoff));
if (!event->isAccepted())
- QSGPaintedItem::keyReleaseEvent(event);
+ QSGImplicitSizeItem::keyReleaseEvent(event);
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::deselect()
+
+ Removes active text selection.
+*/
void QSGTextEdit::deselect()
{
Q_D(QSGTextEdit);
@@ -737,12 +1219,22 @@ void QSGTextEdit::deselect()
d->control->setTextCursor(c);
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::selectAll()
+
+ Causes all text to be selected.
+*/
void QSGTextEdit::selectAll()
{
Q_D(QSGTextEdit);
d->control->selectAll();
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::selectWord()
+
+ Causes the word closest to the current cursor position to be selected.
+*/
void QSGTextEdit::selectWord()
{
Q_D(QSGTextEdit);
@@ -751,6 +1243,19 @@ void QSGTextEdit::selectWord()
d->control->setTextCursor(c);
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::select(int start, int end)
+
+ Causes the text from \a start to \a end to be selected.
+
+ If either start or end is out of range, the selection is not changed.
+
+ After calling this, selectionStart will become the lesser
+ and selectionEnd will become the greater (regardless of the order passed
+ to this method).
+
+ \sa selectionStart, selectionEnd
+*/
void QSGTextEdit::select(int start, int end)
{
Q_D(QSGTextEdit);
@@ -767,6 +1272,12 @@ void QSGTextEdit::select(int start, int end)
updateSelectionMarkers();
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::isRightToLeft(int start, int end)
+
+ Returns true if the natural reading direction of the editor text
+ found between positions \a start and \a end is right to left.
+*/
bool QSGTextEdit::isRightToLeft(int start, int end)
{
Q_D(QSGTextEdit);
@@ -779,18 +1290,33 @@ bool QSGTextEdit::isRightToLeft(int start, int end)
}
#ifndef QT_NO_CLIPBOARD
+/*!
+ \qmlmethod QtQuick2::TextEdit::cut()
+
+ Moves the currently selected text to the system clipboard.
+*/
void QSGTextEdit::cut()
{
Q_D(QSGTextEdit);
d->control->cut();
}
+/*!
+ \qmlmethod QtQuick2::TextEdit::copy()
+
+ Copies the currently selected text to the system clipboard.
+*/
void QSGTextEdit::copy()
{
Q_D(QSGTextEdit);
d->control->copy();
}
+/*!
+ \qmlmethod QtQuick2::TextEdit::paste()
+
+ Replaces the currently selected text by the contents of the system clipboard.
+*/
void QSGTextEdit::paste()
{
Q_D(QSGTextEdit);
@@ -821,7 +1347,7 @@ void QSGTextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event)
}
d->control->processEvent(event, QPointF(0, -d->yoff));
if (!event->isAccepted())
- QSGPaintedItem::mousePressEvent(event);
+ QSGImplicitSizeItem::mousePressEvent(event);
}
/*!
@@ -844,7 +1370,7 @@ void QSGTextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
d->clickCausedFocus = false;
if (!event->isAccepted())
- QSGPaintedItem::mouseReleaseEvent(event);
+ QSGImplicitSizeItem::mouseReleaseEvent(event);
}
/*!
@@ -856,7 +1382,7 @@ void QSGTextEdit::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
Q_D(QSGTextEdit);
d->control->processEvent(event, QPointF(0, -d->yoff));
if (!event->isAccepted())
- QSGPaintedItem::mouseDoubleClickEvent(event);
+ QSGImplicitSizeItem::mouseDoubleClickEvent(event);
}
/*!
@@ -868,7 +1394,7 @@ void QSGTextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
Q_D(QSGTextEdit);
d->control->processEvent(event, QPointF(0, -d->yoff));
if (!event->isAccepted())
- QSGPaintedItem::mouseMoveEvent(event);
+ QSGImplicitSizeItem::mouseMoveEvent(event);
}
/*!
@@ -903,49 +1429,162 @@ QVariant QSGTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
return d->control->inputMethodQuery(property);
}
-/*!
-Draws the contents of the text edit using the given \a painter within
-the given \a bounds.
-*/
-void QSGTextEdit::paint(QPainter *painter)
+void QSGTextEdit::updateImageCache(const QRectF &)
{
- // XXX todo
- QRect bounds(0, 0, width(), height());
Q_D(QSGTextEdit);
- painter->setRenderHint(QPainter::TextAntialiasing, true);
- painter->translate(0,d->yoff);
+ // Do we really need the image cache?
+ if (!d->richText || !d->isComplexRichText) {
+ if (!d->pixmapCache.isNull())
+ d->pixmapCache = QPixmap();
+ return;
+ }
+
+ if (width() != d->pixmapCache.width() || height() != d->pixmapCache.height())
+ d->pixmapCache = QPixmap(width(), height());
+
+ if (d->pixmapCache.isNull())
+ return;
+
+ // ### Use supplied rect, clear area and update only this part (for cursor updates)
+ QRectF bounds = QRectF(0, 0, width(), height());
+ d->pixmapCache.fill(Qt::transparent);
+ {
+ QPainter painter(&d->pixmapCache);
- d->control->drawContents(painter, bounds.translated(0,-d->yoff));
+ painter.setRenderHint(QPainter::TextAntialiasing);
+ painter.translate(0, d->yoff);
+
+ d->control->drawContents(&painter, bounds);
+ }
- painter->translate(0,-d->yoff);
}
-void QSGTextEdit::updateImgCache(const QRectF &rf)
+QSGNode *QSGTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
{
- Q_D(const QSGTextEdit);
- QRect r;
- if (!rf.isValid()) {
- r = QRect(0,0,INT_MAX,INT_MAX);
- } else {
- r = rf.toRect();
- if (r.height() > INT_MAX/2) {
- // Take care of overflow when translating "everything"
- r.setTop(r.y() + d->yoff);
- r.setBottom(INT_MAX/2);
+ Q_UNUSED(updatePaintNodeData);
+ Q_D(QSGTextEdit);
+
+ QSGNode *currentNode = oldNode;
+ if (d->richText && d->isComplexRichText) {
+ QSGImageNode *node = 0;
+ if (oldNode == 0 || d->nodeType != QSGTextEditPrivate::NodeIsTexture) {
+ delete oldNode;
+ node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
+ d->texture = new QSGPlainTexture();
+ d->nodeType = QSGTextEditPrivate::NodeIsTexture;
+ currentNode = node;
+ } else {
+ node = static_cast<QSGImageNode *>(oldNode);
+ }
+
+ qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->pixmapCache.toImage());
+ node->setTexture(0);
+ node->setTexture(d->texture);
+
+ node->setTargetRect(QRectF(0, 0, d->pixmapCache.width(), d->pixmapCache.height()));
+ node->setSourceRect(QRectF(0, 0, 1, 1));
+ node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
+ node->setVerticalWrapMode(QSGTexture::ClampToEdge);
+ node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
+ node->update();
+
+ } else if (oldNode == 0 || d->documentDirty) {
+ d->documentDirty = false;
+
+#if defined(Q_WS_MAC)
+ // Make sure document is relayouted in the paint node on Mac
+ // to avoid crashes due to the font engines created in the
+ // shaping process
+ d->document->markContentsDirty(0, d->document->characterCount());
+#endif
+
+ QSGTextNode *node = 0;
+ if (oldNode == 0 || d->nodeType != QSGTextEditPrivate::NodeIsText) {
+ delete oldNode;
+ node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
+ d->nodeType = QSGTextEditPrivate::NodeIsText;
+ currentNode = node;
} else {
- r = r.translated(0,d->yoff);
+ node = static_cast<QSGTextNode *>(oldNode);
}
+
+ node->deleteContent();
+ node->setMatrix(QMatrix4x4());
+
+ QRectF bounds = boundingRect();
+
+ QColor selectionColor = d->control->palette().color(QPalette::Highlight);
+ QColor selectedTextColor = d->control->palette().color(QPalette::HighlightedText);
+ node->addTextDocument(bounds.topLeft(), d->document, d->color, QSGText::Normal, QColor(),
+ selectionColor, selectedTextColor, selectionStart(),
+ selectionEnd());
+
+#if defined(Q_WS_MAC)
+ // We also need to make sure the document layout is redone when
+ // control is returned to the main thread, as all the font engines
+ // are now owned by the rendering thread
+ d->document->markContentsDirty(0, d->document->characterCount());
+#endif
}
- update(r);
+
+ if (d->nodeType == QSGTextEditPrivate::NodeIsText && d->cursorComponent == 0 && !isReadOnly()) {
+ QSGTextNode *node = static_cast<QSGTextNode *>(currentNode);
+
+ QColor color = (!d->cursorVisible || !d->control->cursorOn())
+ ? QColor(0, 0, 0, 0)
+ : d->color;
+
+ if (node->cursorNode() == 0) {
+ node->setCursor(cursorRectangle(), color);
+ } else {
+ node->cursorNode()->setRect(cursorRectangle());
+ node->cursorNode()->setColor(color);
+ }
+
+ }
+
+ return currentNode;
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::smooth
+
+ This property holds whether the text is smoothly scaled or transformed.
+
+ Smooth filtering gives better visual quality, but is slower. If
+ the item is displayed at its natural size, this property has no visual or
+ performance effect.
+
+ \note Generally scaling artifacts are only visible if the item is stationary on
+ the screen. A common pattern when animating an item is to disable smooth
+ filtering at the beginning of the animation and reenable it at the conclusion.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::canPaste
+
+ Returns true if the TextEdit is writable and the content of the clipboard is
+ suitable for pasting into the TextEdit.
+*/
bool QSGTextEdit::canPaste() const
{
Q_D(const QSGTextEdit);
return d->canPaste;
}
+/*!
+ \qmlproperty bool QtQuick2::TextEdit::inputMethodComposing
+
+
+ This property holds whether the TextEdit has partial text input from an
+ input method.
+
+ While it is composing an input method may rely on mouse or key events from
+ the TextEdit to edit or commit the partial text. This property can be used
+ to determine when to disable events handlers that may interfere with the
+ correct operation of an input method.
+*/
bool QSGTextEdit::isInputMethodComposing() const
{
Q_D(const QSGTextEdit);
@@ -961,12 +1600,22 @@ void QSGTextEditPrivate::init()
q->setSmooth(smooth);
q->setAcceptedMouseButtons(Qt::LeftButton);
q->setFlag(QSGItem::ItemAcceptsInputMethod);
+ q->setFlag(QSGItem::ItemHasContents);
control = new QTextControl(q);
control->setIgnoreUnusedNavigationEvents(true);
control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
control->setDragEnabled(false);
+ // By default, QTextControl will issue both a updateCursorRequest() and an updateRequest()
+ // when the cursor needs to be repainted. We need the signals to be separate to be able to
+ // distinguish the cursor updates so that we can avoid updating the whole subtree when the
+ // cursor blinks.
+ if (!QObject::disconnect(control, SIGNAL(updateCursorRequest(QRectF)),
+ control, SIGNAL(updateRequest(QRectF)))) {
+ qWarning("QSGTextEditPrivate::init: Failed to disconnect updateCursorRequest and updateRequest");
+ }
+
// QTextControl follows the default text color
// defined by the platform, declarative text
// should be black by default
@@ -976,8 +1625,8 @@ void QSGTextEditPrivate::init()
control->setPalette(pal);
}
- QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateImgCache(QRectF)));
-
+ QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateDocument()));
+ QObject::connect(control, SIGNAL(updateCursorRequest()), q, SLOT(updateCursor()));
QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged()));
QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
QObject::connect(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers()));
@@ -1056,7 +1705,7 @@ void QSGTextEdit::updateSelectionMarkers()
QRectF QSGTextEdit::boundingRect() const
{
Q_D(const QSGTextEdit);
- QRectF r = QSGPaintedItem::boundingRect();
+ QRectF r = QSGImplicitSizeItem::boundingRect();
int cursorWidth = 1;
if(d->cursor)
cursorWidth = d->cursor->width();
@@ -1121,7 +1770,7 @@ void QSGTextEdit::updateSize()
} else {
nyoff = 0;
}
- if (nyoff != d->yoff)
+ if (nyoff != d->yoff)
d->yoff = nyoff;
setBaselineOffset(fm.ascent() + d->yoff + d->textMargin);
@@ -1138,12 +1787,31 @@ void QSGTextEdit::updateSize()
setImplicitHeight(newHeight);
d->paintedSize = QSize(newWidth, newHeight);
- setContentsSize(d->paintedSize);
emit paintedSizeChanged();
} else {
d->dirty = true;
}
- update();
+ updateDocument();
+}
+
+void QSGTextEdit::updateDocument()
+{
+ Q_D(QSGTextEdit);
+ d->documentDirty = true;
+
+ if (isComponentComplete()) {
+ updateImageCache();
+ update();
+ }
+}
+
+void QSGTextEdit::updateCursor()
+{
+ Q_D(QSGTextEdit);
+ if (isComponentComplete()) {
+ updateImageCache(d->control->cursorRect());
+ update();
+ }
}
void QSGTextEdit::updateTotalLines()
@@ -1184,12 +1852,59 @@ void QSGTextEditPrivate::updateDefaultTextOption()
QTextOption::WrapMode oldWrapMode = opt.wrapMode();
opt.setWrapMode(QTextOption::WrapMode(wrapMode));
- if (oldWrapMode == opt.wrapMode() && oldAlignment == opt.alignment())
+ bool oldUseDesignMetrics = opt.useDesignMetrics();
+ bool useDesignMetrics = !qmlDisableDistanceField();
+ opt.setUseDesignMetrics(useDesignMetrics);
+
+ if (oldWrapMode == opt.wrapMode()
+ && oldAlignment == opt.alignment()
+ && oldUseDesignMetrics == useDesignMetrics) {
return;
+ }
document->setDefaultTextOption(opt);
}
+
+/*!
+ \qmlmethod void QtQuick2::TextEdit::openSoftwareInputPanel()
+
+ Opens software input panels like virtual keyboards for typing, useful for
+ customizing when you want the input keyboard to be shown and hidden in
+ your application.
+
+ By default the opening of input panels follows the platform style. On Symbian^1 and
+ Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms
+ the panels are automatically opened when TextEdit element gains active focus. Input panels are
+ always closed if no editor has active focus.
+
+ You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
+ and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
+ the behavior you want.
+
+ Only relevant on platforms, which provide virtual keyboards.
+
+ \code
+ import QtQuick 1.0
+ TextEdit {
+ id: textEdit
+ text: "Hello world!"
+ activeFocusOnPress: false
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (!textEdit.activeFocus) {
+ textEdit.forceActiveFocus();
+ textEdit.openSoftwareInputPanel();
+ } else {
+ textEdit.focus = false;
+ }
+ }
+ onPressAndHold: textEdit.closeSoftwareInputPanel();
+ }
+ }
+ \endcode
+*/
void QSGTextEdit::openSoftwareInputPanel()
{
if (qApp) {
@@ -1200,6 +1915,45 @@ void QSGTextEdit::openSoftwareInputPanel()
}
}
+/*!
+ \qmlmethod void QtQuick2::TextEdit::closeSoftwareInputPanel()
+
+ Closes a software input panel like a virtual keyboard shown on the screen, useful
+ for customizing when you want the input keyboard to be shown and hidden in
+ your application.
+
+ By default the opening of input panels follows the platform style. On Symbian^1 and
+ Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms
+ the panels are automatically opened when TextEdit element gains active focus. Input panels are
+ always closed if no editor has active focus.
+
+ You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
+ and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
+ the behavior you want.
+
+ Only relevant on platforms, which provide virtual keyboards.
+
+ \code
+ import QtQuick 1.0
+ TextEdit {
+ id: textEdit
+ text: "Hello world!"
+ activeFocusOnPress: false
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (!textEdit.activeFocus) {
+ textEdit.forceActiveFocus();
+ textEdit.openSoftwareInputPanel();
+ } else {
+ textEdit.focus = false;
+ }
+ }
+ onPressAndHold: textEdit.closeSoftwareInputPanel();
+ }
+ }
+ \endcode
+*/
void QSGTextEdit::closeSoftwareInputPanel()
{
if (qApp) {
@@ -1218,7 +1972,7 @@ void QSGTextEdit::focusInEvent(QFocusEvent *event)
openSoftwareInputPanel();
}
}
- QSGPaintedItem::focusInEvent(event);
+ QSGImplicitSizeItem::focusInEvent(event);
}
void QSGTextEdit::q_canPasteChanged()
diff --git a/src/declarative/items/qsgtextedit_p.h b/src/declarative/items/qsgtextedit_p.h
index 115b25040d..fa61f03f7f 100644
--- a/src/declarative/items/qsgtextedit_p.h
+++ b/src/declarative/items/qsgtextedit_p.h
@@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Declarative)
class QSGTextEditPrivate;
-class Q_AUTOTEST_EXPORT QSGTextEdit : public QSGImplicitSizePaintedItem
+class Q_AUTOTEST_EXPORT QSGTextEdit : public QSGImplicitSizeItem
{
Q_OBJECT
Q_ENUMS(VAlignment)
@@ -260,16 +260,18 @@ public Q_SLOTS:
#endif
private Q_SLOTS:
- void updateImgCache(const QRectF &rect);
void q_textChanged();
void updateSelectionMarkers();
void moveCursorDelegate();
void loadCursorDelegate();
void q_canPasteChanged();
+ void updateDocument();
+ void updateCursor();
private:
void updateSize();
void updateTotalLines();
+ void updateImageCache(const QRectF &rect = QRectF());
protected:
virtual void geometryChanged(const QRectF &newGeometry,
@@ -288,7 +290,8 @@ protected:
void inputMethodEvent(QInputMethodEvent *e);
virtual void itemChange(ItemChange, const ItemChangeData &);
- void paint(QPainter *);
+ QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData);
+
private:
Q_DISABLE_COPY(QSGTextEdit)
Q_DECLARE_PRIVATE(QSGTextEdit)
diff --git a/src/declarative/items/qsgtextedit_p_p.h b/src/declarative/items/qsgtextedit_p_p.h
index c080857e46..e4f74c0ef5 100644
--- a/src/declarative/items/qsgtextedit_p_p.h
+++ b/src/declarative/items/qsgtextedit_p_p.h
@@ -63,21 +63,21 @@ QT_BEGIN_NAMESPACE
class QTextLayout;
class QTextDocument;
class QTextControl;
-class QSGTextEditPrivate : public QSGImplicitSizePaintedItemPrivate
+class QSGTextEditPrivate : public QSGImplicitSizeItemPrivate
{
Q_DECLARE_PUBLIC(QSGTextEdit)
public:
QSGTextEditPrivate()
: color("black"), hAlign(QSGTextEdit::AlignLeft), vAlign(QSGTextEdit::AlignTop),
- imgDirty(true), dirty(false), richText(false), cursorVisible(false), focusOnPress(true),
+ documentDirty(true), dirty(false), richText(false), cursorVisible(false), focusOnPress(true),
showInputPanelOnFocus(true), clickCausedFocus(false), persistentSelection(true),
requireImplicitWidth(false), selectByMouse(false), canPaste(false),
- hAlignImplicit(true), rightToLeftText(false),
+ hAlignImplicit(true), rightToLeftText(false), isComplexRichText(false),
textMargin(0.0), lastSelectionStart(0), lastSelectionEnd(0), cursorComponent(0), cursor(0),
format(QSGTextEdit::AutoText), document(0), wrapMode(QSGTextEdit::NoWrap),
mouseSelectionMode(QSGTextEdit::SelectCharacters),
- yoff(0)
+ yoff(0), nodeType(NodeIsNull), texture(0)
{
#ifdef Q_OS_SYMBIAN
if (QSysInfo::symbianVersion() == QSysInfo::SV_SF_1 || QSysInfo::symbianVersion() == QSysInfo::SV_SF_3) {
@@ -104,12 +104,10 @@ public:
QColor selectedTextColor;
QString style;
QColor styleColor;
- QPixmap imgCache;
- QPixmap imgStyleCache;
QSGTextEdit::HAlignment hAlign;
QSGTextEdit::VAlignment vAlign;
- bool imgDirty : 1;
+ bool documentDirty : 1;
bool dirty : 1;
bool richText : 1;
bool cursorVisible : 1;
@@ -122,6 +120,7 @@ public:
bool canPaste:1;
bool hAlignImplicit:1;
bool rightToLeftText:1;
+ bool isComplexRichText:1;
qreal textMargin;
int lastSelectionStart;
@@ -136,6 +135,15 @@ public:
int lineCount;
int yoff;
QSize paintedSize;
+
+ enum NodeType {
+ NodeIsNull,
+ NodeIsTexture,
+ NodeIsText
+ };
+ NodeType nodeType;
+ QSGTexture *texture;
+ QPixmap pixmapCache;
};
QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgtextinput.cpp b/src/declarative/items/qsgtextinput.cpp
index 405540d419..d30faadb34 100644
--- a/src/declarative/items/qsgtextinput.cpp
+++ b/src/declarative/items/qsgtextinput.cpp
@@ -45,19 +45,43 @@
#include <private/qdeclarativeglobal_p.h>
#include <private/qwidget_p.h>
+#include <private/qsgdistancefieldglyphcache_p.h>
#include <QtDeclarative/qdeclarativeinfo.h>
#include <QtWidgets/qgraphicssceneevent.h>
#include <QtWidgets/qinputcontext.h>
#include <QTextBoundaryFinder>
#include <qstyle.h>
+#include <qsgtextnode_p.h>
+#include <qsgsimplerectnode.h>
QT_BEGIN_NAMESPACE
+DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
+
QWidgetPrivate *qt_widget_private(QWidget *widget);
+/*!
+ \qmlclass TextInput QSGTextInput
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-visual-elements
+ \brief The TextInput item displays an editable line of text.
+ \inherits Item
+
+ The TextInput element displays a single line of editable plain text.
+
+ TextInput is used to accept a line of text input. Input constraints
+ can be placed on a TextInput item (for example, through a \l validator or \l inputMask),
+ and setting \l echoMode to an appropriate value enables TextInput to be used for
+ a password input field.
+
+ On Mac OS X, the Up/Down key bindings for Home/End are explicitly disabled.
+ If you want such bindings (on any platform), you will need to construct them in QML.
+
+ \sa TextEdit, Text, {declarative/text/textselection}{Text Selection example}
+*/
QSGTextInput::QSGTextInput(QSGItem* parent)
-: QSGImplicitSizePaintedItem(*(new QSGTextInputPrivate), parent)
+: QSGImplicitSizeItem(*(new QSGTextInputPrivate), parent)
{
Q_D(QSGTextInput);
d->init();
@@ -67,6 +91,11 @@ QSGTextInput::~QSGTextInput()
{
}
+/*!
+ \qmlproperty string QtQuick2::TextInput::text
+
+ The text in the TextInput.
+*/
QString QSGTextInput::text() const
{
Q_D(const QSGTextInput);
@@ -81,6 +110,111 @@ void QSGTextInput::setText(const QString &s)
d->control->setText(s);
}
+/*!
+ \qmlproperty string QtQuick2::TextInput::font.family
+
+ Sets the family name of the font.
+
+ The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
+ If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
+ If the family isn't available a family will be set using the font matching algorithm.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextInput::font.bold
+
+ Sets whether the font weight is bold.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick2::TextInput::font.weight
+
+ Sets the font's weight.
+
+ The weight can be one of:
+ \list
+ \o Font.Light
+ \o Font.Normal - the default
+ \o Font.DemiBold
+ \o Font.Bold
+ \o Font.Black
+ \endlist
+
+ \qml
+ TextInput { text: "Hello"; font.weight: Font.DemiBold }
+ \endqml
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextInput::font.italic
+
+ Sets whether the font has an italic style.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextInput::font.underline
+
+ Sets whether the text is underlined.
+*/
+
+/*!
+ \qmlproperty bool QtQuick2::TextInput::font.strikeout
+
+ Sets whether the font has a strikeout style.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::TextInput::font.pointSize
+
+ Sets the font size in points. The point size must be greater than zero.
+*/
+
+/*!
+ \qmlproperty int QtQuick2::TextInput::font.pixelSize
+
+ Sets the font size in pixels.
+
+ Using this function makes the font device dependent.
+ Use \c pointSize to set the size of the font in a device independent manner.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::TextInput::font.letterSpacing
+
+ Sets the letter spacing for the font.
+
+ Letter spacing changes the default spacing between individual letters in the font.
+ A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::TextInput::font.wordSpacing
+
+ Sets the word spacing for the font.
+
+ Word spacing changes the default spacing between individual words.
+ A positive value increases the word spacing by a corresponding amount of pixels,
+ while a negative value decreases the inter-word spacing accordingly.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick2::TextInput::font.capitalization
+
+ Sets the capitalization for the text.
+
+ \list
+ \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
+ \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
+ \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
+ \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
+ \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
+ \endlist
+
+ \qml
+ TextInput { text: "Hello"; font.capitalization: Font.AllLowercase }
+ \endqml
+*/
+
QFont QSGTextInput::font() const
{
Q_D(const QSGTextInput);
@@ -112,6 +246,11 @@ void QSGTextInput::setFont(const QFont &font)
emit fontChanged(d->sourceFont);
}
+/*!
+ \qmlproperty color QtQuick2::TextInput::color
+
+ The text color.
+*/
QColor QSGTextInput::color() const
{
Q_D(const QSGTextInput);
@@ -128,6 +267,12 @@ void QSGTextInput::setColor(const QColor &c)
}
}
+
+/*!
+ \qmlproperty color QtQuick2::TextInput::selectionColor
+
+ The text highlight color, used behind selections.
+*/
QColor QSGTextInput::selectionColor() const
{
Q_D(const QSGTextInput);
@@ -144,11 +289,15 @@ void QSGTextInput::setSelectionColor(const QColor &color)
QPalette p = d->control->palette();
p.setColor(QPalette::Highlight, d->selectionColor);
d->control->setPalette(p);
- if (d->control->hasSelectedText())
+ if (d->control->hasSelectedText())
update();
emit selectionColorChanged(color);
}
+/*!
+ \qmlproperty color QtQuick2::TextInput::selectedTextColor
+ The highlighted text color, used in selections.
+*/
QColor QSGTextInput::selectedTextColor() const
{
Q_D(const QSGTextInput);
@@ -165,11 +314,34 @@ void QSGTextInput::setSelectedTextColor(const QColor &color)
QPalette p = d->control->palette();
p.setColor(QPalette::HighlightedText, d->selectedTextColor);
d->control->setPalette(p);
- if (d->control->hasSelectedText())
+ if (d->control->hasSelectedText())
update();
emit selectedTextColorChanged(color);
}
+/*!
+ \qmlproperty enumeration QtQuick2::TextInput::horizontalAlignment
+ \qmlproperty enumeration QtQuick2::TextInput::effectiveHorizontalAlignment
+
+ Sets the horizontal alignment of the text within the TextInput item's
+ width and height. By default, the text alignment follows the natural alignment
+ of the text, for example text that is read from left to right will be aligned to
+ the left.
+
+ TextInput does not have vertical alignment, as the natural height is
+ exactly the height of the single line of text. If you set the height
+ manually to something larger, TextInput will always be top aligned
+ vertically. You can use anchors to align it however you want within
+ another item.
+
+ The valid values for \c horizontalAlignment are \c TextInput.AlignLeft, \c TextInput.AlignRight and
+ \c TextInput.AlignHCenter.
+
+ When using the attached property LayoutMirroring::enabled to mirror application
+ layouts, the horizontal alignment of text will also be mirrored. However, the property
+ \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
+ of TextInput, use the read-only property \c effectiveHorizontalAlignment.
+*/
QSGTextInput::HAlignment QSGTextInput::hAlign() const
{
Q_D(const QSGTextInput);
@@ -250,6 +422,15 @@ void QSGTextInputPrivate::mirrorChange()
}
}
+/*!
+ \qmlproperty bool QtQuick2::TextInput::readOnly
+
+ Sets whether user input can modify the contents of the TextInput.
+
+ If readOnly is set to true, then user input will not affect the text
+ property. Any bindings or attempts to set the text property will still
+ work.
+*/
bool QSGTextInput::isReadOnly() const
{
Q_D(const QSGTextInput);
@@ -270,6 +451,14 @@ void QSGTextInput::setReadOnly(bool ro)
emit readOnlyChanged(ro);
}
+/*!
+ \qmlproperty int QtQuick2::TextInput::maximumLength
+ The maximum permitted length of the text in the TextInput.
+
+ If the text is too long, it is truncated at the limit.
+
+ By default, this property contains a value of 32767.
+*/
int QSGTextInput::maxLength() const
{
Q_D(const QSGTextInput);
@@ -287,6 +476,32 @@ void QSGTextInput::setMaxLength(int ml)
emit maximumLengthChanged(ml);
}
+/*!
+ \qmlproperty bool QtQuick2::TextInput::cursorVisible
+ Set to true when the TextInput shows a cursor.
+
+ This property is set and unset when the TextInput gets active focus, so that other
+ properties can be bound to whether the cursor is currently showing. As it
+ gets set and unset automatically, when you set the value yourself you must
+ keep in mind that your value may be overwritten.
+
+ It can be set directly in script, for example if a KeyProxy might
+ forward keys to it and you desire it to look active when this happens
+ (but without actually giving it active focus).
+
+ It should not be set directly on the element, like in the below QML,
+ as the specified value will be overridden an lost on focus changes.
+
+ \code
+ TextInput {
+ text: "Text"
+ cursorVisible: false
+ }
+ \endcode
+
+ In the above snippet the cursor will still become visible when the
+ TextInput gains active focus.
+*/
bool QSGTextInput::isCursorVisible() const
{
Q_D(const QSGTextInput);
@@ -308,6 +523,10 @@ void QSGTextInput::setCursorVisible(bool on)
emit cursorVisibleChanged(d->cursorVisible);
}
+/*!
+ \qmlproperty int QtQuick2::TextInput::cursorPosition
+ The position of the cursor in the TextInput.
+*/
int QSGTextInput::cursorPosition() const
{
Q_D(const QSGTextInput);
@@ -321,6 +540,10 @@ void QSGTextInput::setCursorPosition(int cp)
d->control->moveCursor(cp);
}
+/*!
+ Returns a Rect which encompasses the cursor, but which may be larger than is
+ required. Ignores custom cursor delegates.
+*/
QRect QSGTextInput::cursorRectangle() const
{
Q_D(const QSGTextInput);
@@ -331,19 +554,49 @@ QRect QSGTextInput::cursorRectangle() const
r.adjust(5 - d->hscroll, 0, -4 - d->hscroll, -1);
return r;
}
+/*!
+ \qmlproperty int QtQuick2::TextInput::selectionStart
+
+ The cursor position before the first character in the current selection.
+
+ This property is read-only. To change the selection, use select(start,end),
+ selectAll(), or selectWord().
+ \sa selectionEnd, cursorPosition, selectedText
+*/
int QSGTextInput::selectionStart() const
{
Q_D(const QSGTextInput);
return d->lastSelectionStart;
}
+/*!
+ \qmlproperty int QtQuick2::TextInput::selectionEnd
+ The cursor position after the last character in the current selection.
+
+ This property is read-only. To change the selection, use select(start,end),
+ selectAll(), or selectWord().
+
+ \sa selectionStart, cursorPosition, selectedText
+*/
int QSGTextInput::selectionEnd() const
{
Q_D(const QSGTextInput);
return d->lastSelectionEnd;
}
+/*!
+ \qmlmethod void QtQuick2::TextInput::select(int start, int end)
+
+ Causes the text from \a start to \a end to be selected.
+
+ If either start or end is out of range, the selection is not changed.
+
+ After calling this, selectionStart will become the lesser
+ and selectionEnd will become the greater (regardless of the order passed
+ to this method).
+ \sa selectionStart, selectionEnd
+*/
void QSGTextInput::select(int start, int end)
{
Q_D(QSGTextInput);
@@ -352,12 +605,32 @@ void QSGTextInput::select(int start, int end)
d->control->setSelection(start, end-start);
}
+/*!
+ \qmlproperty string QtQuick2::TextInput::selectedText
+
+ This read-only property provides the text currently selected in the
+ text input.
+
+ It is equivalent to the following snippet, but is faster and easier
+ to use.
+
+ \js
+ myTextInput.text.toString().substring(myTextInput.selectionStart,
+ myTextInput.selectionEnd);
+ \endjs
+*/
QString QSGTextInput::selectedText() const
{
Q_D(const QSGTextInput);
return d->control->selectedText();
}
+/*!
+ \qmlproperty bool QtQuick2::TextInput::activeFocusOnPress
+
+ Whether the TextInput should gain active focus on a mouse press. By default this is
+ set to true.
+*/
bool QSGTextInput::focusOnPress() const
{
Q_D(const QSGTextInput);
@@ -374,7 +647,12 @@ void QSGTextInput::setFocusOnPress(bool b)
emit activeFocusOnPressChanged(d->focusOnPress);
}
+/*!
+ \qmlproperty bool QtQuick2::TextInput::autoScroll
+ Whether the TextInput should scroll when the text is longer than the width. By default this is
+ set to true.
+*/
bool QSGTextInput::autoScroll() const
{
Q_D(const QSGTextInput);
@@ -395,6 +673,114 @@ void QSGTextInput::setAutoScroll(bool b)
}
#ifndef QT_NO_VALIDATOR
+
+/*!
+ \qmlclass IntValidator QIntValidator
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-visual-elements
+
+ This element provides a validator for integer values.
+
+ IntValidator uses the \l {QLocale::setDefault()}{default locale} to interpret the number and
+ will accept locale specific digits, group separators, and positive and negative signs. In
+ addition, IntValidator is always guaranteed to accept a number formatted according to the "C"
+ locale.
+*/
+/*!
+ \qmlproperty int QtQuick2::IntValidator::top
+
+ This property holds the validator's highest acceptable value.
+ By default, this property's value is derived from the highest signed integer available (typically 2147483647).
+*/
+/*!
+ \qmlproperty int QtQuick2::IntValidator::bottom
+
+ This property holds the validator's lowest acceptable value.
+ By default, this property's value is derived from the lowest signed integer available (typically -2147483647).
+*/
+
+/*!
+ \qmlclass DoubleValidator QDoubleValidator
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-visual-elements
+
+ This element provides a validator for non-integer numbers.
+*/
+
+/*!
+ \qmlproperty real QtQuick2::DoubleValidator::top
+
+ This property holds the validator's maximum acceptable value.
+ By default, this property contains a value of infinity.
+*/
+/*!
+ \qmlproperty real QtQuick2::DoubleValidator::bottom
+
+ This property holds the validator's minimum acceptable value.
+ By default, this property contains a value of -infinity.
+*/
+/*!
+ \qmlproperty int QtQuick2::DoubleValidator::decimals
+
+ This property holds the validator's maximum number of digits after the decimal point.
+ By default, this property contains a value of 1000.
+*/
+/*!
+ \qmlproperty enumeration QtQuick2::DoubleValidator::notation
+ This property holds the notation of how a string can describe a number.
+
+ The possible values for this property are:
+
+ \list
+ \o DoubleValidator.StandardNotation
+ \o DoubleValidator.ScientificNotation (default)
+ \endlist
+
+ If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2).
+*/
+
+/*!
+ \qmlclass RegExpValidator QRegExpValidator
+ \inqmlmodule QtQuick 2
+ \ingroup qml-basic-visual-elements
+
+ This element provides a validator, which counts as valid any string which
+ matches a specified regular expression.
+*/
+/*!
+ \qmlproperty regExp QtQuick2::RegExpValidator::regExp
+
+ This property holds the regular expression used for validation.
+
+ Note that this property should be a regular expression in JS syntax, e.g /a/ for the regular expression
+ matching "a".
+
+ By default, this property contains a regular expression with the pattern .* that matches any string.
+*/
+
+/*!
+ \qmlproperty Validator QtQuick2::TextInput::validator
+
+ Allows you to set a validator on the TextInput. When a validator is set
+ the TextInput will only accept input which leaves the text property in
+ an acceptable or intermediate state. The accepted signal will only be sent
+ if the text is in an acceptable state when enter is pressed.
+
+ Currently supported validators are IntValidator, DoubleValidator and
+ RegExpValidator. An example of using validators is shown below, which allows
+ input of integers between 11 and 31 into the text input:
+
+ \code
+ import QtQuick 1.0
+ TextInput{
+ validator: IntValidator{bottom: 11; top: 31;}
+ focus: true
+ }
+ \endcode
+
+ \sa acceptableInput, inputMask
+*/
+
QValidator* QSGTextInput::validator() const
{
Q_D(const QSGTextInput);
@@ -418,6 +804,15 @@ void QSGTextInput::setValidator(QValidator* v)
}
#endif // QT_NO_VALIDATOR
+/*!
+ \qmlproperty string QtQuick2::TextInput::inputMask
+
+ Allows you to set an input mask on the TextInput, restricting the allowable
+ text inputs. See QLineEdit::inputMask for further details, as the exact
+ same mask strings are used by TextInput.
+
+ \sa acceptableInput, validator
+*/
QString QSGTextInput::inputMask() const
{
Q_D(const QSGTextInput);
@@ -434,12 +829,29 @@ void QSGTextInput::setInputMask(const QString &im)
emit inputMaskChanged(d->control->inputMask());
}
+/*!
+ \qmlproperty bool QtQuick2::TextInput::acceptableInput
+
+ This property is always true unless a validator or input mask has been set.
+ If a validator or input mask has been set, this property will only be true
+ if the current text is acceptable to the validator or input mask as a final
+ string (not as an intermediate string).
+*/
bool QSGTextInput::hasAcceptableInput() const
{
Q_D(const QSGTextInput);
return d->control->hasAcceptableInput();
}
+/*!
+ \qmlsignal QtQuick2::TextInput::onAccepted()
+
+ This handler is called when the Return or Enter key is pressed.
+ Note that if there is a \l validator or \l inputMask set on the text
+ input, the handler will only be emitted if the input is in an acceptable
+ state.
+*/
+
void QSGTextInputPrivate::updateInputMethodHints()
{
Q_Q(QSGTextInput);
@@ -453,7 +865,18 @@ void QSGTextInputPrivate::updateInputMethodHints()
hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
q->setInputMethodHints(hints);
}
-
+/*!
+ \qmlproperty enumeration QtQuick2::TextInput::echoMode
+
+ Specifies how the text should be displayed in the TextInput.
+ \list
+ \o TextInput.Normal - Displays the text as it is. (Default)
+ \o TextInput.Password - Displays asterixes instead of characters.
+ \o TextInput.NoEcho - Displays nothing.
+ \o TextInput.PasswordEchoOnEdit - Displays characters as they are entered
+ while editing, otherwise displays asterisks.
+ \endlist
+*/
QSGTextInput::EchoMode QSGTextInput::echoMode() const
{
Q_D(const QSGTextInput);
@@ -486,6 +909,19 @@ void QSGTextInput::setIMHints(Qt::InputMethodHints hints)
d->updateInputMethodHints();
}
+/*!
+ \qmlproperty Component QtQuick2::TextInput::cursorDelegate
+ The delegate for the cursor in the TextInput.
+
+ If you set a cursorDelegate for a TextInput, this delegate will be used for
+ drawing the cursor instead of the standard cursor. An instance of the
+ delegate will be created and managed by the TextInput when a cursor is
+ needed, and the x property of delegate instance will be set so as
+ to be one pixel before the top left of the current character.
+
+ Note that the root item of the delegate component must be a QDeclarativeItem or
+ QDeclarativeItem derived item.
+*/
QDeclarativeComponent* QSGTextInput::cursorDelegate() const
{
Q_D(const QSGTextInput);
@@ -547,6 +983,15 @@ void QSGTextInput::createCursor()
d->cursorItem->setHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text.
}
+/*!
+ \qmlmethod rect QtQuick2::TextInput::positionToRectangle(int pos)
+
+ This function takes a character position and returns the rectangle that the
+ cursor would occupy, if it was placed at that character position.
+
+ This is similar to setting the cursorPosition, and then querying the cursor
+ rectangle, but the cursorPosition is not changed.
+*/
QRectF QSGTextInput::positionToRectangle(int pos) const
{
Q_D(const QSGTextInput);
@@ -558,6 +1003,24 @@ QRectF QSGTextInput::positionToRectangle(int pos) const
cursorRectangle().height());
}
+/*!
+ \qmlmethod int QtQuick2::TextInput::positionAt(int x, CursorPosition position = CursorBetweenCharacters)
+
+ This function returns the character position at
+ x pixels from the left of the textInput. Position 0 is before the
+ first character, position 1 is after the first character but before the second,
+ and so on until position text.length, which is after all characters.
+
+ This means that for all x values before the first character this function returns 0,
+ and for all x values after the last character this function returns text.length.
+
+ The cursor position type specifies how the cursor position should be resolved.
+
+ \list
+ \o TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x.
+ \o TextInput.CursorOnCharacter - Returns the position before the character that is nearest x.
+ \endlist
+*/
int QSGTextInput::positionAt(int x) const
{
return positionAt(x, CursorBetweenCharacters);
@@ -597,7 +1060,7 @@ void QSGTextInput::keyPressEvent(QKeyEvent* ev)
d->control->processKeyEvent(ev);
}
if (!ev->isAccepted())
- QSGPaintedItem::keyPressEvent(ev);
+ QSGImplicitSizeItem::keyPressEvent(ev);
}
void QSGTextInput::inputMethodEvent(QInputMethodEvent *ev)
@@ -610,7 +1073,7 @@ void QSGTextInput::inputMethodEvent(QInputMethodEvent *ev)
d->control->processInputMethodEvent(ev);
}
if (!ev->isAccepted())
- QSGPaintedItem::inputMethodEvent(ev);
+ QSGImplicitSizeItem::inputMethodEvent(ev);
if (wasComposing != (d->control->preeditAreaText().length() > 0))
emit inputMethodComposingChanged();
@@ -626,7 +1089,7 @@ void QSGTextInput::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
d->control->selectWordAtPos(cursor);
event->setAccepted(true);
} else {
- QSGPaintedItem::mouseDoubleClickEvent(event);
+ QSGImplicitSizeItem::mouseDoubleClickEvent(event);
}
}
@@ -671,7 +1134,7 @@ void QSGTextInput::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
moveCursorSelection(d->xToPos(event->pos().x()), d->mouseSelectionMode);
event->setAccepted(true);
} else {
- QSGPaintedItem::mouseMoveEvent(event);
+ QSGImplicitSizeItem::mouseMoveEvent(event);
}
}
@@ -696,7 +1159,7 @@ void QSGTextInput::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
d->clickCausedFocus = false;
d->control->processEvent(event);
if (!event->isAccepted())
- QSGPaintedItem::mouseReleaseEvent(event);
+ QSGImplicitSizeItem::mouseReleaseEvent(event);
}
bool QSGTextInputPrivate::sendMouseEventToInputContext(
@@ -762,7 +1225,7 @@ bool QSGTextInput::event(QEvent* ev)
handled = d->control->processEvent(ev);
}
if(!handled)
- handled = QSGPaintedItem::event(ev);
+ handled = QSGImplicitSizeItem::event(ev);
return handled;
}
@@ -773,7 +1236,7 @@ void QSGTextInput::geometryChanged(const QRectF &newGeometry,
updateSize();
updateCursorRectangle();
}
- QSGPaintedItem::geometryChanged(newGeometry, oldGeometry);
+ QSGImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
}
int QSGTextInputPrivate::calculateTextWidth()
@@ -827,25 +1290,73 @@ void QSGTextInputPrivate::updateHorizontalScroll()
}
}
-void QSGTextInput::paint(QPainter *p)
+QSGNode *QSGTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
{
- // XXX todo
- QRect r = boundingRect().toRect();
-
+ Q_UNUSED(data);
Q_D(QSGTextInput);
- p->setRenderHint(QPainter::TextAntialiasing, true);
- p->save();
- p->setPen(QPen(d->color));
- int flags = QLineControl::DrawText;
- if(!isReadOnly() && d->cursorVisible && !d->cursorItem)
- flags |= QLineControl::DrawCursor;
- if (d->control->hasSelectedText())
- flags |= QLineControl::DrawSelections;
- QFontMetrics fm = QFontMetrics(d->font);
- // the y offset is there to keep the baseline constant in case we have script changes in the text.
- QPoint offset(-d->hscroll, fm.ascent() - d->control->ascent());
- d->control->draw(p, offset, r, flags);
- p->restore();
+
+ QSGTextNode *node = static_cast<QSGTextNode *>(oldNode);
+ if (node == 0)
+ node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
+ d->textNode = node;
+
+ if (!d->textLayoutDirty) {
+ QSGSimpleRectNode *cursorNode = node->cursorNode();
+ if (cursorNode != 0 && !isReadOnly()) {
+ QFontMetrics fm = QFontMetrics(d->font);
+ // the y offset is there to keep the baseline constant in case we have script changes in the text.
+ QPoint offset(-d->hscroll, fm.ascent() - d->control->ascent());
+ offset.rx() += d->control->cursorToX();
+
+ QRect br(boundingRect().toRect());
+ cursorNode->setRect(QRectF(offset, QSizeF(d->control->cursorWidth(), br.height())));
+
+ if (!d->cursorVisible
+ || (!d->control->cursorBlinkStatus() && d->control->cursorBlinkPeriod() > 0)) {
+ d->hideCursor();
+ } else {
+ d->showCursor();
+ }
+ }
+ } else {
+ node->deleteContent();
+ node->setMatrix(QMatrix4x4());
+
+ QPoint offset = QPoint(0,0);
+ QFontMetrics fm = QFontMetrics(d->font);
+ QRect br(boundingRect().toRect());
+ if (d->autoScroll) {
+ // the y offset is there to keep the baseline constant in case we have script changes in the text.
+ offset = br.topLeft() - QPoint(d->hscroll, d->control->ascent() - fm.ascent());
+ } else {
+ offset = QPoint(d->hscroll, 0);
+ }
+
+ QTextLayout *textLayout = d->control->textLayout();
+ if (!textLayout->text().isEmpty()) {
+ node->addTextLayout(offset, textLayout, d->color,
+ QSGText::Normal, QColor(),
+ d->selectionColor, d->selectedTextColor,
+ d->control->selectionStart(),
+ d->control->selectionEnd() - 1); // selectionEnd() returns first char after
+ // selection
+ }
+
+ if (!isReadOnly() && d->cursorItem == 0) {
+ offset.rx() += d->control->cursorToX();
+ node->setCursor(QRectF(offset, QSizeF(d->control->cursorWidth(), br.height())), d->color);
+ if (!d->cursorVisible
+ || (!d->control->cursorBlinkStatus() && d->control->cursorBlinkPeriod() > 0)) {
+ d->hideCursor();
+ } else {
+ d->showCursor();
+ }
+ }
+
+ d->textLayoutDirty = false;
+ }
+
+ return node;
}
QVariant QSGTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
@@ -879,18 +1390,34 @@ QVariant QSGTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
}
}
+/*!
+ \qmlmethod void QtQuick2::TextInput::deselect()
+
+ Removes active text selection.
+*/
void QSGTextInput::deselect()
{
Q_D(QSGTextInput);
d->control->deselect();
}
+/*!
+ \qmlmethod void QtQuick2::TextInput::selectAll()
+
+ Causes all text to be selected.
+*/
void QSGTextInput::selectAll()
{
Q_D(QSGTextInput);
d->control->setSelection(0, d->control->text().length());
}
+/*!
+ \qmlmethod void QtQuick2::TextInput::isRightToLeft(int start, int end)
+
+ Returns true if the natural reading direction of the editor text
+ found between positions \a start and \a end is right to left.
+*/
bool QSGTextInput::isRightToLeft(int start, int end)
{
Q_D(QSGTextInput);
@@ -903,6 +1430,11 @@ bool QSGTextInput::isRightToLeft(int start, int end)
}
#ifndef QT_NO_CLIPBOARD
+/*!
+ \qmlmethod QtQuick2::TextInput::cut()
+
+ Moves the currently selected text to the system clipboard.
+*/
void QSGTextInput::cut()
{
Q_D(QSGTextInput);
@@ -910,12 +1442,22 @@ void QSGTextInput::cut()
d->control->del();
}
+/*!
+ \qmlmethod QtQuick2::TextInput::copy()
+
+ Copies the currently selected text to the system clipboard.
+*/
void QSGTextInput::copy()
{
Q_D(QSGTextInput);
d->control->copy();
}
+/*!
+ \qmlmethod QtQuick2::TextInput::paste()
+
+ Replaces the currently selected text by the contents of the system clipboard.
+*/
void QSGTextInput::paste()
{
Q_D(QSGTextInput);
@@ -924,12 +1466,41 @@ void QSGTextInput::paste()
}
#endif // QT_NO_CLIPBOARD
+/*!
+ \qmlmethod void QtQuick2::TextInput::selectWord()
+
+ Causes the word closest to the current cursor position to be selected.
+*/
void QSGTextInput::selectWord()
{
Q_D(QSGTextInput);
d->control->selectWordAtPos(d->control->cursor());
}
+/*!
+ \qmlproperty bool QtQuick2::TextInput::smooth
+
+ This property holds whether the text is smoothly scaled or transformed.
+
+ Smooth filtering gives better visual quality, but is slower. If
+ the item is displayed at its natural size, this property has no visual or
+ performance effect.
+
+ \note Generally scaling artifacts are only visible if the item is stationary on
+ the screen. A common pattern when animating an item is to disable smooth
+ filtering at the beginning of the animation and reenable it at the conclusion.
+*/
+
+/*!
+ \qmlproperty string QtQuick2::TextInput::passwordCharacter
+
+ This is the character displayed when echoMode is set to Password or
+ PasswordEchoOnEdit. By default it is an asterisk.
+
+ If this property is set to a string with more than one character,
+ the first character is used. If the string is empty, the value
+ is ignored and the property is not set.
+*/
QString QSGTextInput::passwordCharacter() const
{
Q_D(const QSGTextInput);
@@ -949,12 +1520,32 @@ void QSGTextInput::setPasswordCharacter(const QString &str)
emit passwordCharacterChanged();
}
+/*!
+ \qmlproperty string QtQuick2::TextInput::displayText
+
+ This is the text displayed in the TextInput.
+
+ If \l echoMode is set to TextInput::Normal, this holds the
+ same value as the TextInput::text property. Otherwise,
+ this property holds the text visible to the user, while
+ the \l text property holds the actual entered text.
+*/
QString QSGTextInput::displayText() const
{
Q_D(const QSGTextInput);
return d->control->displayText();
}
+/*!
+ \qmlproperty bool QtQuick2::TextInput::selectByMouse
+
+ Defaults to false.
+
+ If true, the user can use the mouse to select text in some
+ platform-specific way. Note that for some platforms this may
+ not be an appropriate interaction (eg. may conflict with how
+ the text needs to behave inside a Flickable.
+*/
bool QSGTextInput::selectByMouse() const
{
Q_D(const QSGTextInput);
@@ -970,6 +1561,19 @@ void QSGTextInput::setSelectByMouse(bool on)
}
}
+/*!
+ \qmlproperty enum QtQuick2::TextInput::mouseSelectionMode
+
+ Specifies how text should be selected using a mouse.
+
+ \list
+ \o TextInput.SelectCharacters - The selection is updated with individual characters. (Default)
+ \o TextInput.SelectWords - The selection is updated with whole words.
+ \endlist
+
+ This property only applies when \l selectByMouse is true.
+*/
+
QSGTextInput::SelectionMode QSGTextInput::mouseSelectionMode() const
{
Q_D(const QSGTextInput);
@@ -985,6 +1589,12 @@ void QSGTextInput::setMouseSelectionMode(SelectionMode mode)
}
}
+/*!
+ \qmlproperty bool QtQuick2::TextInput::canPaste
+
+ Returns true if the TextInput is writable and the content of the clipboard is
+ suitable for pasting into the TextEdit.
+*/
bool QSGTextInput::canPaste() const
{
Q_D(const QSGTextInput);
@@ -997,6 +1607,43 @@ void QSGTextInput::moveCursorSelection(int position)
d->control->moveCursor(position, true);
}
+/*!
+ \qmlmethod void QtQuick2::TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters)
+
+ Moves the cursor to \a position and updates the selection according to the optional \a mode
+ parameter. (To only move the cursor, set the \l cursorPosition property.)
+
+ When this method is called it additionally sets either the
+ selectionStart or the selectionEnd (whichever was at the previous cursor position)
+ to the specified position. This allows you to easily extend and contract the selected
+ text range.
+
+ The selection mode specifies whether the selection is updated on a per character or a per word
+ basis. If not specified the selection mode will default to TextInput.SelectCharacters.
+
+ \list
+ \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
+ the previous cursor position) to the specified position.
+ \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all
+ words between the specified postion and the previous cursor position. Words partially in the
+ range are included.
+ \endlist
+
+ For example, take this sequence of calls:
+
+ \code
+ cursorPosition = 5
+ moveCursorSelection(9, TextInput.SelectCharacters)
+ moveCursorSelection(7, TextInput.SelectCharacters)
+ \endcode
+
+ This moves the cursor to position 5, extend the selection end from 5 to 9
+ and then retract the selection end from 9 to 7, leaving the text from position 5 to 7
+ selected (the 6th and 7th characters).
+
+ The same sequence with TextInput.SelectWords will extend the selection start to a word boundary
+ before or on position 5 and extend the selection end to a word boundary on or past position 9.
+*/
void QSGTextInput::moveCursorSelection(int pos, SelectionMode mode)
{
Q_D(QSGTextInput);
@@ -1054,6 +1701,45 @@ void QSGTextInput::moveCursorSelection(int pos, SelectionMode mode)
}
}
+/*!
+ \qmlmethod void QtQuick2::TextInput::openSoftwareInputPanel()
+
+ Opens software input panels like virtual keyboards for typing, useful for
+ customizing when you want the input keyboard to be shown and hidden in
+ your application.
+
+ By default the opening of input panels follows the platform style. On Symbian^1 and
+ Symbian^3 -based devices the panels are opened by clicking TextInput. On other platforms
+ the panels are automatically opened when TextInput element gains active focus. Input panels are
+ always closed if no editor has active focus.
+
+ . You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
+ and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
+ the behavior you want.
+
+ Only relevant on platforms, which provide virtual keyboards.
+
+ \qml
+ import QtQuick 1.0
+ TextInput {
+ id: textInput
+ text: "Hello world!"
+ activeFocusOnPress: false
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (!textInput.activeFocus) {
+ textInput.forceActiveFocus()
+ textInput.openSoftwareInputPanel();
+ } else {
+ textInput.focus = false;
+ }
+ }
+ onPressAndHold: textInput.closeSoftwareInputPanel();
+ }
+ }
+ \endqml
+*/
void QSGTextInput::openSoftwareInputPanel()
{
if (qApp) {
@@ -1064,6 +1750,45 @@ void QSGTextInput::openSoftwareInputPanel()
}
}
+/*!
+ \qmlmethod void QtQuick2::TextInput::closeSoftwareInputPanel()
+
+ Closes a software input panel like a virtual keyboard shown on the screen, useful
+ for customizing when you want the input keyboard to be shown and hidden in
+ your application.
+
+ By default the opening of input panels follows the platform style. On Symbian^1 and
+ Symbian^3 -based devices the panels are opened by clicking TextInput. On other platforms
+ the panels are automatically opened when TextInput element gains active focus. Input panels are
+ always closed if no editor has active focus.
+
+ . You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
+ and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
+ the behavior you want.
+
+ Only relevant on platforms, which provide virtual keyboards.
+
+ \qml
+ import QtQuick 1.0
+ TextInput {
+ id: textInput
+ text: "Hello world!"
+ activeFocusOnPress: false
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ if (!textInput.activeFocus) {
+ textInput.forceActiveFocus();
+ textInput.openSoftwareInputPanel();
+ } else {
+ textInput.focus = false;
+ }
+ }
+ onPressAndHold: textInput.closeSoftwareInputPanel();
+ }
+ }
+ \endqml
+*/
void QSGTextInput::closeSoftwareInputPanel()
{
if (qApp) {
@@ -1082,7 +1807,7 @@ void QSGTextInput::focusInEvent(QFocusEvent *event)
openSoftwareInputPanel();
}
}
- QSGPaintedItem::focusInEvent(event);
+ QSGImplicitSizeItem::focusInEvent(event);
}
void QSGTextInput::itemChange(ItemChange change, const ItemChangeData &value)
@@ -1100,6 +1825,18 @@ void QSGTextInput::itemChange(ItemChange change, const ItemChangeData &value)
QSGItem::itemChange(change, value);
}
+/*!
+ \qmlproperty bool QtQuick2::TextInput::inputMethodComposing
+
+
+ This property holds whether the TextInput has partial text input from an
+ input method.
+
+ While it is composing an input method may rely on mouse or key events from
+ the TextInput to edit or commit the partial text. This property can be
+ used to determine when to disable events handlers that may interfere with
+ the correct operation of an input method.
+*/
bool QSGTextInput::isInputMethodComposing() const
{
Q_D(const QSGTextInput);
@@ -1109,12 +1846,16 @@ bool QSGTextInput::isInputMethodComposing() const
void QSGTextInputPrivate::init()
{
Q_Q(QSGTextInput);
+#if defined(Q_WS_MAC)
+ control->setThreadChecks(true);
+#endif
control->setParent(q);//Now mandatory due to accessibility changes
control->setCursorWidth(1);
control->setPasswordCharacter(QLatin1Char('*'));
q->setSmooth(smooth);
q->setAcceptedMouseButtons(Qt::LeftButton);
q->setFlag(QSGItem::ItemAcceptsInputMethod);
+ q->setFlag(QSGItem::ItemHasContents);
q->connect(control, SIGNAL(cursorPositionChanged(int,int)),
q, SLOT(cursorPosChanged()));
q->connect(control, SIGNAL(selectionChanged()),
@@ -1144,6 +1885,12 @@ void QSGTextInputPrivate::init()
selectedTextColor = p.color(QPalette::HighlightedText);
selectionColor = p.color(QPalette::Highlight);
determineHorizontalAlignment();
+
+ if (!qmlDisableDistanceField()) {
+ QTextOption option = control->textLayout()->textOption();
+ option.setUseDesignMetrics(true);
+ control->textLayout()->setTextOption(option);
+ }
}
void QSGTextInput::cursorPosChanged()
@@ -1214,19 +1961,35 @@ void QSGTextInput::q_textChanged()
}
}
+void QSGTextInputPrivate::showCursor()
+{
+ if (textNode != 0 && textNode->cursorNode() != 0)
+ textNode->cursorNode()->setColor(color);
+}
+
+void QSGTextInputPrivate::hideCursor()
+{
+ if (textNode != 0 && textNode->cursorNode() != 0)
+ textNode->cursorNode()->setColor(QColor(0, 0, 0, 0));
+}
+
void QSGTextInput::updateRect(const QRect &r)
{
Q_D(QSGTextInput);
- if(r == QRect())
- update();
- else
- update(QRect(r.x() - d->hscroll, r.y(), r.width(), r.height()));
+ if (!isComponentComplete())
+ return;
+
+ if (r.isEmpty()) {
+ d->textLayoutDirty = true;
+ }
+
+ update();
}
QRectF QSGTextInput::boundingRect() const
{
Q_D(const QSGTextInput);
- QRectF r = QSGPaintedItem::boundingRect();
+ QRectF r = QSGImplicitSizeItem::boundingRect();
int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->control->cursorWidth();
@@ -1243,7 +2006,6 @@ void QSGTextInput::updateSize(bool needsRedraw)
int h = height();
setImplicitHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text.
setImplicitWidth(d->calculateTextWidth());
- setContentsSize(boundingRect().size().toSize());
if(w==width() && h==height() && needsRedraw)
update();
}
diff --git a/src/declarative/items/qsgtextinput_p.h b/src/declarative/items/qsgtextinput_p.h
index 770afa8283..f5ac50e8e1 100644
--- a/src/declarative/items/qsgtextinput_p.h
+++ b/src/declarative/items/qsgtextinput_p.h
@@ -54,7 +54,7 @@ QT_MODULE(Declarative)
class QSGTextInputPrivate;
class QValidator;
-class Q_AUTOTEST_EXPORT QSGTextInput : public QSGImplicitSizePaintedItem
+class Q_AUTOTEST_EXPORT QSGTextInput : public QSGImplicitSizeItem
{
Q_OBJECT
Q_ENUMS(HAlignment)
@@ -204,7 +204,6 @@ public:
bool hasAcceptableInput() const;
- void paint(QPainter *p);
QVariant inputMethodQuery(Qt::InputMethodQuery property) const;
QRectF boundingRect() const;
@@ -261,6 +260,7 @@ protected:
bool event(QEvent *e);
void focusInEvent(QFocusEvent *event);
virtual void itemChange(ItemChange, const ItemChangeData &);
+ QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data);
public Q_SLOTS:
void selectAll();
diff --git a/src/declarative/items/qsgtextinput_p_p.h b/src/declarative/items/qsgtextinput_p_p.h
index f25722972b..0d1f21b54c 100644
--- a/src/declarative/items/qsgtextinput_p_p.h
+++ b/src/declarative/items/qsgtextinput_p_p.h
@@ -65,18 +65,35 @@
QT_BEGIN_NAMESPACE
-class Q_AUTOTEST_EXPORT QSGTextInputPrivate : public QSGImplicitSizePaintedItemPrivate
+class QSGTextNode;
+
+class Q_AUTOTEST_EXPORT QSGTextInputPrivate : public QSGImplicitSizeItemPrivate
{
Q_DECLARE_PUBLIC(QSGTextInput)
public:
- QSGTextInputPrivate() : control(new QLineControl(QString())),
- color((QRgb)0), style(QSGText::Normal),
- styleColor((QRgb)0), hAlign(QSGTextInput::AlignLeft),
- mouseSelectionMode(QSGTextInput::SelectCharacters), inputMethodHints(Qt::ImhNone),
- hscroll(0), oldScroll(0), oldValidity(false), focused(false), focusOnPress(true),
- showInputPanelOnFocus(true), clickCausedFocus(false), cursorVisible(false),
- autoScroll(true), selectByMouse(false), canPaste(false), hAlignImplicit(true),
- selectPressed(false)
+ QSGTextInputPrivate()
+ : control(new QLineControl(QString()))
+ , color((QRgb)0)
+ , style(QSGText::Normal)
+ , styleColor((QRgb)0)
+ , hAlign(QSGTextInput::AlignLeft)
+ , mouseSelectionMode(QSGTextInput::SelectCharacters)
+ , inputMethodHints(Qt::ImhNone)
+ , textNode(0)
+ , hscroll(0)
+ , oldScroll(0)
+ , oldValidity(false)
+ , focused(false)
+ , focusOnPress(true)
+ , showInputPanelOnFocus(true)
+ , clickCausedFocus(false)
+ , cursorVisible(false)
+ , autoScroll(true)
+ , selectByMouse(false)
+ , canPaste(false)
+ , hAlignImplicit(true)
+ , selectPressed(false)
+ , textLayoutDirty(true)
{
#ifdef Q_OS_SYMBIAN
if (QSysInfo::symbianVersion() == QSysInfo::SV_SF_1 || QSysInfo::symbianVersion() == QSysInfo::SV_SF_3) {
@@ -106,6 +123,8 @@ public:
int calculateTextWidth();
bool sendMouseEventToInputContext(QGraphicsSceneMouseEvent *event, QEvent::Type eventType);
void updateInputMethodHints();
+ void hideCursor();
+ void showCursor();
QLineControl* control;
@@ -122,6 +141,7 @@ public:
QPointer<QDeclarativeComponent> cursorComponent;
QPointer<QSGItem> cursorItem;
QPointF pressPos;
+ QSGTextNode *textNode;
int lastSelectionStart;
int lastSelectionEnd;
@@ -141,6 +161,7 @@ public:
bool canPaste:1;
bool hAlignImplicit:1;
bool selectPressed:1;
+ bool textLayoutDirty:1;
static inline QSGTextInputPrivate *get(QSGTextInput *t) {
return t->d_func();
diff --git a/src/declarative/items/qsgtextnode.cpp b/src/declarative/items/qsgtextnode.cpp
index 03a558fe3d..1ae457b6fa 100644
--- a/src/declarative/items/qsgtextnode.cpp
+++ b/src/declarative/items/qsgtextnode.cpp
@@ -47,6 +47,7 @@
#include <private/qsgcontext_p.h>
+#include <QtCore/qpoint.h>
#include <qmath.h>
#include <qtextdocument.h>
#include <qtextlayout.h>
@@ -57,6 +58,7 @@
#include <private/qfont_p.h>
#include <private/qfontengine_p.h>
#include <private/qrawfont_p.h>
+#include <qhash.h>
QT_BEGIN_NAMESPACE
@@ -64,7 +66,7 @@ QT_BEGIN_NAMESPACE
Creates an empty QSGTextNode
*/
QSGTextNode::QSGTextNode(QSGContext *context)
-: m_context(context)
+ : m_context(context), m_cursorNode(0)
{
#if defined(QML_RUNTIME_TESTING)
description = QLatin1String("text");
@@ -119,131 +121,682 @@ void QSGTextNode::setStyleColor(const QColor &styleColor)
}
#endif
-void QSGTextNode::addTextDecorations(Decoration decorations, const QPointF &position,
- const QColor &color, qreal width, qreal lineThickness,
- qreal underlinePos, qreal ascent)
+QSGGlyphNode *QSGTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
+ QSGText::TextStyle style, const QColor &styleColor,
+ QSGNode *parentNode)
{
- QRectF line(position.x(), position.y() - lineThickness / 2.0, width, lineThickness);
+ QSGGlyphNode *node = m_context->createGlyphNode();
+ node->setGlyphs(position + QPointF(0, glyphs.rawFont().ascent()), glyphs);
+ node->setStyle(style);
+ node->setStyleColor(styleColor);
+ node->setColor(color);
+ node->update();
- if (decorations & Underline) {
- int underlinePosition = qCeil(underlinePos);
- QRectF underline(line);
- underline.translate(0.0, underlinePosition);
- appendChildNode(new QSGSimpleRectNode(underline, color));
- }
+ /* We flag the geometry as static, but we never call markVertexDataDirty
+ or markIndexDataDirty on them. This is because all text nodes are
+ discarded when a change occurs. If we start appending/removing from
+ existing geometry, then we also need to start marking the geometry as
+ dirty.
+ */
+ node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
+ node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
- if (decorations & Overline) {
- QRectF overline(line);
- overline.translate(0.0, -ascent);
- appendChildNode(new QSGSimpleRectNode(overline, color));
- }
+ if (parentNode == 0)
+ parentNode = this;
+ parentNode->appendChildNode(node);
- if (decorations & StrikeOut) {
- QRectF strikeOut(line);
- strikeOut.translate(0.0, ascent / -3.0);
- appendChildNode(new QSGSimpleRectNode(strikeOut, color));
- }
+ return node;
}
-QSGGlyphNode *QSGTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
- QSGText::TextStyle style, const QColor &styleColor, QSGGlyphNode *prevNode)
+void QSGTextNode::setCursor(const QRectF &rect, const QColor &color)
{
- QSGGlyphNode *node = prevNode;
+ if (m_cursorNode != 0)
+ delete m_cursorNode;
+
+ m_cursorNode = new QSGSimpleRectNode(rect, color);
+ appendChildNode(m_cursorNode);
+}
+
+namespace {
+
+ struct BinaryTreeNode {
+ enum SelectionState {
+ Unselected,
+ Selected
+ };
+
+ BinaryTreeNode()
+ : selectionState(Unselected)
+ , clipNode(0)
+ , decorations(QSGTextNode::NoDecoration)
+ , leftChildIndex(-1)
+ , rightChildIndex(-1)
+ {
+
+ }
+
+ BinaryTreeNode(const QGlyphRun &g, SelectionState selState, const QRectF &brect,
+ const QSGTextNode::Decorations &decs, const QColor &c,
+ const QPointF &pos)
+ : glyphRun(g)
+ , boundingRect(brect)
+ , selectionState(selState)
+ , clipNode(0)
+ , decorations(decs)
+ , color(c)
+ , position(pos)
+ , leftChildIndex(-1)
+ , rightChildIndex(-1)
+ {
+ }
+
+ QGlyphRun glyphRun;
+ QRectF boundingRect;
+ SelectionState selectionState;
+ QSGClipNode *clipNode;
+ QSGTextNode::Decorations decorations;
+ QColor color;
+ QPointF position;
+
+ int leftChildIndex;
+ int rightChildIndex;
+
+ static void insert(QVarLengthArray<BinaryTreeNode> *binaryTree,
+ const QGlyphRun &glyphRun,
+ SelectionState selectionState,
+ const QColor &textColor,
+ const QPointF &position)
+ {
+ int newIndex = binaryTree->size();
+ QRectF searchRect = glyphRun.boundingRect();
+
+ if (qFuzzyIsNull(searchRect.width()) || qFuzzyIsNull(searchRect.height()))
+ return;
+
+ QSGTextNode::Decorations decorations = QSGTextNode::NoDecoration;
+ decorations |= (glyphRun.underline() ? QSGTextNode::Underline : QSGTextNode::NoDecoration);
+ decorations |= (glyphRun.overline() ? QSGTextNode::Overline : QSGTextNode::NoDecoration);
+ decorations |= (glyphRun.strikeOut() ? QSGTextNode::StrikeOut : QSGTextNode::NoDecoration);
+
+ binaryTree->append(BinaryTreeNode(glyphRun, selectionState, searchRect, decorations,
+ textColor, position));
+ if (newIndex == 0)
+ return;
+
+ int searchIndex = 0;
+ forever {
+ BinaryTreeNode *node = binaryTree->data() + searchIndex;
+ if (searchRect.left() < node->boundingRect.left()) {
+ if (node->leftChildIndex < 0) {
+ node->leftChildIndex = newIndex;
+ break;
+ } else {
+ searchIndex = node->leftChildIndex;
+ }
+ } else {
+ if (node->rightChildIndex < 0) {
+ node->rightChildIndex = newIndex;
+ break;
+ } else {
+ searchIndex = node->rightChildIndex;
+ }
+ }
+ }
+ }
+
+ static void inOrder(const QVarLengthArray<BinaryTreeNode> &binaryTree,
+ QVarLengthArray<int> *sortedIndexes,
+ int currentIndex = 0)
+ {
+ Q_ASSERT(currentIndex < binaryTree.size());
- if (!node)
- node = m_context->createGlyphNode();
+ const BinaryTreeNode *node = binaryTree.data() + currentIndex;
+ if (node->leftChildIndex >= 0)
+ inOrder(binaryTree, sortedIndexes, node->leftChildIndex);
- node->setGlyphs(position, glyphs);
+ sortedIndexes->append(currentIndex);
- if (node != prevNode) {
- if (QSGDistanceFieldGlyphCache::distanceFieldEnabled()) {
- QSGDistanceFieldGlyphNode *dfNode = static_cast<QSGDistanceFieldGlyphNode *>(node);
- dfNode->setStyle(style);
- dfNode->setStyleColor(styleColor);
+ if (node->rightChildIndex >= 0)
+ inOrder(binaryTree, sortedIndexes, node->rightChildIndex);
+ }
+ };
+
+ // Engine that takes glyph runs as input, and produces a set of glyph nodes, clip nodes,
+ // and rectangle nodes to represent the text, decorations and selection. Will try to minimize
+ // number of nodes, and join decorations in neighbouring items
+ class SelectionEngine
+ {
+ public:
+ SelectionEngine() : m_hasSelection(false) {}
+
+ QTextLine currentLine() const { return m_currentLine; }
+
+ void setCurrentLine(const QTextLine &currentLine)
+ {
+ if (m_currentLine.isValid())
+ processCurrentLine();
+
+ m_currentLine = currentLine;
+ }
+
+ void addSelectedGlyphs(const QGlyphRun &glyphRun);
+ void addUnselectedGlyphs(const QGlyphRun &glyphRun);
+
+ void addToSceneGraph(QSGTextNode *parent,
+ QSGText::TextStyle style = QSGText::Normal,
+ const QColor &styleColor = QColor());
+
+ void setSelectionColor(const QColor &selectionColor)
+ {
+ m_selectionColor = selectionColor;
+ }
+
+ void setSelectedTextColor(const QColor &selectedTextColor)
+ {
+ m_selectedTextColor = selectedTextColor;
+ }
+
+ void setTextColor(const QColor &textColor)
+ {
+ m_textColor = textColor;
+ }
+
+ void setPosition(const QPointF &position)
+ {
+ m_position = position;
+ }
+
+ private:
+ struct TextDecoration
+ {
+ TextDecoration() : selectionState(BinaryTreeNode::Unselected) {}
+ TextDecoration(const BinaryTreeNode::SelectionState &s,
+ const QRectF &r,
+ const QColor &c)
+ : selectionState(s)
+ , rect(r)
+ , color(c)
+ {
+ }
+
+ BinaryTreeNode::SelectionState selectionState;
+ QRectF rect;
+ QColor color;
+ };
+
+ void processCurrentLine();
+ void addTextDecorations(const QVarLengthArray<TextDecoration> &textDecorations,
+ qreal offset, qreal thickness);
+
+ QColor m_selectionColor;
+ QColor m_textColor;
+ QColor m_selectedTextColor;
+ QPointF m_position;
+
+ QTextLine m_currentLine;
+ bool m_hasSelection;
+
+ QList<QRectF> m_selectionRects;
+ QVarLengthArray<BinaryTreeNode> m_currentLineTree;
+
+ QList<TextDecoration> m_lines;
+ QVector<BinaryTreeNode> m_processedNodes;
+ };
+
+ void SelectionEngine::addTextDecorations(const QVarLengthArray<TextDecoration> &textDecorations,
+ qreal offset, qreal thickness)
+ {
+ for (int i=0; i<textDecorations.size(); ++i) {
+ TextDecoration textDecoration = textDecorations.at(i);
+
+ {
+ QRectF &rect = textDecoration.rect;
+ rect.setY(qRound(rect.y() + m_currentLine.ascent() + offset));
+ rect.setHeight(thickness);
+ }
+
+ m_lines.append(textDecoration);
}
- node->setColor(color);
}
- node->update();
+ void SelectionEngine::processCurrentLine()
+ {
+ // No glyphs, do nothing
+ if (m_currentLineTree.isEmpty())
+ return;
+
+ // 1. Go through current line and get correct decoration position for each node based on
+ // neighbouring decorations. Add decoration to global list
+ // 2. Create clip nodes for all selected text. Try to merge as many as possible within
+ // the line.
+ // 3. Add QRects to a list of selection rects.
+ // 4. Add all nodes to a global processed list
+ QVarLengthArray<int> sortedIndexes; // Indexes in tree sorted by x position
+ BinaryTreeNode::inOrder(m_currentLineTree, &sortedIndexes);
+
+ Q_ASSERT(sortedIndexes.size() == m_currentLineTree.size());
+
+ BinaryTreeNode::SelectionState currentSelectionState = BinaryTreeNode::Unselected;
+ QRectF currentRect;
+
+ QSGTextNode::Decorations currentDecorations = QSGTextNode::NoDecoration;
+ qreal underlineOffset = 0.0;
+ qreal underlineThickness = 0.0;
+
+ qreal overlineOffset = 0.0;
+ qreal overlineThickness = 0.0;
+
+ qreal strikeOutOffset = 0.0;
+ qreal strikeOutThickness = 0.0;
+
+ QRectF decorationRect = currentRect;
+
+ QColor lastColor;
+
+ QVarLengthArray<TextDecoration> pendingUnderlines;
+ QVarLengthArray<TextDecoration> pendingOverlines;
+ QVarLengthArray<TextDecoration> pendingStrikeOuts;
+ if (!sortedIndexes.isEmpty()) {
+ QSGClipNode *currentClipNode = m_hasSelection ? new QSGClipNode : 0;
+ bool currentClipNodeUsed = false;
+ for (int i=0; i<=sortedIndexes.size(); ++i) {
+ BinaryTreeNode *node = 0;
+ if (i < sortedIndexes.size()) {
+ int sortedIndex = sortedIndexes.at(i);
+ Q_ASSERT(sortedIndex < m_currentLineTree.size());
+
+ node = m_currentLineTree.data() + sortedIndex;
+ }
+
+ if (i == 0)
+ currentSelectionState = node->selectionState;
+
+ // Update decorations
+ if (currentDecorations != QSGTextNode::NoDecoration) {
+ decorationRect.setY(m_position.y() + m_currentLine.y());
+ decorationRect.setHeight(m_currentLine.height());
+
+ if (node != 0)
+ decorationRect.setRight(node->boundingRect.left());
+
+ TextDecoration textDecoration(currentSelectionState, decorationRect, lastColor);
+ if (currentDecorations & QSGTextNode::Underline)
+ pendingUnderlines.append(textDecoration);
+
+ if (currentDecorations & QSGTextNode::Overline)
+ pendingOverlines.append(textDecoration);
+
+ if (currentDecorations & QSGTextNode::StrikeOut)
+ pendingStrikeOuts.append(textDecoration);
+ }
+
+ // If we've reached an unselected node from a selected node, we add the
+ // selection rect to the graph, and we add decoration every time the
+ // selection state changes, because that means the text color changes
+ if (node == 0 || node->selectionState != currentSelectionState) {
+ if (node != 0)
+ currentRect.setRight(node->boundingRect.left());
+ currentRect.setY(m_position.y() + m_currentLine.y());
+ currentRect.setHeight(m_currentLine.height());
+
+ // Draw selection all the way up to the left edge of the unselected item
+ if (currentSelectionState == BinaryTreeNode::Selected)
+ m_selectionRects.append(currentRect);
+
+ if (currentClipNode != 0) {
+ if (!currentClipNodeUsed) {
+ delete currentClipNode;
+ } else {
+ currentClipNode->setIsRectangular(true);
+ currentClipNode->setClipRect(currentRect);
+ }
+ }
+
+ if (node != 0 && m_hasSelection)
+ currentClipNode = new QSGClipNode;
+ else
+ currentClipNode = 0;
+ currentClipNodeUsed = false;
+
+ if (node != 0) {
+ currentSelectionState = node->selectionState;
+ currentRect = node->boundingRect;
+
+ // Make sure currentRect is valid, otherwise the unite won't work
+ if (currentRect.isNull())
+ currentRect.setSize(QSizeF(1, 1));
+ }
+ } else {
+ if (currentRect.isNull())
+ currentRect = node->boundingRect;
+ else
+ currentRect = currentRect.united(node->boundingRect);
+ }
+
+ if (node != 0) {
+ node->clipNode = currentClipNode;
+ currentClipNodeUsed = true;
+
+ decorationRect = node->boundingRect;
+
+ // If previous item(s) had underline and current does not, then we add the
+ // pending lines to the lists and likewise for overlines and strikeouts
+ if (!pendingUnderlines.isEmpty()
+ && !(node->decorations & QSGTextNode::Underline)) {
+ addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness);
+
+ pendingUnderlines.clear();
+
+ underlineOffset = 0.0;
+ underlineThickness = 0.0;
+ }
+
+ // ### Add pending when overlineOffset/thickness changes to minimize number of
+ // nodes
+ if (!pendingOverlines.isEmpty()) {
+ addTextDecorations(pendingOverlines, overlineOffset, overlineThickness);
+
+ pendingOverlines.clear();
+
+ overlineOffset = 0.0;
+ overlineThickness = 0.0;
+ }
+
+ // ### Add pending when overlineOffset/thickness changes to minimize number of
+ // nodes
+ if (!pendingStrikeOuts.isEmpty()) {
+ addTextDecorations(pendingStrikeOuts, strikeOutOffset, strikeOutThickness);
+
+ pendingStrikeOuts.clear();
+
+ strikeOutOffset = 0.0;
+ strikeOutThickness = 0.0;
+ }
+
+ // Merge current values with previous. Prefer greatest thickness
+ QRawFont rawFont = node->glyphRun.rawFont();
+ if (node->decorations & QSGTextNode::Underline) {
+ if (rawFont.lineThickness() > underlineThickness) {
+ underlineThickness = rawFont.lineThickness();
+ underlineOffset = rawFont.underlinePosition();
+ }
+ }
- // A new node, add it to the graph.
- if (node != prevNode) {
- appendChildNode(node);
-
- /* We flag the geometry as static, but we never call markVertexDataDirty
- or markIndexDataDirty on them. This is because all text nodes are
- discarded when a change occurs. If we start appending/removing from
- existing geometry, then we also need to start marking the geometry as
- dirty.
- */
- node->geometry()->setIndexDataPattern(QSGGeometry::StaticPattern);
- node->geometry()->setVertexDataPattern(QSGGeometry::StaticPattern);
+ if (node->decorations & QSGTextNode::Overline) {
+ overlineOffset = -rawFont.ascent();
+ overlineThickness = rawFont.lineThickness();
+ }
+
+ if (node->decorations & QSGTextNode::StrikeOut) {
+ strikeOutThickness = rawFont.lineThickness();
+ strikeOutOffset = rawFont.ascent() / -3.0;
+ }
+
+ currentDecorations = node->decorations;
+ lastColor = node->color;
+ m_processedNodes.append(*node);
+ }
+ }
+
+ // If there are pending decorations, we need to add them
+ if (!pendingUnderlines.isEmpty())
+ addTextDecorations(pendingUnderlines, underlineOffset, underlineThickness);
+
+ if (!pendingOverlines.isEmpty())
+ addTextDecorations(pendingOverlines, overlineOffset, overlineThickness);
+
+ if (!pendingStrikeOuts.isEmpty())
+ addTextDecorations(pendingStrikeOuts, strikeOutOffset, strikeOutThickness);
+ }
+
+ m_currentLineTree.clear();
+ m_currentLine = QTextLine();
+ m_hasSelection = false;
}
- return node;
+ void SelectionEngine::addUnselectedGlyphs(const QGlyphRun &glyphRun)
+ {
+ BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Unselected,
+ m_textColor, m_position);
+ }
+
+ void SelectionEngine::addSelectedGlyphs(const QGlyphRun &glyphRun)
+ {
+ int currentSize = m_currentLineTree.size();
+ BinaryTreeNode::insert(&m_currentLineTree, glyphRun, BinaryTreeNode::Selected,
+ m_textColor, m_position);
+ m_hasSelection = m_hasSelection || m_currentLineTree.size() > currentSize;
+ }
+
+ void SelectionEngine::addToSceneGraph(QSGTextNode *parentNode,
+ QSGText::TextStyle style,
+ const QColor &styleColor)
+ {
+ if (m_currentLine.isValid())
+ processCurrentLine();
+
+ // First, prepend all selection rectangles to the tree
+ for (int i=0; i<m_selectionRects.size(); ++i) {
+ const QRectF &rect = m_selectionRects.at(i);
+
+ parentNode->appendChildNode(new QSGSimpleRectNode(rect, m_selectionColor));
+ }
+
+ // Then, go through all the nodes for all lines and combine all QGlyphRuns with a common
+ // font, selection state and clip node.
+ typedef QPair<QFontEngine *, QPair<QSGClipNode *, QPair<QRgb, int> > > KeyType;
+ QHash<KeyType, BinaryTreeNode *> map;
+ for (int i=0; i<m_processedNodes.size(); ++i) {
+ BinaryTreeNode *node = m_processedNodes.data() + i;
+
+ QGlyphRun glyphRun = node->glyphRun;
+ QRawFont rawFont = glyphRun.rawFont();
+ QRawFontPrivate *rawFontD = QRawFontPrivate::get(rawFont);
+
+ QFontEngine *fontEngine = rawFontD->fontEngine;
+
+ KeyType key(qMakePair(fontEngine,
+ qMakePair(node->clipNode,
+ qMakePair(node->color.rgba(), int(node->selectionState)))));
+
+ BinaryTreeNode *otherNode = map.value(key, 0);
+ if (otherNode != 0) {
+ QGlyphRun &otherGlyphRun = otherNode->glyphRun;
+
+ QVector<quint32> otherGlyphIndexes = otherGlyphRun.glyphIndexes();
+ QVector<QPointF> otherGlyphPositions = otherGlyphRun.positions();
+
+ otherGlyphIndexes += glyphRun.glyphIndexes();
+
+ QVector<QPointF> glyphPositions = glyphRun.positions();
+ for (int j=0; j<glyphPositions.size(); ++j) {
+ otherGlyphPositions += glyphPositions.at(j) + (node->position - otherNode->position);
+ }
+
+ otherGlyphRun.setGlyphIndexes(otherGlyphIndexes);
+ otherGlyphRun.setPositions(otherGlyphPositions);
+
+ } else {
+ map.insert(key, node);
+ }
+ }
+
+ // ...and add clip nodes and glyphs to tree.
+ QHash<KeyType, BinaryTreeNode *>::const_iterator it = map.constBegin();
+ while (it != map.constEnd()) {
+
+ BinaryTreeNode *node = it.value();
+
+ QSGClipNode *clipNode = node->clipNode;
+ if (clipNode != 0 && clipNode->parent() == 0 )
+ parentNode->appendChildNode(clipNode);
+
+ QColor color = node->selectionState == BinaryTreeNode::Selected
+ ? m_selectedTextColor
+ : node->color;
+
+ parentNode->addGlyphs(node->position, node->glyphRun, color, style, styleColor, clipNode);
+
+ ++it;
+ }
+
+ // Finally, add decorations for each node to the tree.
+ for (int i=0; i<m_lines.size(); ++i) {
+ const TextDecoration &textDecoration = m_lines.at(i);
+
+ QColor color = textDecoration.selectionState == BinaryTreeNode::Selected
+ ? m_selectedTextColor
+ : textDecoration.color;
+
+ parentNode->appendChildNode(new QSGSimpleRectNode(textDecoration.rect, color));
+ }
+ }
}
-void QSGTextNode::addTextDocument(const QPointF &position, QTextDocument *textDocument, const QColor &color,
- QSGText::TextStyle style, const QColor &styleColor)
+void QSGTextNode::addTextDocument(const QPointF &, QTextDocument *textDocument,
+ const QColor &textColor,
+ QSGText::TextStyle style, const QColor &styleColor,
+ const QColor &selectionColor, const QColor &selectedTextColor,
+ int selectionStart, int selectionEnd)
{
- Q_UNUSED(position)
QTextFrame *textFrame = textDocument->rootFrame();
- QPointF p = textDocument->documentLayout()->frameBoundingRect(textFrame).topLeft();
+ QPointF position = textDocument->documentLayout()->frameBoundingRect(textFrame).topLeft();
+
+ SelectionEngine engine;
+ engine.setTextColor(textColor);
+ engine.setSelectedTextColor(selectedTextColor);
+ engine.setSelectionColor(selectionColor);
+
+ bool hasSelection = selectionStart >= 0 && selectionEnd >= 0 && selectionStart != selectionEnd;
QTextFrame::iterator it = textFrame->begin();
while (!it.atEnd()) {
- addTextBlock(p, textDocument, it.currentBlock(), color, style, styleColor);
+ Q_ASSERT(!engine.currentLine().isValid());
+
+ QTextBlock block = it.currentBlock();
+
+ QTextBlock::iterator blockIterator = block.begin();
+ while (!blockIterator.atEnd()) {
+ QTextFragment fragment = blockIterator.fragment();
+ if (fragment.text().isEmpty())
+ continue;
+
+ QPointF blockPosition = textDocument->documentLayout()->blockBoundingRect(block).topLeft();
+ engine.setPosition(position + blockPosition);
+
+ QTextCharFormat charFormat = fragment.charFormat();
+ if (!textColor.isValid())
+ engine.setTextColor(charFormat.foreground().color());
+
+ int fragmentEnd = fragment.position() + fragment.length();
+ int textPos = fragment.position();
+ while (textPos < fragmentEnd) {
+ int blockRelativePosition = textPos - block.position();
+ QTextLine line = block.layout()->lineForTextPosition(blockRelativePosition);
+ Q_ASSERT(line.textLength() > 0);
+ if (!engine.currentLine().isValid() || line.lineNumber() != engine.currentLine().lineNumber())
+ engine.setCurrentLine(line);
+
+ int lineEnd = line.textStart() + block.position() + line.textLength();
+
+ int len = qMin(lineEnd - textPos, fragmentEnd - textPos);
+ Q_ASSERT(len > 0);
+
+ int currentStepEnd = textPos + len;
+
+ if (!hasSelection || selectionStart > currentStepEnd || selectionEnd < textPos) {
+ QList<QGlyphRun> glyphRuns = fragment.glyphRuns(blockRelativePosition, len);
+ for (int j=0; j<glyphRuns.size(); ++j)
+ engine.addUnselectedGlyphs(glyphRuns.at(j));
+ } else {
+ if (textPos < selectionStart) {
+ int unselectedPartLength = qMin(selectionStart - textPos, len);
+ QList<QGlyphRun> glyphRuns = fragment.glyphRuns(blockRelativePosition,
+ unselectedPartLength);
+ for (int j=0; j<glyphRuns.size(); ++j)
+ engine.addUnselectedGlyphs(glyphRuns.at(j));
+ }
+
+ if (selectionStart < currentStepEnd && selectionEnd > textPos) {
+ int currentSelectionStart = qMax(selectionStart, textPos);
+ int currentSelectionLength = qMin(selectionEnd - currentSelectionStart,
+ currentStepEnd - currentSelectionStart);
+ int selectionRelativeBlockPosition = currentSelectionStart - block.position();
+
+ QList<QGlyphRun> glyphRuns = fragment.glyphRuns(selectionRelativeBlockPosition,
+ currentSelectionLength);
+ for (int j=0; j<glyphRuns.size(); ++j)
+ engine.addSelectedGlyphs(glyphRuns.at(j));
+ }
+
+ if (selectionEnd >= textPos && selectionEnd < currentStepEnd) {
+ QList<QGlyphRun> glyphRuns = fragment.glyphRuns(selectionEnd - block.position(),
+ currentStepEnd - selectionEnd);
+ for (int j=0; j<glyphRuns.size(); ++j)
+ engine.addUnselectedGlyphs(glyphRuns.at(j));
+ }
+ }
+
+ textPos = currentStepEnd;
+ }
+
+ ++blockIterator;
+ }
+
+ engine.setCurrentLine(QTextLine()); // Reset current line because the text layout changed
++it;
}
+
+ engine.addToSceneGraph(this, style, styleColor);
}
void QSGTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color,
- QSGText::TextStyle style, const QColor &styleColor)
+ QSGText::TextStyle style, const QColor &styleColor,
+ const QColor &selectionColor, const QColor &selectedTextColor,
+ int selectionStart, int selectionEnd)
{
- QList<QGlyphRun> glyphsList(textLayout->glyphRuns());
-
- QSGGlyphNode *prevNode = 0;
-
- QFont font = textLayout->font();
- qreal underlinePosition, ascent, lineThickness;
- int decorations = NoDecoration;
- decorations |= (font.underline() ? Underline : 0);
- decorations |= (font.overline() ? Overline : 0);
- decorations |= (font.strikeOut() ? StrikeOut : 0);
-
- underlinePosition = ascent = lineThickness = 0;
- for (int i=0; i<glyphsList.size(); ++i) {
- QGlyphRun glyphs = glyphsList.at(i);
- QRawFont rawfont = glyphs.rawFont();
- prevNode = addGlyphs(position + QPointF(0, rawfont.ascent()), glyphs, color, style, styleColor);
-
- if (decorations) {
- qreal rawAscent = rawfont.ascent();
- if (decorations & Underline) {
- ascent = qMax(ascent, rawAscent);
- qreal pos = rawfont.underlinePosition();
- if (pos > underlinePosition) {
- underlinePosition = pos;
- // take line thickness from the rawfont with maximum underline
- // position in this case
- lineThickness = rawfont.lineThickness();
- }
- } else {
- // otherwise it's strike out or overline, we take line thickness
- // from the rawfont with maximum ascent
- if (rawAscent > ascent) {
- ascent = rawAscent;
- lineThickness = rawfont.lineThickness();
- }
+ SelectionEngine engine;
+ engine.setTextColor(color);
+ engine.setSelectedTextColor(selectedTextColor);
+ engine.setSelectionColor(selectionColor);
+ engine.setPosition(position);
+
+ for (int i=0; i<textLayout->lineCount(); ++i) {
+ QTextLine line = textLayout->lineAt(i);
+
+ engine.setCurrentLine(line);
+
+ int lineEnd = line.textStart() + line.textLength();
+ if (selectionStart > lineEnd || selectionEnd < line.textStart()) {
+ QList<QGlyphRun> glyphRuns = line.glyphRuns();
+ for (int j=0; j<glyphRuns.size(); ++j)
+ engine.addUnselectedGlyphs(glyphRuns.at(j));
+ } else {
+ if (line.textStart() < selectionStart) {
+ QList<QGlyphRun> glyphRuns = line.glyphRuns(line.textStart(),
+ qMin(selectionStart - line.textStart(),
+ line.textLength()));
+
+ for (int j=0; j<glyphRuns.size(); ++j)
+ engine.addUnselectedGlyphs(glyphRuns.at(j));
+ }
+
+ if (lineEnd >= selectionStart && selectionStart >= line.textStart()) {
+ QList<QGlyphRun> glyphRuns = line.glyphRuns(selectionStart, selectionEnd - selectionStart + 1);
+
+ for (int j=0; j<glyphRuns.size(); ++j)
+ engine.addSelectedGlyphs(glyphRuns.at(j));
+ }
+
+ if (selectionEnd >= line.textStart() && selectionEnd < lineEnd) {
+ QList<QGlyphRun> glyphRuns = line.glyphRuns(selectionEnd + 1, lineEnd - selectionEnd);
+ for (int j=0; j<glyphRuns.size(); ++j)
+ engine.addUnselectedGlyphs(glyphRuns.at(j));
}
}
}
- if (decorations) {
- addTextDecorations(Decoration(decorations), position + QPointF(0, ascent), color,
- textLayout->boundingRect().width(),
- lineThickness, underlinePosition, ascent);
- }
+ engine.addToSceneGraph(this, style, styleColor);
}
@@ -383,49 +936,11 @@ bool QSGTextNode::isComplexRichText(QTextDocument *doc)
return reader.hasError();
}
-void QSGTextNode::addTextBlock(const QPointF &position, QTextDocument *textDocument, const QTextBlock &block,
- const QColor &overrideColor, QSGText::TextStyle style, const QColor &styleColor)
-{
- if (!block.isValid())
- return;
-
- QPointF blockPosition = textDocument->documentLayout()->blockBoundingRect(block).topLeft();
-
- QTextBlock::iterator it = block.begin();
- while (!it.atEnd()) {
- QTextFragment fragment = it.fragment();
- if (!fragment.text().isEmpty()) {
- QTextCharFormat charFormat = fragment.charFormat();
- QColor color = overrideColor.isValid()
- ? overrideColor
- : charFormat.foreground().color();
-
- QList<QGlyphRun> glyphsList = fragment.glyphRuns();
- for (int i=0; i<glyphsList.size(); ++i) {
- QGlyphRun glyphs = glyphsList.at(i);
- QRawFont font = glyphs.rawFont();
- QSGGlyphNode *glyphNode = addGlyphs(position + blockPosition + QPointF(0, font.ascent()),
- glyphs, color, style, styleColor);
- int decorations = (glyphs.overline() ? Overline : 0) |
- (glyphs.strikeOut() ? StrikeOut : 0) |
- (glyphs.underline() ? Underline : 0);
- if (decorations) {
- QPointF baseLine = glyphNode->baseLine();
- qreal width = glyphNode->boundingRect().width();
- addTextDecorations(Decoration(decorations), baseLine, color, width,
- font.lineThickness(), font.underlinePosition(), font.ascent());
- }
- }
- }
-
- ++it;
- }
-}
-
void QSGTextNode::deleteContent()
{
- while (firstChild() > 0)
+ while (firstChild() != 0)
delete firstChild();
+ m_cursorNode = 0;
}
#if 0
diff --git a/src/declarative/items/qsgtextnode_p.h b/src/declarative/items/qsgtextnode_p.h
index 7ff31998c6..4c5199aefa 100644
--- a/src/declarative/items/qsgtextnode_p.h
+++ b/src/declarative/items/qsgtextnode_p.h
@@ -54,10 +54,20 @@ class QColor;
class QTextDocument;
class QSGContext;
class QRawFont;
+class QSGSimpleRectNode;
+class QSGClipNode;
class QSGTextNode : public QSGTransformNode
{
public:
+ enum Decoration {
+ NoDecoration = 0x0,
+ Underline = 0x1,
+ Overline = 0x2,
+ StrikeOut = 0x4
+ };
+ Q_DECLARE_FLAGS(Decorations, Decoration)
+
QSGTextNode(QSGContext *);
~QSGTextNode();
@@ -65,26 +75,24 @@ public:
void deleteContent();
void addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color = QColor(),
- QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor());
+ QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor(),
+ const QColor &selectionColor = QColor(), const QColor &selectedTextColor = QColor(),
+ int selectionStart = -1, int selectionEnd = -1);
void addTextDocument(const QPointF &position, QTextDocument *textDocument, const QColor &color = QColor(),
- QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor());
+ QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor(),
+ const QColor &selectionColor = QColor(), const QColor &selectedTextColor = QColor(),
+ int selectionStart = -1, int selectionEnd = -1);
-private:
- enum Decoration {
- NoDecoration = 0x0,
- Underline = 0x1,
- Overline = 0x2,
- StrikeOut = 0x4
- };
+ void setCursor(const QRectF &rect, const QColor &color);
+ QSGSimpleRectNode *cursorNode() const { return m_cursorNode; }
- void addTextBlock(const QPointF &position, QTextDocument *textDocument, const QTextBlock &block,
- const QColor &overrideColor, QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor());
QSGGlyphNode *addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
- QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor(),
- QSGGlyphNode *node = 0);
- void addTextDecorations(Decoration decorations, const QPointF &position, const QColor &color,
- qreal width, qreal lineThickness, qreal underlinePosition, qreal ascent);
+ QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor(),
+ QSGNode *parentNode = 0);
+
+private:
QSGContext *m_context;
+ QSGSimpleRectNode *m_cursorNode;
};
QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgtranslate.cpp b/src/declarative/items/qsgtranslate.cpp
index fc2e7b3cc2..1de5c6c3ea 100644
--- a/src/declarative/items/qsgtranslate.cpp
+++ b/src/declarative/items/qsgtranslate.cpp
@@ -56,15 +56,29 @@ public:
qreal y;
};
+/*!
+ Constructs an empty QSGTranslate object with the given \a parent.
+*/
QSGTranslate::QSGTranslate(QObject *parent)
: QSGTransform(*new QSGTranslatePrivate, parent)
{
}
+/*!
+ Destroys the graphics scale.
+*/
QSGTranslate::~QSGTranslate()
{
}
+/*!
+ \property QSGTranslate::x
+ \brief the horizontal translation.
+
+ The translation can be any real number; the default value is 0.0.
+
+ \sa y
+*/
qreal QSGTranslate::x() const
{
Q_D(const QSGTranslate);
@@ -81,6 +95,14 @@ void QSGTranslate::setX(qreal x)
emit xChanged();
}
+/*!
+ \property QSGTranslate::y
+ \brief the vertical translation.
+
+ The translation can be any real number; the default value is 0.0.
+
+ \sa x
+*/
qreal QSGTranslate::y() const
{
Q_D(const QSGTranslate);
diff --git a/src/declarative/items/qsgvisualitemmodel.cpp b/src/declarative/items/qsgvisualitemmodel.cpp
index 7e45aeb629..5dfe01dd2b 100644
--- a/src/declarative/items/qsgvisualitemmodel.cpp
+++ b/src/declarative/items/qsgvisualitemmodel.cpp
@@ -57,6 +57,7 @@
#include <private/qdeclarativeglobal_p.h>
#include <private/qlistmodelinterface_p.h>
#include <private/qmetaobjectbuilder_p.h>
+#include <private/qdeclarativeproperty_p.h>
#include <private/qobject_p.h>
#include <QtCore/qhash.h>
@@ -122,11 +123,56 @@ public:
QList<Item> children;
};
+
+/*!
+ \qmlclass VisualItemModel QSGVisualItemModel
+ \inqmlmodule QtQuick 2
+ \ingroup qml-working-with-data
+ \brief The VisualItemModel allows items to be provided to a view.
+
+ A VisualItemModel contains the visual items to be used in a view.
+ When a VisualItemModel is used in a view, the view does not require
+ a delegate since the VisualItemModel already contains the visual
+ delegate (items).
+
+ An item can determine its index within the
+ model via the \l{VisualItemModel::index}{index} attached property.
+
+ The example below places three colored rectangles in a ListView.
+ \code
+ import QtQuick 1.0
+
+ Rectangle {
+ VisualItemModel {
+ id: itemModel
+ Rectangle { height: 30; width: 80; color: "red" }
+ Rectangle { height: 30; width: 80; color: "green" }
+ Rectangle { height: 30; width: 80; color: "blue" }
+ }
+
+ ListView {
+ anchors.fill: parent
+ model: itemModel
+ }
+ }
+ \endcode
+
+ \image visualitemmodel.png
+
+ \sa {declarative/modelviews/visualitemmodel}{VisualItemModel example}
+*/
QSGVisualItemModel::QSGVisualItemModel(QObject *parent)
: QSGVisualModel(*(new QSGVisualItemModelPrivate), parent)
{
}
+/*!
+ \qmlattachedproperty int QtQuick2::VisualItemModel::index
+ This attached property holds the index of this delegate's item within the model.
+
+ It is attached to each instance of the delegate.
+*/
+
QDeclarativeListProperty<QSGItem> QSGVisualItemModel::children()
{
Q_D(QSGVisualItemModel);
@@ -134,6 +180,11 @@ QDeclarativeListProperty<QSGItem> QSGVisualItemModel::children()
d->children_count, d->children_at);
}
+/*!
+ \qmlproperty int QtQuick2::VisualItemModel::count
+
+ The number of items in the model. This property is readonly.
+*/
int QSGVisualItemModel::count() const
{
Q_D(const QSGVisualItemModel);
@@ -198,20 +249,53 @@ QSGVisualItemModelAttached *QSGVisualItemModel::qmlAttachedProperties(QObject *o
//============================================================================
-class VDMDelegateDataType : public QDeclarativeOpenMetaObjectType
+class VDMDelegateDataType : public QDeclarativeRefCount
{
public:
- VDMDelegateDataType(const QMetaObject *base, QDeclarativeEngine *engine) : QDeclarativeOpenMetaObjectType(base, engine) {}
+ VDMDelegateDataType()
+ : metaObject(0)
+ , propertyCache(0)
+ , propertyOffset(0)
+ , signalOffset(0)
+ , shared(true)
+ {
+ }
+
+ VDMDelegateDataType(const VDMDelegateDataType &type)
+ : metaObject(0)
+ , propertyCache(0)
+ , propertyOffset(type.propertyOffset)
+ , signalOffset(type.signalOffset)
+ , shared(false)
+ , builder(type.metaObject, QMetaObjectBuilder::Properties
+ | QMetaObjectBuilder::Signals
+ | QMetaObjectBuilder::SuperClass
+ | QMetaObjectBuilder::ClassName)
+ {
+ builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
+ }
- void propertyCreated(int, QMetaPropertyBuilder &prop) {
- prop.setWritable(false);
+ ~VDMDelegateDataType()
+ {
+ if (propertyCache)
+ propertyCache->release();
+ qFree(metaObject);
}
+
+ QMetaObject *metaObject;
+ QDeclarativePropertyCache *propertyCache;
+ int propertyOffset;
+ int signalOffset;
+ bool shared : 1;
+ QMetaObjectBuilder builder;
};
class QSGVisualDataModelParts;
class QSGVisualDataModelData;
+class QSGVisualDataModelDataMetaObject;
class QSGVisualDataModelPrivate : public QObjectPrivate
{
+ Q_DECLARE_PUBLIC(QSGVisualDataModel)
public:
QSGVisualDataModelPrivate(QDeclarativeContext *);
@@ -228,55 +312,30 @@ public:
QDeclarativeGuard<QDeclarativeContext> m_context;
QList<int> m_roles;
QHash<QByteArray,int> m_roleNames;
- void ensureRoles() {
- if (m_roleNames.isEmpty()) {
- if (m_listModelInterface) {
- m_roles = m_listModelInterface->roles();
- for (int ii = 0; ii < m_roles.count(); ++ii)
- m_roleNames.insert(m_listModelInterface->toString(m_roles.at(ii)).toUtf8(), m_roles.at(ii));
- } else if (m_abstractItemModel) {
- for (QHash<int,QByteArray>::const_iterator it = m_abstractItemModel->roleNames().begin();
- it != m_abstractItemModel->roleNames().end(); ++it) {
- m_roles.append(it.key());
- m_roleNames.insert(*it, it.key());
- }
- if (m_roles.count())
- m_roleNames.insert("hasModelChildren", -1);
- } else if (m_listAccessor) {
- m_roleNames.insert("modelData", 0);
- if (m_listAccessor->type() == QDeclarativeListAccessor::Instance) {
- if (QObject *object = m_listAccessor->at(0).value<QObject*>()) {
- int count = object->metaObject()->propertyCount();
- for (int ii = 1; ii < count; ++ii) {
- const QMetaProperty &prop = object->metaObject()->property(ii);
- m_roleNames.insert(prop.name(), 0);
- }
- }
- }
- }
- }
+
+ void addProperty(int role, int propertyId, const char *propertyName, const char *propertyType, bool isModelData = false);
+ template <typename T> void setModelDataType()
+ {
+ createModelData = &T::create;
+ m_delegateDataType->builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
+ m_delegateDataType->builder.setClassName(T::staticMetaObject.className());
+ m_delegateDataType->builder.setSuperClass(&T::staticMetaObject);
+ m_delegateDataType->propertyOffset = T::staticMetaObject.propertyCount();
+ m_delegateDataType->signalOffset = T::staticMetaObject.methodCount();
}
+ QSGVisualDataModelData *createMetaObject(int index, QSGVisualDataModel *model);
- QHash<int,int> m_roleToPropId;
- int m_modelDataPropId;
- void createMetaData() {
- if (!m_metaDataCreated) {
- ensureRoles();
- if (m_roleNames.count()) {
- QHash<QByteArray, int>::const_iterator it = m_roleNames.begin();
- while (it != m_roleNames.end()) {
- int propId = m_delegateDataType->createProperty(it.key()) - m_delegateDataType->propertyOffset();
- m_roleToPropId.insert(*it, propId);
- ++it;
- }
- // Add modelData property
- if (m_roles.count() == 1 && !m_roleNames.contains("modelData"))
- m_modelDataPropId = m_delegateDataType->createProperty("modelData") - m_delegateDataType->propertyOffset();
- m_metaDataCreated = true;
- }
- }
+ static QSGVisualDataModelData *initializeModelData(int index, QSGVisualDataModel *model) {
+ return get(model)->createMetaObject(index, model);
}
+ typedef QSGVisualDataModelData *(*CreateModelData)(int index, QSGVisualDataModel *model);
+
+ struct PropertyData {
+ int role;
+ bool isModelData : 1;
+ };
+
struct ObjectRef {
ObjectRef(QObject *object=0) : obj(object), ref(1) {}
QObject *obj;
@@ -338,11 +397,12 @@ public:
friend class QSGVisualItemParts;
VDMDelegateDataType *m_delegateDataType;
+ CreateModelData createModelData;
+
friend class QSGVisualDataModelData;
- bool m_metaDataCreated : 1;
- bool m_metaDataCacheable : 1;
bool m_delegateValidated : 1;
bool m_completePending : 1;
+ bool m_objectList : 1;
QSGVisualDataModelData *data(QObject *item);
@@ -352,164 +412,315 @@ public:
QModelIndex m_root;
QList<QByteArray> watchedRoles;
QList<int> watchedRoleIds;
-};
-
-class QSGVisualDataModelDataMetaObject : public QDeclarativeOpenMetaObject
-{
-public:
- QSGVisualDataModelDataMetaObject(QObject *parent, QDeclarativeOpenMetaObjectType *type)
- : QDeclarativeOpenMetaObject(parent, type) {}
- virtual QVariant initialValue(int);
- virtual int createProperty(const char *, const char *);
-
-private:
- friend class QSGVisualDataModelData;
+ QVector<PropertyData> m_propertyData;
};
class QSGVisualDataModelData : public QObject
{
-Q_OBJECT
+ Q_OBJECT
+ Q_PROPERTY(int index READ index NOTIFY indexChanged)
public:
QSGVisualDataModelData(int index, QSGVisualDataModel *model);
~QSGVisualDataModelData();
- Q_PROPERTY(int index READ index NOTIFY indexChanged)
int index() const;
void setIndex(int index);
- int propForRole(int) const;
- int modelDataPropertyId() const {
- QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model);
- return model->m_modelDataPropId;
- }
-
- void setValue(int, const QVariant &);
- bool hasValue(int id) const {
- return m_meta->hasValue(id);
- }
-
- void ensureProperties();
-
Q_SIGNALS:
void indexChanged();
-private:
- friend class QSGVisualDataModelDataMetaObject;
+public:
int m_index;
QDeclarativeGuard<QSGVisualDataModel> m_model;
- QSGVisualDataModelDataMetaObject *m_meta;
};
-int QSGVisualDataModelData::propForRole(int id) const
+class QSGVisualDataModelDataMetaObject : public QAbstractDynamicMetaObject
{
- QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model);
- QHash<int,int>::const_iterator it = model->m_roleToPropId.find(id);
- if (it != model->m_roleToPropId.end())
- return *it;
+public:
+ QSGVisualDataModelDataMetaObject(QSGVisualDataModelData *data, VDMDelegateDataType *type)
+ : m_data(data)
+ , m_type(type)
+ {
+ QObjectPrivate *op = QObjectPrivate::get(m_data);
+ *static_cast<QMetaObject *>(this) = *type->metaObject;
+ op->metaObject = this;
+ m_type->addref();
+ }
- return -1;
-}
+ ~QSGVisualDataModelDataMetaObject() { m_type->release(); }
-void QSGVisualDataModelData::setValue(int id, const QVariant &val)
-{
- m_meta->setValue(id, val);
-}
+ QSGVisualDataModelData *m_data;
+ VDMDelegateDataType *m_type;
+};
-int QSGVisualDataModelDataMetaObject::createProperty(const char *name, const char *type)
+class QSGVDMAbstractItemModelDataMetaObject : public QSGVisualDataModelDataMetaObject
{
- QSGVisualDataModelData *data =
- static_cast<QSGVisualDataModelData *>(object());
+public:
+ QSGVDMAbstractItemModelDataMetaObject(QSGVisualDataModelData *object, VDMDelegateDataType *type)
+ : QSGVisualDataModelDataMetaObject(object, type) {}
+
+ int metaCall(QMetaObject::Call call, int id, void **arguments)
+ {
+ if (call == QMetaObject::ReadProperty && id >= m_type->propertyOffset) {
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_data->m_model);
+ if (m_data->m_index == -1 || !model->m_abstractItemModel)
+ return -1;
+ *static_cast<QVariant *>(arguments[0]) = model->m_abstractItemModel->index(
+ m_data->m_index, 0, model->m_root).data(model->m_propertyData.at(id - m_type->propertyOffset).role);
+ return -1;
+ } else {
+ return m_data->qt_metacall(call, id, arguments);
+ }
+ }
+};
- if (!data->m_model)
- return -1;
+class QSGVDMAbstractItemModelData : public QSGVisualDataModelData
+{
+ Q_OBJECT
+ Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT)
+public:
+ bool hasModelChildren() const
+ {
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model);
+ return model->m_abstractItemModel->hasChildren(model->m_abstractItemModel->index(m_index, 0, model->m_root));
+ }
- QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(data->m_model);
- if (data->m_index < 0 || data->m_index >= model->modelCount())
- return -1;
+ static QSGVisualDataModelData *create(int index, QSGVisualDataModel *model) {
+ return new QSGVDMAbstractItemModelData(index, model); }
+private:
+ QSGVDMAbstractItemModelData(int index, QSGVisualDataModel *model)
+ : QSGVisualDataModelData(index, model)
+ {
+ new QSGVDMAbstractItemModelDataMetaObject(
+ this, QSGVisualDataModelPrivate::get(m_model)->m_delegateDataType);
+ }
+};
- if ((!model->m_listModelInterface || !model->m_abstractItemModel) && model->m_listAccessor) {
- if (model->m_listAccessor->type() == QDeclarativeListAccessor::ListProperty) {
- model->ensureRoles();
- if (qstrcmp(name,"modelData") == 0)
- return QDeclarativeOpenMetaObject::createProperty(name, type);
+class QSGVDMListModelInterfaceDataMetaObject : public QSGVisualDataModelDataMetaObject
+{
+public:
+ QSGVDMListModelInterfaceDataMetaObject(QSGVisualDataModelData *object, VDMDelegateDataType *type)
+ : QSGVisualDataModelDataMetaObject(object, type) {}
+
+ int metaCall(QMetaObject::Call call, int id, void **arguments)
+ {
+ if (call == QMetaObject::ReadProperty && id >= m_type->propertyOffset) {
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_data->m_model);
+ if (m_data->m_index == -1 || !model->m_listModelInterface)
+ return -1;
+ *static_cast<QVariant *>(arguments[0]) = model->m_listModelInterface->data(
+ m_data->m_index, model->m_propertyData.at(id - m_type->propertyOffset).role);
+ return -1;
+ } else {
+ return m_data->qt_metacall(call, id, arguments);
}
}
- return -1;
-}
+};
-QVariant QSGVisualDataModelDataMetaObject::initialValue(int propId)
+class QSGVDMListModelInterfaceData : public QSGVisualDataModelData
{
- QSGVisualDataModelData *data =
- static_cast<QSGVisualDataModelData *>(object());
+public:
+ static QSGVisualDataModelData *create(int index, QSGVisualDataModel *model) {
+ return new QSGVDMListModelInterfaceData(index, model); }
+private:
+ QSGVDMListModelInterfaceData(int index, QSGVisualDataModel *model)
+ : QSGVisualDataModelData(index, model)
+ {
+ new QSGVDMListModelInterfaceDataMetaObject(
+ this, QSGVisualDataModelPrivate::get(m_model)->m_delegateDataType);
+ }
+};
- Q_ASSERT(data->m_model);
- QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(data->m_model);
+class QSGVDMListAccessorData : public QSGVisualDataModelData
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariant modelData READ modelData CONSTANT)
+public:
+ QVariant modelData() const {
+ return QSGVisualDataModelPrivate::get(m_model)->m_listAccessor->at(m_index); }
- QByteArray propName = name(propId);
- if ((!model->m_listModelInterface || !model->m_abstractItemModel) && model->m_listAccessor) {
- if (propName == "modelData") {
- if (model->m_listAccessor->type() == QDeclarativeListAccessor::Instance) {
- QObject *object = model->m_listAccessor->at(0).value<QObject*>();
- return object->metaObject()->property(1).read(object); // the first property after objectName
- }
- return model->m_listAccessor->at(data->m_index);
+ static QSGVisualDataModelData *create(int index, QSGVisualDataModel *model) {
+ return new QSGVDMListAccessorData(index, model); }
+private:
+ QSGVDMListAccessorData(int index, QSGVisualDataModel *model)
+ : QSGVisualDataModelData(index, model)
+ {
+ }
+};
+
+class QSGVDMObjectDataMetaObject : public QSGVisualDataModelDataMetaObject
+{
+public:
+ QSGVDMObjectDataMetaObject(QSGVisualDataModelData *data, VDMDelegateDataType *type)
+ : QSGVisualDataModelDataMetaObject(data, type)
+ , m_object(QSGVisualDataModelPrivate::get(data->m_model)->m_listAccessor->at(data->m_index).value<QObject *>())
+ {}
+
+ int metaCall(QMetaObject::Call call, int id, void **arguments)
+ {
+ if (id >= m_type->propertyOffset
+ && (call == QMetaObject::ReadProperty
+ || call == QMetaObject::WriteProperty
+ || call == QMetaObject::ResetProperty)) {
+ if (m_object)
+ QMetaObject::metacall(m_object, call, id - m_type->propertyOffset + 1, arguments);
+ return -1;
+ } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) {
+ QMetaObject::activate(m_data, this, id, 0);
+ return -1;
} else {
- // return any property of a single object instance.
- QObject *object = model->m_listAccessor->at(data->m_index).value<QObject*>();
- return object->property(propName);
+ return m_data->qt_metacall(call, id, arguments);
}
- } else if (model->m_listModelInterface) {
- model->ensureRoles();
- QHash<QByteArray,int>::const_iterator it = model->m_roleNames.find(propName);
- if (it != model->m_roleNames.end()) {
- QVariant value = model->m_listModelInterface->data(data->m_index, *it);
- return value;
- } else if (model->m_roles.count() == 1 && propName == "modelData") {
- //for compatibility with other lists, assign modelData if there is only a single role
- QVariant value = model->m_listModelInterface->data(data->m_index, model->m_roles.first());
- return value;
+ }
+
+ int createProperty(const char *name, const char *)
+ {
+ if (!m_object)
+ return -1;
+ const QMetaObject *metaObject = m_object->metaObject();
+
+ const int previousPropertyCount = propertyCount() - propertyOffset();
+ int propertyIndex = metaObject->indexOfProperty(name);
+ if (propertyIndex == -1)
+ return -1;
+ if (previousPropertyCount + 1 == metaObject->propertyCount())
+ return propertyIndex + m_type->propertyOffset - 1;
+
+ if (m_type->shared) {
+ VDMDelegateDataType *type = m_type;
+ m_type = new VDMDelegateDataType(*m_type);
+ type->release();
}
- } else if (model->m_abstractItemModel) {
- model->ensureRoles();
- QModelIndex index = model->m_abstractItemModel->index(data->m_index, 0, model->m_root);
- if (propName == "hasModelChildren") {
- return model->m_abstractItemModel->hasChildren(index);
- } else {
- QHash<QByteArray,int>::const_iterator it = model->m_roleNames.find(propName);
- if (it != model->m_roleNames.end()) {
- return model->m_abstractItemModel->data(index, *it);
- } else if (model->m_roles.count() == 1 && propName == "modelData") {
- //for compatibility with other lists, assign modelData if there is only a single role
- return model->m_abstractItemModel->data(index, model->m_roles.first());
+
+ const int previousMethodCount = methodCount();
+ int notifierId = previousMethodCount;
+ for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - 1; ++propertyId) {
+ QMetaProperty property = metaObject->property(propertyId + 1);
+ QMetaPropertyBuilder propertyBuilder;
+ if (property.hasNotifySignal()) {
+ m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()");
+ propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId);
+ ++notifierId;
+ } else {
+ propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName());
+ }
+ propertyBuilder.setWritable(property.isWritable());
+ propertyBuilder.setResettable(property.isResettable());
+ propertyBuilder.setConstant(property.isConstant());
+ }
+
+ if (m_type->metaObject)
+ qFree(m_type->metaObject);
+ m_type->metaObject = m_type->builder.toMetaObject();
+ *static_cast<QMetaObject *>(this) = *m_type->metaObject;
+
+ notifierId = previousMethodCount;
+ for (int i = previousPropertyCount; i < metaObject->propertyCount(); ++i) {
+ QMetaProperty property = metaObject->property(i);
+ if (property.hasNotifySignal()) {
+ QDeclarativePropertyPrivate::connect(
+ m_object, property.notifySignalIndex(), m_data, notifierId);
+ ++notifierId;
}
}
+ return propertyIndex + m_type->propertyOffset - 1;
+ }
+
+ QDeclarativeGuard<QObject> m_object;
+};
+
+class QSGVDMObjectData : public QSGVisualDataModelData
+{
+ Q_OBJECT
+ Q_PROPERTY(QObject *modelData READ modelData CONSTANT)
+public:
+ QObject *modelData() const { return m_metaObject->m_object; }
+
+ static QSGVisualDataModelData *create(int index, QSGVisualDataModel *model) {
+ return new QSGVDMObjectData(index, model); }
+
+private:
+ QSGVDMObjectData(int index, QSGVisualDataModel *model)
+ : QSGVisualDataModelData(index, model)
+ , m_metaObject(new QSGVDMObjectDataMetaObject(this, QSGVisualDataModelPrivate::get(m_model)->m_delegateDataType))
+ {
}
- Q_ASSERT(!"Can never be reached");
- return QVariant();
+
+ QSGVDMObjectDataMetaObject *m_metaObject;
+};
+
+void QSGVisualDataModelPrivate::addProperty(
+ int role, int propertyId, const char *propertyName, const char *propertyType, bool isModelData)
+{
+ PropertyData propertyData;
+ propertyData.role = role;
+ propertyData.isModelData = isModelData;
+ m_delegateDataType->builder.addSignal("__" + QByteArray::number(propertyId) + "()");
+ QMetaPropertyBuilder property = m_delegateDataType->builder.addProperty(
+ propertyName, propertyType, propertyId);
+ property.setWritable(false);
+
+ m_propertyData.append(propertyData);
}
-QSGVisualDataModelData::QSGVisualDataModelData(int index,
- QSGVisualDataModel *model)
-: m_index(index), m_model(model),
-m_meta(new QSGVisualDataModelDataMetaObject(this, QSGVisualDataModelPrivate::get(model)->m_delegateDataType))
+QSGVisualDataModelData *QSGVisualDataModelPrivate::createMetaObject(int index, QSGVisualDataModel *model)
{
- ensureProperties();
+ Q_ASSERT(!m_delegateDataType);
+
+ m_objectList = false;
+ m_propertyData.clear();
+ if (m_listAccessor
+ && m_listAccessor->type() != QDeclarativeListAccessor::ListProperty
+ && m_listAccessor->type() != QDeclarativeListAccessor::Instance) {
+ createModelData = &QSGVDMListAccessorData::create;
+ return QSGVDMListAccessorData::create(index, model);
+ }
+
+ m_delegateDataType = new VDMDelegateDataType;
+ if (m_listModelInterface) {
+ setModelDataType<QSGVDMListModelInterfaceData>();
+ QList<int> roles = m_listModelInterface->roles();
+ for (int propertyId = 0; propertyId < roles.count(); ++propertyId) {
+ const int role = roles.at(propertyId);
+ const QByteArray propertyName = m_listModelInterface->toString(role).toUtf8();
+ addProperty(role, propertyId, propertyName, "QVariant");
+ m_roleNames.insert(propertyName, role);
+ }
+ if (m_propertyData.count() == 1)
+ addProperty(roles.first(), 1, "modelData", "QVariant", true);
+ } else if (m_abstractItemModel) {
+ setModelDataType<QSGVDMAbstractItemModelData>();
+ QHash<int, QByteArray> roleNames = m_abstractItemModel->roleNames();
+ for (QHash<int, QByteArray>::const_iterator it = roleNames.begin(); it != roleNames.end(); ++it) {
+ addProperty(it.key(), m_propertyData.count(), it.value(), "QVariant");
+ m_roleNames.insert(it.value(), it.key());
+ }
+ if (m_propertyData.count() == 1)
+ addProperty(roleNames.begin().key(), 1, "modelData", "QVariant", true);
+ } else if (m_listAccessor) {
+ setModelDataType<QSGVDMObjectData>();
+ m_objectList = true;
+ } else {
+ Q_ASSERT(!"No model set on VisualDataModel");
+ return 0;
+ }
+ m_delegateDataType->metaObject = m_delegateDataType->builder.toMetaObject();
+ if (!m_objectList) {
+ m_delegateDataType->propertyCache = new QDeclarativePropertyCache(
+ m_context ? m_context->engine() : qmlEngine(q_func()), m_delegateDataType->metaObject);
+ }
+ return createModelData(index, model);
}
-QSGVisualDataModelData::~QSGVisualDataModelData()
+QSGVisualDataModelData::QSGVisualDataModelData(int index, QSGVisualDataModel *model)
+ : m_index(index)
+ , m_model(model)
{
}
-void QSGVisualDataModelData::ensureProperties()
+QSGVisualDataModelData::~QSGVisualDataModelData()
{
- QSGVisualDataModelPrivate *modelPriv = QSGVisualDataModelPrivate::get(m_model);
- if (modelPriv->m_metaDataCacheable) {
- if (!modelPriv->m_metaDataCreated)
- modelPriv->createMetaData();
- if (modelPriv->m_metaDataCreated)
- m_meta->setCached(true);
- }
}
int QSGVisualDataModelData::index() const
@@ -571,8 +782,8 @@ QSGVisualDataModelParts::QSGVisualDataModelParts(QSGVisualDataModel *parent)
QSGVisualDataModelPrivate::QSGVisualDataModelPrivate(QDeclarativeContext *ctxt)
: m_listModelInterface(0), m_abstractItemModel(0), m_visualItemModel(0), m_delegate(0)
-, m_context(ctxt), m_modelDataPropId(-1), m_parts(0), m_delegateDataType(0), m_metaDataCreated(false)
-, m_metaDataCacheable(false), m_delegateValidated(false), m_completePending(false), m_listAccessor(0)
+, m_context(ctxt), m_parts(0), m_delegateDataType(0), createModelData(&initializeModelData)
+, m_delegateValidated(false), m_completePending(false), m_objectList(false), m_listAccessor(0)
{
}
@@ -586,6 +797,26 @@ QSGVisualDataModelData *QSGVisualDataModelPrivate::data(QObject *item)
//---------------------------------------------------------------------------
+/*!
+ \qmlclass VisualDataModel QSGVisualDataModel
+ \inqmlmodule QtQuick 2
+ \ingroup qml-working-with-data
+ \brief The VisualDataModel encapsulates a model and delegate
+
+ A VisualDataModel encapsulates a model and the delegate that will
+ be instantiated for items in the model.
+
+ It is usually not necessary to create VisualDataModel elements.
+ However, it can be useful for manipulating and accessing the \l modelIndex
+ when a QAbstractItemModel subclass is used as the
+ model. Also, VisualDataModel is used together with \l Package to
+ provide delegates to multiple views.
+
+ The example below illustrates using a VisualDataModel with a ListView.
+
+ \snippet doc/src/snippets/declarative/visualdatamodel.qml 0
+*/
+
QSGVisualDataModel::QSGVisualDataModel()
: QSGVisualModel(*(new QSGVisualDataModelPrivate(0)))
{
@@ -605,6 +836,20 @@ QSGVisualDataModel::~QSGVisualDataModel()
d->m_delegateDataType->release();
}
+/*!
+ \qmlproperty model QtQuick2::VisualDataModel::model
+ This property holds the model providing data for the VisualDataModel.
+
+ The model provides a set of data that is used to create the items
+ for a view. For large or dynamic datasets the model is usually
+ provided by a C++ model object. The C++ model object must be a \l
+ {QAbstractItemModel} subclass or a simple list.
+
+ Models can also be created directly in QML, using a \l{ListModel} or
+ \l{XmlListModel}.
+
+ \sa {qmlmodels}{Data Models}
+*/
QVariant QSGVisualDataModel::model() const
{
Q_D(const QSGVisualDataModel);
@@ -658,9 +903,8 @@ void QSGVisualDataModel::setModel(const QVariant &model)
d->m_roleNames.clear();
if (d->m_delegateDataType)
d->m_delegateDataType->release();
- d->m_metaDataCreated = 0;
- d->m_metaDataCacheable = false;
- d->m_delegateDataType = new VDMDelegateDataType(&QSGVisualDataModelData::staticMetaObject, d->m_context?d->m_context->engine():qmlEngine(this));
+ d->m_delegateDataType = 0;
+ d->createModelData = &QSGVisualDataModelPrivate::initializeModelData;
QObject *object = qvariant_cast<QObject *>(model);
if (object && (d->m_listModelInterface = qobject_cast<QListModelInterface *>(object))) {
@@ -672,7 +916,6 @@ void QSGVisualDataModel::setModel(const QVariant &model)
this, SLOT(_q_itemsRemoved(int,int)));
QObject::connect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)),
this, SLOT(_q_itemsMoved(int,int,int)));
- d->m_metaDataCacheable = true;
if (d->m_delegate && d->m_listModelInterface->count())
emit itemsInserted(0, d->m_listModelInterface->count());
return;
@@ -687,7 +930,6 @@ void QSGVisualDataModel::setModel(const QVariant &model)
this, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
QObject::connect(d->m_abstractItemModel, SIGNAL(modelReset()), this, SLOT(_q_modelReset()));
QObject::connect(d->m_abstractItemModel, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged()));
- d->m_metaDataCacheable = true;
if (d->m_abstractItemModel->canFetchMore(d->m_root))
d->m_abstractItemModel->fetchMore(d->m_root);
return;
@@ -710,13 +952,19 @@ void QSGVisualDataModel::setModel(const QVariant &model)
d->m_listAccessor = new QDeclarativeListAccessor;
d->m_listAccessor->setList(model, d->m_context?d->m_context->engine():qmlEngine(this));
if (d->m_listAccessor->type() != QDeclarativeListAccessor::ListProperty)
- d->m_metaDataCacheable = true;
if (d->m_delegate && d->modelCount()) {
emit itemsInserted(0, d->modelCount());
emit countChanged();
}
}
+/*!
+ \qmlproperty Component QtQuick2::VisualDataModel::delegate
+
+ 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 {qmlmodels}{Data Model}.
+*/
QDeclarativeComponent *QSGVisualDataModel::delegate() const
{
Q_D(const QSGVisualDataModel);
@@ -741,6 +989,35 @@ void QSGVisualDataModel::setDelegate(QDeclarativeComponent *delegate)
}
}
+/*!
+ \qmlproperty QModelIndex QtQuick2::VisualDataModel::rootIndex
+
+ QAbstractItemModel provides a hierarchical tree of data, whereas
+ QML only operates on list data. \c rootIndex allows the children of
+ any node in a QAbstractItemModel to be provided by this model.
+
+ This property only affects models of type QAbstractItemModel that
+ are hierarchical (e.g, a tree model).
+
+ For example, here is a simple interactive file system browser.
+ When a directory name is clicked, the view's \c rootIndex is set to the
+ QModelIndex node of the clicked directory, thus updating the view to show
+ the new directory's contents.
+
+ \c main.cpp:
+ \snippet doc/src/snippets/declarative/visualdatamodel_rootindex/main.cpp 0
+
+ \c view.qml:
+ \snippet doc/src/snippets/declarative/visualdatamodel_rootindex/view.qml 0
+
+ If the \l model is a QAbstractItemModel subclass, the delegate can also
+ reference a \c hasModelChildren property (optionally qualified by a
+ \e model. prefix) that indicates whether the delegate's model item has
+ any child nodes.
+
+
+ \sa modelIndex(), parentModelIndex()
+*/
QVariant QSGVisualDataModel::rootIndex() const
{
Q_D(const QSGVisualDataModel);
@@ -767,6 +1044,18 @@ void QSGVisualDataModel::setRootIndex(const QVariant &root)
}
}
+/*!
+ \qmlmethod QModelIndex QtQuick2::VisualDataModel::modelIndex(int index)
+
+ QAbstractItemModel provides a hierarchical tree of data, whereas
+ QML only operates on list data. This function assists in using
+ tree models in QML.
+
+ Returns a QModelIndex for the specified index.
+ This value can be assigned to rootIndex.
+
+ \sa rootIndex
+*/
QVariant QSGVisualDataModel::modelIndex(int idx) const
{
Q_D(const QSGVisualDataModel);
@@ -775,6 +1064,18 @@ QVariant QSGVisualDataModel::modelIndex(int idx) const
return QVariant::fromValue(QModelIndex());
}
+/*!
+ \qmlmethod QModelIndex QtQuick2::VisualDataModel::parentModelIndex()
+
+ QAbstractItemModel provides a hierarchical tree of data, whereas
+ QML only operates on list data. This function assists in using
+ tree models in QML.
+
+ Returns a QModelIndex for the parent of the current rootIndex.
+ This value can be assigned to rootIndex.
+
+ \sa rootIndex
+*/
QVariant QSGVisualDataModel::parentModelIndex() const
{
Q_D(const QSGVisualDataModel);
@@ -783,6 +1084,33 @@ QVariant QSGVisualDataModel::parentModelIndex() const
return QVariant::fromValue(QModelIndex());
}
+/*!
+ \qmlproperty object QtQuick2::VisualDataModel::parts
+
+ The \a parts property selects a VisualDataModel which creates
+ delegates from the part named. This is used in conjunction with
+ the \l Package element.
+
+ For example, the code below selects a model which creates
+ delegates named \e list from a \l Package:
+
+ \code
+ VisualDataModel {
+ id: visualModel
+ delegate: Package {
+ Item { Package.name: "list" }
+ }
+ model: myModel
+ }
+
+ ListView {
+ width: 200; height:200
+ model: visualModel.parts.list
+ }
+ \endcode
+
+ \sa Package
+*/
QString QSGVisualDataModel::part() const
{
Q_D(const QSGVisualDataModel);
@@ -881,12 +1209,11 @@ QSGItem *QSGVisualDataModel::item(int index, const QByteArray &viewId, bool comp
QDeclarativeContext *ccontext = d->m_context;
if (!ccontext) ccontext = qmlContext(this);
QDeclarativeContext *ctxt = new QDeclarativeContext(ccontext);
- QSGVisualDataModelData *data = new QSGVisualDataModelData(index, this);
- if ((!d->m_listModelInterface || !d->m_abstractItemModel) && d->m_listAccessor
- && d->m_listAccessor->type() == QDeclarativeListAccessor::ListProperty) {
- ctxt->setContextObject(d->m_listAccessor->at(index).value<QObject*>());
- ctxt = new QDeclarativeContext(ctxt, ctxt);
+ if (d->m_objectList) {
+ ctxt->setContextObject(d->m_listAccessor->at(index).value<QObject *>());
+ ctxt = new QDeclarativeContext(ctxt);
}
+ QSGVisualDataModelData *data = d->createModelData(index, this);
ctxt->setContextProperty(QLatin1String("model"), data);
ctxt->setContextObject(data);
d->m_completePending = false;
@@ -975,7 +1302,7 @@ QString QSGVisualDataModel::stringValue(int index, const QString &name)
if (QObject *nobj = d->m_cache.item(index))
data = d->data(nobj);
if (!data) {
- data = new QSGVisualDataModelData(index, this);
+ data = d->createModelData(index, this);
tempData = true;
}
@@ -1031,6 +1358,21 @@ void QSGVisualDataModel::_q_itemsChanged(int index, int count,
}
}
+ QVector<int> signalIndexes;
+ for (int i = 0; i < roles.count(); ++i) {
+ const int role = roles.at(i);
+ if (!changed && d->watchedRoleIds.contains(role))
+ changed = true;
+ for (int propertyId = 0; propertyId < d->m_propertyData.count(); ++propertyId) {
+ if (d->m_propertyData.at(propertyId).role == role)
+ signalIndexes.append(propertyId + d->m_delegateDataType->signalOffset);
+ }
+ }
+ if (roles.isEmpty()) {
+ for (int propertyId = 0; propertyId < d->m_propertyData.count(); ++propertyId)
+ signalIndexes.append(propertyId + d->m_delegateDataType->signalOffset);
+ }
+
for (QHash<int,QSGVisualDataModelPrivate::ObjectRef>::ConstIterator iter = d->m_cache.begin();
iter != d->m_cache.end(); ++iter) {
const int idx = iter.key();
@@ -1038,41 +1380,8 @@ void QSGVisualDataModel::_q_itemsChanged(int index, int count,
if (idx >= index && idx < index+count) {
QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
QSGVisualDataModelData *data = d->data(objRef.obj);
- for (int roleIdx = 0; roleIdx < roles.count(); ++roleIdx) {
- int role = roles.at(roleIdx);
- if (!changed && !d->watchedRoleIds.isEmpty() && d->watchedRoleIds.contains(role))
- changed = true;
- int propId = data->propForRole(role);
- if (propId != -1) {
- if (data->hasValue(propId)) {
- if (d->m_listModelInterface) {
- data->setValue(propId, d->m_listModelInterface->data(idx, role));
- } else if (d->m_abstractItemModel) {
- QModelIndex index = d->m_abstractItemModel->index(idx, 0, d->m_root);
- data->setValue(propId, d->m_abstractItemModel->data(index, role));
- }
- }
- } else {
- QString roleName;
- if (d->m_listModelInterface)
- roleName = d->m_listModelInterface->toString(role);
- else if (d->m_abstractItemModel)
- roleName = QString::fromUtf8(d->m_abstractItemModel->roleNames().value(role));
- qmlInfo(this) << "Changing role not present in item: " << roleName;
- }
- }
- if (d->m_modelDataPropId != -1) {
- // Handle the modelData role we add if there is just one role.
- if (data->hasValue(d->m_modelDataPropId)) {
- int role = d->m_roles.at(0);
- if (d->m_listModelInterface) {
- data->setValue(d->m_modelDataPropId, d->m_listModelInterface->data(idx, role));
- } else if (d->m_abstractItemModel) {
- QModelIndex index = d->m_abstractItemModel->index(idx, 0, d->m_root);
- data->setValue(d->m_modelDataPropId, d->m_abstractItemModel->data(index, role));
- }
- }
- }
+ for (int i = 0; i < signalIndexes.count(); ++i)
+ QMetaObject::activate(data, signalIndexes.at(i), 0);
}
}
if (changed)