QML crashes when changing Text.text - qml

I am new to QML and so I tried a couple of things. Most works, but the Programm always crashes, when I try to change the text-element.
E.g.:
import QtQuick 2.3
import QtQuick.Window 2.2
Window {
id: root
visible: true
width: 640
height: 480
Text {
id: mytex
// text: area.mouseX + " / " + area.mouseY
}
MouseArea {
id: area
anchors.fill: parent
// hoverEnabled: true
// onClicked: { mytex.text = mouseX + " / " + mouseY }
onMouseXChanged: { mytex.text = mouseX + " / " + mouseY }
}
}
All I want is a text, that shows the current mouse coordinates (whenever I click or better, whenever I hover the MouseArea )
I use QtQuick 2.3 with mingw on a Windows-Machine.
Edit:
I tried the following code:
import QtQuick 2.3
import QtQuick.Window 2.2
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property int count: 0
property string countstr: count.toString()
MouseArea {
anchors.fill: parent
onClicked: {
count += 1
console.log(countstr)
}
}
Text {
text: qsTr(countstr + ' times clicked')
anchors.centerIn: parent
}
}
with the QTcreator on both my Windows and my Linux machine.
On Linux it runs flawless.
Therefore I conclude, it should work (and can finally stop the frustrating search for a fault in my code) but I still don't know why it does not work on Windows.
In debugging mode it sais (for the latter code):
qrc:/main.qml:20:5: QML Text: Binding loop detected for property "text"
qrc:/main.qml:20:5: QML Text: Binding loop detected for property "text"
qrc:/main.qml:20:5: QML Text: Binding loop detected for property "text"
qrc:/main.qml:20:5: QML Text: Binding loop detected for property "text"
QQmlExpression: Expression qrc:/main.qml:21:15 depends on non-NOTIFYable properties:
...

Seems mouseX and mouseY don't get updated even if hover is enabled. A short test in qmlscene worked for the code you find on the bottom. Hope it helps.
import QtQuick 2.3
import QtQuick.Window 2.2
Item {
id: root
visible: true
width: 640
height: 480
Text {
id: mytex
}
MouseArea {
id: area
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
mytex.text = mouseX + " / " + mouseY
}
}
}

The solution was simple and easy: My graphic card was running in compatibility mode. Installing the appropriate propriatery driver solved this problem.
Therfore the OpenGL-Engine crashed. -.-'

Related

QML progress bar is not updating smoothly

I want to update my progress bar every 5 ms to get smooth looking decrasing progress bar. I created timer and progres bar. Problem is that my progres bar looks like it is "jumping" from 100-80-60-40-20, nothing smooth.
import QtQuick
import QtQuick.Controls
ApplicationWindow {
id: root
visible: true
minimumWidth: 840
minimumHeight: 600
property real prgVal1: 100
Timer {
interval: 5
running: true
repeat: true
onTriggered: root.updateProgress()
}
function updateProgress() {
if (root.prgVal1 > 0)
root.prgVal1 -= 0.1
else
root.prgVal1 = 100
}
ProgressBar {
visible: true
width: 120
height: 40
x: 20
y: 50
value: root.prgVal1
from: 0
to: 100
}
}
Can anyone help me please?
Gif can be seen here: https://ibb.co/Wk4w2bn
This isn't an issue caused by your hardware but rather by the operating system. Because you didn't specify a specific style in your application it will pick up the native style of your OS, this is why it works on Ubuntu and not on Windows. The native Windows style of the QQuickProgressBar for some reason only updates in multiple of 5%. I couldn't find the related location in the code to share here.
You can work around the issue by using a different style by default like QtQuick.Controls.Universal.
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Universal
ApplicationWindow {
id: root
visible: true
width: 320
height: 240
Timer {
interval: 5
running: true
repeat: true
onTriggered: {
if (progressBar.value > 0)
progressBar.value -= 0.1
else
progressBar.value = 100
}
}
ProgressBar {
id: progressBar
anchors.centerIn: parent
width: 120
height: 80
from: 0
to: 100
}
}
Just use already existing components, instead of reinventing the things. Think declarative not imperative.
All you need is assign the value, nothing more. The Timer here is for example only.
ProgressBar {
id: progressBar
width: parent.width * 0.8
anchors.centerIn: parent
from: 0
to: 100
value: 0
Behavior on value {
PropertyAnimation { duration: 300; easing.type: Easing.OutBack }
}
}
Timer {
interval: 1000;
running: true;
repeat: true
onTriggered: {
var val = Math.floor(Math.random() * (progressBar.to - progressBar.from + 1) + progressBar.from);
progressBar.value = val;
}
}
[Edit: Original answers deleted]
#WITC okay, I have installed pyside6 (it appears to be based on Qt6.3.0) on Ubuntu 20 and used the following Python to start my application.
# main.py
import sys
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('main.qml')
sys.exit(app.exec_())
Then I did small changes to your QML program so that I can capture all the image frames I'm seeing to keyframe PNGs.
// main.qml
import QtQuick
import QtQuick.Controls 6.3
ApplicationWindow {
id: root
visible: true
minimumWidth: 840
minimumHeight: 600
property real prgVal1:100
property int count: 0
Frame {
id: frame
background: Rectangle {
color: "white"
border.color: "black"
}
ProgressBar {
id: progressBar1
visible: true
implicitWidth: 120
implicitHeight: 40
value: prgVal1
from:0
to: 100
}
}
function step() {
frame.grabToImage(function (res) {
res.saveToFile(`/tmp/img/grab${count}.png`);
count++;
if (prgVal1 >= 1.0) {
prgVal1 -= 1.0;
Qt.callLater(step);
return;
}
} );
}
Component.onCompleted: step()
}
For combining the above image frames into an animated GIF, I used ffmpeg as follows:
# mkanim.sh
ffmpeg -y -framerate 20 -i '/tmp/img/grab%d.png' -vf fps=20,palettegen /tmp/img/pal.png
ffmpeg -y -framerate 20 -i '/tmp/img/grab%d.png' -i /tmp/img/pal.png -lavfi "fps=20 [x]; [x][1:v] paletteuse" anim.gif
You note that the animation I get is smooth.

Visual feedback (color transition) on TextField?

I have an app with numerous numbers in text fields (in a GridLayout) and would like to visually highlight fields which get changed (something like color changing from the original to red and then back, so something similar).
I am new to animations/transitions and so on, so I would like to ask about what is the correct approach to this.
I was looking at tutorials in Qt Creator but they attach transitions to elements in the QML code already, whereas I would like to get the element by its id and say, now run the highlight transition, without adding something to its code. Is that possible?
You have to implement your own control, derived from Text and add inside all the animation logic you want.
For example:
MyItem.qml
import QtQuick 2.12
Text {
id: txt
property bool hightlight: false
property color textColor: color
property color hightlightColor: "red"
onHightlightChanged: {
if(hightlight)
anim.running = true;
}
SequentialAnimation
{
id: anim
running: false
PropertyAnimation {
target: txt
property: "color"
to: hightlightColor
duration: 500
}
PropertyAnimation {
target: txt
property: "color"
to: textColor
duration: 500
}
ScriptAction {
script: txt.hightlight = false;
}
}
}
Usage:
import QtQuick 2.12
import QtQuick.Controls 2.3
ApplicationWindow {
id: window
title: "Test"
visible: true
height: 250
width: 200
MyItem {
id: item
text: "Hello"
anchors.centerIn: parent
hightlightColor: "red"
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
item.hightlight = true;
}
}
}
}

Change calendar style on button click

I need to change the Calendar style when clicking a Button. Currently, in the code below, the style change only works when the object is created for the first time but I need to do style change manually whenever the Button is clicked.
Below is the QML code:
import QtQuick 2.0
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0
import QtQuick.Controls.Styles 1.1
ApplicationWindow {
visible: true
width: 640
height: 400
minimumWidth: 400
minimumHeight: 300
color: "#f4f4f4"
id: root
Calendar {
id: cal_panel
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter;
frameVisible:false
style: CalendarStyle {
gridVisible: false
dayDelegate: Rectangle {
color: styleData.selected ? "#FF2E7BD2" : (styleData.visibleMonth && styleData.valid ? "#191919" : "#191919");
Text {
id:day_txt
text: styleData.date.getDate()
font.bold: true
anchors.centerIn: parent
color: {
var color = "#dddddd";
if (styleData.valid) {
color = styleData.visibleMonth ? "#bbb" : "#444";
var sel = root.getHiglightDates();
for(var i=0;i<sel.length;i++){
if(sel[i]===Qt.formatDateTime(styleData.date,"dd:MM:yyyy"))
color="red"
}
if (styleData.selected) {
color = "black";
}
}
color;
}
}
}
}
}
Button{
anchors.top:cal_panel.bottom
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
text:"Higlight"
onClicked: {
console.log("Higlight here....")
}
}
function getHighlightDates(){
var sel = ["10:11:2015","12:11:2015","11:11:2015","08:11:2015","09:11:2015"];
return sel;
}
}
Edit:
The return value of the function getHighlightDates() changes each time. In the snippet above I've just returned a predefined array for testing. In that case I am conduced how to edit style element which is already created.
Here is the screen shot:
As a simple solution, you can reassign the style on click event, forcing an under the hood refresh of the Calendar item.
To do that you can use
cal_panel.style=cal_panel.style
Be aware that this solution is not exactly performance friendly. :-)
Based on the comments in the question and in #folibis's answer, it looks the question might just revolve around how to get the calendar style to reflect the updated list of selected dates (from getHiglightDates()) after a user has updated the list by clicking a button.
What about just adding a new property selectedDates to store the selected dates (previously held in getHighlightDates()) like in the code below. By making use of property binding, the appearance of selected dates will automatically be updated whenever selectedDates changes. In the code below, the color of the "day_txt" Text is updated when selectedData is updated (which in turn is updated when selectedDates is updated).
import QtQuick 2.0
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.1
ApplicationWindow {
visible: true
width: 640
height: 400
minimumWidth: 400
minimumHeight: 300
color: "#f4f4f4"
id: root
property variant selectedDates : ["10:11:2015","12:11:2015","11:11:2015","08:11:2015","09:11:2015"]
Calendar {
id: cal_panel
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter;
frameVisible:false
style: CalendarStyle {
gridVisible: false
dayDelegate: Rectangle {
property bool selectedDate: selectedDates.indexOf(Qt.formatDateTime(styleData.date,"dd:MM:yyyy")) > -1
color: styleData.selected ? "#FF2E7BD2" : (styleData.visibleMonth && styleData.valid ? "#191919" : "#191919");
Text {
id:day_txt
text: styleData.date.getDate()
font.bold: true
anchors.centerIn: parent
color: selectedDate ? "red" : (styleData.selected ? "black" : (styleData.visibleMonth ? "#bbb" : "#444"));
}
}
}
}
Button{
anchors.top:cal_panel.bottom
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
text:"Higlight"
onClicked: {
var updatedDates = selectedDates
updatedDates.push(Qt.formatDateTime(cal_panel.selectedDate,"dd:MM:yyyy"))
selectedDates = updatedDates
# See http://stackoverflow.com/questions/19583234/qml-binding-to-an-array-element for why its done this way...
}
}
}
as #skypjack already suggested, you just can assign a new style on click. The style property is a Component so there is no problem to do something like this:
Component {
id: style1
CalendarStyle {
background: Rectangle { color: "lightyellow" }
}
}
Component {
id: style2
CalendarStyle {
background: Rectangle { color: "orange" }
}
}
Calendar {
id: calendar
anchors.fill: parent
style: style1
onClicked: {
calendar.style = style2;
}
}

QML Dialog is broken?

I have this code:
import QtQuick 2.3
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
Dialog {
standardButtons: StandardButton.Ok | StandardButton.Cancel
width: layout.implicitWidth
height: layout.implicitHeight
RowLayout {
id: layout
anchors.fill: parent
Item {
width: 10
height: 1
}
GridLayout {
columns: 2
rowSpacing: 10
Layout.fillHeight: true
Layout.fillWidth: true
Text {
text: "Hello world? "
}
Text {
text: "Hello world!"
}
Text {
text: "Goodbye world? "
}
Text {
text: "Goodbye world!"
}
}
Item {
width: 10
height: 1
}
}
}
When you run it it looks like this, and the dialog can be resized to any size. Also the RowLayout actually doesn't fill its parent as you can see.
How can I make it so that the dialog can't be resized below the minimum size of the layout, and so that the layout fills the dialog?
Unfortunately this is a bug in Qt. Currently the documentation is misleading and Dialog does not size itself correctly to the contents. Consider this working example, which I based on the DefaultFontDialog:
AbstractDialog {
title: "Hello"
id: root
// standardButtons: StandardButton.Ok | StandardButton.Cancel
modality: Qt.NonModal
Rectangle {
id: content
implicitWidth: mainLayout.implicitWidth + outerSpacing * 2
implicitHeight: mainLayout.implicitHeight + outerSpacing * 2
property real spacing: 6
property real outerSpacing: 12
color: "white"
GridLayout {
id: mainLayout
anchors { fill: parent; margins: content.outerSpacing }
rowSpacing: content.spacing
columnSpacing: content.spacing
columns: 5
Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" }
Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" }
Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" }
Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" } Text { text: "Hello" }
}
}
}
This works exactly as expected, though of course you don't get the buttons.
If you just change it to a Dialog and uncomment the standardButtons, then it stops working - you can resize the dialog to clip its contents (width-wise at least), and the contents do not expand to the dialog size.
The reason for the minimum width not working becomes clear when we look at the source code for Dialog (in qtquickcontrols/src/dialogs/DefaultDialogWrapper.qml):
AbstractDialog {
id: root
default property alias data: defaultContentItem.data
onVisibilityChanged: if (visible && contentItem) contentItem.forceActiveFocus()
Rectangle {
id: content
property real spacing: 6
property real outerSpacing: 12
property real buttonsRowImplicitWidth: minimumWidth
property bool buttonsInSingleRow: defaultContentItem.width >= buttonsRowImplicitWidth
property real minimumHeight: implicitHeight
property real minimumWidth: Screen.pixelDensity * 50
implicitHeight: defaultContentItem.implicitHeight + spacing + outerSpacing * 2 + buttonsRight.implicitHeight
implicitWidth: Math.min(root.__maximumDimension, Math.max(
defaultContentItem.implicitWidth, buttonsRowImplicitWidth, Screen.pixelDensity * 50) + outerSpacing * 2);
minimumWidth is hardcoded to Screen.pixelDensity * 50!! There was never any hope that it would match the dialog contents. minimumHeight does work better (though not perfect, I believe because the spacing isn't considered).
I'm not sure why the defaultContentItem does not expand correctly, but anyway. It looks like the only solution at the moment is to use AbstractDialog and implement the buttons and accepted()/rejected()/etc. signals yourself. Bit of a pain.
Edit / Solution
I did some further investigation.
The reason the defaultContentItem doesn't expand is because it's bottom anchor isn't tied to the top of the button row:
Item {
id: defaultContentItem
anchors {
left: parent.left
right: parent.right
top: parent.top
margins: content.outerSpacing
}
implicitHeight: childrenRect.height
}
Minimum sizes just don't work that well with anchor-based layouts. They do with GridLayout-based layouts.
Unfortunately childrenRect has no implicitWidth/Height so we have to actually have the child items go into a ColumnLayout rather than be the ColumnLayout.
...
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
// A Dialog that resizes properly. The defualt dialog doesn't work very well for this purpose.
AbstractDialog {
id: root
default property alias data: defaultContentItem.data
onVisibilityChanged: if (visible && contentItem) contentItem.forceActiveFocus()
Rectangle {
id: content
property real spacing: 6
property real outerSpacing: 12
property real buttonsRowImplicitWidth: minimumWidth
property bool buttonsInSingleRow: defaultContentItem.width >= buttonsRowImplicitWidth
property real minimumHeight: implicitHeight
property real minimumWidth: implicitWidth // Don't hard-code this.
implicitWidth: Math.min(root.__maximumDimension, Math.max(Screen.pixelDensity * 10, mainLayout.implicitWidth + outerSpacing * 2))
implicitHeight: Math.min(root.__maximumDimension, Math.max(Screen.pixelDensity * 10, mainLayout.implicitHeight + outerSpacing * 2))
color: palette.window
Keys.onPressed: {
event.accepted = true
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
reject()
break
case Qt.Key_Enter:
case Qt.Key_Return:
accept()
break
default:
event.accepted = false
}
}
SystemPalette { id: palette }
// We use layouts rather than anchors because there are no minimum widths/heights
// with the anchor system.
ColumnLayout {
id: mainLayout
anchors { fill: parent; margins: content.outerSpacing }
spacing: content.spacing
// We have to embed another item so that children don't go after the buttons.
ColumnLayout {
id: defaultContentItem
Layout.fillWidth: true
Layout.fillHeight: true
}
Flow {
Layout.fillWidth: true
id: buttonsLeft
spacing: content.spacing
Repeater {
id: buttonsLeftRepeater
Button {
text: (buttonsLeftRepeater.model && buttonsLeftRepeater.model[index] ? buttonsLeftRepeater.model[index].text : index)
onClicked: root.click(buttonsLeftRepeater.model[index].standardButton)
}
}
Button {
id: moreButton
text: qsTr("Show Details...")
visible: false
}
}
Flow {
Layout.fillWidth: true
id: buttonsRight
spacing: content.spacing
layoutDirection: Qt.RightToLeft
Repeater {
id: buttonsRightRepeater
// TODO maybe: insert gaps if the button requires it (destructive buttons only)
Button {
text: (buttonsRightRepeater.model && buttonsRightRepeater.model[index] ? buttonsRightRepeater.model[index].text : index)
onClicked: root.click(buttonsRightRepeater.model[index].standardButton)
}
}
}
}
}
function setupButtons() {
buttonsLeftRepeater.model = root.__standardButtonsLeftModel()
buttonsRightRepeater.model = root.__standardButtonsRightModel()
if (!buttonsRightRepeater.model || buttonsRightRepeater.model.length < 2)
return;
var calcWidth = 0;
function calculateForButton(i, b) {
var buttonWidth = b.implicitWidth;
if (buttonWidth > 0) {
if (i > 0)
buttonWidth += content.spacing
calcWidth += buttonWidth
}
}
for (var i = 0; i < buttonsRight.visibleChildren.length; ++i)
calculateForButton(i, buttonsRight.visibleChildren[i])
content.minimumWidth = calcWidth + content.outerSpacing * 2
for (i = 0; i < buttonsLeft.visibleChildren.length; ++i)
calculateForButton(i, buttonsLeft.visibleChildren[i])
content.buttonsRowImplicitWidth = calcWidth + content.spacing
}
onStandardButtonsChanged: setupButtons()
Component.onCompleted: setupButtons()
}
You have to use it a bit differently to a normal Dialog. Just imagine it is a ColumnLayout (this is a slightly different example to the original question):
ColumnLayoutDialog {
id: dialog1
standardButtons: StandardButton.Ok | StandardButton.Cancel
Text {
text: "Hello world? "
}
Text {
text: "Hello world!"
}
// Spacer.
Item {
Layout.fillHeight: true;
}
Text {
text: "Goodbye world? "
}
Text {
text: "Goodbye world!"
}
}
By the way you could change the ColumnLayout to a GridLayout and expose the columns property if you want. That might make more sense.
A small issue
It turns out a QWindow's minimum width and height only ensure that the dialog isn't actively resized to be less than its content. It doesn't ensure that the dialog is never smaller than its content, because the content can grow after the dialog is created (e.g. extra items added). To workaround this I added this function to my ColumnLayoutDialog:
// The minimumWidth/Height values of content are accessed by the C++ class, but they
// only ensure that the window isn't resized to be smaller than its content. They
// don't ensure that if the content grows the window grows with it.
function ensureMinimumSize()
{
if (root.width < content.minimumWidth)
root.width = content.minimumWidth;
if (root.height < content.minimumHeight)
root.height = content.minimumHeight;
}
It has to be called manually when you change the dialog contents. Or to do it automatically you can add this to the content rectangle:
onMinimumHeightChanged: {
if (root.height < content.minimumHeight)
root.height = content.minimumHeight;
}
onMinimumWidthChanged: {
if (root.width < content.minimumWidth)
root.width = content.minimumWidth;
}
This is a bug in QT up to version 5.6.0. Most likely the bug number 49058. The code from the question works as expected in QT 5.6.1 and 5.7.0.
A partial workaround for the old versions is to remove the lines
width: layout.implicitWidth
height: layout.implicitHeight
and replace
anchors.fill: parent
with
anchors.right: parent.right
anchors.left: parent.left
The dialog then respects the minimum height and the contents expand horizontally.
Here is also a complete workaround, but it relies on undocumented implementation details of Dialog, so it should be used with caution. It works in 5.5.1, 5.6.0, 5.6.1 and 5.7.0. Note also that the second Item is changed to a red Rectangle to make the behavior more apparent.
import QtQuick 2.3
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
Dialog {
visible: true
standardButtons: StandardButton.Ok | StandardButton.Cancel
RowLayout {
id: layout
// In the horizontal direction, expansion and shrinking can be achieved with anchors.
anchors.left: parent.left
anchors.right: parent.right
// Used only for guessing the height of the Dialog's standard buttons.
Button {
id: hiddenButton
visible: false
}
// Repeats until the relevant parts of the dialog (parent of the parent of the RowLayout)
// are complete, then overwrites the minimum width and implicit height and stops repeating.
Timer {
id: timer
interval: 50; running: true; repeat: true;
onTriggered: {
if(layout.parent.parent) {
var lp = layout.parent
var lpp = layout.parent.parent
lpp.minimumWidth = layout.implicitWidth + 2 * lpp.outerSpacing
layout.buttonHeight = 2 * lpp.outerSpacing + hiddenButton.implicitHeight + lpp.spacing
lp.implicitHeight = layout.implicitHeight + 2 * lpp.outerSpacing
running = false
}
}
}
// The guessed space needed for the Dialog's buttons.
property int buttonHeight: 80
// Expand and shrink vertically when the dialog is resized.
height: parent.parent ? Math.max(parent.parent.height-buttonHeight, implicitHeight) : implicitHeight
Item {
width: 10
height: 1
}
GridLayout {
columns: 2
rowSpacing: 10
Layout.fillHeight: true
Layout.fillWidth: true
Text {
text: "Hello world? "
}
Text {
text: "Hello world!"
}
Text {
text: "Goodbye world? "
}
Text {
text: "Goodbye world!"
}
}
Rectangle {
Layout.fillHeight: true
color: 'red'
width: 10
}
}
}

How to get position (X,Y) of any visual node after page rendered?

I need to create a draggable button. The drag starts from multiple source and ends at a sink. Is there any way to get position of the sources and the sink after creation of the page.
Container {
horizontalAlignment: HorizontalAlignment.Fill
Label {
id: preId
preferredWidth: 700
text: rootContainer.data.part1
multiline: true
textStyle {
textAlign: TextAlign.Center
color: Color.Black
}
horizontalAlignment: HorizontalAlignment.Center
}
}
Container {
horizontalAlignment: HorizontalAlignment.Center
SinkButton {
id: sinkId
preferredHeight: 100
preferredWidth: 686
textColor: Color.Black
enabled: false
layoutProperties: AbsoluteLayoutProperties {
}
}
}
Container {
horizontalAlignment: HorizontalAlignment.Fill
topPadding: -5
bottomPadding: squareDisplay ? 10 : 50
Label {
id: postId
text: rootContainer.data.postData
multiline: true
textStyle {
textAlign: TextAlign.Center
color: Color.Black
}
horizontalAlignment: HorizontalAlignment.Center
}
}
}
I want to know the position of "sinkId" relative to the window. Its position varies depending on "preId" text length. LayoutUpdateHandler is a possible solution but layoutFrame.x or layoutFrame.y always comes zero on checking. Is there any way to get position of any visual node on/after page creation?
First, LayoutUpdateHandler reports layoutFrame properties quite well, not sure why it's not working in your case. Here is the working code demonstrating that if you want to utilise this class for your needs:
import bb.cascades 1.0
Page {
Container {
layout: DockLayout {}
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
Button {
id: button
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: "click me"
onClicked: {
console.log("Button layout - x:" + layoutHandler.layoutFrame.x + ", y:" + layoutHandler.layoutFrame.y +
", width:"+ layoutHandler.layoutFrame.width + ", height: " + layoutHandler.layoutFrame.height);
}
attachedObjects: [
LayoutUpdateHandler {
id: layoutHandler
}
]
}
}
}
Also, if you want to respond to touch events of your widget which I guess draggable functionality means, the best bet here would be to use a series of touch-input reactions provided by touchBehaviors which could added to any VisualNode as via QML as via C++, doesn't matter. So in order to do this, you need to create a TouchBehavior object with a defined TouchReaction with respective properties.
So, for example, for tracking touch events of some VisualNode upon receiving a touch down event until a touch up one occurs you need to define a respective touchBehaviour object and connect to the onTouch() slot of that VisualNode where you get all the events captured by your touchBehaviour event filter. Here is the code demonstrating this approach:
import bb.cascades 1.0
Page {
Container {
layout: DockLayout {}
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
Button {
id: button
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: "click me"
touchBehaviors: [
TouchBehavior {
TouchReaction {
eventType: TouchType.Down
phase: PropagationPhase.AtTarget
response: TouchResponse.StartTracking
}
}
]
onTouch: {
console.log("===== got touch event of type " + event.touchType + " =====");
console.log("\twindowX - " + event.windowX);
console.log("\twindowX - " + event.windowX);
console.log("\twindowY - " + event.windowY);
console.log("\tlocalX - " + event.localX);
console.log("\tlocalY - " + event.localY);
console.log("\n");
}
}
}
}
Then you can add you logic in onTouch() slot handler.
Additional information on the subject:
TouchBehavior official documentation
TouchEvent official documentation
VisualNode touch event function official documentation
LayoutUpdateHandler will tell you the position of your element in relation to the parent, which in the case of SinkId is a Container wrapping it. LayoutUpdateHandler will always return you 0 in this case.
Either remove the container and set the horizontalAlignment: HorizontalAlignment.Center property on sinkId directly and use a LayoutUpdateHandler, or use a LayoutUpdateHandler on the container surrounding sinkId.