I'm developing a BlackBerry 10 mobile application using the Momentics IDE 2.1.2 (native SDK).
I'm looking for a sample that shows how to build a horizontal pagination like in the image below that I will put it inside a container replacing with that a listview with a grid display.
Can anyone help me with this ?
You can easy build simple pagination using ListView only. Hope the example below will be usefull:
import QtQuick 2.4
Item {
width: 600
height: 800
ListView {
id: listView
anchors.fill: parent
orientation: ListView.Horizontal
snapMode: ListView.SnapToItem
highlightRangeMode: ListView.StrictlyEnforceRange
model: 5
clip: true
delegate: Rectangle {
width: listView.width
height: listView.height
color: Qt.rgba(Math.random(),Math.random(),Math.random(),1);
Text {
anchors.centerIn: parent
text: "page " + index
}
}
}
Item {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 30
Row {
anchors.centerIn: parent
Repeater {
model: listView.model
Rectangle {
width: 20
height: 20
border.width:2
border.color: "white"
color: index == listView.currentIndex ? "orange" : "white"
Text {
anchors.centerIn: parent
text: index
}
}
}
}
}
}
Related
I'm Developing an application with Qt 5.13.0 on a Windows 10 professional computer. I need to create a Whatsapp like message view, and to achieve that, I used a GridView component as a base. However, in order to draw the messages correctly, I need to create each GridView row with a different height, depending on the message text.
But to my surprise, I could not find any solution on the internet to achieve that, although I thought it was a simple formality. I tried a solution by myself, which I attached below, however it doesn't work. The issue is that all the rows take the height of the last resized one.
I have no doubt that Qt can do this, unfortunately, I have been looking for days now, and I can not find a solution to this issue. I simply have no idea about how to achieve that. So someone can explain to me how to create a GridView with variable height rows, or if the GridWiew isn't the appropriate component to do that, whcih I should use instead?
Here is my qss file:
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Window 2.12
import QtQuick.Layouts 1.11
Window
{
visible: true
width: 640
height: 480
title: qsTr("Grid view")
color: "#ffffff"
ColumnLayout
{
transformOrigin: Item.Center
spacing: 0
x: 0
y: 0
width: parent.width
height: parent.height
/**
* Grid view item
*/
Component
{
id: itGridItem
Item
{
Column
{
Rectangle
{
property int messageWidth: (gvMessageGrid.cellWidth / 2) - 50
id: itemRect
x: senderIsMyself ? 25 : gvMessageGrid.cellWidth - (25 + messageWidth)
y: 5
width: messageWidth
height: itemTextID.height + 20
color: senderIsMyself ? "#d5d5d5" : "#800b940e"
radius: 5
clip: true
Text
{
id: itemTextID
width: parent.width - 20
text: itemText
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
wrapMode: Text.WordWrap
font.family: "Segoe UI Emoji"
font.pixelSize: 18
anchors.margins: 10
anchors.left: parent.left
anchors.top: parent.top
color: "#101010"
}
onHeightChanged: gvMessageGrid.cellHeight = height + 10
}
}
}
}
/**
* Messages grid view
*/
GridView
{
id: gvMessageGrid
y: 0
Layout.fillHeight: true
flickableDirection: Flickable.VerticalFlick
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
clip: true
contentWidth: 700
contentHeight: 300
cellWidth: contentWidth
cellHeight: 150
model: lmGridModel
delegate: itGridItem
ScrollBar.vertical: ScrollBar
{
visible: true
}
onWidthChanged: cellWidth = width
}
}
}
--- Edited on October 18, 2019
Following the eyllanesc suggestion below, here are 2 screenshots of what I want and what I get:
what I want
what I get
NOTE I'm using several languages, for test purposes, because my application must have international support. However the issue I face has nothing to do with that, i.e it's not an error with text calculation, because 1. The green rect sizes around the text are always correct, and 2. I face the exact same issue with pure English text.
I suggest you to use ListView instead of GridView. You can easily view anything inside full-width list item. Let me share with you my ListDelegate class from an old project. Pay attention to usage of isIncoming property in anchor binding, for example:
anchors {
left: isIncoming? undefined : parent.left
right: isIncoming? parent.right : undefined
}
Full listing:
import QtQuick 2.5
import "units.js" as Units
Rectangle {
id: chatMsgDelegRoot
property bool isIncoming: !model.out
property bool isSelected: model.isSelected
signal clicked(int index)
width: parent.width
height: dlgColumn.height + Units.gu(2.5)
color: "#edf1f5"
Column {
id: dlgColumn
spacing: Units.gu(4)
width: parent.width
anchors.verticalCenter: parent.verticalCenter
BorderImage {
id: borderImage
source: isIncoming?
(isSelected ? "/images/img/MsgOut_Selected_2.png" : "/images/img/MsgOut_2.png") :
(isSelected ? "/images/img/MsgIn_Selected_2.png" : "/images/img/MsgIn_2.png")
// Texture-dependent.
border {
left: isIncoming? 20 : 30
top: 20
right: isIncoming? 30 : 20
bottom: 35
}
anchors {
left: isIncoming? undefined : parent.left
right: isIncoming? parent.right : undefined
}
width: Math.max(content.width + Units.gu(15), Units.gu(21))
height: content.height + Units.gu(9)
MouseArea {
id: msgDelegateMa
anchors.fill: parent
onClicked: chatMsgDelegRoot.clicked(model.index)
}
Loader {
id: content
sourceComponent: model.type === "Text" ? textComponent : controlComponent
anchors {
left: isIncoming? undefined : parent.left
right: isIncoming? parent.right : undefined
leftMargin: Units.gu(10)
rightMargin: Units.gu(10)
top: parent.top
topMargin: Units.gu(4)
}
}
Text {
text: model.date.toTimeString()
font.pointSize: 8
font.italic: true;
color: "lightgrey"
anchors {
left: isIncoming? undefined : parent.left
right: isIncoming? parent.right : undefined
rightMargin: Units.gu(7.5)
leftMargin: Units.gu(7.5)
bottom: parent.bottom
bottomMargin: Units.gu(1)
}
}
} // BorderImage
} // Column
// TODO To separate files.
Component {
id: textComponent
Rectangle {
id: textComponentRoot
color: "#00000000"
width: msgText.paintedWidth
height: msgText.height
Text {
id: msgText
font.pointSize: 10
textFormat: Text.RichText
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
width: chatMsgDelegRoot.width * 0.7
text: model.body
color: isSelected? "white" : "black"
}
}
} // Component
Component {
id: controlComponent
Rectangle {
id: textComponentRoot
color: "#00000000"
width: innerColumn.width
height: innerColumn.height
Column {
id: innerColumn
spacing: Units.gu(1)
Text {
id: fileNameText
font.pointSize: 10
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
width: chatMsgDelegRoot.width * 0.7
elide: Text.ElideRight
text: "File transfer: " + model.body
color: isSelected? "white" : "black"
}
Row {
id: innerRow
anchors.right: parent.right
spacing: Units.gu(1)
SimpleButton {
id: allowBtn
width: Units.gu(15)
height: Units.gu(8)
text: "Allow"
}
SimpleButton {
id: denyBtn
width: Units.gu(15)
height: Units.gu(8)
text: "Deny"
}
}
} // Column
}
} // Component
}
I have a tab bar with a stacklayout like the following:
Rectangle {
id: rect
height: 190
anchors.right: parent.right
anchors.left: parent.left
color: "transparent"
anchors.top: uniqueHandleText.bottom
anchors.topMargin: 100
TabBar {
id: frame
anchors.right: parent.right
anchors.left: parent.left
background: Rectangle {
color: "#737373"
}
x: -hbar.position * width
Repeater {
model: wizard.categories
TabButton {
id: tabData
property bool selected: false
text: modelData.name
width: 200
font.pixelSize: 18
contentItem: Text {
text: tabData.text
font: tabData.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
wrapMode: Text.WordWrap
color: "#FFFFFF"
}
background: Rectangle {
implicitWidth: frame.width
implicitHeight: 180
opacity: enabled ? 1 : 0.3
color: tabData.checked ? "#BD9CBE": "#737373"
}
}
}
}
ScrollBar {
id: hbar
hoverEnabled: true
active: hovered || pressed
orientation: Qt.Horizontal
size: rect.width / frame.width
anchors.left: parent.left
anchors.right: parent.right
anchors.top: frame.bottom
}
Text {
font.pixelSize: 18
text: "Next"
anchors.right: parent.right
visible: frame.x != frame.width ? true: false
}
StackLayout {
id: stack1
anchors.left: parent.left
anchors.right: parent.right
anchors.top: frame.bottom
currentIndex: frame.currentIndex
Repeater {
model: wizard.categories
Item {
id: homeTab
TabBar {
id: homeTabTab
anchors.right: parent.right
anchors.left: parent.left
anchors.top: parent.top
height: 180
background: Rectangle {
color: "#958096"
}
Repeater {
model: modelData.sub_categories
TabButton {
property bool selected: false
id: currentTab
text: modelData.name
width: 200
font.pixelSize: 18
background: Rectangle {
implicitWidth: frame.width
implicitHeight: 180
opacity: enabled ? 1 : 0.3
color: currentTab.checked ? "#958096": "#8D758E"
}
contentItem: Text {
text: currentTab.text
font: currentTab.font
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
wrapMode: Text.WordWrap
color: "#FFFFFF"
MouseArea {
anchors.fill: parent
onClicked: {
if(currentTab.checked){
currentTab.checked = false
} else {
currentTab.checked = true
}
}
onDoubleClicked: {
currentTab.selected = true
var found = false;
var someText = frame.itemAt(stack1.currentIndex).text;
print(someText)
for(var i = 0; i<wizard.selectedSkills.count; i++){
if(wizard.selectedSkills.get(i).name === someText){
wizard.selectedSkills.get(i).sub_categories.append({"name":currentTab.text});
wizard.skills.push({"name": someText})
found = true;
}
}
if(!found){
print(currentTab.text)
wizard.selectedSkills.append({"name":someText, "sub_categories":[{"name":currentTab.text}]})
}
print(window.selectedSkills)
}
}
}
}
}
}
}
}
}
}
I've tried many different things to add a scrollbar or to figure out how to use the flickable functionality that TabBar has. However, the documentation doesn't specify how it works, it just does. Therefore, they are not accessible (or even rewritteable, to use those properties). I want to add a small indicator like an arrow to specify that there is more elements for ease of navigation on desktop on top of the TabBar functionality.
It doesn't seem like the necessary properties are exposed in order to make this happen the easy way.
However, since this is QML, it means the whole object tree is gaping wide open to introspection, allowing us to establish that the item that does the flicking is the contentItem of a ListView inside the Container the ToolBar inherits. The view happens to be the second visible child, although this is technically "private implementation" that one should not rely on. So it is better to take some extra care to establish whether or not you have the correct object.
ApplicationWindow {
id: main
width: 640
height: 480
visible: true
TabBar {
id: toolbar
width: parent.width
height: 50
Repeater {
model: 10
TabButton {
text: "test " + index
width: 100
}
}
}
Rectangle {
height: 5
width: main.width * (view ? view.visibleArea.widthRatio : toolbar.width / toolbar.contentWidth)
color: "red"
anchors.top: toolbar.bottom
x: view ? (main.width - width) * (view.contentX / (view.contentWidth - view.width)) : 0
}
property ListView view: {
var l = toolbar.visibleChildren.length
while (--l) if ("cacheBuffer" in toolbar.visibleChildren[l]) return toolbar.visibleChildren[l]
return null
}
}
And there you have it. We iterate the tabview children until we find one that has a property cacheBuffer which is fairly unique to ListView, and once we have that, we can access the needed properties. As you see, for the indicator width we can do even without the list view, as the toolbar exposes a contentWidth property, but for the indicator position there is no workaround.
And it works:
I have a file named page2.qml like below
Page {
id: page
Rectangle {
id: container
anchors.fill: parent
width: parent.width * 0.8
Rectangle {
id: title
anchors.top: parent.top
width: parent.width
height: 50
color: "salmon"
}
ListView {
id: listView
currentIndex: -1
anchors.top: title.bottom
anchors.bottom: parent.bottom
delegate: Rectangle {
height: 20
width: 100
border.color: "red"
color: "pink"
Text {
text: model.index
}
}
model: 100
}
}
}
the result is in this image:
Since the listview contain 100 item, how can i make the whole page scrollable? i can make just listview scrollable but not the whole page.
If you don't need the ListView to be scrollable by itself but your whole container need to, you could use a Repeater instead and put it inside a Column wrapped in a Flickable :
Flickable {
id: container
contentHeight: column.implicitHeight
contentWidth: width
width: parent.width * 0.8
height: parent.height
Column {
id: column
width: parent.width
Rectangle {
id: title
width: parent.width
height: 50
color: "salmon"
}
Repeater {
id: listView
model: 100
delegate: Rectangle {
height: 20
width: 100
border.color: "red"
color: "pink"
Text {
text: model.index
}
}
}
}
}
I want to create an animated accordion-like element that expands on click. Here's how it should work.
When the user clicks one of the red rectangles, the green rectangle which is the actual content, should expand. I want this expansion to be animated. The height of the contents of the green rectangles could be different for each red header.
I have been able to implement the click-to-expand behavior, but there's no animation. Here is the code I currently have.
AccordionElement.qml
import QtQuick 2.5
import QtQuick.Layouts 1.1
ColumnLayout {
id: rootElement
property string title: ""
property bool isOpen: false
default property alias accordionContent: contentPlaceholder.data
anchors.left: parent.left; anchors.right: parent.right
// Header element
Rectangle {
id: accordionHeader
color: "red"
anchors.left: parent.left; anchors.right: parent.right
height: 50
MouseArea {
anchors.fill: parent
Text {
text: rootElement.title
anchors.centerIn: parent
}
cursorShape: Qt.PointingHandCursor
onClicked: {
rootElement.isOpen = !rootElement.isOpen
}
}
}
// This will get filled with the content
ColumnLayout {
id: contentPlaceholder
visible: rootElement.isOpen
anchors.left: parent.left; anchors.right: parent.right
}
}
And this is how it is used from the parent element:
Accordion.qml
ColumnLayout {
Layout.margins: 5
visible: true
AccordionElement {
title: "Title1"
accordionContent: Rectangle {
anchors.left: parent.left; anchors.right: parent.right
height: 20
color: "green"
}
}
AccordionElement {
title: "Title2"
accordionContent: Rectangle {
anchors.left: parent.left; anchors.right: parent.right
height: 50
color: "green"
}
}
AccordionElement {
title: "Title3"
accordionContent: Rectangle {
anchors.left: parent.left; anchors.right: parent.right
height: 30
color: "green"
}
}
// Vertical spacer to keep the rectangles in upper part of column
Item {
Layout.fillHeight: true
}
}
This produces the following result (when all rectangles are expanded):
Ideally I would like the green rectangles to roll out of the red rectangles (like paper out of a printer). But I am stuck on how to do this. I have tried several approaches using the height property, and I got the green rectangle to disappear but the white space remains under the red rectangle.
Any help would be appreciated. Is there an approach I'm missing?
Here is a quick and simple example:
// AccItem.qml
Column {
default property alias item: ld.sourceComponent
Rectangle {
width: 200
height: 50
color: "red"
MouseArea {
anchors.fill: parent
onClicked: info.show = !info.show
}
}
Rectangle {
id: info
width: 200
height: show ? ld.height : 0
property bool show : false
color: "green"
clip: true
Loader {
id: ld
y: info.height - height
anchors.horizontalCenter: info.horizontalCenter
}
Behavior on height {
NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }
}
}
}
// Acc.qml
Column {
spacing: 5
AccItem {
Rectangle {
width: 50
height: 50
radius: 50
color: "blue"
anchors.centerIn: parent
}
}
AccItem {
Rectangle {
width: 100
height: 100
radius: 50
color: "yellow"
anchors.centerIn: parent
}
}
AccItem {
Rectangle {
width: 75
height: 75
radius: 50
color: "cyan"
anchors.centerIn: parent
}
}
}
You are needlessly over-complicating it with the anchors and the layouts. It doesn't seem the problem calls for any of those.
Update: I slightly refined the implementation, compared to the initial one the content would actually slide out of the header as paper out of printer rather than simply being unveiled, and also removed the source of a false positive binding loop warning.
I have a nested ScrollView, similar to the following QML:
import QtQuick 2.0
import QtQuick.Controls 1.1
Rectangle {
width: 200
height: 600
ScrollView {
id: sView
anchors.fill: parent
ListView {
id: list
boundsBehavior: Flickable.StopAtBounds
clip: true
focus: true
interactive: true
model: 5
delegate: Component {
MouseArea {
id: hoverArea
width: 100
height: 200
onClicked: list.currentIndex = index;
Rectangle {
id: fauxParent
anchors.fill: parent
border.width: 1
border.color: "black"
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
height: parent.height
width: parent.width / 2
border.width: 1
border.color: "purple"
color: "green"
Text {
anchors.centerIn: parent
text: "stuff"
}
}
ScrollView {
//parent: sView
anchors.top: fauxParent.top
anchors.right: fauxParent.right
height: fauxParent.height
width: fauxParent.width / 2
ListView {
model: 3
delegate: Component {
Rectangle {
radius: 10
height: 100
width: 100
color: "blue"
}
}
}
}
}
}
}
}
}
}
It seems to run correctly, except that the inner ScrollView won't respond to the mousewheel: the outer ScrollView intercepts that event. The only fix I've found in research for this, is to set the inner scrollview's parent directly to the outer scrollview (uncomment the parent: sView line). Unfortunately, this re-positions all five scrollview delegates onto the top right corner of the outer scrollview. It seems that ScrollView positions itself based on its parent?
For the record, my actual application is wrapping a large section of the page in a scrollview so as to allow the user to access sections of it that may be out of bounds for the current window size. The content of this section, though, has a variety of different controls for a variety of different purposes, including some scrollviews. So I'd also accept an alternate way of moving around a set of generic content that's too large for the window.
This is a Windows desktop app, so I don't need to consider mobile-specific issues.
You nested four elements that handle scroll Events.
Why do you put a ScrollView arround a ListView?
If you remove the ScrollViews the Mousewheel work fine.
Rectangle {
width: 200
height: 600
ListView {
anchors.fill: parent
id: list
boundsBehavior: Flickable.StopAtBounds
clip: true
focus: true
interactive: true
model: 5
delegate: Component {
MouseArea {
id: hoverArea
width: 100
height: 200
onClicked: list.currentIndex = index;
Rectangle {
id: fauxParent
anchors.fill: parent
border.width: 1
border.color: "black"
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
height: parent.height
width: parent.width / 2
border.width: 1
border.color: "purple"
color: "green"
Text {
anchors.centerIn: parent
text: "stuff"
}
}
ListView {
anchors.top: fauxParent.top
anchors.right: fauxParent.right
height: fauxParent.height
width: fauxParent.width / 2
model: 3
delegate: Component {
Rectangle {
radius: 10
height: 100
width: 100
color: "blue"
}
}
}
}
}
}
}
}
If you miss the Scrollbar look at this:
How to create scrollbar in QtQuick 2.0?