How are implicit dimensions of QtQuick items propagated? - qml

I am trying to implement a component which should by default (if no width was explicitly set) take up as much space as it needs (i.e. depending on its implicitWidth). And if width was set at definition it should shrink its contents to fit in the provided area.
Here's an example:
import QtQuick 2.2
import QtQuick.Window 2.0
Window {
width: 200
height: 100
visible: true
property bool restricted: false
Component {
id: external
FocusScope {
implicitWidth: column.implicitWidth
implicitHeight: column.implicitHeight
focus: true
Column {
id: column
width: parent.width > 0 ? parent.width : undefined
Text {
id: label
width: parent.width > 0 ? parent.width : undefined
elide: Text.ElideRight
font.pixelSize: 24
text: "1234567890"
}
}
Keys.onRightPressed: label.text += label.text
}
}
Loader {
width: restricted ? 100 : undefined
sourceComponent: external
focus: true
Keys.onReturnPressed: restricted = !restricted
}
}
In this sample two modes are controlled by auxiliary bool property, and I want it to support two forms of declaration:
Explicit width. Text should elide.
Loader {
width: 100
sourceComponent: external
focus: true
}
Loader width should be enough to fit the whole text without eliding.
Loader {
sourceComponent: external
focus: true
}
Motivation is that such a component will be defined in a separate file and is being designed to be placed in different parts of UI with both behaviors desired depending on current needs. This sample with inline component declaration is only for demonstration purpose.
UPDATE:
The following trick parent.width > 0 ? parent.width : undefined works, but only for initial setup. If component contents change and implicitWidth is updated (in an unrestricted mode) the width of the component does not change (i.e. Text remains elided).
For example, press right key just right after launching example. You should see that Text has become elided, but its width did not increased
twice regardless the fact that string was duplicated.

Related

How to use MouseArea in ScrollView and Flow

I do want to use an MouseArea to show a clickable Button within a Flow with in a ScrollView. Reason for this structure is that the Objects are created dynamically from the user, so I want to place them in a Flow to ensure vertical placement and in a ScrollView to ensure scrolling if the become to many objects.
ScrollView {
clip: true
id: shiftsScrollView
wheelEnabled: true
//ScrollBar.vertical.policy: ScrollBar.AlwaysOn
height: 100
width: 300
anchors.left: shiftsTitle.left
anchors.top: shiftsTitle.bottom
//spacing: 0
anchors.leftMargin: 0
anchors.topMargin: 20
Flow {
id: scrollableFlow
layoutDirection: Qt.RightToLeft
flow: Flow.TopToBottom
// Objects will be filled in here at runtime
}
}
This is how the Objects will be created within a js function:
for (let i=0; i<amountOfShifts; i++) {
var myShift = Qt.createQmlObject(
'ShiftListElement {
width: 300;
height: 25;
shiftText: "'+splittedShifts[i]+'";
shiftName: "'+shiftNames[i]+'";
}',
scrollableFlow,
"shiftElement");
shiftListObjects.push(myShift);
shiftsScrollView.contentHeight = amountOfShifts * 25
}
Note that the ID 'scrollableFlow' is the one in the ScrollView and you can see visually that it works. The Objects get placed vertically in order without knowing each other before.
QML Object which contains the non-working MouseArea:
Item {
id: item1
property string shiftText: "shift";
property string shiftName: "name";
width: 200
height: 100
Text {
id: text1
text: item1.shiftText
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 12
anchors.horizontalCenterOffset: -30
anchors.horizontalCenter: parent.horizontalCenter
}
Button {
id: button
width: 20
height: 20
text: qsTr("X")
anchors.verticalCenter: text1.verticalCenter
anchors.left: text1.right
anchors.leftMargin: 20
MouseArea {
id: buttonCommitShiftMouseArea
anchors.fill: parent
preventStealing: true
onClicked:
{
console.log("I work!");
}
}
}
}
I found some answers recommending the property "preventStealing", but it doesn't seem to have an effect in this case. I also tried removing Flow, which doesn't seem to make a difference either. It did work before outside of a ScrollView in a regular Item, which makes me think that the ScrollView steals all MouseEvents within. The ScrollView is scrollable as wished and appears in wanted size.
Did I miss something? Thank you for your help.

QML property - read-only, assignment, binding

If I had a rectangle with a property width, there are three options to set a value that I confuse:
read-only property int widthReadOnly: 200
Rectangle{
width: 200 //first
width: widthReadOnly //second
Component.onCompleted: {width = 200} //third
}
Could you tell me when to use each of them?
Thank you.
at first all of your examples do quite the same.
First and second examle create a binding to the values, but beacause they are an int (first), or a read-only property (second) they will never change. Because there will never be a ...Changed() Signal, they are also like your third example whitch is only an assignment (if the assigned value changes, the change will not change the assignee).
The intendet use of the Bindings is when you bind to some value that is Changeable, maybe the width of the parent item. So if the parent width changes it will be propagated to the child item:
import QtQuick 2.0
import QtQuick.Controls 1.4
Item {
Rectangle {
id: papa
width: 100
height: 100
color: "red"
Rectangle {
id: child
anchors.centerIn: parent
width: parent.width / 2
height: parent.height / 2
color: "lightsteelblue"
}
}
Button {
id: button
onClicked: {
papa.width = 100 + (Math.random() * 100)
}
}
}
As you see, if the papa width is updated, it also changes the width of the child item.

How Can I Scroll the Images In QML Automatically?

I have a ListView Containing only Images. I assigned the orientation of ListView as horizontal Direction. How can I change, i.e. scroll, the images automatically with some time gap?
Use a Timer. When it is triggered, update the currentIndex of the ListView. This will scroll automatically with default animations. Finally, according to the documentation, positionViewAtIndex is
The correct way to bring an item into view is with positionViewAtIndex
Indeed the method provides a more fine-grained control over the appearance of Items via the PositionMode parameter. See the documentation for further details.
Minimal example:
import QtQuick 2.5
import QtQuick.Window 2.0
Window {
visible: true
width: 200
height: 15
ListView {
id: list
anchors.fill: parent
orientation: ListView.Horizontal
model: 10
delegate: Text {
width: 40
id: name
text: index
}
}
Timer {
interval: 500
repeat: true
running: true
onTriggered: {
//list.currentIndex += 1 // this...
//list.incrementCurrentIndex() // ...or this!
//list.positionViewAtIndex(list.currentIndex, ListView.Center)
}
}
}

Why does adding a MouseArea with anchors.fill: parent cause the rows to stack up on one another?

Given that this ListView works fine:
ListView {
id: myListView
model: myListModel
anchors.fill: parent
delegate: Row {
id: row
spacing: 5
Text {
text: id
width: 25
horizontalAlignment: Text.AlignHCenter
}
Text {
text: description
}
}
}
Why does adding a MouseArea with anchors.fill: parent cause the rows to stack up on one another? How do I get back the automatic vertical spacing that I had before adding MouseArea? I have already tried putting the Row in a Rectangle and also in a Component.
ListView {
id: myListView
model: myListModel
anchors.fill: parent
delegate: Row {
MouseArea {
anchors.fill: parent
onClicked: myListView.currentIndex = index
}
id: row
spacing: 5
Text {
text: id
width: 25
horizontalAlignment: Text.AlignHCenter
}
Text {
text: description
}
}
}
The items stack up for a simple reason: their height is not set. A delegate must always have an height set. Since you did not specify one, delegate height is zero and the enclosing text is rendered over the same y (zero), stacking up.
However, that's not the only problem here. You defined the MouseArea to be anchored. Rows, as well as Columns, force a specific arrangement for items inside themselves. Adding anchors can interfer with this automatic mechanism.
Also docs are clear about this. You can read that...
Row is a type that positions its child items along a single row. It can be used as a convenient way to horizontally position a series of items without using anchors.
...and also that...
[...]since a Row automatically positions its children horizontally, a
child item within a Row should not set its x position or horizontally
anchor itself using the left, right, anchors.horizontalCenter, fill or
centerIn anchors.
Probably, the anchoring error generates an inconsistent state such that Row does not inherit height from the enclosing text, as it did without anchoring items. This in turn results in the zero height and the stacking.
In this particular case, you can include the Row inside an Item and apply the filling MouseArea to the latter. The resulting code, with also the delegate height and width correctly set, would look similar to the following (mind you, I've removed roles and model in your code since the latter was not available in the provided code snippet):
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
ApplicationWindow {
visible: true
width: 200
height: 300
ListView {
id: myListView
model: 20
anchors.fill: parent
delegate: Item {
width: myListView.width
height: text1.height // set the height!
Row {
id: row
anchors.fill: parent
spacing: 5
Text {
id: text1
text: "id"
width: 25
horizontalAlignment: Text.AlignHCenter
}
Text {
text: "description"
}
}
MouseArea { // fills the delegate Item, not the Row!
anchors.fill: parent
onClicked: {
myListView.currentIndex = index
console.info("Area clicked! Index: " + index)
}
}
}
}
}

Implement component supporting implicit and explicit width

I'd like to have a row of two one-line text items. The first one should be always fully visible. The second one should add to main item's implicitWidth by the value of own width if the root item has not its width property set explicitly. In other case, the second text item should be elided so that it fits into remaining space.
Here's an example of what I am trying to do. The variant 1 is the first thing that came to my mind, but it does not work. The second variant shows how I expect it to be. Alas, I don't like since it requires to sum up all the child items of the Row. Fortunately, I have only two of them. What if I want to add more?
import QtQuick 2.2
Item {
// variant 1
// implicitWidth: row.implicitWidth
// variant 2
implicitWidth: Math.ceil(name.implicitWidth + value.implicitWidth)
implicitHeight: row.implicitHeight
Row {
id: row
anchors {
left: parent.left
right: parent.right
}
Text {
id: name
text: "Label"
}
Text {
id: value
width: parent.width - x
text: "Value"
elide: Text.ElideRight
horizontalAlignment: Text.AlignRight
}
}
}