Apprendre le glisser/déposer avec Free Pascal/Lazarus
Partie 1/3, par Gilles Vasseur
Le 2017-05-10 14:28:56, par gvasseur58, Responsable Lazarus & Pascal
Si les utilisateurs de Lazarus savent sans doute que leur EDI prend en charge le glisser-déposer (ou Drag and drop en anglais), peut-être n'imaginent-ils pas la richesse des possibilités qui s'ouvrent à eux. Le travail qui suit entend montrer la diversité des approches et la souplesse d'une panoplie d'outils finalement peu utilisés, et encore ne prétend-il pas à l'exhaustivité !
La première partie expose les techniques de base du glisser-déposer :
http://gilles-vasseur.developpez.com/tutoriels/draganddrop1-techniques-base/
Elle sera prochainement suivie de deux autres parties :
- la personnalisation du glisser-déposer ;
- la maîtrise du dessin et des gestionnaires d'événements.
-
thewolfMembre habituéBonjour.
Étant donné un site web où il faut charger une image. Ce site propose deux possibilités : soit cliquer sur un bouton, ce qui affiche un dialogue d'ouverture de fichier permettant de naviguer sur son disque dur jusqu'au fichier souhaité, soit effectuer un glisser-déposer du gestionnaire de fichiers vers une zone de saisie.
Il y a quelque temps, j'ai donc cherché (sans trouver !) comment utiliser cette deuxième option pour indiquer le fichier à charger à partir d'un programme développé avec Lazarus (le programme détermine à partir d'un certain nombre de critères le chemin complet du fichier).
Je profite de votre tutoriel sur le glisser-déposer pour reprendre ce sujet. Si vous avez une idée, merci d'avance.le 14/05/2017 à 14:05 -
gvasseur58Responsable Lazarus & PascalBonjour,
Vous parlez de bouton à cliquer et de site Web : en quoi est écrit ce site ?
S'il s'agissait d'une application de bureau Lazarus autonome, il suffirait d'appliquer ce qui est décrit dans la première partie du tutoriel.
S'il s'agit de HTML (HTML 5 accompagné de JavaScript par exemple), le problème peut (sans doute) être résolu, mais il faudrait plutôt s'adresser au forum adéquat.
Il se peut aussi que j'aie mal compris le problème...le 15/05/2017 à 8:22 -
JipétéExpert éminent séniorBonsoir,
Non.
J'ai fait afficher google images dans mon navigateur sous Linux, j'ai pris un fichier .jpg sur le bureau, je l'ai tiré jusque sur la page affichée, pas dans la zone indiquée par le texte ("faire glisser une image ici", c'est le "ici" qui induit en erreur), et dès l'entrée de la souris dans le navigateur l'icône du pointeur change, ainsi que le texte de google, qui affiche du coup l'image ci-dessus.
Suffit de lâcher" l'image et hop !, elle est affichée.
Non !
Le navigateur charge la photo comme un fichier local ("file:///chemin/fichier.ext" dans la barre d'adresse) en remplaçant purement et simplement la page affichée : le site est perdu.
Je le sais parce que la manip fonctionne également avec un vieux site tout moche dont je suis l'admin et pour lequel je sais très bien qu'il n'y a pas de drag-and-drop de codé, que ce soit en javascript, css ou autre.
Il suffirait donc, en s'inspirant du code proposé par Gilles (et je l'avoue humblement, je ne l'ai pas regardé), de détecter comme cible la présence d'un navigateur et ça devrait le faire.
Mais ça le fera aussi pour n'importe quel navigateur affichant n'importe quel site, à moins de faire une détection du titre affiché dans la barre de titre, justement, de la fenêtre du navigateur.
Maintenant, si on lâche l'image sur la zone "faire glisser une image ici", je vois passer en bas de la fenêtre un javascript(void) qui indique que "quelque chose" a pris la main, mal, car il ne se passe rien...
Ça a mieux fonctionné dans la grande zone "Déposez une image ici" (image bien détectée, infos remontées) -- peut-être lié à des problèmes de cache ou autre...le 18/05/2017 à 23:18 -
JipétéExpert éminent séniorBonsoir,
je trouve le § "III-A Travail entre deux contrôles" pas très clair...
1re phrase, pas grand chose, il manque juste "de" : "depuis le gestionnaire fichiers"
2e phrase c'est plus ennuyeux car on dirait qu'il manque des mots au tout début.
Pour moi, Le contrôle dont le contenu doit être autorisé à le faire me fait poser la question "faire quoi ?", alors j'improvise ça :
Le contrôle dont le contenu "va être tiré" doit être autorisé à le faire en vérifiant que sa propriété DragKind est bien à sa valeur par défaut,
tiré ou déplacé ou lâché, etc.
3e phrase, je me demande si ça n'est pas l'inverse :
Du côté du contrôle qui va accepter de déposer du contenu, --> Du côté du contrôle qui va accepter de recevoir du contenu,
Si je me trompe, alors peut-être remplacer "déposer" par "exporter" afin de lever toute ambiguïté ?le 30/06/2017 à 20:00 -
gvasseur58Responsable Lazarus & PascalBonjour Jipété,
Tu es trop indulgent, car ce début de chapitre est devenu illisible par ma faute, je le confesse. Claude Leloup avait tout revu et corrigé avec la rigueur qu'on lui connaît et j'ai fait une mauvaise manipulation qui a gommé certaines des corrections.
J'espère que la mise à jour proposée convient à présent.
Merci
Gillesle 05/07/2017 à 18:25 -
thewolfMembre habituéBonjour. Merci pour votre réponse.
Il s'agit d'un site web auquel il faut fournir des images. C'est possible en faisant un glisser-déposer depuis le gestionnaire de fichiers vers une zone du site. Ce site est en HTML5 + javascript et, en ayant "fouillé" dans le code source, il utilise pour ce faire un produit appelé "plupload" mais tout cela n'est pas a priori important.
En effet, mon raisonnement est le suivant :
- en faisant un glisser-déposer sur la zone du site prévue à cet effet, le site (plus précisément le plugin "plupload"reçoit l'information suivante : l'utilisateur de l'ordinateur a fait un glisser-déposer en fournissant par exemple l'information suivante : "c:\data\monfichier.jpg".
- Lazarus sait comment traiter ce genre d'information, c'est l'objet de votre premier chapitre "glisser-déposer depuis le gestionnaire de fichiers" : le programme créé va diagnostiquer qu'il a reçu un signal de cette nature, y réagir et en déterminer le contenu (le chemin du ou des fichiers déposés). Donc il devrait savoir comment le créer.
- mon souhait est donc, à l'intérieur de mon programme écrit en Lazarus, de pouvoir simuler ce que fait le gestionnaire de fichiers, par exemple : je fais un glisser-déposer depuis un TLabel ou un Bouton vers la zone "sensible" du site et cela envoie la même information que celle fournie par le gestionnaire de fichiers à savoir dans mon exemple précédent "c:\data\monfichier.jpg", sachant que le nom du fichier est déterminé par le programme en fonction de certains critères ("monfichier" pouvant valoir "img2451" ou "img3727" ou ...)
J'espère avoir été clair.
Bonne soirée
PS : je peux vous fournier le nom du site si vous le souhaitez mais ce n'est pas pertinent, le problème étant général (par exemple Google - Recherche par image).le 15/05/2017 à 21:40 -
gvasseur58Responsable Lazarus & PascalBonjour,
Si j'ai bien compris, voici une unité qui permet de charger une image depuis l'intitulé (Caption) d'un TLabel. La fiche ne contient qu'un TLabel nommé lblFile dont la propriété Caption vaut le nom de l'image (fichier avec son chemin) à afficher et une TImage nommée imgMain dont la propriété Stretch a été mise à True pour l'étirer.Code : 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47unit main; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls; type { TForm1 } TForm1 = class(TForm) imgMain: TImage; lblFile: TLabel; procedure ImgMainDragDrop(Sender, Source: TObject; X, Y: Integer); procedure ImgMainDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); private { private declarations } public { public declarations } end; var Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.ImgMainDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin Accept := (Source is TLabel); end; procedure TForm1.ImgMainDragDrop(Sender, Source: TObject; X, Y: Integer); begin imgMain.Picture.LoadFromFile((Source as TLabel).Caption); end; end.
le 17/05/2017 à 14:06 -
thewolfMembre habituéBonjour.
Je suis désolé car je pensais avoir été clair mais ce n'est visiblement pas le cas.
A priori, dans un drag and drop, il y a une source et une cible. Je veux que la source soit mon programme écrit en Lazarus mais la cible doit être un site internet (plus précisément une zone ad hoc du site).
Exemple :
Google Images accepte le glisser-déposer (je cite : "Faire glisser une image ici". Cela fonctionne depuis le gestionnaire de fichiers, également depuis le bureau : au survol, l'apparence du site change et quand on relache, le site charge la photo ...
Comme écrit précédemment, j'aimerais simuler ce que fait le gestionnaire de fichiers. Je démarre le drag and drop de mon programme (le TLabel dans votre exemple) mais je veux relâcher sur le site (Google Images dans mon exemple) et non sur un autre contrôle du programme (TImage dans votre exemple). Mais le "dragover" n'est même pas diagnostiqué par le navigateur. De plus, je ne vois pas comment intégrer l'information à transmettre au départ.
Donc, en résumé, je veux démarrer un glisser-déposer depuis mon programme et que, quand je vais relâcher au-dessus d'un navigateur (Google Images dans mon exemple mais cela doit fonctionner avec tout site qui l'accepte), il soit reconnu par le site et transmette une information particulière (ici une chaine de caractères représentant le chemin d'un fichier image). Exactement ce qui se passe si je fais un glisser-déposer du gestionnaire de fichiers vers le navigateur.
Encore merci.le 17/05/2017 à 15:31 -
gvasseur58Responsable Lazarus & PascalSi, si, nous allons réussir à nous comprendre
.
Le cas de figure décrit est celui que j'écarte explicitement (au moins à court terme), car il n'existe pas de techniques multiplateformes simples pour réaliser ce que vous décrivez.
Voici une unité (en anglais) décrivant une solution pour Windows. Elle permet de déposer du texte à partir d'une application vers une zone de contrôle de type TEdit. Elle est extraite d'une page du wiki free pascal :Code : 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Windows, ActiveX, ComObj; type { TForm1 } TForm1 = class(TForm, IDropTarget) Memo1: TMemo; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private // IDropTarget function DragEnter(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult;StdCall; function DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult;StdCall; function DragLeave: HResult;StdCall; function Drop(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD):HResult;StdCall; // IUnknown // Ignore referance counting function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; public { public declarations } end; var Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject); begin OleInitialize(nil); OleCheck(RegisterDragDrop(Handle, Self)); end; procedure TForm1.FormDestroy(Sender: TObject); begin RevokeDragDrop(Handle); OleUninitialize; end; function TForm1.DragEnter(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult; StdCall; begin dwEffect := DROPEFFECT_COPY; Result := S_OK; end; function TForm1.DragOver(grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD ): HResult; StdCall; begin dwEffect := DROPEFFECT_COPY; Result := S_OK; end; function TForm1.DragLeave: HResult; StdCall; begin Result := S_OK; end; function TForm1._AddRef: Integer; stdcall; begin Result := 1; end; function TForm1._Release: Integer; stdcall; begin Result := 1; end; function TForm1.Drop(const dataObj: IDataObject; grfKeyState: DWORD; pt: TPoint; var dwEffect: DWORD): HResult; StdCall; var aFmtEtc: TFORMATETC; aStgMed: TSTGMEDIUM; pData: PChar; begin {Make certain the data rendering is available} if (dataObj = nil) then raise Exception.Create('IDataObject-Pointer is not valid!'); with aFmtEtc do begin cfFormat := CF_TEXT; ptd := nil; dwAspect := DVASPECT_CONTENT; lindex := -1; tymed := TYMED_HGLOBAL; end; {Get the data} OleCheck(dataObj.GetData(aFmtEtc, aStgMed)); try {Lock the global memory handle to get a pointer to the data} pData := GlobalLock(aStgMed.hGlobal); { Replace Text } Memo1.Text := pData; finally {Finished with the pointer} GlobalUnlock(aStgMed.hGlobal); {Free the memory} ReleaseStgMedium(aStgMed); end; Result := S_OK; end; end.
. Le problème est que la réception doit se faire côté web et donc que c'est là que doit de régler le problème du lâcher...
Notez que ce genre de problème est plutôt délicat à résoudre : il suffit d'étudier la bibliothèque développée pour Delphi pour s'en rendre compte (encore une fois, celle proposée ne fonctionne que sous Windows).
Cordialement,
Gillesle 17/05/2017 à 19:25 -
thewolfMembre habituéBonjour.
Le cas de figure décrit est celui que j'écarte explicitement (au moins à court terme), car il n'existe pas de techniques multiplateformes simples pour réaliser ce que vous décrivez.
J'ai bien trouvé sur le wiki de free pascal l'unité que vous avez indiquée mais il s'agit malheureusement d'importer du texte vers l'application Lazarus et non de l'exporter ...
You can drag and drop e.g. text from another application (e.g.t notepad) to a control on your application. The way this is implemented is platform-dependent.
Windows
This code lets you drag and drop selected text from other applications onto an edit control.
TForm1 = class(TForm, IDropTarget)
Le problème est que la réception doit se faire côté web et donc que c'est là que doit de régler le problème du lâcher...
Comme exposé précédemment, je pensais naïvement que puisque Lazarus sait réagir (à) et traiter un glisser-déposer depuis l'explorateur de fichiers, il devrait être possible d'envoyer le même genre de signal dans l'autre sens. En fait, cela l'est (au moins pour Windows) puisque vous avez mentionné une bibliothèque développée pour Delphi. Je partage votre avis, cela semble bien compliqué ... Est-il envisagé de porter cette bibliothèque vers Lazarus ?
Cordialement.
Christian
PS : merci pour vos tutoriels très intéressantsle 18/05/2017 à 20:07