DropArea doesn't notify about actions onEntered, onExited, onDropped - qt

I have Rectangle filled with MouseArea which on onPressAndHold() handler reveals second Rectangle and transfers drag action to that Rectangle. The problem is that when I move that second Rectangle over DropArea it doesn't notify about any actions (onEntered, onExited, onDropped). I tried to do this in many combinations but it has never worked. Here is an example, am I missing something?
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
id: appDrawerRoot
visible: true
width: 360; height: 360
property bool isRectVisible: false
Rectangle{
id:rect
color: "blue"
x:50; y:50
width: 50; height: 50
MouseArea{
anchors.fill: parent
onPressed: {
cloneRect.x = rect.x
cloneRect.y = rect.y
}
onPressAndHold: {
isRectVisible = true
drag.target = cloneRect
}
onReleased: {
drag.target = undefined
isRectVisible = false
cloneRect.x = rect.x
cloneRect.y = rect.y +100
}
}
}
Item{
id: cloneRect
width: 50; height:50
visible: isRectVisible
MouseArea{
id: mouseArea
width:50; height:50
anchors.centerIn: parent
Rectangle{
id:tile
width: 50; height:50
color:"black"
opacity: 0.5
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Drag.hotSpot.x: 25
Drag.hotSpot.y: 25
}
}
}
DropArea {
id:dropArea
x:153
y:158
z:-1
width:100; height: 100
Rectangle{
anchors.fill: parent
color: "Green"
}
onEntered: {
drag.source.opacity = 1
console.log("ENTERED")
}
onExited: {
drag.source.opacity = 0.5
console.log("EXITED")
}
onDropped:
{
console.log("DROPPED")
}
}
}

The main problem with your code is that you don't set the active property of the drag. Modify you code like this:
//..........................
Item{
id: cloneRect
width: 50; height:50
visible: isRectVisible
Drag.active: visible // Add this line of code
//.....................
For more information please refer to Qt examples. At Qt Creator's "Welcome" screen hit "Examples" button and search for "drag and drop qml".

Related

QT QML how to create a reusable button component

I have some simulated buttons in on rectangle. These buttons I need at different places in the app. Is it possible to make a kind of component out of it?
This is how it is currently
RowLayout {
anchors.fill: parent
Rectangle {
id: button1
height: _buttonsHeight * 0.6
width: height
radius: height / 2
border.width: 1
border.color: "black"
Image {
anchors.centerIn: parent
source: "image://iconProvider/icons/128/button1.png"
sourceSize.height: parent.height * 0.8
}
MouseArea {
anchors.fill: parent
onPressed: btnHome.border.width = 2
onReleased: btnHome.border.width = 0
onClicked: userInputDevice.buttonClicked("button1")
}
}
Rectangle {
id: button2
height: _buttonsHeight * 0.6
width: height
radius: height / 2
border.width: 1
border.color: "black"
Image {
anchors.centerIn: parent
source: "image://iconProvider/icons/128/button2.png"
sourceSize.height: parent.height * 0.8
}
MouseArea {
anchors.fill: parent
onPressed: btnHome.border.width = 2
onReleased: btnHome.border.width = 0
onClicked: userInputDevice.buttonClicked("button2")
}
}
}
Something like this would be the goal
RowLayout {
anchors.fill: parent
Button {id = "button1", height = _buttonsHeight * 0.6, icon = "button1.png", command = "button1", parent = this}
Button {id = "button2", height = _buttonsHeight * 0.6, icon = "button2.png", command = "button2", parent = this}
}
Is something like this feasible ?
Thanks and best regards
Arne
You should read the documentation about QML defining types.
Don't use the assignment operator, but the colon to create bindings. No need to set any parent.
For the nested Image.source property you should create an alias property in the root of your component like so property alias imageSource: <imageID>.source to be able to set it from the outside.
import QtQuick
import QtQuick.Layouts
Window {
id: root
width: 320
height: 240
visible: true
property int btnHeight: 80
component CustomButton : Rectangle {
property alias icon: image.source
signal pressed
signal released
signal clicked
id: buttonRoot
height: 20
width: height
radius: height / 2
border.width: 1
border.color: "black"
Image {
id: image
anchors.centerIn: parent
source: "https://upload.wikimedia.org/wikipedia/commons/7/72/Variable_Resistor.svg"
fillMode: Image.PreserveAspectFit
width: buttonRoot.height / Math.sqrt(2)
height: image.width
}
MouseArea {
anchors.fill: parent
onPressed: buttonRoot.pressed()
onReleased: buttonRoot.released()
onClicked: buttonRoot.clicked()
}
}
RowLayout {
anchors.centerIn: parent
spacing: 20
CustomButton {
id: button1
height: root.btnHeight
onClicked: console.log("Button 1 clicked")
}
CustomButton {
id: button2
height: root.btnHeight
icon: "https://upload.wikimedia.org/wikipedia/commons/f/fd/Crystal_Clear_app_download_manager.svg"
onClicked: console.log("Button 2 clicked")
}
}
}
You can create a new qml file let's name it MyButton.qml
MyButton is a custom button with icon and text you can change it to fit your needs.
MyButton.qml:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Qt.labs.platform 1.0
import QtLocation 5.12
import QtQuick.Controls.Material 2.15
MouseArea {
id: control
width: control.w
height: control.h
property string icon_btn
property bool icon_visible: true
property string text
property int border: 3
property string borderColor: "white"
property string colorr: "#222222"
property int w: 50
property int h: 50
property bool text_visible: true
property bool clicked: false
property int radius: 12
property int contentLeftMargin: 0
property string buttonColor: enabled ? control.colorr : "grey"
cursorShape: Qt.PointingHandCursor
Rectangle {
border.color: control.borderColor
border.width: control.border
radius: control.radius
anchors.fill: parent
color: control.buttonColor
RowLayout{
anchors.fill: parent
Item { Layout.fillWidth: true }
Image{
id: img
visible: control.icon_visible
source: control.icon_btn
Layout.preferredWidth: 35
Layout.preferredHeight: 35
//Layout.leftMargin: control.contentLeftMargin
}
Text{
id: txt
text: control.text
font.pointSize: 17
font.bold: true
color: "white"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
visible: control.text_visible ? x + width + 15 < control.width : 0
//Layout.leftMargin: control.contentLeftMargin
}
Item { Layout.fillWidth: true }
}
}
Layout.bottomMargin: 10
}
Then use it in your main qml file.
For example:
Window {
MyButton{
id: btn_refresh
icon_btn: "qrc:/Icons/outline_refresh_white_48pt_3x.png"
onClicked: {
//do something on click
}
}
}

Can QML StackLayout detect drag from a component outside itself

I have a StackLayout with 3 stack items (2 static and one dynamically generated) which switches on button click. I want to make the second and third stack items a Droparea (third one is dynamically created) where I want to drag items from a Listview which is outside the StackLayout. Is it possible or I am doing something wrong?
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page{
id: pageid
width: parent.width
height: parent.height
Row{
id: row1
Button{
text: "0"
onClicked: layout.currentIndex = 0
}
Button{
text: "1"
onClicked: layout.currentIndex = 1
}
Button{
text: "2"
onClicked:{
var str = 'import QtQuick 2.15; Rectangle {id: rect2; color: "red"; DropArea{anchors.fill: parent; onEntered: {rect2.color = "silver"}}}'
var comp = Qt.createQmlObject(str,layout,"dynamicSnippet1")
onClicked: layout.currentIndex = 2
}
}
}
// Stacklayout block
StackLayout {
id: layout
anchors.top: row1.bottom
height: parent.height - row1.height - dragger.height
width: parent.width
currentIndex: 0
// Component 0
Rectangle {
id: rect0
color: 'teal'
}
// Component 1
Rectangle {
id:rect1
color: 'plum'
DropArea{
anchors.fill: parent
onEntered: {rect1.color = "gold"}
}
}
}
// Drag rectangles
ListView{
id: dragger
anchors.top: layout.bottom
height: 30
width: parent.width
orientation: Qt.Horizontal
model: 3
delegate: Rectangle{
id: xrect
height: 30
width: 60
color:"grey"
border.width: 1
border.color: "orange"
MouseArea{
id: ma
anchors.fill: parent
onReleased: parent.Drag.drop()
drag.target: parent
}
}
}
}
You forgot to set property Drag.active for your draggable target, e.g. put statement
Drag.active: ma.drag.active
into your xrect to make it work.
In addition you could check more dragging signals in your DropArea:
onDropped: console.error("# dropped")
onContainsDragChanged: console.error("# containsDrag", containsDrag)

How can I call a custom signal from a QML Component?

Is there a way to call a signal from a mouseArea included in a component which is loaded somewhere else?
onClicked in the example below is not entered when i click on the rectangle.
The structure needs to remain as defined. To be able to define a qml component that applies a shadow to any source
Here is the code:
Window{
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Item
{
id: mainRectangle
anchors.centerIn: parent
width: loaderId.width + 60
height: loaderId.height + 60
Rectangle {
id: rect2
anchors.right: mainRectangle.right
anchors.top: mainRectangle.top
anchors.rightMargin: -30
anchors.topMargin: -30
width: 100
height: 100
color: "red"
opacity: 0.5
}
Loader {
id: loaderId
anchors.centerIn: parent
sourceComponent: component
active:true
}
visible: false
}
ShaderEffectSource {
id: shader
anchors.fill: mainRectangle
anchors.margins: -30
sourceItem: mainRectangle
opacity: 0.5
visible: true
}
Component {
id: component
Rectangle {
id: rect
width: 100
height: 100
color: "black"
MouseArea {
anchors.fill: parent
onClicked: {
console.log("Clicked!")
// call a signal from here
}
}
}
}
}
In the end the should show what it does now and the mouseArea should work.
onClicked in the example below is not entered when i click on the rectangle.
mainRectangle is not visible, and items that aren't visible don't get input events. Since the MouseArea is a child of mainRectangle, it won't get events either.
In the end the should show what it does now and the mouseArea should work.
Instead of hiding the source item and using ShaderEffectSource, you can set the opacity directly on mainRectangle and use item layers (the link has a similar example) to ensure that there is no overlap due to transparency:
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Item {
id: mainRectangle
anchors.centerIn: parent
width: loaderId.width + 60
height: loaderId.height + 60
opacity: 0.5
layer.enabled: true
Rectangle {
id: rect2
anchors.right: mainRectangle.right
anchors.top: mainRectangle.top
anchors.rightMargin: -30
anchors.topMargin: -30
width: 100
height: 100
color: "red"
}
Loader {
id: loaderId
anchors.centerIn: parent
sourceComponent: component
active: true
}
}
Component {
id: component
Rectangle {
id: rect
width: 100
height: 100
color: "black"
MouseArea {
anchors.fill: parent
onClicked: {
console.log("Clicked!")
// call a signal from here
}
}
}
}
}

MouseArea inside Flickable is preventing it from flicking

I'm implementing gesture catcher (swipe left/right) with MouseArea. It should work inside Flickable with vertical flickableDirection. Also it should propagate mouse events to other elements beneath it in visual stack order. The problem is that child mouseArea with propagateComposedEvents set to true is blocking any parent's flicks before exact one click is made. After first click is made it's working correctly. Here is simplified code that is showing this.
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
id: __root
visible: true
width: 460; height: 640
Flickable {
id: mainFlickable
width: parent.width
height: parent.height
contentHeight: column.height
flickableDirection: Flickable.VerticalFlick
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
z: 1
}
Column {
id: column
width: parent.width
Repeater {
model: 5
Rectangle {
width: __root.width
height: 200
color: "yellow"
border.width: 2
MouseArea {
anchors.fill: parent
onClicked: {
console.log("clicked")
}
}
}
} //repeater
} //column
} //flickable
} //window
I spent quite some time trying to fix this and will appreciate any help. Thanks in advance!
I found that following signal handler in MouseArea is a workaround for this and don't break my code:
onReleased: {
if (!propagateComposedEvents) {
propagateComposedEvents = true
}
}
propagateComposedEvents should be set to false on the declaration (or ommited).
Thank you all for the efforts!
I found little workaround for this. Hope it will fit your needs (at least until better solution will be provided).
Here is your updated code:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
id: __root
visible: true
width: 460; height: 640
Flickable {
id: mainFlickable
width: parent.width
height: parent.height
contentHeight: column.height
flickableDirection: Flickable.VerticalFlick
onDragStarted: ma.enabled = false
onDragEnded: ma.enabled = true
MouseArea {
id: ma
anchors.fill: parent
enabled: false
propagateComposedEvents: true
z: 100
onClicked: {
print("CLICKED ON UPPER")
mouse.accepted = false
}
}
Column {
id: column
width: parent.width
Repeater {
model: 5
Rectangle {
width: __root.width
height: 200
color: "yellow"
border.width: 2
MouseArea {
anchors.fill: parent
onClicked: console.log("clicked on child")
}
}
} //repeater
} //column
} //flickable
} //window

QT 5.3 / QML: Resizable StackView depending on currentItem

I'm working on a QML StackView that starts with a list of items to select from. Once selected I want to transition _.push(...) to a input form which has larger dimensions than the initialItem.
The only way I have trial-and-errored my way into a situation that works is by making the form Item a nested borderless window.
Q1. A nested window can't be the right type of concept to use for this... right ? there must be another way to do it. What is the right way ?
Q2. My goal after this is to have a transition animation that grows or shrinks between stacks of different sizes. Advice that doesn't preclude that would be best.
code
Main.qml :
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
ApplicationWindow {
property int itemHeight: 30
property int cornerRadius : 5
visible: true
color: "transparent"
flags: Qt.FramelessWindowHint
ListModel {
id: searchFacets
ListElement {
title: "Topics"
page: "content/TopicSearch.qml"
}
// ListElement {
// title: "Domains"
// }
}
StackView {
id: stackView
focus: true
initialItem: SearchFacets {
id: facets
}
delegate: StackViewDelegate {
pushTransition: StackViewTransition {
PropertyAnimation {
target: enterItem
property: "opacity"
from: 0
to: 1
}
}
}
}
}
Initial Item:
import QtQuick 2.3
Item {
height : listView.count * itemHeight
ListView {
id: listView
model: searchFacets
anchors.fill: parent
focus: true
highlightFollowsCurrentItem: false
highlight: Rectangle {
width: parent.width
height: itemHeight
radius : cornerRadius
color: "green"
opacity: 0.5
z:2
x: listView.currentItem.x;
y: listView.currentItem.y
Behavior on y {
SpringAnimation {
spring: 60
damping: 1.0
}
}
}
delegate: Component {
Item {
width: parent.width
height : itemHeight
Rectangle {
anchors.fill: parent
color: "#212126"
radius: cornerRadius
z:0
border.width: 2
border.color : "white"
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
console.log("clicked index: " + index)
listView.currentIndex = index
// listView.forceActiveFocus()
stackView.push(Qt.resolvedUrl(page))
}
}
Text {
text: title
font.pixelSize: 24
font.bold: true
z:1
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
color: "white"
antialiasing: true
}
}
}
}
}
Input Form:
import QtQuick 2.3
import QtQuick.Window 2.0
Item {
Window {
width: 400
height: 400
visible: true
flags: Qt.FramelessWindowHint
Rectangle {
anchors.fill: parent
visible: true
color: "red"
}
}
}
One possible solution is to update the size of the dimensions of the StackView in the click handler that causes the transition. I do not know if that causes any problems with animating the transition.
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
console.log("clicked index: " + index)
listView.currentIndex = index
var component = Qt.createComponent(page)
var res = component.createObject(stackView)
stackView.height = res.height
stackView.width = res.width
stackView.push(res)
}
}

Resources