Multi item swipedelegate - qt

Edited due to insufficient intial posting.
Hi,
thanks for your help!
You're right, I guess it is better to include the whole file, in spite of the size:
import QtQuick 2.5
import QtQuick.LocalStorage 2.0
import QtQuick.Dialogs 1.2
import QtQuick.Controls 2.1
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.0
import QtQuick.Controls.Material 2.1
import "./database.js" as Database
ApplicationWindow {
visible: true
width: 640
height: 480
id: appWindow
x: Screen.width / 2 - width / 2
y: Screen.height / 2 - height / 2
title: qsTr("Project Stats")
Material.theme: Material.Dark
ListModel {
id: projectModel
ListElement {
projectID: "123654"
manager: "Schneider"
sponsor: "3466"
}
}
Component {
id: projectDelegate
SwipeDelegate {
id: projectSwipeDelegate
width: parent.width
height: projectDelegateItem.implicitHeight
anchors.horizontalCenter: parent.horizontalCenter
spacing: 10
contentItem: Item {
id: projectDelegateItem
Text {
id: projectID_text
text: "Project ID: " + projectID
font.pointSize: 20
anchors.horizontalCenter: parent.horizontalCenter
font.weight: Font.Black
color: "white"
}
Text {
id: manager_text
text: 'Manager: ' + manager + " Sponsor: " + sponsor
anchors.top: projectID_text.bottom
anchors.horizontalCenter: parent.horizontalCenter
font.weight: Font.Thin
color: "lightgrey"
}
}
onClicked: {
console.log(index, projectModel.get(index).projectID)
if (swipe.complete)
projectModel.remove(index)
else {
//var component= Qt.createComponent("timepointsstackview.qml")
//var loadwin = component.createObject(appWindow)
//loadwin.selected_project = projectModel.get(index).projectID
// stackView.push(Qt.resolvedUrl("timepointsstackview.qml"), {properties: {selected_project: projectModel.get(index).projectID}})
stackView.push(component, {properties: {selected_project: projectModel.get(index).projectID}})
}
}
swipe.right: Label {
id: deleteLabel
text: qsTr("Delete")
color: "white"
verticalAlignment: Label.AlignVCenter
padding: 12
height: parent.height
anchors.right: parent.right
SwipeDelegate.onClicked: projectListView.model.remove(index)
background: Rectangle {
color: deleteLabel.SwipeDelegate.pressed ? Qt.darker("tomato", 1.1) : "tomato"
}
}
}
}
Item {
Component.onCompleted: {
Database.getDatabase()
Database.getProjects()
}
}
StackView {
id: stackView
anchors.fill: parent
// Implements back key navigation
focus: true
Keys.onReleased: if (event.key === Qt.Key_Back && stackView.depth > 1) {
stackView.pop();
event.accepted = true;
}
initialItem: Item {
width: parent.width
height: parent.height
ListView {
id: projectListView
anchors.fill: parent
clip: true
model: projectModel
delegate: projectDelegate
}
}
}
onClosing: {
if (Qt.platform.os == "android") {
close.accepted = false;
// if (stack.depth > 1) stack.pop();
}
}
}
Meanwhile I already had removed the row/column stuff, which I put in to get it working somehow though I started without it.
I also experimented with implicitheight before intially posting, but sadly to no avail. The above is my current code, though putting in
height: projectDelegateItem.implicitHeight
in that spot (probabaly not the correct one or the wrong reference? Had to change it from your suggestion as I already took out the row) leads to rendering in one spot only.
Thanks for your time so far and also if you still have the patience to give me a clue where to turn the screws...

Ok, first of all:
Take warnings serious. If qml tells you, you should not try to use anchors within rows or columns, don't do it!
QML Row: Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row. Row will not function.
QML Column: Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column. Column will not function.
Also don't do it, if you can't see those warnings. It will mess up a lot.
A row automatically anchors all its children side by side to each other. A column does the same, just horizontraly. If you mess with it, everything breaks.
Frankly: I don't even understand why you use this strange Row/Column-Setup.
For your case it seems way better to just resort to anchoring. If you have reasons for that, why not take a grid?
Secondly: You need to specify a height for your delegate. Unfortunately it seems like, it does not calculate an implicit height.
The SwipeDelegate calculates its own implcitHeight based on the implicitHeight of its contentItem.
The problem is, that you don't assign the row (which has a proper implicitHeight) as the contentItem, but add it as a child instead.
Assigning it as contentItem would fix that for you.
Regarding your edit, and removal of the Row: The Item you use now does not calculate a implicitHeight based on its children. So you need to provide your calculation yourself.
This will set a proper height to your delegate, and your delegates won't overlap.
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
Window {
width: 1024
height: 800
visible: true
ListView {
width: 400
height: 800
model: ListModel {
ListElement { projectID: 0; manager: 'I'; sponsor: 'mom' }
ListElement { projectID: 1; manager: 'YOU'; sponsor: 'dad' }
ListElement { projectID: 1; manager: 'HE'; sponsor: 'auntie' }
}
delegate: SwipeDelegate {
id: projectSwipeDelegate
width: parent.width
// height: <--- provide a height, if the contentItem does not provide it.
contentItem: Row{ // <--- Add your content as contentItem.
id: rowProjectDelegate
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
Column {
id: column
// anchors.horizontalCenter: parent.horizontalCenter <--- Don't do that!
width: parent.width
Rectangle{ // If you don't want to have them in a column, they can't be siblings. If you want to, then you should.
height: 10
width: 250
color: "red"
Rectangle {
height: 10
width: 200
color: "blue"
}
}
Label {
id: projectID_text
text: "Project ID: " + projectID
font.pointSize: 20
font.weight: Font.Black
color: "white"
}
Label {
id: manager_text
text: 'Manager: ' + manager + " Sponsor: " + sponsor
// anchors.top: projectID_text.bottom <--- Don't do that!
font.weight: Font.Thin
color: "lightgrey"
}
}
}
}
}
}

Related

How to have a numerical model start at 1 in a combobox from QML

In the combobox from qml we can give them a model with a numerical value.
The options of the combobox then start from 0 to the model value minus 1.
What i want is to show the values on the combobox plus 1.
But still, if i try to access, with qml javascript, the value that is selected in that combobox is the original value, what i mean is, if it's the first option it's the value 0. The numbers shall only start from 1 in the display part of combobox.
I'm going to give an example (with a model=32):
display of the combobox
the options:
what i want is them to start at one, like this:
The code used was the following:
import QtQuick 2.9
import QtQuick.Controls 2.0
ApplicationWindow {
id: window
title: "Stack"
visible: true
height: 200
width: 400
Item {
id: page
anchors.fill: parent
width:parent.width
height: parent.height
Column{
width:parent.width
spacing:10
ComboBox {
id:comboBox
model: 32
objectName: "test"
implicitHeight: 30
displayText: currentText
// delegate:
// Button {
// id:buttonCombo
// width: parent.width
// text: index+1
// height:40
// contentItem: Text {
// text: buttonCombo.text
// font: comboBoxCustom.font
// leftPadding: 5
// horizontalAlignment: Text.AlignLeft
// verticalAlignment: Text.AlignVCenter
// elide: Text.ElideRight
// }
// background: Rectangle {
// color: buttonCombo.hovered ? (buttonCombo.pressed ? "#d8d8d8" : "#e8e8e8" ) : "#fff"
// }
// }
anchors.topMargin: 10
}
}
}
}
Maybe this could be done:
displayText: Number(currentText)+1
but i don't know if its the best solution...

QML reference errors

I have a small QML-project and I'm facing a problem with qml component references. So I'm trying to start the NumComponent.qml's numberTimer from startButton in the main.qml.
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
NumComponent{} //my component written in NumComponent.qml
Rectangle{
id: startButton
anchors.centerIn: parent
height: parent.height * 0.2
width: height
color: "lightblue"
MouseArea{
anchors.fill: parent
onClicked: {
numberTimer.start();
}
}
}
}
NumComponent.qml
import QtQuick 2.0
Rectangle {
id: numberRect
color: "red"
height: parent.height * 0.4
width: height
Text{
id: numberText
anchors.centerIn: parent
text: ""
}
Timer{
id: numberTimer
interval: 100
repeat: true
onTriggered: {
numberText.text = Math.floor(Math.random() * 8);
}
}
}
I get this error: "qrc:/main.qml:22: ReferenceError: numberRect is not defined"
Give your NumComponent in main.qml an id:
NumComponent{
id: numComponent
} //my component written in NumComponent.qml
change your onClicked handler to:
numComponent.startTimer();
Another variant:
Add to your numberRect a property alias:
property alias timed: numberTimer.running
Change you onClicked handler in main to:
numComponent.timed = !numComponent.timed;
Add to your NumComponent.qml in your root item:
function startTimer() {
numberTimer.start();
}
Now you can start and stop your timer.

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 apply Qt Graphical Effect on Delegate of Repeater in QML

I want to apply the QtGraphicalEffect ColorOverlay to an Image in a Repeater delegate. The problem is that I have to set the id of the Image as the source of the ColorOverlay, but I don't know the id, because it is dynamically created by the Repeater.
import QtQuick 2.4
import QtGraphicalEffects 1.0
Item {
id:mainItem
width: 800
height: 400
property string vorneColor: "red"
ListModel {
id: safeRailModel
ListElement {name: "vorne"; imageSource:"images/saferail/ring_vorne.png";}
ListElement {name: "vorneLinks"; imageSource:"images/saferail/ring_vorne_links.png"; }
}
Component {
id: imageDelegate
Image {
source: imageSource
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
fillMode: Image.PreserveAspectFit
opacity: 1
visible: true
}
}
Repeater {
id: safeRailRepeater
model: safeRailModel
delegate: imageDelegate
}
Component {
id: effectsDelegate
Item{
id:effectsItem
ColorOverlay {
anchors.fill: safeRailRepeater.itemAt(index)// <-- This doesn't work
source: safeRailRepeater.itemAt(index)// <-- This doesn't work
color: vorneColor
}
}
}
Repeater {
id: safeRailEffectsRepeater
model: safeRailModel
delegate: effectsDelegate
}
}
How can I set source and anchors.fill properties?
I searched everywhere, but I've only found something along the lines of safeRailRepeater.itemAt(index) or safeRailRepeater.itemAt(index).item but neither the former nor the latter works.
Side note: the ColorOverlay doesn't need to be in a seperate delegate and Repeater.
It would be great if somebody has a solution for this problem or could point me in the right direction.
Thank you very much!
The problem is that the itemAt() function call returns null because the other Repeater hasn't loaded its items yet. Also, the function call won't ever be reevaluated, because none of its arguments ever change, so you'll always get null.
The design is a bit odd though; I'd suggest moving the ColorOverlay into the same delegate, as you mentioned that it doesn't have to be in a separate Repeater:
import QtQuick 2.0
import QtQuick.Window 2.0
import QtGraphicalEffects 1.0
Window {
id: mainItem
width: 800
height: 400
visible: true
property string vorneColor: "red"
ListModel {
id: safeRailModel
ListElement { name: "vorne"; vorneColor: "salmon"; }
ListElement { name: "vorneLinks"; vorneColor: "steelblue"; }
}
Component {
id: imageDelegateComponent
Rectangle {
id: delegate
color: "grey"
opacity: 1
visible: true
width: 64
height: 64
layer.enabled: true
layer.effect: ColorOverlay {
color: vorneColor
}
}
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
Repeater {
id: safeRailRepeater
model: safeRailModel
delegate: imageDelegateComponent
}
}
}
Using the layer API of Item is a convenient way of specifying graphical effects.
I also changed the Image to a Rectangle, since we don't have access to those images, and put the Repeater within a row, so that you can see all of the delegates.

How to limit the size of drop-down of a ComboBox in QML

I am using a ComboBox in QML and when populated with a lot of data it exceeds my main windows bottom boarder. From googling I have learned that the drop-down list of a ComboBox is put on top of the current application window and therefore it does not respect its boundaries.
Ideally I would want the ComboBox to never exceed the main applications boundary, but I can not find any property in the documentation.
A different approach would be to limit the number of visible items of the drop-down list so that it do not exceed the window limits for a given window geometry. I was not able to find this in the documentation either and I have run out of ideas.
Take a look to the ComboBox source code, the popup is of a Menu type and it doesn't have any property to limit its size. Moreover, the z property of the Menu is infinite, i.e. it's always on top.
If you Find no way but to use the ComboBox of Qt you can create two models one for visual purpose, I will call it visual model, you will show it in your ComboBox and the complete one , it will be the reference model. Items count in your VisualModel wil be equal to some int property maximumComboBoxItemsCount that you declare . you'll need o find a way that onHovered find the index under the mouse in the visualmodel if it's === to maximumComboBoxIemsCount you do visualModel.remove(0) et visualModel.add(referenceModel.get(maximum.. + 1) and you'll need another property minimumComboBoxIemsCount, same logic but for Scroll Up , I dont know if it will work. but it's an idea
I think there is no solution using the built-in component and you should create your own comboBox. You can start from the following code.
ComboBox.qml
import QtQuick 2.0
Item {
id: comboBox
property string initialText
property int maxHeight
property int selectedItem:0
property variant listModel
signal expanded
signal closed
// signal sgnSelectedChoice(var choice)
width: 100
height: 40
ComboBoxButton {
id: comboBoxButton
width: comboBox.width
height: 40
borderColor: "#fff"
radius: 10
margin: 5
borderWidth: 2
text: initialText
textSize: 12
onClicked: {
if (listView.height == 0)
{
listView.height = Math.min(maxHeight, listModel.count*comboBoxButton.height)
comboBox.expanded()
source = "qrc:/Images/iconUp.png"
}
else
{
listView.height = 0
comboBox.closed()
source = "qrc:/Images/iconDown.png"
}
}
}
Component {
id: comboBoxDelegate
Rectangle {
id: delegateRectangle
width: comboBoxButton.width
height: comboBoxButton.height
color: "#00000000"
radius: comboBoxButton.radius
border.width: comboBoxButton.borderWidth
border.color: comboBoxButton.borderColor
Text {
color: index == listView.currentIndex ? "#ffff00" : "#ffffff"
anchors.centerIn: parent
anchors.margins: 3
font.pixelSize: 12
text: value
font.bold: true
}
MouseArea {
anchors.fill: parent
onClicked: {
listView.height = 0
listView.currentIndex = index
comboBox.selectedItem = index
tools.writePersistence(index,5)
comboBoxButton.text = value
comboBox.closed()
}
}
}
}
ListView {
id: listView
anchors.top: comboBoxButton.bottom
anchors.left: comboBoxButton.left
width: parent.width
height: 0
clip: true
model: listModel
delegate: comboBoxDelegate
currentIndex: selectedItem
}
onClosed: comboBoxButton.source = "qrc:/Images/iconDown.png"
Component.onCompleted: {
var cacheChoice = tools.getPersistence(5);
listView.currentIndex = tools.toInt(cacheChoice)
selectedItem = listView.currentIndex
comboBoxButton.text = cacheModel.get(selectedItem).value
}
}
ComboBoxButton.qml
import QtQuick 2.0
Item {
id: container
signal clicked
property string text
property alias source : iconDownUp.source
property string color: "#ffffff"
property int textSize: 12
property string borderColor: "#00000000"
property int borderWidth: 0
property int radius: 0
property int margin: 0
Rectangle {
id: buttonRectangle
anchors.fill: parent
color: "#00000000"
radius: container.radius
border.width: container.borderWidth
border.color: container.borderColor
Image {
id: image
anchors.fill: parent
source: "qrc:/Images/buttonBackground.png"
Image {
id: iconDownUp
source: "qrc:/Images/iconDown.png"
sourceSize.height:20
sourceSize.width: 20
anchors.verticalCenter: parent.verticalCenter
}
}
Text {
id:label
color: container.color
anchors.centerIn: parent
font.pixelSize: 10
text: container.text
font.bold: true
}
MouseArea {
id: mouseArea;
anchors.fill: parent
onClicked: {
container.clicked()
buttonRectangle.state = "pressed"
startTimer.start()
}
}
Timer{
id:startTimer
interval: 200
running: false;
repeat: false
onTriggered: buttonRectangle.state = ""
}
states: State {
name: "pressed"
when: mouseArea.pressed
PropertyChanges { target: image; scale: 0.7 }
PropertyChanges { target: label; scale: 0.7 }
}
transitions: Transition {
NumberAnimation { properties: "scale"; duration: 200; easing.type: Easing.InOutQuad }
}
}
}
I've used it in some software of mine, hence it is possible that It could not work "out of the box". I use it like this:
ComboBox{
id:cacheChoice
initialText: "None"
anchors.top: baseContainer.top
anchors.topMargin: 2
anchors.right: baseContainer.right
maxHeight: 500
listModel: cacheModel
onExpanded: {
cacheChoice.height = 500
}
onClosed: {
cacheChoice.height = 20
}
}
In case you are working with ComboBox from Qt Quick Controls 2, here's the source code for it:
https://github.com/qt/qtquickcontrols2/blob/5.12/src/imports/controls/ComboBox.qml
Based on that, this override of the behavior works to limit the height to something reasonable:
myComboBox.popup.contentItem.implicitHeight = Qt.binding(function () {
return Math.min(250, myComboBox.popup.contentItem.contentHeight);
});
It is possible to access the hidden MenuStyle within the ComboBoxStyle component. There you can use all the things and hidden things you have within a MenuStyle, including its maximum height.
The thing looks roughly like this.
Not pretty but it works well enough.
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.3
import QtQuick.Window 2.2
ComboBox {
id: comboBox
style: ComboBoxStyle {
// drop-down customization here
property Component __dropDownStyle: MenuStyle {
__maxPopupHeight: 400
__menuItemType: "comboboxitem" //not 100% sure if this is needed
}
}
As it came up resonantly in our team, here is a updated version of the idea shown above. The new version restricts the size automatically to the size of your application.
ComboBox {
id: root
style: ComboBoxStyle {
id: comboBoxStyle
// drop-down customization here
property Component __dropDownStyle: MenuStyle {
__maxPopupHeight: Math.max(55, //min value to keep it to a functional size even if it would not look nice
Math.min(400,
//limit the max size so the menu is inside the application bounds
comboBoxStyle.control.Window.height
- mapFromItem(comboBoxStyle.control, 0,0).y
- comboBoxStyle.control.height))
__menuItemType: "comboboxitem" //not 100% sure if this is needed
} //Component __dropDownStyle: MenuStyle
} //style: ComboBoxStyle
} //ComboBox

Resources