Draggable item get hidden behind other UI elements - Qt Qml - qt

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.

Related

Why drag and drop not working

This is the basic example I found on qt doc. In tile Rectangle, i add anchors.fill: parent and commented all 3 lines below that because I feel like all those 3 line can be covered by anchors.fill: parent. But once I do that I cannot drag the element anymore.
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
Item {
id: root
width: 64; height: 64
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.centerIn: parent
drag.target: tile
onReleased: parent = tile.Drag.target !== null ? tile.Drag.target : root
Rectangle {
id: tile
anchors.fill: parent
// width: 64; height: 64
// anchors.verticalCenter: parent.verticalCenter
// anchors.horizontalCenter: parent.horizontalCenter
color: "red"
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 }
}
}
}
}
}
Why filling the rectangle of the parent disables the drag and drop functionality?
You anchor a boat so that it is fixed at something. If you want to move it, you will need to break the anchor or pull the thing you anchored it to, with you.
That is what an anchor is for - to fix the anchored object to some other object. If you want to have it movable, you can't anchor it.
You still have the dragged object the same size as your parent, by setting:
width: parent.width
height: parent.height
But though it is the same size, once you move it, it will obviously not fill the parent anymore.
Now you might say:
"But hey! They do anchor the object, too!"
Yes, they do, but there are also the lines:
states: State {
when: mouseArea.drag.active
ParentChange { target: tile; parent: root }
AnchorChanges { target: tile; anchors.verticalCenter: undefined; anchors.horizontalCenter: undefined }
}
which will lift the anchor when the tile is being dragged.

QML StackView, how to navigate within the pushed item?

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.

Combine 2 MouseAreas on GridView

I have code like this:
GridView {
// ... declarations ...
model: theModel
delegate: MouseArea {
id: cellMouseArea
onClicked: // open the cell
}
MouseArea {
id: gridViewMouseArea
// here process horizontal mouse press/release actions
}
}
with a MouseArea defined in each delegate and an overall MouseArea covering my GridView. In the cellMouseArea I want to perform an open item action whereas in the gridViewMouseArea I want to implement mouseX handle to open/close a sidebar. However, the two MouseAreas do not work together. How can I carry it out?
You can exploit propagateComposedEvents:
If propagateComposedEvents is set to true, then composed events will
be automatically propagated to other MouseAreas in the same location
in the scene. Each event is propagated to the next enabled MouseArea
beneath it in the stacking order, propagating down this visual
hierarchy until a MouseArea accepts the event. Unlike pressed events,
composed events will not be automatically accepted if no handler is
present.
You can set the property to true on the GridView MouseArea. In this way click events are propagated to the MouseAreas in the delegates whereas the outer MouseArea can implement other behaviours such as drag or hoven.
Here is an example in which outer MouseArea defines drag property to slide in/out a Rectangle ( simulating your sidebar) and thanks to the propagateComposedEvents clicks are managed by the single delegates.
import QtQuick 2.4
import QtQuick.Window 2.2
ApplicationWindow {
width: 300; height: 400
color: "white"
Component {
id: appDelegate
Item {
width: 100; height: 100
Text {
anchors.centerIn: parent
text: index
}
MouseArea {
anchors.fill: parent
onClicked: {
parent.GridView.view.currentIndex = index
console.info("Index clicked: " + index)
}
}
}
}
Component {
id: appHighlight
Rectangle { width: 80; height: 80; color: "lightsteelblue" }
}
GridView {
anchors.fill: parent
cellWidth: 100; cellHeight: 100
highlight: appHighlight
focus: true
model: 12
delegate: appDelegate
MouseArea {
anchors.fill: parent
z:1
propagateComposedEvents: true // the key property!
drag.target: dragged
drag.axis: Drag.XAxis
drag.minimumX: - parent.width
drag.maximumX: parent.width / 2
onMouseXChanged: console.info(mouseX)
}
}
Rectangle{
id: dragged
width: parent.width
height: parent.height
color: "steelblue"
x: -parent.width
}
}

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:

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