L'Xtra MUI (des boîtes de dialogue facilement ?) 

 

 

La création de boîtes de dialogue a toujours été l'enfer du développeur Director. Nombreux sont ceux qui, sur Macintosh, préféraient faire appel aux ressources du système plutôt que d'utiliser les MIAW peu maniables et coûteuses en mémoire. Leur projet y perdait sa portabilité. Livrée avec la version 6 de Director, l'Xtra MUI prétend résoudre ce dilemme et faciliter enfin la création de dialogues élégants indépendants de la plate-forme utilisée. Un seul effort exigé mais de taille : la manipulation de listes de propriétés imbriquées. Suivez nous :

 

Créer une alerte simple

Mui autorise la création de nombreux type pré définis de dialogues : Alerte, Dialogue d'ouverture ou d'enregistrement de fichier, dialogue de saisie d'une URL. L'Xtra permet aussi la création pure et simple d'une boîte dont tous les paramètres sont définis par le développeur. Dans tous les cas, Une instance de MUI devra être créée et les paramètres nécessaires devront lui être passés sous forme de liste de propriétés. Un exemple ?

Nous voulons une boîte d'alerte bien spécifique dont nous devons définir l'aspect. Nous devons choisir les boutons et, parmi eux, celui qui sera le bouton par défaut, nous devons désigner l'icône, le message que l'alerte affichera, décider si la boîte sera déplaçable, enfin afficher un intitulé dans sa barre de titre. L'objet recevra ces indications sous la forme d'une liste (DetailDeLAlerte) où chaque propriété (#buttons, #default, #icon, #message, #movable, #title) se voit associée une valeur(#YesNo, TRUE, "bonjour",...).

set DetailDeLAlerte to [#buttons:#YesNo, #default: 2, #icon:#caution, #message:"êtes-vous sûr ?", #movable:true, #title:"Confirmation requise"]

Nous détaillerons plus tard les valeurs possibles pour chaque propriété. Voyons vite à quoi notre alerte ressemble et pour ce faire, invoquons l'Xtra et créons un enfant CRAPO.

set CRAPO to new(xtra "MUI")

Les méthodes de l'objet sont connues par la commande put mmessageList(xtra "MUI") saisie dans la fenêtre message. Celle dont nous avons besoin est d'utilisation simple : Nous passons en paramètre la liste DetailDeLAlerte

alert(CRAPO, DetailDeLAlerte)

résultat :

 

 

La boîte est déplaçable, le deuxième bouton est le bouton par défaut, l'icône affichée est familière aux utilisateurs. C'est presque beau ! Maintenant après le clic de l'utilisateur nous connaissons son choix avec autant de facilité :

put the result
-- 1

L'utilisateur a cliqué sur le premier bouton "Yes".

On n'oublie pas de détruire l'objet devenu inutile.

set CRAPO to 0

Quelles sont les possibilités de personnalisation offertes par la méthode alert ? Chaque propriétés peut, dans la liste de propriété qu'il faut transmettre, se voir associer différentes valeurs :

#buttons:

#Ok
#OkCancel
#AbortRetryIgnore    -- plantage quasi assuré sous MacOS
#YesNoCancel
#YesNo
#RetryCancel       -- plantage quasi assuré sous MacOS

#default:

0 -- aucun bouton par défaut
1 -- le premier bouton est le bouton par défaut
2 -- le deuxième bouton est le bouton par défaut
3 -- le troisième...

#icon:

0 -- aucune icône
#stop
#note
#caution
#question
#error

#message:

"une chaîne entre guillemets" Pour imposer un retour à la ligne, insérer ..." & RETURN & "...

#movable:

true
false

#title :

"un intitulé entre guillemets, qui sera affiché dans la barre de titre"

Une fois la liste des propriétés de la boîte de dialogue constituée, on peut aisément modifier la valeur affectée à l'une de ces propriétés :

set the message of DetailDeLAlerte to "nouveau message"
set the movable of DetailDeLAlerte to false
...

La même alerte peut donc resservir à un autre usage.

MUI permet aussi la création de boîtes de dialogue où de fenêtres plus personnelles mais la mise en œuvre de l'Xtra se révèle alors beaucoup plus ardue. Seuls les plus résolus suivront le Crapo jusque là :

 

Créer un dialogue personnalisé

Pour créer notre fenêtre personnalisée, il nous faut bien sûr commencer par créer une instance de MUI.

set CRAPO to new(xtra "MUI")

Les méthodes Run et Stop affiche et masque une fenêtre modale. Mais l'on ne peut les appeler avant d'avoir défini la dite fenêtre. Cette description devra être passée à l'objet sous la forme d'une liste de deux propriétés : #windowPropList et #windowItemList.

#windowPropList

Il s'agit d'une liste propriétés décrivant l'aspect général et extérieur de la fenêtre.

#windowItemList

C'est une liste linéaire de listes de propriétés. Chaque liste de propriétés reprenant pour le décrire le détail d'un élément du dialogue.

On a donc le shéma suivant :

 

 

et en Lingo :

set DescriptionDeLaFenetre to
[
           #windowPropList :
              [ liste des propriétés de la fenêtre],
          #windowItemList :
              [
          [liste des propriétés du premier élément de la fenêtre],
          [liste des propriétés du deuxième élément de la fenêtre],
          [liste des propriétés du troisième élément de la fenêtre],
          [liste des propriétés du troisième élément de la fenêtre],
          ...]
]

Nous voilà prévenu ! la manipulation de ces listes imbriquées risque fort de nous être difficile et nous avons tout intérêt à procéder par ordre. Nous commençons par définir les propriétés de la fenêtre dans une variable "ProprieteDeLaFenetre".

 

Définir la fenêtre

La fonction GetWindowPropList renvoie une liste pré définie de propriétés et de valeurs par défaut. Il est plus rapide de partir d'une liste pré définie obtenue par GetWindowPropList et de la modifier à notre convenance.

set ProprieteDeLaFenetre to GetWindowPropList(CRAPO)

On demande sans attendre de voir la liste ainsi créée.

put ProprieteDeLaFenetre
-- [#type: #normal, #name: "window", #callback: "nothing", #mode: #data, #xPosition: 100, #yPosition: 120, #width: 200, #height: 210, #modal: 1, #toolTips: 0, #closeBox: 1, #canZoom: 0]

Il n'entre pas dans notre but ici de détailler toutes ces propriétés. La propriété #mode ayant pour valeur #data, la boite sera dimensionnée, positionnée, et construite automatiquement en fonction de son contenu (avec #mode:#pixel nous devrions placer chaque objet avec précision, avec #dialogunit la boîte serait dimensionnée de façon à s'adapter au bureau). #toolTips est une fonction que notre version de MUI ne supporte pas. Il s'agirait dans les version future de permettre l'apparition d'une bulle d'aide au dessus des éléments de dialogue. La propriété #closeBox détermine sur PC si la boîte doit ou non présenter une case de fermeture. Sur Mac changer cette valeur n'a aucun effet. Nous n'avons besoin que de changer l'intitulé de la barre de titre. Nous souhaitons aussi des dimensions automatiquement calculées pour cette fenêtre :

set the name of ProprieteDeLaFenetre to "le CrapoWeb"

set the width of ProprieteDeLaFenetre to 0
set the height of ProprieteDeLaFenetre to 0
       -- ceci a pour effet de laisser le système
       -- d'exploitation dimensionner la fenêtre

 

Définir les éléments de la fenêtre

Chaque élément (contrôle, bouton, zone de texte...) de la fenêtre est défini par un liste de propriétés renseignant sur son type, ses attributs... Il y aura donc autant de listes de propriétés qu'il y aura d'éléments. On regroupera toutes ces listes de propriétés dans une liste linéaire "ProprietesDesElements"

set ProprietesDesElements to []

Nous devons maintenant produire une à une les listes de propriétés des éléments que nous souhaitons utiliser et les ajouter aussitôt à ProprietesDesElements.

Pour produire la liste des propriétés d'un élément quelconque nous partirons à chaque fois de la liste par défaut obtenue par la fonction GetItemPropList. GetItemPropList renvoie une liste de propriétés aux valeurs par défaut, que nous modifierons si besoin est.

set PremierElement to GetItemPropList(CRAPO)

on veut voir la liste créée par défaut :

put PremierElement
-- [#value: 0, #type: #checkBox, #attributes: [], #title: "title", #tip: "tip", #locH: 20, #locV: 24, #width: 200, #height: 210, #enabled: 1]

La propriété #type définit le type de l'élément. Voici quelques unes des valeurs que peut prendre cette propriétés :#bitmap, #checkBox, #radioButton, ,#PopupList , #IntegerSliderH, #editText ,...

#attributes requiert une liste (encore!) définissant plus précisément l'élément. Les attributs disponibles dépendent étroitement du type de l'élément. Pour un élément de type texte par exemple, la propriété #attributes peut se voir associée la liste suivante :

#attributes : [#textSize:#Normal, #textStyle:[ #bold, #italic]]

Parmi les attributs utilisables : #valueList : ["choix un", "choix deux"] , pour une menu; #bitmapStyle : [#bitmapIcon:#caution], pour un élément de type bitmap, #layoutStyle : [] enfin qui ne sert qu'en mode construction automatique (#data ou #pixel), pour forcer malgré tout, un aspect de l'élément. On notera que tous les attributs disponibles ne sont pas documentés par Macromedia. Certains attributs sont sans effet sur l'une ou l'autre plateforme ou sur les deux !

#title est une chaîne de caractère donnant l'intitulé de l'élément. Ex: un bouton., une case à cocher. Tous les types n'en font pas usage.

#loch, #locv, #width et #height seront modifiés sans effet si la fenêtre est dessinée automatiquement (c'est-à-dire si dans les propriétés de la fenêtre, voir plus haut, le mode est #data et non #pixel (ou #dialogUnit sous Windows). nous ignorerons ici #loch, #locv...

#enabled determine l'état actif (disponible) ou indisponible de l'élément

Le premier élément de la fenêtre (tout comme le dernier) a un statut particulier dans la construction de la fenêtre et nous ne devons nous préoccuper que de sa propriété #type :

set the type of PremierElement = #windowBegin

Enfin nous l'ajoutons à la liste ProprietesDesElements.

add ProprietesDesElements , PremierElement

----------------------------------------------------------------

On définit maintenant les propriétés du premier contrôle, deuxième élément , premier objet visible du dialogue. Nous choisissons de faire apparaître un curseur horizontal. Nous ne modifions de la liste des propriétés par défaut obtenue par GetItemPropList que celles qui nous intéressent:

set LeCurseur to GetItemPropList(CRAPO)
set the type of LeCurseur to #integerSliderH
set the attributes of LeCurseur to [#valueRange: [#min:0.0, #max:100.0, #jump:5.0, #acceleration:0.5], #sliderStyle:[#ticks]]

Les attributs disponibles d'un slider" sont nombreux. Certains comme #jump et #Acceleration ne sont pas documentés par Macromedia. On peut conjecturer qu'il s'agit d'options permettant d'utiliser un slider comme jauge d'attente. On ne les cite ici que pour mémoire. Leur présence n'aura aucun effet pour ce qui nous concerne. Il existe de même une propriété #increment que notre version de MUI ignore superbement. La propriété #sliderStyle est plus interessante pour nous, elle détermine le type d'affichage du curseur : avec ou sans graduation et/ou avec ou sans valeur numérique d'accompagnement. L'affichage le plus complet s'obtiendrait ainsi #sliderStyle:[#ticks,#value]. On ajoute aussitôt la liste de propriétés définissant le curseur à ProprietesDesElements,

add ProprietesDesElements, LeCurseur

----------------------------------------------------------------

Pour les troisième et quatrième éléments on souhaite classiquement deux boutons de validation et d'annulation. On les définit un à un de la même façon.

set BoutonOk to GetItemPropList(CRAPO)
set the type of BoutonOk = #pushButton
set the title of BoutonOk = "D'accord"
add ProprietesDesElements, BoutonOk

------------------------------------------------------------------

set BoutonAnnuler to GetItemPropList(crapo)
set the type of BoutonAnnuler = #pushButton
set the title of BoutonAnnuler = "Pas d'accord"
add ProprietesDesElements, BoutonAnnuler

------------------------------------------------------------------

Dernier élément, enfin, son type DOIT être windowEnd.

set DernierElement to GetItemPropList(crapo)
set the type of DernierElement = #windowEnd
add ProprietesDesElements, DernierElement

 

Afficher la fenêtre

On dispose désormais des deux listes requises : ProprieteDeLaFenetre, une liste de propriétés définissant la fenêtre elle-même et ProprietesDesElements, une liste linéaire de 5 listes de propriétés dont chacune définit un élément de la fenêtre. Nous sommes en mesure de passer à l'objet CRAPO ces listes en paramètre.

 set DescriptionDeLaFenetre to [#windowPropList : ProprieteDeLaFenetre,  #windowItemList :  ProprietesDesElements]

La méthode Initialize instruit l'objet CRAPO des caractéristiques de notre fenêtre.

 Initialize(CRAPO, DescriptionDeLaFenetre)

La méthode Run, affiche une fenêtre de type modale (Stop la supprime).

 Run(CRAPO)

Le résultat, il faut bien le dire et ce en dépit de tous nos efforts, n'est pas très réussi :

La touche Esc fait disparaître cette ébauche. Nous avons voulu aller vite et nous contenter des valeurs par défaut. peut-être notre fenêtre gagnerait-elle à être un peu repensée. Les propriétés sont toutes accessibles désormais et se prêtent à de multiples essais et tâtonnements ( nos modification sont en rouge ici). Nous pourrions entre autres changement créer un groupe horizontal rassemblant les deux boutons mais alors c'est la liste ProprietesDesElements tout entière qu'il nous faut modifier. Deux éléments, dont seul le type importe #groupHBegin et #groupHEnd, doivent être insérés dans notre liste.

set ProprietesDesElements to []

set PremierElement to GetItemPropList(CRAPO)
set the type of PremierElement = #windowBegin
add ProprietesDesElements , PremierElement

-------------------------------------------
---------------------Définition du curseur

set LeCurseur to GetItemPropList(CRAPO)
set the type of LeCurseur to #integerSliderH
-- on modifie la propriété #attributes
-- Les propriétés #width et #height sont sans utilité dès lors que
-- la fenêtre (voir ProprieteDeLaFenetre) est en mode #data
set the attributes of LeCurseur to [#valueRange: [#min:0.0, #max:300, #increment: 10], #sliderStyle:[#value], #LayoutStyle:[#centerH]]
-- on affecte une valeur par défaut au curseur
set the value of leCurseur to 10
add ProprietesDesElements, LeCurseur

-----------------------------------------------
----------------------- Création d'un groupe

set DebutDeGroupeHorizontal to GetItemPropList(CRAPO)
set the type of DebutDeGroupeHorizontal to #groupHBegin
add ProprietesDesElements, DebutDeGroupeHorizontal

----------------------------------------------------------
set BoutonAnnuler to GetItemPropList(CRAPO)
set the type of BoutonAnnuler = #pushButton
set the title of BoutonAnnuler = "Annuler"
add ProprietesDesElements, BoutonAnnuler

---------------------------------------------------------
set BoutonOk to GetItemPropList(CRAPO)
set the type of BoutonOk = #defaultPushButton
set the title of BoutonOk = "Redéfinir"
add ProprietesDesElements, BoutonOk

--------------------------------------------------
-------------------- Fin du groupe

set FinDeGroupeHorizontal to GetItemPropList(CRAPO)
set the type of FinDeGroupeHorizontal to #groupHEnd
add ProprietesDesElements, FinDeGroupeHorizontal

------------------------------------------------------------------
set DernierElement to GetItemPropList(CRAPO)
set the type of DernierElement = #windowEnd
add ProprietesDesElements, DernierElement

Les modifications faites, on en instruit CRAPO puis on affiche la fenêtre de nouveau.

 Initialize(CRAPO, DescriptionDeLaFenetre)
 Run(CRAPO)

En l'absence de gestionnaire d'événements attribué (propriété #callback de la liste ProprieteDeLaFenetre, voir plus bas), la touche Esc seule fait disparaître la boîte (sur Mac).

 

Gérer les événements utilisateurs

Comparée à la facilité de création d'une interface dans ResEdit, Les listes imbriquées de MUI n'ont que bien peu de chance de séduire le développeur Mac. Seule la portabilité des dialogues sur toute plate-forme comme aussi, on va le voir, la possibilité de récupérer des entrées utilisateurs complexes (choix dans une liste déroulante, saisie dans une zone de texte, action sur un curseur...) sans écrire une ligne de C justifient notre Xtra. Voyons ce qu'il en est de la gestion des événements utilisateurs. Reprenons notre script. Nous allons modifier une des propriétés de la fenêtre afin que TOTO soit le gestionnaire appelé par l'objet CRAPO lorsque la boîte de dialogue enregistre un événement.



on VASY
global CRAPO
if not objectP(CRAPO) then

set CRAPO to new(xtra "MUI")

set ProprieteDeLaFenetre to GetWindowPropList(CRAPO)
set the name of ProprieteDeLaFenetre to "le CrapoWeb"
set the callback of ProprieteDeLaFenetre to "TOTO"

set ProprietesDesElements to []

set PremierElement to GetItemPropList(CRAPO)
set the type of PremierElement = #windowBegin
add ProprietesDesElements , PremierElement
--------------------------------------------------------
set LeCurseur to GetItemPropList(CRAPO)
set the type of LeCurseur to ...
...
...
...
...
set the type of DernierElement = #windowEnd
add ProprietesDesElements, DernierElement

set DescriptionDeLaFenetre to [#windowPropList : ProprieteDeLaFenetre,  #windowItemList : ProprietesDesElements]
Initialize(CRAPO, DescriptionDeLaFenetre)
Run(CRAPO)

end if
end

Dès l'ouverture du dialogue et pendant l'utilisateur en manipule les contrôles, TOTO est incessamment appelé. Lorsque l'objet invoque le gestionnaire TOTO, il lui transmet trois arguments essentiels. Ces arguments décrivent respectivement le type d'événement ( ouverture de la fenêtre, déplacement d'un contrôle, clic...), Le numéro d'ordre de l'élément concerné par l'événement ("void" si l'événement est l'ouverture de la fenêtre) enfin la liste des propriétés définissant cet élément (permettant ainsi de connaître les modifications subies). On peut utiliser chacun de ces arguments indifféremment afin de connaître les événements utilisateur et d'y réagir.

Les types d'événement sont retournés par des symboles. Ce peuvent-être : #windowOpening signalant l'ouverture de la boîte, #itemChanged, qui indique une modification quelconque, #itemClicked, #windowClosed, etc.

on TOTO QuelSymboledEvenement, QuelElementConcerne, ProprietesDelElementConcerne

case QuelSymboledEvenement of

#windowOpening :
beep
-- on salue ainsi le premier évènement utilisateur !

#itemChanged : -- un élément a été modifié, on ne sait lequel encore
if the type of ProprietesDelElementConcerne = #integerSliderH then
     -- on vérifie que l'évènement concerne
     -- notre curseur en sondant
     -- la liste de propriétés transmise
  set VALEURMODIFIEE to the value of ProprietesDelElementConcerne
     -- on enregistre le choix utilisateur dans
     -- la variable VALEURMODIFIEE
end if


#itemClicked :
     -- ici deux cas sont possibles,
     -- pour les distinguer on utilise indifférement
     -- l'un ou l'autre des arguments passés,
     -- QuelElementConcerne ou ProprietesDelElementConcerne.

if QuelElementConcerne = 4 then FermeLaFenetre
     -- le bouton annuler est le quatrième de la liste
     -- Le gestionnaire FermeLaFenetre
     -- executera les opérations
     -- de nettoyage (destruction de l'objet CRAPO)

if the title of ProprietesDelElementConcerne = "Redéfinir" then
     FermeLaFenetre
     Utilise VALEURMODIFIEE
     -- le gestionnaire utilise se charge de traiter l'entrée utilisateur
end if

otherwise :
nothing

end case

end


Plus encore que la portabilité du code (un telle chose existe-t-elle ?), C'est cette facilité avec laquelle on récupère les entrées utilisateurs complexes qui fait de l'Xtra MUI un outil intéressant. Pour quel autre raison sinon, voudrait-on se perdre dans ce dédale de listes imbriquées ?



LXtra MUI fait partie du package Director 6. Elle est chargée au lancement de l'application. La version de MUI livrée avec Director 6.0x est notoirement boguée. Macromedia soi-même note que les modes de création du dialogue #pixel et #dialogUnit y sont intervertis.

Pour une étude détaillée de la programmation mui, on se reportera aux quelques exemples rassemblés par le Crapo. L'Xtra EasyDialog permet la création de dialogue en WYSIWYG puis la génération automatique d'un script MUI dans la distribution Director. Un bon moyen d'étudier la programmation MUI.

Le saviez-vous ? un dialogue MUI ne se comporte pas tout à fait comme un dialogue système. On peut, au moins sur Mac, déformer ses contrôles lors que, le dialogue étant affiché, on clique sur l'un d'entre eux avec les touches Commande, Contrôle, Shift et Option enfoncées. Parfaitement inutile !