QML transfer mouse ownership dynamically while mouse pressed - qt

I have an application with a list of images on the right side (using
a ListView) and a viewer on the left. The user can drag an image from the list to the viewer, AND keep the image in the list (something similar to a preview of the list, but with drag and drop functionality).
To do that, when the user "pressAndHold" on an image from the list, I create a copy of that image and place it in front of the one from the list (I change the border so I know it's the copy one).
If I then release and click again on the copy, I am able to move the copy to the viewer, and once I release the copy, I destroy the copy and process the drop if dropped on the viewer area.
I cannot do it unless I release and click on the copy , because I am not able to transfer the "mouse ownership" from the list-image mousearea to the copy-image mousearea while mouse is onhold.
Any ideas? Thanks in advance!

To anyone looking for something similar, this is how I did it:
On the delegate, I added the mouse area:
MouseArea { // This is the mouse area on the original image-list
id: thumbnailDelegateMA
anchors { fill: parent }
cursorShape: containsMouse ? (drag.active ? Qt.ClosedHandCursor : Qt.PointingHandCursor) : Qt.ArrowCursor
property var copyThumbnail: undefined
drag.target: copyThumbnail ? copyThumbnail : null
onPressAndHold: {
if(!copyThumbnail){
copyThumbnail = createThumbnailCopy(imageID, parent)
parent = copyThumbnail
anchors.fill = copyThumbnail
}
}
onReleased:{
if(copyThumbnail){
parent = copyThumbnail.original
anchors.fill = copyThumbnail.original
copyThumbnail.destroy()
}
}
}
where:
function createThumbnailCopy(uid, cparent){
var main_window = cparent.parent.parent;
var mapPos = mapFromItem(main_window, cparent.x, cparent.y);
var thumbnailCopy = thumbnailCopyComponent.createObject(
main_window,
{ "original": cparent,
"id": uid
"x": mapPos .x,
"y": mapPos .y
});
return thumbnailCopy;
}
And:
Component{
id: thumbnailCopyComponent
Image {
id: thumbnailCopy
property string id;
property var original: undefined
Drag.hotSpot: Qt.point(width/2, 0)
Drag.active: true
fillMode: Image.PreserveAspectFit
source: "image://thumbnailProvider/" + id
}
}

Related

QML Button - How to check that button is pressed with Control modifier?

How we can detect the QML button is clicked (checked) with "Control" key button pressed simultaniously?
AFAIK there's no way to check if the CTRL key is pressed while you're in an onPressed handler of a button, but you could work around that:
Let QML inform you when the CTRL key is pressed/released. Save that status in a property and use it in the onPressed handler of the button.
// Example code
Item {
id: rootItem
anchors.fill: parent
focus: true
property bool ctrlPressed: false
Keys.onPressed: {
if (event.key === Qt.Key_Control) {
ctrlPressed = true
}
}
Keys.onReleased: {
if (event.key === Qt.Key_Control) {
ctrlPressed = false
}
}
Button {
anchors.centerIn: parent
text: "Press me"
onPressed: {
if (rootItem.ctrlPressed)
console.log("Click with CTRL")
else
console.log("Click without CTRL")
}
}
}
This is just a 'workaround' and has some issues:
The rootItem has to have the focus
when the Application loses focus (ALT+TAB or minimize) while the CTRL button is pressed you might get unexpected behaviour
Another option is simply adding your own MouseArea on top of the button. You could use propogateComposedEvents to allow the Button's default mouse handling to still occur, or you could manually regenerate things like the clicked signal yourself. Neither option is ideal, but maybe one works for you.
Button {
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onClicked: {
if ((mouse.button == Qt.LeftButton) && (mouse.modifiers & Qt.ControlModifier)) {
doSomething();
}
}
}
}
I used #ParkerHalo's answer. The issue he noticed when the app loses focus with ctrl pressed can be avoided by placing this in the top level Window:
onActiveChanged: if( ! active ) rootItem.ctrlPressed = false;
One more solution is to use a TapHandler (a relatively new feature, see blog post).
There's an example in the docs for a new TreeViewDelegate which can be modified for your case with Button (they both inherit AbstractButton).
This way you won't have to remember any state or propagate events to underlying internal mouse area of the button:
Button {
text: "Test"
TapHandler {
acceptedModifiers: Qt.ControlModifier
onTapped: console.log("tapped with Qt.ControlModifier")
}
TapHandler {
acceptedModifiers: Qt.NoModifier
onTapped: console.log("tapped with Qt.NoModifier")
}
}

How to get id of child in qml

When I tried to get children ID's slightly modifying this http://qmlbook.github.io/en/ch01/index.html example
// animate.qml
import QtQuick 2.0
Item
{
id: root
width: background.width
height: background.height
property string information: "this is root"
property int rotationStep: 45
Image {
id: background
source: "images/background.png"
}
Image {
id: pole
property string information: "this is pole"
x: (root.width - width)/2+2
y: root.height - height
source: "images/pole.png"
}
Image {
id: pinwheel
anchors.centerIn: parent
source: "images/pinwheel.png"
property string information: "this is pinweel"
// visible: false
Behavior on rotation {
NumberAnimation { duration: 125 }
}
}
Image {
id: blur
opacity: 0
anchors.centerIn: parent
source: "images/blur.png"
property string information: "this is blur"
// visible: false
Behavior on rotation {
NumberAnimation { duration: 125 }
}
Behavior on opacity {
NumberAnimation { duration: 125 }
}
}
// M1>>
focus: true
Keys.onLeftPressed: {
blur.opacity = 0.8
pinwheel.rotation -= root.rotationStep
blur.rotation -= root.rotationStep
}
Keys.onRightPressed: {
blur.opacity = 0.5
pinwheel.rotation += root.rotationStep
blur.rotation += root.rotationStep
}
Keys.onReleased: {
blur.opacity = 0
}
Keys.onSpacePressed:
{
for (var i=0; i<root.children.length; ++i)
console.info(root.children[i].information)
}
Keys.onDeletePressed:
{
for (var i=0; i<root.children.length; ++i)
console.info(root.children[i].id)
}
// <<M1
}
Unfortunately pressing Delete key gives me an error:
qml: undefined
qml: undefined
qml: undefined
qml: undefined
as opposed to pressing spacebar:
qml: undefined
qml: this is pole
qml: this is pinweel
qml: this is blur
Why this script returns undefined id's ?
I need to traverse some objects and be able to tell what is what - so I need to know how to traverse root tree to get id's of its childs and their object types.
Unfortunately I was unable to print the most trival id's and had to add some simple property to get it done but this means a lot of work in real life project since every object needs information property :(
So to reiterate:
Why the id's in this example are undefined?
How to traverse object tree using qml and print its id's and types ?
Id is not an ordinary object property, so it is undefined when you try to assess it through js. And qml doesn't provide operators like typeof. So you need to add type or objectname property manually. I would consider subclassing Image and adding type. Ref: How to do a "is_a", "typeof" or instanceof in QML?
Item
{
id: root
Image {
id: background
type: "image"
}
Image {
id: pole
type: "image"
}
function iter(){
for(var i = 0; i < root.children.length; ++i)
if(root.children[i].type==="image"){
//balabala
}
}
}
}

Unable to access QML variable / id globally

I have QtQuick 1.0
I use the following code:
Rectangle {
Component {
id: appDelegate
MouseArea{
id:myMouseArea
hoverEnabled: true
onClicked:{
onClicked: load.source = page;
}
}
Loader {
id: load
}
}
GridView {
id: view
// I am unable to access myMouseArea here.
highlight: myMouseArea.containsMouse ? appHighlight : !appHighlight
delegate: appDelegate
}
}
It gives me the following error:
ReferenceError: Can't find variable: myMouseArea
/usr/lib/i386-linux-gnu/qt4/bin/qmlviewer exited with code 0
I don't know if the details I provided are sufficient, please let me know if theres anything else I am missing.
I am using this code as an example:
http://docs.knobbits.org/qt4/declarative-modelviews-gridview-qml-gridview-example-gridview-example-qml.html
You cannot access myMouseArea because it's created inside delegate context. You cannot access delegate other then currentItem. But you can freely access view inside the context of delegate, to set currentIndex to attached property index.
This is a corrected code:
Rectangle {
width: 360
height: 360
Component { // It must be a component, if we want use it as delegate
id: appDelegate
// its not possible to have more than one element inside component
Rectangle
{
// need to set size of item, anchors wont work here
// could use view.cellWidth and view.cellHeight to keep it DRY
width: 96
height: 66
color: "green" // color only to see the place of MouseArea
MouseArea {
id:myMouseArea
anchors.fill: parent // this setup the size to whole rectangle
// it this item have the size 0,0 it will simple do not work
hoverEnabled: true
onEntered: {
// we know the mouse is inside this region
// setting this value will show the highlight rectangle
view.currentIndex = index;
}
onClicked:{
onClicked: load.source = page;
}
}
Loader {
// this is not needed but it's wise to not keep zero size
anchors.fill: parent
id: load
}
}
}
GridView {
id: view
// the size of GridView must be set,
// as otherwise no delegate will not show
anchors.fill: parent
anchors.margins: 5
cellWidth: 100
cellHeight: 70
// Rectangle will act as a border.
// Size and position is set by GridView
// to the size and position of currentItem.
// This is no a item, this makes a Component
// as highlight property needs one.
// You can create a Component like appDelegate.
highlight : Rectangle {
border.width: 2
border.color: "blue"
}
// some ListModel to setup the page variable inside delegate context
model: ListModel {
ListElement { page: "test1.qml"; }
ListElement { page: "test2.qml"; }
ListElement { page: "test3.qml"; }
}
delegate: appDelegate
}
}

how to imitate the button in QML

I want to imitate the Button in QML, but why does it only work with containsMouse and !containsMouse, it doesn't work with OnPressed. can you help me ? Thank you.
Item {
id: area
property string str_noPressed;
property string str_hover;
property string str_pressed;
Image {
id: background
width: 75; height: 75
source: userArea.containsMouse ? str_hover : str_noPressed
MouseArea {
id: userArea
anchors.fill: parent
onClicked: {}
onPressed: background.source = str_pressed
hoverEnabled: true
}
}
}
Obviously, in the button is pressed, it also contains the mouse.
I assume containsMouse takes precedence over onPressed. Try:
onContainsMouseChanged: {
if (containsMouse && !pressed) {
background.source = str_pressed
}
}

How can I switch the focus for the pop-up window?

I encounter a problem which is that the pop-up window cannot get the focus when it is shown. I tried to use the activefocus function in main window, but it doesn't work. It is supposed that if I press the enter key, the pop-window will be closed. How can I get the focus for the pop-up window? Thanks.
...
GridView {
id:grid_main
anchors.fill: parent
focus: true
currentIndex: 0
model: FileModel{
id: myModel
folder: "c:\\folder"
nameFilters: ["*.mp4","*.jpg"]
}
highlight: Rectangle { width: 80; height: 80; color: "lightsteelblue" }
delegate: Item {
width: 100; height: 100
Text {
anchors { top: myIcon.bottom; horizontalCenter: parent.horizontalCenter }
text: fileName
}
MouseArea {
anchors.fill: parent
onClicked: {
parent.GridView.view.currentIndex = index
}
}
}
Keys.onPressed: { //pop up window
if (event.key == 16777220) {//enter
subWindow.show();
subWindow.forceActiveFocus();
event.accepted = true;
grid_main.focus = false;
}
}
}
Window {
id: subWindow
Keys.onPressed: {
if (event.key == 16777220) {//press enter
subWindow.close();
}
}
}
...
Let's start with some basics:
Keys.onPressed: { //pop up window
if (event.key == 16777220) {//enter
subWindow.show()
...
event.accepted = true
}
}
Not to mention how error-prone it is, just for the sake of readability, please don't hard-code enum values like 16777220. Qt provides Qt.Key_Return and Qt.Key_Enter (typically located on the keypad) and more conveniently, Keys.returnPressed and Keys.enterPressed signal handlers. These convenience handlers even automatically set event.accepted = true, so you can replace the signal handler with a lot simpler version:
Keys.onReturnPressed: {
subWindow.show()
...
}
Now, the next thing is to find the correct methods to call. First of all, the QML Window type does not have such method as forceActiveFocus(). If you pay some attention to the application output, you should see:
TypeError: Property 'forceActiveFocus' of object QQuickWindowQmlImpl(0x1a6253d9c50) is not a function
The documentation contains a list of available methods: Window QML type. You might want to try a combination of show() and requestActivate().
Keys.onReturnPressed: {
subWindow.show()
subWindow.requestActivate()
}
Then, you want to handle keys in the sub-window. Currently, you're trying to attach QML Keys to the Window. Again, if you pay attention to the application output, you should see:
Could not attach Keys property to: QQuickWindowQmlImpl(0x1ddb75d7fe0) is not an Item
Maybe it's just the simplified test-case, but you need to get these things right when you give a testcase, to avoid people focusing on wrong errors. Anyway, what you want to do is to create an item, request focus, and handle keys on it:
Window {
id: subWindow
Item {
focus: true
Keys.onReturnPressed: subWindow.close()
}
}
Finally, to put the pieces together, a working minimal testcase would look something like:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: window
width: 300
height: 300
visible: true
GridView {
focus: true
anchors.fill: parent
// ...
Keys.onReturnPressed: {
subWindow.show()
subWindow.requestActivate()
}
}
Window {
id: subWindow
Item {
focus: true
anchors.fill: parent
Keys.onReturnPressed: subWindow.close()
}
}
}
PS. Key events rely on focus being in where you expect it to be. This may not always be true, if the user tab-navigates focus elsewhere, for example. Consider using the Shortcut QML type for a more reliable way to close the popup.

Resources