How to change the page to the next or previous item of a SwipeView by clicking on the right and left arrow respectively? - qt

I have a SwipeView that loads its internal elements through a Repeater and a Loader.
I would like to swipe between the items forward and backward by just clicking the arrows on the right and left of the SwipeView.
How can I implement this behavior in QML?
SwipeView {
id: __swipeView
Layout.fillHeight: true
Layout.fillWidth: true
Repeater {
model: 3
Loader {
source: "qrc:/../SwipeDelegate.qml"
}
}
}

Within your delegate, you can access the SwipeView via the SwipeView attached property, and then increment or decrement the current index as necessary:
import QtQuick 2.6
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
SwipeView {
anchors.fill: parent
Repeater {
model: 3
Item {
id: delegate
Button {
text: "<"
enabled: index > 0
onClicked: delegate.SwipeView.view.decrementCurrentIndex()
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
}
Label {
text: "Page " + (index + 1)
anchors.centerIn: parent
}
Button {
text: ">"
enabled: index < delegate.SwipeView.view.count - 1
onClicked: delegate.SwipeView.view.incrementCurrentIndex()
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
}
It's important to use the functions as opposed to setting currentIndex directly, for the reasons described here.

Related

QML: Current Index / Item is changin if swipeview is populated using a loader or repater inside a asyncronous parent Loader

Dynamic loading data into a QML SwipeView loaded asyncronous is autmomatically increment the current index.
Loader{
asynchronous: true
anchors.fill: parent
sourceComponent: ColumnLayout{
//Any content here that can take some time to load
TabBar{
Layout.fillWidth: true
Repeater{
model: swRepeater.model
delegate: TabButton{
text: "Tab button "+index
}
}
}
SwipeView{
// anchors.fill: parent
Layout.fillWidth: true
Layout.fillHeight: true
currentIndex: 0
Repeater{
id: swRepeater
model: 5
delegate: Item{
Label{
anchors.centerIn: parent
text: qsTr("Test "+index)
}
}
}
onCurrentIndexChanged: {
console.debug("Index was modified to ",currentIndex)
}
}
}
}
At the end of the loading data the current item and index will be 4, and not 0 as explected.
Also is displayed the last item from stack.
The same behavior can be se on the TabBar item so the problem looks to came from ListView or Container.
Any suggestion on this behavior?

How to implement swipeview QtQuick 2.5

I have QT 5.5 and it won't support SwipeView. I tried doing with listView but I am not getting what I expected. I want a similar functionality code in QT 5.5 like the code given below which is written in QT 5.6. Please help
import QtQuick 2.6
import QtQuick.Controls 2.0
import QtQuick.Window 2.0
Window {
visible: true
width: 200
height: 400
title: qsTr("Hello World")
id: page
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: 0
Page {
Label {
text: qsTr("First page")
anchors.centerIn: parent
}
}
Page {
Label {
text: qsTr("Second page")
anchors.centerIn: parent
}
}
Page {
Label {
text: qsTr("Third page")
anchors.centerIn: parent
}
}
Page {
Label {
text: qsTr("Fourth page")
anchors.centerIn: parent
}
}
Page {
Label {
text: qsTr("Fifth page")
anchors.centerIn: parent
}
}
}
Rectangle
{
id:minus
width:parent.width/2
height:100
anchors.left:parent.left
anchors.bottom:parent.bottom
color:"red"
MouseArea
{
anchors.fill:parent
onClicked:{
if(swipeView.currentIndex>0)
swipeView.currentIndex--
}
}
}
Rectangle
{
id:plus
width:parent.width/2
height:100
anchors.right:parent.right
anchors.bottom:parent.bottom
color:"green"
MouseArea
{
anchors.fill:parent
onClicked:{
if(swipeView.currentIndex<4)
swipeView.currentIndex++
}
}
}
}
If you cannot update your Qt version, you can indeed approximate a SwipeView using a ListView, a VisualItemModel, and a default qml property.
SwipeView.qml
ListView
{
id: root
// Allow to add pages as you would for a QtQuick.Controls 2 SwipeView
// Each item you declare in your SwipeView will be reparented to itemModel
default property alias items: itemModel.children
// SwipeView is horizontal
orientation: Qt.Horizontal
// Hide out of bounds pages
clip: true
// Do not stop between two pages
snapMode: ListView.SnapToItem
// Update currentIndex as you swipe through the pages
highlightRangeMode: ListView.StrictlyEnforceRange
model: VisualItemModel {
id: itemModel
// Used to bind the pages size to the swipeView size
onChildrenChanged: {
for(var i=0;i<children.length; i++)
{
children[i].width = Qt.binding(function(){return root.width})
children[i].height = Qt.binding(function(){return root.height})
}
}
}
}
Page.qml
Item {
property string title
Rectangle
{
anchors.fill: parent
border.width: 1
}
Text
{
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 20
text: title
}
}
PageIndicator.qml
Row
{
id: root
property int count
property int currentIndex
property Component delegate: bullet
property bool interactive
spacing: 5
Component
{
id: bullet
Rectangle
{
height: 10
width: height
radius: height/2
color:"black"
opacity: currentIndex==index?0.8:0.2
}
}
Repeater
{
model: root.count
Loader
{
property int index: model.index
sourceComponent: delegate
}
}
}
main.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow
{
id: window
visible: true
width: 300
height: 300
SwipeView
{
id: swipeView
anchors.fill: parent
Page
{
title: "Page 1"
}
Page
{
title: "Page 2"
}
Page
{
title: "Page 3"
}
}
PageIndicator
{
id: pageIndicator
anchors.bottom: swipeView.bottom
anchors.bottomMargin: 10
anchors.horizontalCenter: swipeView.horizontalCenter
count: swipeView.count
currentIndex: swipeView.currentIndex
}
}
Qt Quick Controls 2 was introduced in Qt 5.7:
Qt Quick Controls 2 provides a set of controls that can be used to build complete interfaces in Qt Quick. The module was introduced in Qt 5.7.
Qt Labs Controls was introduced in Qt 5.6, so the code that you referenced would have to use the Qt.labs.controls 1.0 import in order to run with Qt 5.6.
You need to use a newer Qt version (5.6 or newer).

Keep input field in view while using on-screen keyboard

I've a virtual keyboard which pops-up from the bottom of the screen and always stays on top. I'm going to use this in my application and have a small problem.
If the text input field which accepts input from this keyboard is in middle / bottom of the view (main window / screen), it gets hidden behind the keyboard i.e., can't see whats been entered until the keyboard is hidden.
Keyboard is running as platforminputcontext plugin which will know the field that is accepting the input.
void KeyboardPlatformInputContext::setFocusObject(QObject* object)
{
qDebug() << m_focusedObject << object;
m_focusedObject = object;
}
When the keys are pressed, they are passed as QEvents like this
void KeyboardPlatformInputContext::processNormalKeyClick(const QString& key)
{
qDebug() << m_focusedObject << key;
if (m_focusedObject) {
QInputMethodEvent inputEvent;
inputEvent.setCommitString(key);
QGuiApplication::sendEvent(m_focusedObject, &inputEvent);
}
}
Now, with the available information (m_focusedObject and QGuiApplication) can it be possible to do something to keep the input field in view. Always.
Kuba has the right idea; I'll just expand on it. You can use Flickable, for example, to manage the content of your application. For example, suppose your application was laid out like a form:
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
id: root
width: 480
height: 800
visible: true
Column {
anchors.fill: parent
anchors.margins: 20
spacing: 20
Repeater {
model: 20
Row {
spacing: 20
Text {
text: "Input #" + (index + 1)
anchors.verticalCenter: parent.verticalCenter
}
TextInput {
width: 100
height: 30
onActiveFocusChanged: {
if (activeFocus)
keyboardRect.visible = activeFocus
}
Rectangle {
border.width: 1
anchors.fill: parent
anchors.margins: -1
z: -1
}
}
}
}
}
Rectangle {
id: keyboardRect
width: parent.width
height: parent.height * 0.3
anchors.bottom: parent.bottom
color: "grey"
visible: false
}
}
To make it usable with a virtual keyboard, move the content into a Flickable:
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
id: root
width: 480
height: 800
visible: true
Flickable {
id: flickable
anchors.fill: parent
anchors.margins: 20
anchors.bottomMargin: keyboardRect.visible ? keyboardRect.height : anchors.margins
contentWidth: column.implicitWidth
contentHeight: column.implicitHeight
flickableDirection: Flickable.VerticalFlick
Column {
id: column
spacing: 20
Repeater {
model: 20
Row {
spacing: 20
Text {
text: "Input #" + (index + 1)
anchors.verticalCenter: parent.verticalCenter
}
TextInput {
width: 100
height: 30
onActiveFocusChanged: {
if (activeFocus) {
keyboardRect.visible = activeFocus
var posWithinFlickable = mapToItem(column, 0, height / 2);
flickable.contentY = posWithinFlickable.y - flickable.height / 2;
}
}
Rectangle {
border.width: 1
anchors.fill: parent
anchors.margins: -1
z: -1
}
}
}
}
}
}
Rectangle {
id: keyboardRect
width: parent.width
height: parent.height * 0.3
anchors.bottom: parent.bottom
color: "grey"
visible: false
}
}
A few things to note:
anchors.bottomMargin: keyboardRect.visible ? keyboardRect.height : anchors.margins
This ensures that the content is "pushed" up when the keyboard is visible, so that nothing is hidden below it.
onActiveFocusChanged: {
if (activeFocus) {
keyboardRect.visible = activeFocus
var posWithinFlickable = mapToItem(column, 0, height / 2);
flickable.contentY = posWithinFlickable.y - flickable.height / 2;
}
}
This code doesn't account for losing focus and hence the keyboard always stays open.
We focus the Flickable on the current input field by mapping the position of the field to the Column.
Finally, you'll see a bit of jumping around when you click on the fields near the top or bottom of the column. This can be probably solved by not setting the contentY if the field is near the top or bottom. An exercise for the reader. :)
For me correct answer is above (first one) plus following:
https://doc.qt.io/qt-5/qtvirtualkeyboard-deployment-guide.html#creating-inputpanel
import QtQuick 2.0
import QtQuick.VirtualKeyboard 2.1
Item {
id: root
Item {
id: appContainer
anchors.left: parent.left
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: inputPanel.top
...
}
InputPanel {
id: inputPanel
y: Qt.inputMethod.visible ? parent.height - inputPanel.height : parent.height
anchors.left: parent.left
anchors.right: parent.right
}
}
Quote:
The input panel must be a sibling element next to the application
container. It is important not to put the input panel within the
application container, as it would then overlap with the contents of
the application. Also, the input panel height will be automatically
updated according to the available width; the aspect ratio of the
input panel is constant.

how to load a new screen in QML showing a new list based on previous user click input?

In my application i show a nested list, that shows groups and folders as its children. I have built the functions necesary to generate a new list in the backend in c++ based on which item is clicked by the user.
I allready have the necesary functionality to pass the list to qml through QProperty.
so my question is, how do i previous listviews and show new ones dynamically. Considering it should also be possible to click the button "back", which should load the previous page again showing the groups and the folders.
this is the code i have now, showing the groups and its children(folders)
import QtQuick 2.4
import QtQuick.Window 2.2
//import ListMode 1.0
Rectangle {
height: 250
width: 140
color: "pink"
//property var aNum: 0
Component {
id: folderDelegate
Item {
width: 140
height: col2.childrenRect.height
Column {
id: col2
anchors.left: parent.left
anchors.right: parent.right
Rectangle {
height: 20
width: parent.width
border.color: "black"
MouseArea {
anchors.fill: parent
onClicked: treemodel.getObject(model.ID + ":" + model.Name)
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
id: name1
text: model.Name
}
}
}
}
}
ListView {
id: outer
model: myModel
delegate: groupsDelegate
anchors.fill: parent
}
Component {
id: groupsDelegate
Item {
width: 140
height: col.childrenRect.height
Column {
id: col
anchors.left: parent.left
anchors.right: parent.right
Text {
anchors.horizontalCenter: parent.horizontalCenter
id: t1
font.bold: true
font.underline: true
font.pointSize: 9
text: model.Name
}
ListView {
id: folderlist
model: treemodel.lists[treemodel.modIndex]
delegate: folderDelegate
contentHeight: contentItem.childrenRect.height
height: childrenRect.height
anchors.left: parent.left
anchors.right: parent.right
clip: true
}
}
}
}
}
i have been reading documentations and searching forums, but the information is pretty overwhelming. So a pointer in the right direction would be appreciated.
the main model is setup for each item to have its own unique ID. So when an item is clicked, i run a function that grabs and stores the item based on the ID + name that was clicked
MouseArea {
anchors.fill: parent
onClicked :{
treemodel.getObject(model.ID + ":" + model.Name)
stackView.push(Qt.resolvedUrl("content/ButtonPage.qml"))
}
}
next, based on the item that was clicked i have functions that fill different QList items which are loaded into the ButtonPage.qml.
the function in c++ that is invoked is:
Q_INVOKABLE void getObject(QString index) {
clickedItemID = index;
getClickedItem();
getFilesByFolder();
}
now, i am not sure if this is a good solution. But for me it works. Maybe it will work for someone else too.

QML ListView current item not changing with keystrokes or mouse

I have a very simple ListView.
ListView {
id: logListView
anchors.fill: parent
model: LogEntryListModel
delegate:
Text {
text: "Log Item: " + timestamp + ", " + verb
}
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
focus: true
clip: true
}
It shows the model fine and highlights the first item. It does not move the highlight when I click on another item nor when I use the arrow keys. I know how to control the highlighted item manually by adding event handlers but I see references in the docs to automatic handling of the selectedItem. I was wondering:
Does QML provide an automatic changing of the selected item highlighting? What do I need to add to turn it on?
The keyboard handling is done automatically:
import QtQuick 2.0
import QtQuick.Controls 1.1
Rectangle {
width: 400
height: 400
ListView {
id: logListView
anchors.fill: parent
model: 10
delegate: Text {
text: "Log Item: " + modelData
}
highlight: Rectangle {
color: "lightsteelblue";
radius: 5
}
focus: true
clip: true
}
}
If using the up and down arrow keys does not change the selected item for you, using the code above, then it's a bug.
Using a mouse to select items is not handled by default, however; only flicking/dragging of the list is. It's easy to add, though:
import QtQuick 2.0
import QtQuick.Controls 1.1
Rectangle {
width: 400
height: 400
ListView {
id: logListView
anchors.fill: parent
model: 10
delegate: Text {
text: "Log Item: " + modelData
MouseArea {
anchors.fill: parent
onClicked: logListView.currentIndex = index
}
}
highlight: Rectangle {
color: "lightsteelblue";
radius: 5
}
focus: true
clip: true
}
}

Resources