- Published on
Un glisser/déposer en QML sans déplacement (copie, icône fantôme)
- Authors
- Name
- Anthony Rabine
On rencontre très souvent les mêmes exemples quant il s'agit de se renseigner sur l'implémentation d'un glisser-déposer en Qt/QML. Les exemples montrent surtout un déplacement d'objet graphique. Dans cet article, nous allons apprendre comment effectuer un glisser-déposer sans déplacement, avec une copie de l'icône qui suit le curseur pendant le drag.
Usage
On trouve souvent ce type de comportement de glisser-déposer au sein de logiciels professionnels. Il s'agit généralement d'un objet que l'on souhaite ajouter à son projet ; par exemple, dans un outil de modélisation UML, on va glisser-déposer un nouveau bloc de type "Classe" dans la zone de travail.
Voyons comment effectuer cela avec le QML, ce n'est pas si trivial, il y a beaucoup de petits détails à régler pour que cela fonctionne convenablement.
Voici ce que vous obtiendrez à la fin de cet article :
La barre d'outil générique
Notre barre d'outils prendra la forme d'un rectangle avec un layout horizontal contenant un élément Repeater.
La généricité vient de cet élément qui va instancier N composants à partir d'un modèle. Notre modèle, côté QML, contiendra le nom et l'emplacement de chaque icône. De cette manière, l'icône déposée sera convenablement identifiée par son nom.
Rectangle {
ListModel {
id: idToolBarModel
ListElement {
icon: "qrc:/step_screwdriver.png"
name: "tightening"
}
ListElement {
icon: "qrc:/step_pick_to_light.png"
name: "picktolight"
}
ListElement {
icon: "qrc:/step_input.png"
name: "input"
}
ListElement {
icon: "qrc:/step_user_input.png"
name: "userInput"
}
ListElement {
icon: "qrc:/step_switch.png"
name: "switch"
}
}
RowLayout {
id: idToolBarLayout
anchors.fill: parent
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
spacing: 5
property var dragedComp;
Repeater {
model: idToolBarModel
delegate: Item {
width: 64
height: 64
TileBody {
anchors.fill: parent
title: name // titre de notre icône
imageName: icon // emplacement de l'icône (dans un QRC)
}
}
}
}
}
Rien de sorcier pour le moment, c'est du classique. Mais il faut aller prudemment et comme toujours, bien tester les étapes intermédiaires.
L'astuce
Un élément est important ici : c'est notre propriété de type "var" (le type fourre-tout du QML et Javascript) appelé draggedComp. Cette variable va contenir la copie de l'icône que l'on veut déplacer.
Car l'astuce est là : pour obtenir notre effet, il va falloir dupliquer l'icône source.
En QML, on va préparer les choses en déclarant l'existance d'un composant instanciable :
Component {
id: itemComponent
TileBody {
}
}
Le coeur de notre système est donc de créer une copie de notre icône qui se baladera tant que le drag n'est pas terminé (par un succès, le drop, ou un abandon).
Pour créer le drag, entourons notre élément d'un objet MouseArea. Dans tous les cas, lorsque le bouton est relâché, succès ou non, on détruit la copie.
Lorsque le bouton est pressé, on crée notre copie à l'aide de la fonciton createObject() à partir du composant, et on paramètre tout : sa position, son titre, son icône et les paramètres de drag.
MouseArea {
id: mouseArea
anchors.fill: parent
onPressed: (mouse)=> {
console.log("Pressed");
addItem(mouse.x, mouse.y);
}
onReleased: {
Drag.active = false;
if (idToolBarLayout.dragedComp !== null) {
idToolBarLayout.dragedComp.Drag.drop();
idToolBarLayout.dragedComp.destroy();
}
}
function addItem(x, y)
{
var positionInWindow = mapToItem(idStepsToolBar, x, y)
idToolBarLayout.dragedComp = itemComponent.createObject(idToolBarLayout, { title: name, imageName: icon, x: positionInWindow.x - 25, y: positionInWindow.y - 25 });
if (idToolBarLayout.dragedComp !== null) {
idToolBarLayout.dragedComp.parent = idStepsToolBar;
idToolBarLayout.dragedComp.z = 100;
idToolBarLayout.dragedComp.Drag.keys = [ "step" ]
idToolBarLayout.dragedComp.Drag.active = true;
idToolBarLayout.dragedComp.Drag.hotSpot.x = 32;
idToolBarLayout.dragedComp.Drag.hotSpot.y = 32;
mouseArea.drag.target = idToolBarLayout.dragedComp;
Drag.active = true;
}
}
}
Le drop
Le drop est simple, nous acceptons uniquement le type désiré grâce à la propriété "keys" (on l'avait initialisé à la bonne valeur lors de la création de la copie).
Insérez ensuite votre code dans l'événement "onDropped". Le code présenté dans l'exemple ajoute un petit effet graphique sur l'élément de destination afin de rendre le drop plus visuel.
DropArea {
anchors.fill: parent
keys: ["step"]
onEntered: {
console.log("ENTERED");
idDropOverlay.visible = true;
}
onExited: {
console.log("EXIT");
idDropOverlay.visible = false;
}
onDropped: {
console.log("DROP");
idDropOverlay.visible = false;
console.log(drag.source.title)
}
Text {
anchors.centerIn: parent
text: "Target item!"
}
}
Conclusion
Nous obtenons notre effet désiré ! Le code source complet est ici :