The task is to track item's absolute position.
Since item's coordinates are specified relative to its direct parent there's no way to know when item's absolute position have changed in case its parent moves.
Considering example below, I'd like to know when rect2 visually moves due to its parent, i.e. rect1, motion triggered by spacebar key stroke:
import QtQuick 2.2
Item {
id: root
width: 600
height: 200
focus: true
Rectangle {
id: rect1
x: 200
width: 400
height: 200
color: "salmon"
Rectangle {
id: rect2
x: 200
width: 200
height: 200
color: "seagreen"
onXChanged: console.log("rect2 x changed:", x)
}
}
Keys.onSpacePressed: rect1.x = 0
}
As explained on this page, you can add signals among different elements. For your case, you can add the signal xChanged of rect1 to that of rect2.
Component.onCompleted: {
rect1.xChanged.connect(rect2.xChanged);
}
Also, note that it will print 200 still because its position hasn't changed relative to it's parent.
For that you could do something like :
onXChanged: console.log("rect2 x changed:", rect1.x + rect2.x)
Related
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.
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.
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.
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
}
}
}
I want to try out the new calendar object of Qml in Qt5.3. Therefor I want to add some items (e.g. rectangles) into the cells at specific coordinates of the cell. To add a rectangle into one cell I use dayDelegate:
Calendar{
id: calendar
width: parent.width * 0.5
height: parent.height * 0.5
style: CalendarStyle {
dayDelegate:
Rectangle { //the background of the cell
id: cellRect
color: "#505"
Rectangle{ //a rectangle in the cell
height: parent.height * 0.5
width: parent.width
}
Now I have a model in my C++-code which stores several items belonging to specific dates. That means you can have for one date more than one item. I want to show these items as rectangles like a list in the cell of the given date. So what is the best way to do that in QML? One item stores its date and a group id to which this item belongs to. I pass the items as a QQmlPropertyList to QML. I already realized a function which is telling me, whether for the current date an item is available:
function apAvailable(d) {
for (var i in ApModel.items) {
var item = ApModel.items[i];
if (item.startDate.getTime() === d.getTime()) {
return true;
}
}
return false;
}
I can call this function in the dayDelegate passing the current processed day of the delegate.
Rectangle {
id: cellRect
color: "#505"
Rectangle{
visible: apAvailable(styleData.date)
height: parent.height * 0.5
width: parent.width
}
}
Now my question: I want to show now all items for this date. So I have to draw one rectangle for each item available. How can I process this in the for-iteration of the function? Can I create a QML-Component and add for each item a rectangle into this component and then return it to the cellRect? Do you know a better way to realize that?
Another further problem will be that an item belongs to a group of items but each with another date(e.g. 3 days, one after another = 3 items in one group). I want to combine these items visually. In fact, there shall be one big rectangle starting at the first date and ending at the last date. In case of three days, this rectangle shall be displayed over these three cells. My first thoughts were that I draw for each item an individual rect but pay attention to the same y-coordinate within the cells. Is there a good way to group these single rectangles to have for instances only one big component?
I hope you can give me some advices how to do all this in a nice way.
How can I process this in the for-iteration of the function? Can I
create a QML-Component and add for each item a rectangle into this
component and then return it to the cellRect? Do you know a better way
to realize that?
Did you try it? It really helps if you have an existing attempt at it that you can share with us, so that you can point out why you think it may not be satisfactory.
Is there a good way to group these single rectangles to have for
instances only one big component?
You can use ListView, amongst other things. The Calendar Example already provides a similar backend to what you're describing, so let's use that as a base:
ListView {
id: rectView
anchors.left: parent.left
anchors.right: parent.right
anchors.top: dayDelegateText.bottom
anchors.bottom: parent.bottom
anchors.margins: -1
clip: true
interactive: false
model: eventModel.eventsForDate(styleData.date)
delegate: Rectangle {
color: eventColours[index % eventColours.length]
width: parent.width
height: 10
}
}
This doesn't take care of each colour belonging to the same group ID, however, because it relies on the assumption that events that overlap several days will be at a certain index in the model. However, until you share your attempt at the problem, this should give you some idea. The changes I made to the example, as a diff:
http://pastebin.com/U14iKUeQ
Or you can just replace the Calendar in the example with this one:
Calendar {
id: calendar
width: parent.width * 0.6 - row.spacing / 2
height: parent.height
selectedDate: new Date(2014, 0, 1)
focus: true
style: CalendarStyle {
readonly property var eventColours: ["lightblue", "darkorange", "purple"]
dayDelegate: Item {
readonly property color sameMonthDateTextColor: "#444"
readonly property color selectedDateColor: Qt.platform.os === "osx" ? "#3778d0" : __syspal.highlight
readonly property color selectedDateTextColor: "white"
readonly property color differentMonthDateTextColor: "#bbb"
readonly property color invalidDatecolor: "#dddddd"
Rectangle {
id: selectionRect
anchors.fill: parent
border.color: "transparent"
color: styleData.date !== undefined && styleData.selected ? selectedDateColor : "transparent"
anchors.margins: styleData.selected ? -1 : 0
}
Label {
id: dayDelegateText
text: styleData.date.getDate()
font.pixelSize: 14
anchors.left: parent.left
anchors.leftMargin: 2
anchors.top: parent.top
anchors.topMargin: 2
color: {
var color = invalidDatecolor;
if (styleData.valid) {
// Date is within the valid range.
color = styleData.visibleMonth ? sameMonthDateTextColor : differentMonthDateTextColor;
if (styleData.selected) {
color = selectedDateTextColor;
}
}
color;
}
}
ListView {
id: rectView
anchors.left: parent.left
anchors.right: parent.right
anchors.top: dayDelegateText.bottom
anchors.bottom: parent.bottom
anchors.margins: -1
clip: true
interactive: false
model: eventModel.eventsForDate(styleData.date)
delegate: Rectangle {
color: eventColours[index % eventColours.length]
width: parent.width
height: 10
Label {
text: modelData.name
anchors.fill: parent
font.pixelSize: 8
fontSizeMode: Text.Fit
}
}
}
}
}
}