|
|||||||||||||||||||||||||||||||||||||||||||||||||||
|
1. Introduction L'interface utilisateur d'un programme moderne doit permettre, entre autres, de manipuler rapidement et facilement des fichiers de données. Imaginez un utilisateur qui doit fréquemment cliquer sur un système de menus, de barres d'outils et de requêtes pour pouvoir ouvrir un fichier : se sentira-t-il à l'aise avec le programme ? Ne serait-il pas plus rapide et plus simple si l'utilisateur pouvait simplement "déposer" le fichier dans la fenêtre, puis laisser le programme faire ce qu'il est censé faire avec le fichier ? L'environnement de bureau de l'Amiga, le Workbench, offre cette fonctionnalité depuis des décennies. Dans ce tutoriel, nous allons voir comment vous pouvez fournir et utiliser cette fonctionnalité pratique dans vos programmes basés sur ReAction. L'article donne également un aperçu de l'utilisation des crochets sous AmigaOS 4. 2. La terminologie Le sous-système chargé d'informer un programme qu'un fichier (ou plus précisément : son icône) a été déposé dans la fenêtre du programme est la bibliothèque Workbench. Cependant, les programmes ne reçoivent pas cette information par défaut : deux étapes sont nécessaires. Tout d'abord, la fenêtre du programme doit être configurée pour être informée des messages provenant du Workbench. Ensuite, le programme doit mettre en oeuvre un code pour sa routine de gestion des entrées qui identifie les messages du Workbench et réagit en conséquence. Une fenêtre qui peut recevoir des messages du Workbench est appelée "AppWindow" ; les messages eux-mêmes sont appelés "AppMessages" (d'après la structure AppMessage, une structure de données système qui contient les informations envoyées par le Workbench). Attention à un éventuel malentendu. La bibliothèque application.library d'AmigaOS 4 peut également envoyer des messages aux programmes en cours d'exécution. Bien que ces "messages d'application" soient fondamentalement différents des AppMessages du Workbench, la similitude de nom peut prêter à confusion (peut-être est-il temps de donner aux messages de l'application.library un nom sans ambiguïté). 3. Ai-je besoin d'AppWindows ? Cela dépend. En règle générale, si votre programme possède une interface graphique et implémente une fonction "Ouvrir", "Charger" ou "Importer" pour utiliser des fichiers de données, vous devez en faire une AppWindow et permettre à l'utilisateur de déposer des fichiers comme alternative aux fonctions de votre menu ou de votre barre d'outils. Les éditeurs de texte, les visionneurs/éditeurs d'images, les lecteurs de musique et de vidéo... doivent tous se comporter comme une AppWindow. La différence en termes de confort d'utilisation peut être énorme ! Comme exemple, je citerai le célèbre émulateur de Commodore 64, VICE. Dans la version actuelle d'AmigaOS 4, le processus de chargement d'un jeu est incroyablement compliqué. Pourtant, tous les tracas liés au chargement de fichiers images et à la saisie de commandes BASIC disparaîtraient si la fenêtre de l'émulateur était implémentée en tant qu'AppWindow (c'est ce que fait la version Windows de VICE, et elle est donc beaucoup plus agréable à utiliser). N'oubliez pas : le mot clé ici est "workflow", et non "workslow" ! 4. ReAction et AppMessages La classe de fenêtre ReAction permet non seulement de recevoir des AppMessages, mais aussi de créer très facilement des AppWindows. Transformer une fenêtre ReAction en AppWindow est en effet plus rapide que de faire la même chose avec une fenêtre Intuition classique. Votre code d'interface graphique n'a pas besoin d'appeler les fonctions de la bibliothèque Workbench, la classe les appelle en interne. Tout ce que vous avez à faire pour que votre fenêtre devienne une AppWindow est de :
Un port de message est un type de ressource système, donc l'AppPort pour votre fenêtre doit être alloué et initialisé par la fonction AllocSysObject() (et libéré en conséquence ; voir section 7) :
Pour que tout cela fonctionne, la définition de l'objet de votre fenêtre doit contenir les balises suivantes :
Il n'est pas nécessaire de fournir la balise "WINDOW_AppWindow" si vous n'avez besoin que d'une iconification, il suffit de fournir l'AppPort. 5. Gestion des AppMessages La clé du mécanisme de gestion des messages de ReAction est la méthode "WM_HANDLEINPUT", qui traite tous les événements d'entrée : sélections de gadgets, choix de menus, pressions de touches, etc. Le résultat de l'appel de la méthode vous informe de la plupart des événements reçus par la fenêtre. Je dis bien la plupart, mais pas tous. Les valeurs de résultat prédéfinies (voir "classes/window.h") ne couvrent que les types d'événements d'entrée les plus courants : si vous avez besoin d'autres événements, vous devez utiliser une extension appelée "hook" (crochet). Un exemple typique serait un événement IDCMP "non géré", tel que GADGETDOWN. La classe Window gère ces événements, mais elle les considère comme moins courants et moins susceptibles de se produire que d'autres types d'événements (tels que GADGETUP ou MENUPICK). Elle laisse donc au programmeur le soin d'installer un crochet IDCMP et d'y traiter tous les autres événements. Les AppMessages sont traités de la même manière. Le seul AppMessage que la méthode WM_HANDLEINPUT peut identifier par la valeur du résultat est "WMHI_UNICONIFY", c'est-à-dire que le Workbench demande à l'application de rouvrir sa fenêtre. Pour les autres AppMessages, vous devez fournir un crochet AppMessage et passer le pointeur du crochet à l'objet fenêtre via la balise "WINDOW_AppMsgHook". Vous remarquerez que dans une documentation plus ancienne, à savoir le "ROM Kernel Manual: Libraries", les AppMessages sont traités différemment dans la boucle d'entrée. La technique consistait à mettre en place un signal pour la fonction Wait() d'Exec, puis à traiter le message via GetMsg()/ReplyMsg(). Ce n'est pas la façon de procéder dans ReAction. L'utilisation de l'ancienne technique en combinaison avec la méthode WM_HANDLEINPUT reviendrait à mélanger les choses. Utilisez plutôt le crochet AppMessage. 6. Le crochet AppMessage Au cas où vous n'auriez jamais utilisé de crochet auparavant et que vous ne sauriez pas de quoi nous parlons : le crochet ("hook") est une pratique qui consiste à intercepter des appels de fonction, des messages ou des événements avec du code personnalisé. Ce code est appelé une fonction de crochet. Lorsque des conditions spécifiques sont remplies, la section du code du programme en cours d'exécution donne le contrôle à la fonction d'accroche et le code personnalisé est exécuté. Pour faire le lien entre la section de code interceptée et la fonction d'accroche, vous avez besoin d'une structure de données appelée "hook". Une fois en place, le crochet est chargé d'appeler la fonction de crochet et de lui fournir les données du code intercepté. L'API d'AmigaOS utilise les crochets de manière assez extensive : de nombreux composants du système permettent l'accroche ("hooking") afin d'étendre leur fonctionnalité de manière transparente et standardisée. La méthode WM_HANDLEINPUT de ReAction fonctionne également de cette manière : au lieu de vérifier tous les types d'événements possibles, elle en gère un nombre limité et vous permet d'installer un crochet si vous en avez besoin de plus. Pour utiliser un crochet, vous devez :
La fonction "hook" est une fonction personnalisée que vous écrivez afin de pouvoir gérer une situation spécifique résultant de l'interception du code principal (par exemple, le traitement d'un message). La fonction doit être conçue pour accepter trois arguments, dont l'ordre et le type sont donnés. Sur Amiga 68k, les arguments devaient être placés dans des registres spécifiques du processeur (A0, A2 et A1). Ce n'est plus le cas sous AmigaOS 4. L'utilisation des crochets est maintenant plus facile et plus transparente. Le premier argument est un pointeur sur "struct Hook", la structure de données du crochet (voir 6.2 ci-dessous). Le second est un pointeur générique vers un objet et dépend du contexte dans lequel la fonction "hook" a été appelée. Par exemple, si l'appel au crochet a été déclenché par un gadget ou une fenêtre ReAction, l'argument pointera sur l'instance de l'objet correspondant (les crochets AppMessage sont appelés par la classe Window, l'objet pointera donc vers l'objet "window"). Le troisième argument est un pointeur vers un message, dont le type dépend également du contexte. Par exemple, si nous fournissons un crochet IDCMP, le troisième argument pointera vers une "struct IntuiMessage" ; si nous fournissons un crochet AppMessage, l'argument pointera vers une "struct AppMessage". Tout ce qu'il faut pour traiter les AppMessages à l'intérieur de la fonction de crochet est de lire la structure du message, d'identifier le type de message et d'agir en conséquence. Dans l'extrait de code suivant, nous allons traiter un message AppWindow, censé notifier qu'une icône de fichier a été déposée dans notre fenêtre. Nous obtiendrons le nom du fichier et appellerons notre hypothétique routine de chargement :
6.2 La structure du crochet Maintenant que notre fonction de crochet est prête, nous devons préparer la structure de données du crochet. Sous AmigaOS 4, cette structure est considérée comme un objet ressource système et vous êtes donc supposé l'allouer en utilisant AllocSysObjectTags(). N'initialisez pas ses membres directement, comme le montre l'ancienne documentation. Au lieu de cela, déclarez un pointeur sur "struct Hook" et initialisez-le en lui passant les balises :
ASOHOOK_Entry pointe directement vers la fonction de crochet. La fonction peut recevoir des données arbitraires via la balise ASOHOOK_Data (nous n'en avons pas besoin pour notre crochet AppWindow, la valeur du pointeur est donc NULL ici). Ces données sont ensuite accessibles à l'intérieur de la fonction de crochet en utilisant le pointeur de crochet, que la fonction reçoit comme premier argument. Par exemple, si nous passons la chaîne de caractères "Hello world!" comme donnée de crochet (ASOHOOK_Data, "Hello world!", ...), vous pouvez afficher la chaîne de caractères à l'intérieur de la fonction de crochet comme ceci :
6.3 Installation du crochet Il ne reste plus qu'à installer le crochet "AppWindow" dans l'objet "ReAction window". Vous pouvez le faire au moment de la création de l'objet, en mettant "WINDOW_AppMsgHook, appMessageHook, ..." dans la liste des balises de définition de la fenêtre, mais vous pouvez aussi le faire plus tard, en utilisant SetAttrs() :
7. Élimination des ressources Outre la fenêtre ReAction que nous avons définie et utilisée comme AppWindow, nous devons nous débarrasser de deux autres ressources lorsque le programme est arrivé à son terme : le port d'application et le crochet. Il ne s'agit pas d'objets BOOPSI et la classe Window ne les libère donc pas automatiquement au moment de DisposeObject(). Après avoir disposé de l'objet "window", vous devez appeler FreeSysObject() pour libérer la mémoire allouée à l'AppPort et au crochet, respectivement :
8. Exemple de code Pour vous donner une meilleure idée de la manière dont les AppMessages sont gérés par la classe Window et traités dans le code, j'ai créé un exemple complet en C (voir ci-dessous). Le programme ouvre une fenêtre ReAction avec une zone d'affichage qui est, en fait, un gros gadget Button en lecture seule. La fenêtre est configurée comme une AppWindow bien sûr. Si vous déposez une icône de fichier dans la fenêtre, le gadget modifiera son texte et affichera le nom du fichier.
|