QML StackView, how to navigate within the pushed item? - qt

My app has a stackview that has two QML files.
And I need to navigate from one QML file to another from inside that QML file itself where I won't have access to stackview to push or pop.
How should I design this?
Main.qml
Rectangle
{
id: mainBkg
color: "white"
width: Screen.width
height: Screen.height
//Tickes every globalTimer.interval
property int globalTick: 0
Statusbar
{
width: Screen.width
height: Screen.height*0.2
id: statusBar
Rectangle
{
width: parent.width
height: parent.height*0.2
anchors.fill: parent
color: "green"
Text {
id: t
text: qsTr("QMLfile1")
}
}
}//end of statusbar
StackView {
id: stackView
anchors.top: statusBar.bottom
anchors.centerIn: parent
//What should be here? So that stackView
//will be available inside the loaded items.
initialItem:
}
}
Following are the two QML files:
QMLfile1.qml
Rectangle
{
width: parent.width
height: parent.height*0.2
anchors.fill: parent
color: "green"
Text {
id: t
text: qsTr("QMLfile1")
}
MouseArea:{
onClicked: //move to other QML file in stackview
}
}
I have another QML file like the above one.

You have tons of options.
One would be, to add a signal to the root-element of the QMLfile1.qml, and connect to it in the main.qml. The change will be performed there.
You can also add a property var callback to the root-element of the QMLfile1.qml where you inject the push-method of the StackView when instantiating it.
Finally you can just not shadow the id of the StackView and access it across file boundaries. I generally don't like that.

Related

Send a signal between two object instances in different QML files

I'm trying to send a signal from an object in one QML file to another object in a different QML file, but can't seem to find any good resources to use as a guide. Most of the examples I have come across show signals and slots being used to communicate between either two objects implemented in the same QML file (i.e. inside the same component), or in two different component files that come together inside a third QML file, which differs from my use case.
I need to send a string value from an object in a QML file (which represents a screen) to another object in a different QML file (representing yet another screen). The way the screens are linked currently is via StackView QML type in the main.qml file.
The closest I have seen the same problem described is here. The problem with the accepted answer in my case is the fact that the objects Rect1 and Rect2 are later defined in the same file. This means that they can be given an id and the signal and slot can be connected together, something I'm unable to do on my side.
Here's some code to demonstrate the problem.
main.qml:
ApplicationWindow {
id: app_container
width: 480
height: 600
visible: true
StackView {
id: screen_stack
anchors.fill: parent
initialItem: Screen1 {
}
}
}
Screen1:
Item {
id: screen_1
width: 480
height: 600
property var input
TextField {
id: user_input
width: parent.width
height: parent.height - 100
anchors.horizontalCenter: parent.horizontalCenter
placeholderText: qsTr("Enter your name")
onEditingFinsihed: {
input = user_input.text
}
}
Button {
width: parent.width
height: 100
anchors.top: user_input.bottom
anchors.horizontalCenter: parent.horizontalCenter
onClicked: {
console.log("Moving to Screen2")
screen_stack.push("qrc:/Screen2.qml")
}
}
}
Screen2:
Item {
id: screen_2
width: 480
height: 600
Rectangle {
anchors.fill: parent
color: "yellow"
Text {
id: txt_rect
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: qsTr("")
}
}
}
What I would like to be able to do is send the user input from TextField user_input in Screen1 to Text txt_rect in Screen2. How can I achieve this?
You can push properties:
screen_stack.push("qrc:/Screen2.qml", {"inputText": user_input.text})
Screen2:
Item {
id: screen_2
width: 480
height: 600
property var inputText
Rectangle {
anchors.fill: parent
color: "yellow"
Text {
id: txt_rect
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: screen_2.inputText
}
}
}

Draggable item get hidden behind other UI elements - Qt Qml

In my application window I have two ItemPanels which is orgnized inside a RawLayout.
Item {
RowLayout {
anchors.fill: parent
spacing: 1
ItemPanel {
Layout.preferredWidth: 250
Layout.fillHeight: true
panelHeader: "Components"
DraggableItem {
width: 100; height: 100;
}
}
ItemPanel {
Layout.preferredWidth: 250
Layout.fillHeight: true
panelHeader: "Actions"
}
}
}
The Components panel contains DraggableItems. Droppable area is the Actions panel. But if I tried to drag an DraggableItem to the Actions panel, it will be hidden while I'm dragging it.
DraggableItem
Item {
id: root
property string colorKey: "red"
width: 64; height: 64
MouseArea {
id: mouseArea
width: 64; height: 64
anchors.centerIn: parent
drag.target: tile
onReleased: parent = tile.Drag.target !== null ? tile.Drag.target : root
Rectangle {
id: tile
width: 64; height: 64
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
color: colorKey
Drag.keys: [ colorKey ]
Drag.active: mouseArea.drag.active
Drag.hotSpot.x: 32
Drag.hotSpot.y: 32
states: State {
when: mouseArea.drag.active
ParentChange { target: tile; parent: root }
AnchorChanges { target: tile; anchors.verticalCenter: undefined; anchors.horizontalCenter: undefined }
}
}
}
}
I was thinking adding a transparent layer and change the parent to it while dragging but I have no idea how to add a new layer on top of everything. Any other workarounds for this?
The easiest way would be to change the z-value of the ItemPanel with the panelHeader: "Components" to something greater than the siblings. Then automatically, all the children will be layered above them, too.
Reparenting is also a good solution. If you use ApplicationWindow as root object in your project, you don't even need to create add a new layer - it is already there.
You can use the attached property ApplicationWindow to reparent your draggable to ApplicationWindow.overlay. The only challange left is, that you need to specify the right position. You can use mapToItem() and mapFromItem() for this.
If you don't have ApplicationWindow as root, you can add a regualr Item as a child to your root and set the z-value to be the greates among its siblings. Then you assing it an id. If you don't shadow this id (having the same name for other identifiers, beeing resolved with higher priority) you can adress this Item from wherever. See more on this here.

TextEdit line background color

Is it possible to set up background color for particular line of TextEdit (for instance when line is clicked)?
I will have TextEdit with width:500px and with 10 lines. I will click on line number 5, which is empty line, but i still want to change the background color of whole line. Is it possible?
I need to know if it is possible to develop fully customized code editor with Qt and Qml.
Here's a solution which relies on text edit's cursor rectangle:
FocusScope {
id: root
property alias font: textEdit.font
property alias text: textEdit.text
Rectangle {
color: "lightyellow"
height: textEdit.cursorRectangle.height
width: root.width
visible: root.focus
y: textEdit.cursorRectangle.y
}
TextEdit {
id: textEdit
anchors.fill: parent
focus: true
}
}
Original Answer:
Here's my proof of concept solution. It is a custom TextEdit component which highlights current line. It lacks proper line height calculation, but it can be either hard coded if only one font size is used or obtained from QFontMetrics.
import QtQuick 2.3
Item {
id: root
property alias font: textEdit.font
property alias text: textEdit.text
Column {
anchors.fill: parent
Repeater {
model: root.height / root.lineHeight
Rectangle {
color: index === textEdit.currentLine ? "lightyellow" : "transparent"
height: root.lineHeight
width: root.width
}
}
}
TextEdit {
id: textEdit
property int currentLine: text.substring(0, cursorPosition).split(/\r\n|\r|\n/).length - 1
// FIXME: Use proper line height (e.g. from QFontMetrics)
property int lineHeight: font.pixelSize + 2
anchors.fill: parent
}
}
If you want to highlight even empty lines, then you need to handle mouse clicks and keypad events and manually change colours of corresponding rectangles.
You can use cursorRectangle property of the TextEdit and FontMetrics for this purpose:
Item {
id: root
height: te.implicitHeight
FontMetrics {
id: fontMetrics
font: te.font
}
Rectangle {
x: 0; y: te.cursorRectangle.y
height: fontMetrics.height
width: te.width
color: "#e7e7e7"
visible: te.activeFocus
}
TextEdit {
id: te
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
}
}
You can see the result for my commercial project:

QObject::findChild() returns None without obvious reason

I'm new to Qt Quck and Qt5/PyQt, and now I've faced a strange problem. I'm trying to find an object with objectName "test" in the below QML definition like this:
self.rootObject().findChild(QObject, "test")
But the call returns None. However, if I move the objectName: "test" property to the parent Tab element, then it's found successfully. It's only not found whem inside the child Item. Similarly, addChannel, modifyChannel and removeChannel objects are also not found by findChild().
import QtQuick 2.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.1
import "TouchStyles"
Item {
ListModel { }
TouchButtonFlatStyle { id: touchButtonFlat }
TouchTabViewStyle { id: touchTabView }
Rectangle {
width: 480
height: 230
TabView {
currentIndex: 0
tabPosition: 1
anchors.fill: parent
style: touchTabView
Tab {
title: "Play"
Item {
anchors.fill: parent
PianoKeyboard { anchors.centerIn: parent }
}
}
Tab {
title: "Channels"
Item {
objectName: "test"
ListView {
anchors.fill: parent
model: listModel
delegate: Channel {}
}
BorderImage {
border.bottom: 8
anchors.bottom: parent.bottom
source: "images/toolbar.png"
width: parent.width
height: 50
RowLayout {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Button { text: "Add"; objectName: "addChannel" }
Button { text: "Modify"; objectName: "modifyChannel" }
Button { text: "Remove"; objectName: "removeChannel" }
}
}
}
}
}
}
}
What am I doing wrong? The Qt documentation says that the search is performed recursively. Why doesn't it traverse the entire object tree?
The problem is related to the fact that tabs are "instantiated" only on demand. The first tab is always instantiated, so if you put the objectName there it will be found.
It will be found in the second tab only if you instantiate the second tab (select it). Similarly, using findChild on the TabView probably instantiates each tab (since it looking for them), so after that a findChild works even if second tab was not selected.
Conclusion: instantiate all tabs first (doing a findChild on the TabView is one way but may be a hack), then do the findChild for the item.

Add elements dynamically to SplitView in QML

I am working with QML and I want to add elements to SplitView dynamically eg. onMouseClick, but so far I didn't find the answer.
What I've found out so far is that the SplitView has it's default property set to it's first child's data property. So I guess I should try and add new dynamically created components with the parent set to that child (splitView1.children[0]). Unfortunately that doesn't work either. What is more the number of children of that first child is zero after the component has finished loading (seems like the SplitLayout's Component.onCompleted event calls a function that moves those children somewhere else). Thus the added children do not render (and do not respond to any of the Layout attached properties).
Please see the following code snippet:
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
ApplicationWindow {
width: 600
height: 400
SplitView {
anchors.fill: parent
Rectangle {
id: column
width: 200
Layout.minimumWidth: 100
Layout.maximumWidth: 300
color: "lightsteelblue"
}
SplitView {
id: splitView1
orientation: Qt.Vertical
Layout.fillWidth: true
Rectangle {
id: row1
height: 200
color: "lightblue"
Layout.minimumHeight: 1
}
// Rectangle { //I want to add Rectangle to splitView1 like this one, but dynamicly eg.onMouseClick
// color: "blue"
// }
}
}
MouseArea {
id: clickArea
anchors.fill: parent
onClicked: {
console.debug("clicked!")
console.debug("len: " + splitView1.__contents.length); // __contents is the SplitView's default property - an alias to the first child's data property
var newObject = Qt.createQmlObject('import QtQuick 2.1; Rectangle {color: "blue"}',
splitView1, "dynamicSnippet1"); //no effect
// var newObject = Qt.createQmlObject('import QtQuick 2.1; import QtQuick.Layouts 1.0; Rectangle {color: "blue"; width: 50; height: 50}',
// splitView1, "dynamicSnippet1"); //rectangle visible, but not in layout(?) - not resizeable
}
}
}
Is there any way I can make the dynamically created components render properly in the SplitView as the statically added ones?
It appears that the API does not provide support for dynamic insertion of new elements. Even if you do get it to work it would be a hack and might break with future releases. You may need to roll your own control to mimic the behavior you want. Ideally it should be backed by some sort of model.
As of QtQuick Controls 1.3, SplitView has an addItem(item) method.
you have to use the Loader for load dinamicaly objects. in onClicked handle you have to declare sourceComponent property to change the source of the Loader, something like this:
ApplicationWindow {
width: 600
height: 400
SplitView {
anchors.fill: parent
Rectangle {
id: column
width: 200
Layout.minimumWidth: 100
Layout.maximumWidth: 300
color: "lightsteelblue"
}
SplitView {
id: splitView1
orientation: Qt.Vertical
Layout.fillWidth: true
Rectangle {
id: row1
height: 200
color: "lightblue"
Layout.minimumHeight: 1
}
Loader {
id:rect
}
}
}
MouseArea {
id: clickArea
anchors.fill: parent
onClicked: {
console.debug("clicked!")
console.debug("len: " + splitView1.__contents.length) // __contents is the SplitView's default property - an alias to the first child's data property
rect.sourceComponent = algo
}
}
Component {
id:algo
Rectangle {
anchors.fill: parent
color: "blue"
}
}
}
I saw the source code of SplitView, it calculate each split region when Component.onCompleted signal. So I think that is a key point. No matter how you do (insert, dynamic create). The region won't be reset after you insert a new region for split.

Resources