samedi, octobre 06, 2007

Glasspane en SWT ...

S'il y a bien une chose assez pénible dans la toolkit SWT quand on vient du monde Swing, c'est l'absence de glasspane au niveau des fenêtres. Pour rappel le glasspane est une couche (layout) au niveau d'une fenêtre permettant de fournir une zone translucide. L'intérêt est de pouvoir dessiner ce que l'on souhaite et de combiner la partie translucide avec le contenu de la fenêtre de l'application. Concrètement, le glasspane permet d'ajouter des effets assez intéressants (voir démos de Romain Guy).

L'exemple proposé dans ce billet n'a pas la prétention d'émuler à l'identique le glasspane de Swing. Il s'agit d'une première ébauche ...

Le principe utilisé consiste à ajouter à la fenêtre principale de l'application deux sous fenêtres. Celles-ci utilisent la transparence via une fonction native de l'OS pour aboutir à un effet glasspane (Pour Win32 il s'agit de la fonction SetLayeredWindowAttributes). La première sous fenêtre appelée transparencyShell à un niveau de transparence complet. L'alpha est placé au minimum à 1, pas à 0, sinon la Shell serait invisible et donc il n'y aurait plus de réaction aux événements utilisateur mais assez pour ne plus la voir. La seconde fenêtre appelée glassPaneShell est aussi transparente mais uniquement pour une couleur donnée (couleur à choisir).

Plusieurs scénarios pour comprendre le fonctionnement
  • quand le glasspane est activé (les deux sous Shell sont visibles et la transparence est activée) et si l'interaction est effectuée sur une zone balayée par le couleur transparente, transparencyShell récupère alors l'événement et le route à glassPaneShell.
  • quand le glasspane est activé et si l'interaction est effectuée sur une zone non balayée par la couleur transparente, glassPaneShell récupère l'événement.
Par conséquent, transparencyShell n'est utile que pour bloquer les événements à la fenêtre principale et à les transférer à glassPaneShell.

La mécanique du glasspane en SWT est déléguée à la classe SWTGlassPane (création des sous Shell's, activation de la transparence, routage des événements, etc.).

Pour tester le tout, je me suis inspiré de l'exemple fourni par le tutorial de Swing au sujet du Glasspane.

Sans Glasspane
Avec Glasspane

L'aspect intéressant montré dans cet exemple est la possibilité de communiquer avec le contenu de la Shell principale. Si le Glasspane est activé, il n'est pas possible d'interagir directement avec l'IHM de la Shell principale. En effet, tous les événements sont routés à glassPaneShell. Par conséquent, du côté client il faut préciser les retours possibles sur l'IHM de la fenêtre principale.

Les problèmes à résoudre prochainement :
  • quand le Glasspane est activé et qu'une interaction via la sours se produit, le focus sur la fenêtre principale est perdu. Normal le focus passe sur une sous Shell.
  • le niveau d'opacité sur la Shell de glassPaneShell.
  • les deux sous fenêtres (transparencyShell et glasspaneShell) sont liées à la fenêtre applicative. Ainsi tout déplacement/redimensionnement de cette dernière implique le déplacement et le redimensionnement des deux autres. Pour l'instant, un problème de refresh subsiste.
  • ...
Voici le code source : ici

Si vous avez des commentaires ou des propositions d'amélioration, n'hésitez pas.

La démonstration Java Web Start de SWTTipOfTheDay

Keulkeul

Shell transparente via JNA

Dans un précédent billet, j'avais présenté JNA. Je vous propose ici un petit exemple permettant de rendre transparent une Shell SWT tout en utilisant JNA.

Capture d'écran : sans transparence

Capture d'écran : avec transparence


Il faut cependant passer par un appel natif. Pour simplifier les choses je me limiterai à la plateforme Windows. Sous WIN32, un appel aux fonctions suivantes est nécessaire :
  • int getWindowLong(int hWnd, int nIndex)
  • int setWindowLong(int hWnd, int nIndex, int dwNewLong)
  • boolean setLayeredWindowAttributes(int hWnd, int crKey, byte bAlpha, int dwFlags)
Par l'intermédiaire de JNA, l'appel aux fonctions natives se fait très facilement, adieu code C ... Ci-dessous vous trouverez l'interface qui contiendra les définitions des fonctions natives.
public interface User32 extends StdCallLibrary {
User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class, DEFAULT_OPTIONS);
int GWL_EXSTYLE = -20;
int WS_EX_LAYERED = 0x80000;

int LWA_COLORKEY = 1;
int LWA_ALPHA = 2;

int GetWindowLong(int hWnd, int nIndex);
int SetWindowLong(int hWnd, int nIndex, int dwNewLong);

boolean SetLayeredWindowAttributes(int hwnd, int crKey, byte bAlpha, int dwFlags);
}
La transparence est gérée par setLayeredWindowAttributes. hWnd correspond au handle window de la Shell, nIndex désigne la couleur transparente, bAlpha précise le niveau de transparence et enfin dwFlags permet d'indiquer le type de transparence : soit par une couleur, soit au niveau globale, soit un mixte des deux (utilisation d'un pipe).
myButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
User32 lib = User32.INSTANCE;
int flags = lib.GetWindowLong(myShell.handle, User32.GWL_EXSTYLE);
flags |= User32.WS_EX_LAYERED;
lib.SetWindowLong(myShell.handle, User32.GWL_EXSTYLE, flags);
Color myColor = Display.getDefault().getSystemColor(SWT.COLOR_DARK_BLUE);
lib.SetLayeredWindowAttributes(myShell.handle, myColor.handle, (byte)200, User32.LWA_COLORKEY | User32.LWA_ALPHA);
}
});
Dans cet exemple, je réalise à la fois une transparence de la fenêtre globale et une transparence de la couleur (DARK_BLUE).

Je ne vais pas m'attarder à expliquer le code, vous trouverez l'exemple ici.

A noter que je me suis basé sur le code fournit par le projet JNA.

Je profite de la discussion autour de JNA pour vous donner un lien sur un billet très intéressant à ce sujet (présentation JNI puis JNA) : blog Developpez.com