Swiping through full resolution pictures
The last page we have to implement in gallery-mobile is the full resolution picture page. In Chapter 12, Conquering the Desktop UI, we navigated through the pictures using previous/next buttons. In this chapter, we target the mobile platform. Therefore, the navigation should be done using a touch-based gesture: a fling.
Here is the implementation of this new PicturePage.qml file:
import QtQuick 2.0 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.0 import "." PageTheme { property string pictureName property int pictureIndex toolbarTitle: pictureName ListView { id: pictureListView model: pictureModel anchors.fill: parent spacing: 5 orientation: Qt.Horizontal snapMode: ListView.SnapOneItem currentIndex: pictureIndex Component.onCompleted: { positionViewAtIndex(currentIndex, ListView.SnapPosition) } delegate: Rectangle { property int itemIndex: index property string itemName: name width: ListView.view.width == 0 ? parent.width : ListView.view.width height: pictureListView.height color: "transparent" Image { fillMode: Image.PreserveAspectFit cache: false width: parent.width height: parent.height source: "image://pictures/" + index + "/full" } } } }
We first define two properties, pictureName and pictureIndex. The current pictureName property is displayed in the toolbarTitle and pictureIndex is used to initialize the correct currentIndex in ListView, currentIndex: pictureIndex.
To be able to swipe through the pictures, we again use a ListView. Here, each item (a simple Image element) will take the full size of its parent. When the component is loaded, even if currentIndex is correctly set, the view has to be updated to be positioned at the correct index. This is what we do in pictureListView with this:
Component.onCompleted: { positionViewAtIndex(currentIndex, ListView.SnapPosition) }
This will update the position of the current visible item to currentIndex. So far so good. Nonetheless, when a ListView is created, the first thing it does is to initialize its delegate. A ListView has a view property, which is filled with the delegate content. That implies that the ListView.view (yes, it hurts) does not have any width in Component.onCompleted(). As a consequence, the positionViewAtIndex() function does... absolutely nothing. To prevent this behavior, we have to provide a default initial width to the delegate with the ternary expression ListView.view.width == 0 ? parent.width : ListView.view.width. The view will then have a default width on the first load and the positionViewAtIndex() function can happily move until ListView.view is properly loaded.
To swipe through each picture, we set the snapMode value of the ListView to ListView.SnapOneItem. Each fling will snap to the next or previous picture without continuing the motion.
The Image item of the delegate looks very much like the thumbnail version. The sole difference is the source property, where we request PictureImageProvider class with the full resolution.
When PicturePage opens, the correct pictureName property is displayed in the header. However, when the user flings to another picture, the name is not updated. To handle this, we have to detect the motion state. Add the following callbacks in pictureListView:
onMovementEnded: { currentIndex = itemAt(contentX, contentY).itemIndex } onCurrentItemChanged: { toolbarTitleLabel.text = currentItem.itemName }
The onMovementEnded() class is triggered when the motion started by the swipe has ended. In this function, we update the ListViewcurrentIndex with the itemIndex of the visible item at the contentX and contentY coordinates.
The second function, onCurrentItemChanged(), is called upon the currentIndex update. It will simply update the toolbarTitleLabel.text with the picture name of the current item.
To display PicturePage.qml, the same MouseArea pattern is used in the thumbnailList delegate of AlbumPage.qml:
MouseArea { anchors.fill: parent onClicked: { thumbnailList.currentIndex = index pageStack.push("qrc:/qml/PicturePage.qml", { pictureName: name, pictureIndex: index }) } }
Again, the PicturePage.qml file is pushed on the pageStack and the needed parameters (pictureName and pictureIndex) are provided in the same manner.