How can I stop the movement of the Pathview element in QML when it reaches the last element in the model(like the list view and grid view default behaviour).ie,I don't want a cyclic movement.
in the below code if we swipe right after the first("first") item last("last") will be shown (then "Jane Doe") .what can be done to stop the further swipe in right when first is reached and further swipe in left when the last item is reached.it should stop when first or last is reached
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListModel {
id:contactModel
ListElement {
name: "first"
icon: "pic.png"
}
ListElement {
name: "Jane Doe"
icon: "pic.png"
}
ListElement {
name: "John Smith"
icon: "pic.png"
}
ListElement {
name: "Bill Jones"
icon: "pic.png"
}
ListElement {
name: "Jane Doe"
icon: "pic.png"
}
ListElement {
name: "John Smith"
icon: "pic.png"
}
ListElement {
name: "Bill Jones"
icon: "pic.png"
}
ListElement {
name: "Jane Doe"
icon: "pic.png"
}
ListElement {
name: "last"
icon: "pic.png"
}
}
Component {
id: delegate
Column {
id: wrapper
Image {
anchors.horizontalCenter: nameText.horizontalCenter
width: 64; height: 64
source: icon
}
Text {
id: nameText
text: name
font.pointSize: 5
color: wrapper.PathView.isCurrentItem ? "red" : "black"
}
}
}
PathView {
anchors.fill: parent
model: contactModel
delegate: delegate
snapMode:PathView.SnapOneItem
path: Path {
startX: 0; startY: 100
PathLine { x: 640; y: 400; }
}
}
}
Here it's how implementation might look (I was not able to find available properties to do that)
PathView {
property int previousCurrentIndex: 0
currentIndex: 0
...
onCurrentIndexChanged: {
var lastIndex = contactModel.count - 1
if (currentIndex == 0 && previousCurrentIndex == lastIndex) {
pathViewId.positionViewAtIndex(lastIndex, PathView.Beginning)
} else if (currentIndex == lastIndex && previousCurrentIndex == 0) {
pathViewId.positionViewAtIndex(0, PathView.Beginning)
}
previousCurrentIndex = currentIndex
}
}
Custom property previousCurrentIndex is introduced and calculation of actual currentIndex is done on emitting of onCurrentIndexChanged signal
You can add the following code in your PathView. It'll stop the view moving from the end to the start and the other way around.
property real previousOffsetVal: 0.
onOffsetChanged: {
/* 10 is a threshhold, should be smaller than the total elements count*/
if (Math.abs( previousOffsetVal - offset) > 10 ) {
offset = previousOffsetVal
}
previousOffsetVal = offset
}
Related
I have a ListView and its delagates. My intention is to make expandable delegates (eg. I have three parent delegates, if the parent delegate is clicked, it will expand its children). I made this demonstration:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
id: root
width: 1406; height: 536
ListModel {
id: animalsModel
ListElement { name: "Cats"; isExpanded: false; type: "parent"}
ListElement { name: "Puss in Boots 1"; isExpanded: false; type: "Cats"}
ListElement { name: "Puss in Boots 2"; isExpanded: false; type: "Cats"}
ListElement { name: "Puss in Boots 3"; isExpanded: false; type: "Cats"}
ListElement { name: "Dogs"; isExpanded: false; type: "parent"}
ListElement { name: "Pug 1"; isExpanded: false; type: "Dogs" }
ListElement { name: "Birds"; isExpanded: false; type: "parent"}
ListElement { name: "Parrot 1"; isExpanded: false; type: "Birds" }
ListElement { name: "Parrot 2"; isExpanded: false; type: "Birds" }
ListElement { name: "Parrot 3"; isExpanded: false; type: "Birds" }
}
ListView {
id: listing
width: 181
height: parent.height
model: animalsModel
delegate: listdelegate
}
Component {
id: listdelegate
Rectangle {
id: menuItem
width: 181
height: type === "parent" ? 55 : 0
color: type === "parent" ? "lightblue" : "white"
border.width: 1
states: State {
name: "expanded"
when: isExpanded
PropertyChanges {
target: menuItem
height: 55
}
}
transitions:[
Transition {
from: ""
to: "expanded"
reversible: true
SequentialAnimation {
PropertyAnimation { property: "height"; duration: 1000 }
}
}
]
Text {
id: text
text: name
clip: true
anchors.fill: parent
}
MouseArea {
function expand(isExpanded) {
for(var i = 0; i < animalsModel.count; ++i) {
var animal = animalsModel.get(i)
if(animal.type === name) {
animal.isExpanded = isExpanded
}
}
}
anchors.fill: parent
onClicked: {
if (type == "parent") {
if (isExpanded == false) {
// expand
expand(true)
}
else {
// collapse
expand(false)
}
isExpanded = !isExpanded
}
}
}
}
}
}
It works perfectly except one small detail: the height animation is very expensive in my case (I have more complex delagate). How can I make the expanding animation height independent. My idea is to not resize the delagetes, only hide them behind the parent. Like a train going into the tunnel. But I cannot manipulate the x property of delagates in case of vertical ListView.
Any idea how to achive this? In the best scenario how to make the animation I described?
import QtQuick 2.7
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
Rectangle {
width: 640
height: 480
ListModel {
id: tstModel
ListElement { animal: "dog" }
}
TableView {
id: tableView
anchors.fill: parent
model: tstModel
headerDelegate: Item{
height: 50
width: animalColumn.width
TextField{
text: styleData.value
anchors.fill: parent
}
}
TableViewColumn {
id: animalColumn
title: "Animal"
role: "animal"
width: 50
resizable: false
movable: false
}
}
}
This code is not acting as I expect. Textfield gets focus only when I am clicking the header with right button. And there is a Textfield works fine next to the first column header, but it's not what I want.
How to add a editable TableView header in qml?
I find this headerDelegate to be rather buggy or not supposed to be used in this scenario. I would rather implemnt a header from scrath to use for your use-case. Try this as a starting point:
Rectangle {
width: 640
height: 480
ListModel {
id: tstModel
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
ListElement { animal: "cat" }
}
TextField{
id: tableViewCustomHeader
property int curRow: tableView.currentRow
property bool isActual: curRow >= 0
function reloadText() { text = tstModel.get(curRow).animal }
function saveText() { tstModel.setProperty(curRow, "animal", text); tableView.model = tstModel; }
width: animalColumn.width
height: isActual ? 20 : 0
onCurRowChanged: {
if ( isActual >= 0 ) reloadText();
focus = true;
}
onTextChanged: if ( isActual >= 0 ) saveText()
}
TableView {
id: tableView
anchors {
top: tableViewCustomHeader.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
model: tstModel
headerVisible: false
TableViewColumn {
id: animalColumn
title: "Animal"
role: "animal"
width: 200
resizable: false
movable: false
}
}
}
You can edit this anyway you want afterwards.
If it's a strict requirement to go with headerDelegate - this what I've end up with:
Rectangle {
width: 640
height: 480
ListModel {
id: tstModel
ListElement { animal: "dog" }
ListElement { animal: "cat" }
}
TableView {
id: tableView
anchors.fill: parent
model: tstModel
headerDelegate:
TextField{
property int curRow: tableView.currentRow
function reloadText() { text = tstModel.get(curRow).animal }
function saveText() { tstModel.setProperty(curRow, "animal", text); tableView.model = tstModel; }
onCurRowChanged: {
if ( curRow >= 0 ) reloadText()
}
onTextChanged: saveText()
width: parent.width
}
TableViewColumn {
id: animalColumn
title: "Animal"
role: "animal"
width: 200
resizable: false
movable: false
}
}
}
But, again, it works in a very strange way once you are adding TextField to it, so I would vote against it.
I want to disable the dragging of my List view using pressing and holding a mouse button while moving the cursor. I am trying to implement some other feature such as multiple selection using this command. I would only like the scrolling to be enabled. The interactive property of the flickable totally disables the movement of the view. Is there some workaround for this?
I guess there are some ways to achieve what you need, but the following solution works.
The idea is to have a MouseArea and set the interactive property to false when the signals onPressed and onClicked are emitted. interactive should be set to true again in the onReleased handler.
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListModel {
id: listModel
ListElement {
name: "ttt"
number: "111"
}
ListElement {
name: "rrr"
number: "222"
}
ListElement {
name: "sss"
number: "333"
}
ListElement {
name: "xxx"
number: "444"
}
ListElement {
name: "yyy"
number: "555"
}
ListElement {
name: "zzz"
number: "666"
}
ListElement {
name: "aaa"
number: "777"
}
ListElement {
name: "bbb"
number: "888"
}
ListElement {
name: "ccc"
number: "999"
}
ListElement {
name: "ddd"
number: "011"
}
ListElement {
name: "eee"
number: "022"
}
ListElement {
name: "fff"
number: "033"
}
}
ListView {
id: myList
width: 180; height: 100
clip: true
Component {
id: contactsDelegate
Rectangle {
id: wrapper
width: 180
height: contactInfo.height
color: "lightblue"
Text {
id: contactInfo
text: name + ": " + number
color: "black"
}
MouseArea {
anchors.fill: parent
onPressed: {
myList.interactive = false
console.debug("onPressed")
}
onClicked: {
myList.interactive = false
console.debug("onClicked")
}
onReleased: {
myList.interactive = true
console.debug("onReleased")
}
}
}
}
model: listModel
delegate: contactsDelegate
focus: true
}
}
I've found the following code in a QML example. I have a question about sections: is there a way to hide all the elements belonging to a section when the user clicks on the section header? For example, is it possible to hide "Ant" and "Flea" when the user clicks the "Tiny" header?
Here is the code:
Rectangle {
id: container
width: 300
height: 360
ListModel {
id: animalsModel
ListElement { name: "Ant"; size: "Tiny" }
ListElement { name: "Flea"; size: "Tiny" }
ListElement { name: "Parrot"; size: "Small" }
ListElement { name: "Guinea pig"; size: "Small" }
ListElement { name: "Rat"; size: "Small" }
ListElement { name: "Butterfly"; size: "Small" }
ListElement { name: "Dog"; size: "Medium" }
ListElement { name: "Cat"; size: "Medium" }
ListElement { name: "Pony"; size: "Medium" }
ListElement { name: "Koala"; size: "Medium" }
ListElement { name: "Horse"; size: "Large" }
ListElement { name: "Tiger"; size: "Large" }
ListElement { name: "Giraffe"; size: "Large" }
ListElement { name: "Elephant"; size: "Huge" }
ListElement { name: "Whale"; size: "Huge" }
}
// The delegate for each section header
Component {
id: sectionHeading
Rectangle {
id: sectionHeadingRectangle
width: container.width
height: childrenRect.height
color: "lightsteelblue"
Text {
text: section
font.bold: true
font.pixelSize: 20;
}
}
}
Component {
id: section
Rectangle {
width: container.width
height: mainText.height
Text { id: mainText; text: name; font.pixelSize: 18 }
}
}
ListView {
id: view
anchors.fill: parent
// width: parent.width
model: animalsModel
delegate: section
section.property: "size"
section.criteria: ViewSection.FullString
section.delegate: sectionHeading
}
}
It is possible to do this by sending a custom signal when a section is clicked and having all delegates connect to this signal and check if the section clicked correspond to theirs, and hide accordingly :
Rectangle {
id: container
width: 300
height: 360
ListModel {
id: animalsModel
ListElement { name: "Ant"; size: "Tiny" }
ListElement { name: "Flea"; size: "Tiny" }
ListElement { name: "Parrot"; size: "Small" }
ListElement { name: "Guinea pig"; size: "Small" }
ListElement { name: "Rat"; size: "Small" }
ListElement { name: "Butterfly"; size: "Small" }
ListElement { name: "Dog"; size: "Medium" }
ListElement { name: "Cat"; size: "Medium" }
ListElement { name: "Pony"; size: "Medium" }
ListElement { name: "Koala"; size: "Medium" }
ListElement { name: "Horse"; size: "Large" }
ListElement { name: "Tiger"; size: "Large" }
ListElement { name: "Giraffe"; size: "Large" }
ListElement { name: "Elephant"; size: "Huge" }
ListElement { name: "Whale"; size: "Huge" }
}
// The delegate for each section header
Component {
id: sectionHeading
Rectangle {
id: sectionHeadingRectangle
width: container.width
height: childrenRect.height
color: "lightsteelblue"
Text {
text: section
font.bold: true
font.pixelSize: 20;
}
MouseArea {
anchors.fill: parent
onClicked: view.sectionClicked(section)
}
}
}
Component {
id: section
Rectangle {
id: rect
width: container.width
height: shown ? mainText.height : 0
visible: shown
property bool shown: true
Text { id: mainText; text: name; font.pixelSize: 18 }
Connections {
target: rect.ListView.view
onSectionClicked: if (rect.ListView.section === name) shown = !shown;
}
}
}
ListView {
id: view
anchors.fill: parent
// width: parent.width
signal sectionClicked(string name)
model: animalsModel
delegate: section
section.property: "size"
section.criteria: ViewSection.FullString
section.delegate: sectionHeading
}
}
I'm trying to develop an application which has multi-layered navigation in drop-downs.
Below is the snapshot of the requirement. On clicking QSR only the subitems of QSR should pop out with proper alignment.
But with my implementation, I'm getting following result. The pop out window is not aligned, but extending itself to the list.
Below is the my code snippet
import QtQuick 1.1
Item {
width: 500
height: 300
ListView {
anchors.fill: parent
model: nestedModel
delegate: categoryDelegate
}
ListModel {
id: nestedModel
ListElement {
categoryName: "QSR"
collapsed: true
subItems: [
ListElement { itemName: "KFC" },
ListElement { itemName: "Mc Donalds" },
ListElement { itemName: "Pizza Hut" },
ListElement { itemName: "Brain's" }
]
}
ListElement {
categoryName: "Drinks"
collapsed: true
subItems: [
ListElement { itemName: "Pepsi" },
ListElement { itemName: "Coke" },
ListElement { itemName: "7up" },
ListElement { itemName: "Bacardi" }
]
}
}
Component {
id: categoryDelegate
Column {
width: 200
Rectangle {
id: categoryItem
border.color: "black"
border.width: 5
color: "white"
height: 50
width: 200
anchors.left: parent.right
Text {
anchors.right: parent.right
x: 15
font.pixelSize: 24
text: categoryName
}
MouseArea {
anchors.fill: parent
onClicked: nestedModel.setProperty(index, "collapsed", !collapsed)
}
}
Loader {
id: subItemLoader
visible: !collapsed
property variant subItemModel : subItems
sourceComponent: collapsed ? null : subItemColumnDelegate
onStatusChanged: if (status == Loader.Ready) item.model = subItemModel
}
}
}
Component {
id: subItemColumnDelegate
Column {
property alias model : subItemRepeater.model
width: 200
Repeater {
id: subItemRepeater
delegate: Rectangle {
color: "#cccccc"
height: 40
width: 200
border.color: "black"
border.width: 2
Text {
anchors.verticalCenter: parent.verticalCenter
x: 30
font.pixelSize: 18
text: itemName
}
}
}
}
}
}
Any help to achieve original requirement will be appreciable.
Have a close look at the delegate for your main list view: It's Column (put items on top of each over) of the main menu item and Loader for the popup menu. Obviously as soon as you load a popup menu it's displayed under its corresponding menu item in the list.
To get the behavior you intended you have to move the Loader for the popup menu out of the ListView:
import QtQuick 1.1
Item {
width: 400
height: 300
Row {
anchors.fill: parent
Loader {
id: subItemLoader
width: 200
height: parent.height
property variant subItemModel: null
sourceComponent: subItemModel == null? null: subItemColumnDelegate
function setModel() {
if (status == Loader.Ready) item.model = subItemModel
}
onStatusChanged: setModel()
onSubItemModelChanged: setModel()
}
ListView {
width: 200
height: parent.height
model: nestedModel
delegate: categoryDelegate
}
}
ListModel {
id: nestedModel
ListElement {
categoryName: "QSR"
subItems: [
ListElement { itemName: "KFC" },
ListElement { itemName: "Mc Donalds" },
ListElement { itemName: "Pizza Hut" },
ListElement { itemName: "Brain's" }
]
}
ListElement {
categoryName: "Drinks"
subItems: [
ListElement { itemName: "Pepsi" },
ListElement { itemName: "Coke" },
ListElement { itemName: "7up" },
ListElement { itemName: "Bacardi" }
]
}
}
Component {
id: categoryDelegate
Rectangle {
id: categoryItem
border.color: "black"
border.width: 5
color: "white"
height: 50
width: 200
Text {
anchors.centerIn: parent
x: 15
font.pixelSize: 24
text: categoryName
}
MouseArea {
anchors.fill: parent
onClicked: {
if (subItemLoader.subItemModel != subItems)
subItemLoader.subItemModel = subItems
else
subItemLoader.subItemModel = null
}
}
}
}
Component {
id: subItemColumnDelegate
Column {
property alias model : subItemRepeater.model
width: 200
height: 500
Repeater {
id: subItemRepeater
delegate: Rectangle {
color: "#cccccc"
height: 40
width: 200
border.color: "black"
border.width: 2
Text {
anchors.verticalCenter: parent.verticalCenter
x: 30
font.pixelSize: 18
text: itemName
}
}
}
}
}
}