Parent seems to be lost after drag and drop - qt

I am trying to implement a drag and drop soultion in qml without using models and delegates. So I create four containers and four rectangles with diferent colors and I want to move the rectangles dragging them and when one rectangle is dropped on another one i need to exchange the positions.
So far i've managed to acomplise that using reparenting but the second time I try to move another rectangle (or the same one it complains about cannot anchor to a null Item (as if the parent were destroyed or something
It has to be another easier way to drag and drop whitout using lists or models... I would apreciate any insight about this.
import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
ApplicationWindow{
id:root
minimumWidth: 1024
minimumHeight: 700
visible: true
title: "PlannerX2"
Component.onCompleted: {
root.showMaximized();
}
property int toIndex: -1
property int fromIndex: -1
property string sobreEscrito:""
property var posiciones: ["rojo","verde","azul","naranja"]
property var contenedores: ["vtk2dSceneAxialHolder","vtk2dSceneSagitalHolder","vtk2dSceneCoronalHolder","vtk3dSceneHolder"]
function returnFather( i)
{
switch (contenedores[i])
{
case "vtk2dSceneAxialHolder":
return vtk2dSceneAxialHolder
case "vtk2dSceneSagitalHolder":
return vtk2dSceneSagitalHolder
case "vtk2dSceneCoronalHolder":
return vtk2dSceneCoronalHolder
case "vtk3dSceneHolder":
return vtk3dSceneHolder
}
}
Rectangle {
id: screenCanvasUI
anchors.fill: parent
// Frame {
// id: view2dFrame
// visible: true
// rightPadding: 0
// leftPadding: 0
// topPadding: 0
// bottomPadding: 0
// width: parent.width/3
// anchors.left: parent.left
// anchors.top: parent.top
// anchors.bottom: parent.bottom
Rectangle{
id: vtk2dSceneAxialHolder
property int minHeight
height: parent.height/3
Layout.minimumHeight: minHeight
anchors.top: parent.top
anchors.left: parent.left
width: parent.width/3
DropArea{
anchors.fill: parent
onEntered: {
console.log("entrando en area Axial")
toIndex=0
}
onExited: {
console.log("saliendo de area Axial")
toIndex=-1
}
}
Rectangle{
id:rojo
color: "red"
width: parent.width
height: parent.height
Drag.active: rojoMouseArea.drag.active
Drag.hotSpot.x: rojo.width/2
Drag.hotSpot.y: rojo.height/2
property bool update:false
states:[
State{
name: "moving"
when: (rojo.Drag.active && verde.Drag.active===false && azul.Drag.active===false&& naranja.Drag.active===false)
ParentChange{
target: rojo
parent: screenCanvasUI
}
AnchorChanges{
target:rojo
anchors.horizontalCenter: undefined
anchors.verticalCenter: undefined
anchors.left: undefined
anchors.right: undefined
anchors.bottom: undefined
anchors.top: undefined
}
PropertyChanges {
target: rojo
width: 200
height:200
anchors.left: undefined
anchors.top: undefined
}
},
State{
name: "dropping"
when: (toIndex!==-1 && rojo.Drag.active===false && verde.Drag.active===false && azul.Drag.active===false&& naranja.Drag.active===false && toIndex!==-1 )
ParentChange{
target: rojo
parent:returnFather(posiciones.indexOf("rojo"))
}
PropertyChanges {
target: rojo
width: parent.width
height:parent.height
anchors.left: parent.left
anchors.top: parent.top
}
}
]
Item{
anchors.left: parent.left
anchors.top: parent.top
width: 20
height: 20
Frame{
anchors.fill:parent
}
MouseArea{
id:rojoMouseArea
anchors.fill: parent
drag.target: rojo
onClicked:
{
toIndex=-1
fromIndex=-1
console.log(screenCanvasUI)
console.log(toIndex)
console.log(fromIndex)
console.log(rojoMouseArea.drag.active)
}
drag.onActiveChanged: {
if (rojoMouseArea.drag.active)
{
console.log(toIndex)
console.log(fromIndex)
toIndex=-1
rojo.update=false
azul.update=false
verde.update=false
naranja.update=false
fromIndex=posiciones.indexOf("rojo");
console.log(toIndex)
console.log(fromIndex)
}
else{
var temp= posiciones[fromIndex];
posiciones[fromIndex]=posiciones[toIndex]
posiciones[toIndex]=temp;
rojo.Drag.drop()
}
}
}
}
}
}
Rectangle{
id: vtk2dSceneSagitalHolder
anchors.left: parent.left
anchors.top :vtk2dSceneAxialHolder.bottom
height: parent.height/3
width: parent.width/3
DropArea{
anchors.fill: parent
onEntered: {
console.log("entrando en area Sagital")
toIndex=1
}
onExited: {
console.log("saliendo de area Sagital")
toIndex=-1
}
}
Rectangle{
id:verde
color: "green"
width: parent.width
height: parent.height
property bool update:false
Drag.active: verdeMouseArea.drag.active
Drag.hotSpot.x: verde.width/2
Drag.hotSpot.y: verde.height/2
states:[
State{
name: "moving"
when: (verde.Drag.active && rojo.Drag.active===false && azul.Drag.active===false && naranja.Drag.active===false)
ParentChange{
target: verde
parent: screenCanvasUI
}
AnchorChanges{
target:verde
anchors.horizontalCenter: undefined
anchors.verticalCenter: undefined
anchors.left: undefined
anchors.right: undefined
anchors.bottom: undefined
anchors.top: undefined
}
PropertyChanges {
target: verde
width: 200
height:200
anchors.left: undefined
anchors.top: undefined
}
},
State{
name: "dropping"
when: ( toIndex!==-1 && verde.Drag.active===false && rojo.Drag.active===false && azul.Drag.active===false && naranja.Drag.active===false)
ParentChange{
target: verde
parent:returnFather(posiciones.indexOf("verde"))//{contenedores[toIndex]}
}
PropertyChanges {
target: verde
width: parent.width
height:parent.height
anchors.left: parent.left
anchors.top: parent.top
}
}
]
Item{
anchors.left: parent.left
anchors.top: parent.top
width: 20
height: 20
Frame{
anchors.fill:parent
}
MouseArea{
id:verdeMouseArea
anchors.fill: parent
drag.target: verde
drag.onActiveChanged: {
if (verdeMouseArea.drag.active)
{
toIndex=-1
fromIndex=posiciones.indexOf("verde");
rojo.update=false
azul.update=false
verde.update=false
naranja.update=false
}
else{
var temp= posiciones[fromIndex];
posiciones[fromIndex]=posiciones[toIndex]
posiciones[toIndex]=temp;
verde.Drag.drop();
}
}
}
}
}
}
Rectangle{
id: vtk2dSceneCoronalHolder
height: parent.height/3
anchors.left: parent.left
anchors.bottom: parent.bottom
width: parent.width/3
DropArea{
anchors.fill: parent
onEntered: {
console.log("entrando en area Coronal")
toIndex=2
}
onExited: {
console.log("saliendo de area Coronal")
toIndex=-1
}
}
Rectangle{
id:azul
color: "blue"
width: parent.width
height: parent.height
property bool update:false
Drag.active: azulMouseArea.drag.active
Drag.hotSpot.x: azul.width/2
Drag.hotSpot.y: azul.height/2
states:[
State{
name: "moving"
when: (azul.Drag.active && verde.Drag.active===false && rojo.Drag.active===false&& naranja.Drag.active===false)
ParentChange{
target: azul
parent: screenCanvasUI
}
AnchorChanges{
target:azul
anchors.horizontalCenter: undefined
anchors.verticalCenter: undefined
anchors.left: undefined
anchors.right: undefined
anchors.bottom: undefined
anchors.top: undefined
}
PropertyChanges {
target: azul
width: 200
height:200
anchors.left: undefined
anchors.top: undefined
}
},
State{
name: "dropping"
when: (toIndex!== -1 && azul.Drag.active===false && verde.Drag.active===false && rojo.Drag.active===false&& naranja.Drag.active===false )
ParentChange{
target: azul
parent:returnFather(posiciones.indexOf("azul"))//{contenedores[toIndex]}
}
PropertyChanges {
target: azul
width: parent.width
height:parent.height
anchors.left: parent.left
anchors.top: parent.top
}
}
]
Item{
anchors.left: parent.left
anchors.top: parent.top
width: 20
height: 20
Frame{
anchors.fill:parent
}
MouseArea{
id:azulMouseArea
anchors.fill: parent
drag.target: azul
drag.onActiveChanged: {
if (azulMouseArea.drag.active)
{
toIndex=-1
fromIndex=posiciones.indexOf("azul");
rojo.update=false
azul.update=false
verde.update=false
naranja.update=false
}
else{
var temp= posiciones[fromIndex];
posiciones[fromIndex]=posiciones[toIndex]
posiciones[toIndex]=temp;
azul.Drag.drop();
}
}
}
}
}
}
}
//}
Rectangle{
id: vtk3dSceneHolder
width: parent.width*2/3
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
DropArea{
id: vtk3dDropArea
anchors.fill: parent
onEntered: {
console.log("entrando en area 3D")
toIndex=3
}
onExited: {
console.log("saliendo de area 3D")
toIndex=-1
}
}
Rectangle{
id:naranja
color: "orange"
width: parent.width
height: parent.height
property bool update:false
Drag.active: naranjaMouseArea.drag.active
Drag.hotSpot.x: naranja.width/2
Drag.hotSpot.y: naranja.height/2
states:[
State{
name:"moving"
when: (naranja.Drag.active && rojo.Drag.active===false && verde.Drag.active===false && azul.Drag.active===false)
ParentChange{
target: naranja
parent: screenCanvasUI
}
AnchorChanges{
target:naranja
anchors.horizontalCenter: undefined
anchors.verticalCenter: undefined
anchors.left: undefined
anchors.right: undefined
anchors.bottom: undefined
anchors.top: undefined
}
PropertyChanges {
target: naranja
width: 200
height:200
anchors.left: undefined
anchors.top: undefined
}
},
State{
name: "dropping"
when: (toIndex!=-1 && naranja.Drag.active===false && rojo.Drag.active===false && verde.Drag.active===false && azul.Drag.active===false)
ParentChange{
target:naranja
parent: returnFather(posiciones.indexOf("naranja"))//{contenedores[fromIndex] }
}
PropertyChanges {
target: naranja
width: parent.width
height:parent.height
anchors.left: parent.left
anchors.top: parent.top
update:false
}
}
]
Item{
anchors.left: parent.left
anchors.top: parent.top
width: 20
height: 20
Frame{
anchors.fill:parent
}
MouseArea{
id:naranjaMouseArea
anchors.fill: parent
drag.target: naranja
drag.onActiveChanged: {
if (naranjaMouseArea.drag.active)
{
toIndex=-1
fromIndex=posiciones.indexOf("naranja");
}
else{
var temp= posiciones[fromIndex];
posiciones[fromIndex]=posiciones[toIndex]
posiciones[toIndex]=temp;
naranja.Drag.drop();
}
}
}
}
}
}
}
As I said, the first time y drag something it works, but after that not anymore and i get this error:
qrc:/main.qml:388:13: QML Rectangle: Cannot anchor to a null item.
qrc:/main.qml:388:13: QML Rectangle: Cannot anchor to a null item.
qrc:/main.qml:248:13: QML Rectangle: Cannot anchor to a null item.
qrc:/main.qml:248:13: QML Rectangle: Cannot anchor to a null item.
qrc:/main.qml:85:13: QML Rectangle: Cannot anchor to a null item.
qrc:/main.qml:85:13: QML Rectangle: Cannot anchor to a null item.

Related

QML: Move ScrollView to bottom when its height is changed?

I am trying to make a message composer widget. I want to scroll to the bottom of the ScrollView when its height is changed so that the user can keep up with what they are writing. How can I achieve this sort of functionality?
Here is my code for the ScrollView I am using:
ScrollView {
id: scrollView
anchors.left: parent.left
anchors.right: clearTextBtn.left
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.leftMargin: 0
anchors.rightMargin: 10
clip: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
hoverEnabled: true
onHoveredChanged: {
if (hovered) {
borderWidth = 2
cursorShape = Qt.IBeamCursor
} else {
borderWidth = focus ? 2 : 0
cursorShape = Qt.ArrowCursor
}
}
onFocusChanged: {
if (focus) {
borderWidth = 2
} else {
borderWidth = 0
}
}
TextEdit {
id: textEdit
width: scrollView.width
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignTop
anchors.bottomMargin: 0
anchors.topMargin: 0
clip: true
color: textColor
anchors.left: parent.left
wrapMode: Text.WordWrap
anchors.leftMargin: 0
padding: 10
selectByMouse: true
Label {
id: placeholderTxt
text: qsTr("Compose Message...")
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
verticalAlignment: Text.AlignTop
anchors.topMargin: 10
anchors.rightMargin: 223
anchors.leftMargin: 10
visible: textEdit.length == 0 && !textEdit.activeFocus
color: "#a3a3a3"
}
}
}
Try this :
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
id: box
width: 640
height: 180
visible: true
title: qsTr("ScrollBar")
Flickable {
id: inputWrapper
anchors.fill: parent
contentHeight: input.implicitHeight
contentWidth: input.implicitWidth
ScrollBar.vertical: ScrollBar {
id: scrollBar
policy: ScrollBar.AlwaysOn
anchors.left: box.right
}
Keys.onUpPressed: scrollBar.decrease()
Keys.onDownPressed: scrollBar.increase()
clip: true
flickableDirection: Flickable.VerticalFlick
function ensureVisible(r)
{
if (contentX >= r.x)
contentX = r.x;
else if (contentX+width <= r.x+r.width)
contentX = r.x+r.width-width;
if (contentY >= r.y)
contentY = r.y;
else if (contentY+height <= r.y+r.height)
contentY = r.y+r.height-height;
}
TextEdit {
id: input
anchors.fill: parent
text: ""
focus: true
wrapMode: TextEdit.Wrap
onCursorRectangleChanged: inputWrapper.ensureVisible(cursorRectangle)
} // TextEdit
} // Flickable
}

QML ListView::contentWidth is wider than actual content

Trying to implement ListView's content scroll by clicking on a button. When scrolling towards the end of the view ListView's content does not stop at the end of the last picture it overscrolls. Below I provided the minimum working example as well as the preview what goes wrong. Just change the .img path to make it work on your PC. I was looking for some help in sources of ListView and its inherited parent Flickable but nothing that could help to resolve the problem. How to make it stop at the end of the last picture?
import QtQuick 2.14
import QtQuick.Window 2.14
Window {
visible: true
width: 1024
height: 300
Item {
id: root
anchors.fill: parent
property var imagesUrlModel: ["file:///C:/Users/mikha/OneDrive/Изображения/toyota.jpg",
"file:///C:/Users/mikha/OneDrive/Изображения/toyota.jpg"
]
property int _width: 0
Component {
id: imageDelegate
Image {
id: image
sourceSize.height: 300
source: modelData
fillMode: Image.Stretch
}
}
Rectangle {
id: leftButton
anchors.top: root.top
anchors.bottom: parent.bottom
anchors.topMargin: 15
anchors.leftMargin: 10
anchors.left: parent.left
color: "green"
width: 25
MouseArea {
anchors.fill: parent
onClicked: {
listView.contentX = listView.contentX > 0
? listView.contentX - 50 > 0 ? listView.contentX - 50 : 0
: 0
}
}
}
Rectangle {
id: rightButton
anchors.top: root.top
anchors.bottom: parent.bottom
anchors.topMargin: 15
anchors.rightMargin: 10
anchors.right: parent.right
color: "green"
width: 25
MouseArea {
anchors.fill: parent
onClicked: {
listView.contentX = listView.contentX < listView.contentWidth
? listView.contentX + 50
: listView.contentWidth
//wrong content width
}
}
}
ListView{
id: listView
clip: true
boundsBehavior: Flickable.StopAtBounds
anchors.topMargin: 15
anchors.left: leftButton.right
anchors.right: rightButton.left
anchors.top: root.top
anchors.bottom: parent.bottom
spacing: 5
orientation: ListView.Horizontal
model: root.imagesUrlModel
delegate: imageDelegate
}
}
}
In your example just change listView.contentWidth to listView.contentWidth-listView.width in onClicked event for rightButton. But that's not enough. You should check whether the listView.contentX+50 is not overflowing listView.contentWidth-listView.width before you update the listView.contentX. In such case you need to update listView.contentX with difference between listView.contentWidth and listView.width.
Here it is:
listView.contentX = listView.contentX+50 <= listView.contentWidth-listView.width
? listView.contentX + 50
: listView.contentWidth - listView.width
I used another approach with repeater and scrollview and it has worked!
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
Window {
visible: true
width: 1024
height: 300
Item {
id: contentItem
anchors.fill: parent
Rectangle {
id: rightButton
anchors.top: contentItem.top
anchors.bottom: contentItem.bottom
anchors.rightMargin: 10
anchors.right: contentItem.right
color: "green"
width: 25
MouseArea {
anchors.fill: parent
onClicked: {
var allowedWidth = scrollView.flickableItem.contentWidth - scrollView.viewport.width
if(row.width < scrollView.viewport.width){
return
}
var offset = scrollView.flickableItem.contentX + 50
if(offset <= allowedWidth){
scrollView.flickableItem.contentX += 50
}
else {
scrollView.flickableItem.contentX = allowedWidth
}
}
}
}
ScrollView {
id: scrollView
anchors.left: contentItem.left
anchors.right: rightButton.left
anchors.top: contentItem.top
anchors.bottom: contentItem.bottom
clip: true
verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
property var imagesUrlModel: [
"file:///C:/Users/mikha/OneDrive/Изображения/toyota.jpg",
"file:///C:/Users/mikha/OneDrive/Изображения/toyota.jpg"
]
Row {
id: row
spacing: 15
Repeater {
id: repeater
anchors.left: parent.left
anchors.right: parent.right
model: scrollView.imagesUrlModel
delegate: Component {
id: imageDelegate
Image {
id: image
sourceSize.height: 300
source: modelData
fillMode: Image.Stretch
}
}
}
}
}
}
}

How can I change the anchors of an item to attach it to the right or left of another item based on a condition?

The layout below works. I am showing it as reference. startSelectionRect is always anchored to the left of a line as I have intended.
Rectangle {
id: startLine
x: ui.selectionModel.startPixel
width: 1
height: parent.height
color: QQProperties.Colors.text
}
Rectangle {
id: startSelectionRect
color: QQProperties.Colors.transparentSelectionBackground
anchors.top: parent.top
anchors.right: startLine.left
implicitHeight: startSelectionLayout.implicitHeight + QQProperties.Margins.small * 2
implicitWidth: startSelectionLayout.implicitWidth + QQProperties.Margins.medium * 2
ColumnLayout {
id: startSelectionLayout
spacing: QQProperties.Margins.small
anchors.topMargin: QQProperties.Margins.small
anchors.bottomMargin: QQProperties.Margins.small
anchors.leftMargin: QQProperties.Margins.medium
anchors.rightMargin: QQProperties.Margins.medium
anchors.fill: parent
QQComponents.HeaderText {
id: startText
text: Converter.formatDuration(ui.selectionModel.startUs, Converter.MicroSeconds, Converter.MicroSeconds)
}
}
}
Here I try to anchor startSelectionRect to the right or left of a line based on a condition.
function selectionFromLowestBound() {
console.log("selectionStartingOnLowestBound: ", ui.selectionModel.startPixel === ui.selectionModel.lowerBoundaryPixel);
return ui.selectionModel.startPixel === ui.selectionModel.lowerBoundaryPixel;
}
Rectangle {
id: startLine
x: ui.selectionModel.startPixel
width: 1
height: parent.height
color: QQProperties.Colors.text
}
Rectangle {
id: startSelectionRect
color: QQProperties.Colors.transparentSelectionBackground
anchors.top: parent.top
anchors.right: selectionFromLowestBound() ? startLine.left : undefined
anchors.left: !selectionFromLowestBound() ? startLine.right : undefined
implicitHeight: startSelectionLayout.implicitHeight + QQProperties.Margins.small * 2
implicitWidth: startSelectionLayout.implicitWidth + QQProperties.Margins.medium * 2
ColumnLayout {
id: startSelectionLayout
spacing: QQProperties.Margins.small
anchors.topMargin: QQProperties.Margins.small
anchors.bottomMargin: QQProperties.Margins.small
anchors.leftMargin: QQProperties.Margins.medium
anchors.rightMargin: QQProperties.Margins.medium
anchors.fill: parent
QQComponents.HeaderText {
id: startText
text: Converter.formatDuration(ui.selectionModel.startUs, Converter.MicroSeconds, Converter.MicroSeconds)
}
}
}
I can see with console.log that the condition is evaluated and that it is sometimes true and sometimes false. But startSelectionRect is always anchored to the right of the line. And startSelectionRect has lost its background color.
What am I doing wrong?
EDIT:
Here I am trying to follow the answer by Amfasis, but am failing thus far.
function selectionFromLowestBound() {
return ui.selectionModel.startPixel === ui.selectionModel.lowerBoundaryPixel;
}
Rectangle {
id: startLine
x: ui.selectionModel.startPixel
width: 1
height: parent.height
color: QQProperties.Colors.text
}
Rectangle {
id: startSelectionRect
color: QQProperties.Colors.transparentSelectionBackground
anchors.top: parent.top
implicitHeight: startSelectionLayout.implicitHeight + QQProperties.Margins.small * 2
implicitWidth: startSelectionLayout.implicitWidth + QQProperties.Margins.medium * 2
states: [
State {
name: "lowerBound"
when: selectionFromLowestBound()
PropertyChanges {
target: startSelectionRect
anchors.right: startLine.left
}
},
State {
name: "upperBound"
when: !selectionFromLowestBound()
PropertyChanges {
target: startSelectionRect
anchors.left: startLine.right
}
}
]
ColumnLayout {
id: startSelectionLayout
spacing: QQProperties.Margins.small
anchors.topMargin: QQProperties.Margins.small
anchors.bottomMargin: QQProperties.Margins.small
anchors.leftMargin: QQProperties.Margins.medium
anchors.rightMargin: QQProperties.Margins.medium
anchors.fill: parent
QQComponents.HeaderText {
id: startText
text: Converter.formatDuration(ui.selectionModel.startUs, Converter.MicroSeconds, Converter.MicroSeconds)
}
}
}
You can add states to a QML item in which it is also possible to set different anchors. See the following example:
Rectangle {
id: rect
property bool left : true
states: [
State {
name: "left"
when: rect.left
PropertyChanges {
target: rect
anchors.left: parent.right
}
},
State {
name: "right"
when: !rect.left
PropertyChanges {
target: rect
anchors.right: parent.left //note switched with above ;-)
}
}
]
}

How to animate a message that appears on bottom?

I'm showing a message on the bottom:
Msg.qml
import QtQuick 2.4
Item {
property alias text: mf.text
anchors.fill: parent
antialiasing: false
opacity: 0.9
z: 100
MsgForm {
id: mf
width: parent.width
y: parent.height - height - 5
}
}
MsgForm.ui.qml
import QtQuick 2.4
Item {
property alias text: msg.text
width: 200
id: message
height: msg.height+10
Rectangle {
id: rectangle
color: "#fb9191"
anchors.fill: parent
border.color: "#fd6666"
border.width: 2
Text {
id: msg
anchors.top: parent.top
anchors.topMargin: 2
textFormat: Text.PlainText
anchors.right: parent.right
anchors.rightMargin: 4
anchors.left: parent.left
anchors.leftMargin: 4
wrapMode: Text.WordWrap
clip: false
font.bold: true
font.pointSize: 12
font.family: "Tahoma"
}
}
}
How can I animate the form to appear from the bottom smoothly?
After the animation, if the window resizes, the message must stay always on the bottom.
You can play with anchors.bottomMargin property to raise the message item from the bottom.
import QtQuick 2.4
Item {
property alias text: mf.text
anchors.fill: parent
antialiasing: false
opacity: 0.9
z: 100
MsgForm {
id: mf
property bool showing: false
width: parent.width
anchors{
bottom: parent.bottom
bottomMargin: mf.showing ? 0 : -mf.height
Behavior on bottomMargin{
NumberAnimation{ }
}
}
}
}
Thanks everyone. In the end I've solved the issue by following advices received in the qtcentre forum.
The desired effect can be achieved easily by defining a local numerical property that is use to bind to either an anchors.XXXXmargin or the y property expression.
Following this approach a possible solution is the following:
MsgForm {
property bool showing: false
property int position: showing ? height : 0
width: parent.width
y: parent.height - position
Behavior on position {
NumberAnimation {duration: 500}
}
}
You can make an animation on the opacity change:
Msg.qml
import QtQuick 2.4
Item {
property alias text: mf.text
anchors.fill: parent
antialiasing: false
opacity: 0.9
z: 100
MouseArea{
anchors.fill: parent
onClicked: mf.opacity = !mf.opacity
}
MsgForm {
id: mf
//y: parent.height - height - 5
opacity:0
Behavior on opacity {
NumberAnimation{
duration:600
}
}
width: parent.width
anchors.bottom: parent.bottom
}
}
or any other NumberAnimation. I recommand you to create States, within it do some propertyChanges, and on some actions, example button clicked change states.
example in your MsgForm.ui.qml add:
MouseArea{
anchors.fill: parent
onClicked: mf.state= "show"
}
and in the action, example:
in my mouseArea I change the state of mf
MouseArea{
anchors.fill: parent
onClicked: mf.state= "show"
}
If you want an Animation on the y try this:
MsgForm.ui.qml
import QtQuick 2.4
Item {
id: message
property alias text: msg.text
width: parent.width
height: msg.height+10
Rectangle {
id: rectangle
color: "#fb9191"
anchors.fill: parent
border.color: "#fd6666"
border.width: 2
Text {
id: msg
anchors.top: parent.top
anchors.topMargin: 2
textFormat: Text.PlainText
anchors.right: parent.right
anchors.rightMargin: 4
anchors.left: parent.left
anchors.leftMargin: 4
wrapMode: Text.WordWrap
clip: false
font.bold: true
font.pointSize: 12
font.family: "Tahoma"
}
}
Behavior on y {
NumberAnimation{
duration:300
}
}
states: [
State {
name: "show"
PropertyChanges {
target: message
y: parent.height - height
}
},
State {
name: "hide"
PropertyChanges {
target: message
y: parent.height + height + 5
}
}
]
}
Msg.qml
import QtQuick 2.4
Rectangle {
property alias text: mf.text
width:800
height: 480
antialiasing: false
opacity: 0.9
z: 100
MouseArea{
anchors.fill: parent
onClicked: mf.state= "show"
}
MsgForm {
id: mf
//y: parent.height - height - 5
y: parent.height +height + 5
width: parent.width
}
}

QML : Drop-down menu that opens upward

I need to have drop-down that opens upwards on clicking on Menu button in QML.
I have tried to use the listview for the same, but in the implementation we are getting drop-down which opens downwards.
Any suggestions with reference to below snippet.
import QtQuick 1.1
Rectangle {
width: 800
height: 480
Rectangle {
id:comboBox
property variant items: ["Red", "Blue", "Green"]
signal comboClicked;
x: 651
y: 344
width: 141
height: 30;
z: 0
smooth:true;
Rectangle {
id:chosenItem
radius:4;
width:parent.width;
height:comboBox.height;
color: "#454b4d"
smooth:true;
Text {
anchors.top: parent.top;
anchors.margins: 8;
id:chosenItemText
x: 11
y: 5
color: "#ffffff"
text:"Menu";
anchors.topMargin: 5
anchors.left: parent.left
anchors.leftMargin: 12
font.family: "Arial"
font.pointSize: 14;
smooth:true
}
MouseArea {
width: 400
height: 30
anchors.bottomMargin: 0
anchors.fill: parent;
onClicked: {
comboBox.state = comboBox.state==="dropDown"?"":"dropDown"
}
}
}
Rectangle {
id:dropDown
width:comboBox.width;
height:0;
clip:true;
radius:4;
anchors.top: chosenItem.bottom;
anchors.margins: 2;
color: "lightblue"
ListView {
id:listView
height:500;
model: comboBox.items
currentIndex: 0
delegate: Item{
width:comboBox.width;
height: comboBox.height;
Text {
text: modelData
anchors.top: parent.top;
anchors.left: parent.left;
anchors.margins: 5;
}
MouseArea {
anchors.fill: parent;
onClicked: {
comboBox.state = ""
chosenItemText.text = modelData;
listView.currentIndex = index;
}
}
}
}
}
states: State {
name: "dropDown";
PropertyChanges { target: dropDown; height:30*comboBox.items.length }
}
transitions: Transition {
NumberAnimation { target: dropDown; properties: "height"; easing.type: Easing.OutExpo; duration: 1000 }
}
}
}
Try to change the anchors of the dropDown item:
that
anchors.top: chosenItem.bottom;
should become
anchors.bottom: chosenItem.top;

Resources