Applet Java con SWT

Ya me he estado inclinando mucho con Ubuntu, pero ya regrese a Java, y como siempre intentando publicar cosas nuevas o que si existen, estas llegan a ser muy confusas o complicadas de entender, en esta ocasión les traigo hasta ustedes la utilización de SWT en applets Java.

SWT ha empezado a gustarme, ya que tiene una excelente integración con el escritorio ya se ha windows, linux, mac u otro, y aunque SWT recibe muchas criticas por ser denominado “incompleto”, quiero aplaudir al impresionante trabajo de los desarrolladores, ya que unificar todas las diferentes API de componentes visuales en una, es una tarea titánica…

Recientemente estuve intentando integrar SWT dentro de un applet y la verdad es que los ejemplos que se encuentran en internet son escasos y confusos además de no funcionar en Linux, y de los que funcionan en windows tienen problemas con manejo de cambio de Foco con TAB y la invocación del botón predeterminado.

Finalmente logre desarrollar una pequeña clase para utilizar SWT en applet

SWTApplet

Y se utiliza como:

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class MiSWTApplet extends SWTApplet {
	static {
		Display.setAppName("MiSWTApplet");
	}

	@Override
	protected void createGUI(Shell shell) {
		shell.setLayout(new RowLayout());
		new Button(shell, SWT.PUSH).setText("Botón 1");
		new Button(shell, SWT.PUSH).setText("Botón 2");
		new Text(shell, SWT.SINGLE | SWT.BORDER).setMessage("Texto...");
	}

	@Override
	protected void destroyGUI() {
	}
}


Realmente la que hace todo el trabajo es la clase SWTApplet:

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JApplet;

import org.eclipse.swt.SWT;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;

abstract public class SWTApplet extends JApplet {
	static {
		System.setProperty("sun.awt.xembedserver", "true");
	}

	private Display display;
	private Shell shell;
	private Canvas canvas;
	private Control focusControl;

	protected Display getDisplay() {
		return display;
	}

	abstract protected void createGUI(Shell shell);

	abstract protected void destroyGUI();

	private void buildSWTBase() {
		display = new Display();

		// activar soporte para TAB y SHIF-TAB
		display.addFilter(SWT.FocusIn, new Listener() {
			@Override
			public void handleEvent(Event event) {
				focusControl = (Control)event.widget;
			}
		});
		// -----------------------------------

		shell = SWT_AWT.new_Shell(display, canvas);
		createGUI(shell);
		shell.open();
		while (!shell.isDisposed())
			if (!display.readAndDispatch())
				display.sleep();
		destroyGUI();
		display.dispose();
	}

	@Override
	public void start() {
		// agregar contenedor Swing
		canvas = new Canvas();
		canvas.setFocusable(true);
		this.setLayout(new BorderLayout());
		this.add(canvas, BorderLayout.CENTER);
		// ------------------------

		// crear interface SWT
		new Thread() {
			@Override
			public void run() {
				buildSWTBase();
			};
		}.start();
		// -------------------

		// activar soporte para TAB y SHIF-TAB
		canvas.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				final KeyEvent _e = e;
				display.syncExec(new Runnable() {
				@Override
				public void run() {
					if (_e.getKeyCode() == KeyEvent.VK_TAB
							&& _e.getModifiers() == 0)
						focusControl.traverse(SWT.TRAVERSE_TAB_NEXT);
					else if (_e.getKeyCode() == KeyEvent.VK_TAB
							&& _e.getModifiers() == KeyEvent.SHIFT_MASK)
						focusControl.traverse(SWT.TRAVERSE_TAB_PREVIOUS);
					else if (_e.getKeyCode() == KeyEvent.VK_ENTER
							&& _e.getModifiers() == 0)
						if (shell.getDefaultButton() != null &&
								shell.getDefaultButton().isVisible())
							shell.getDefaultButton().notifyListeners(
								SWT.Selection, null);
				}
				});
			}
		});
		// -----------------------------------
	}
}

Como funciona eso: Bueno, primero para empezar por que no funciona con GTK. Se debe a que para los Unix es necesario utilizar XEmbed protocol para poder incorporar una interfaces GTK sobre un display X11 (que es java), así que solo es necesario activar el soporte para XEmbed.

static {
	System.setProperty("sun.awt.xembedserver", "true");
}

Segundo, una vez logrado visualizar el Shell SWT dentro de un Applet los acceso rápidos estándar como TAB (para establecer el foco en el siguiente componente) y Shif+TAB (para regresar el foco) no funcionan, ya que estos eventos son capturados por el objeto Canvas, para solucionarlo es necesario transferir estos eventos de teclado a la interface SWT en el applet.

canvas.addKeyListener(new KeyAdapter() {
	@Override
	public void keyPressed(KeyEvent e) {
		final KeyEvent _e = e;
		display.syncExec(new Runnable() {
		@Override
		public void run() {
			if (_e.getKeyCode() == KeyEvent.VK_TAB
					&& _e.getModifiers() == 0)
				focusControl.traverse(SWT.TRAVERSE_TAB_NEXT);
			else if (_e.getKeyCode() == KeyEvent.VK_TAB
					&& _e.getModifiers() == KeyEvent.SHIFT_MASK)
				focusControl.traverse(SWT.TRAVERSE_TAB_PREVIOUS);
			else if (_e.getKeyCode() == KeyEvent.VK_ENTER
					&& _e.getModifiers() == 0)
				if (shell.getDefaultButton() != null &&
						shell.getDefaultButton().isVisible())
					shell.getDefaultButton().notifyListeners(
						SWT.Selection, null);
		}
		});
	}
});

Esta clase es preliminar, pero es una muy buena base para comenzar a utilizar SWT en un applet.

Ahora para poder hacer el deploy del applet en Web es necesario cargar la librería SWT especifica para cada sistema operativo y arquitectura. Esto se hace utilizando Applet con JNLP.

El archivo HTML debe quedar:

<applet name="MiSWTApplet" title="MiSWTApplet" alt="MiSWTApplet"
		width="300" height="100"
		codebase="." code="mipaquete.MiSWTApplet">
	<param name="jnlp_href" value="miswtapplet.jnlp"/>
</applet>

Y el archivo JNLP (miswtapplet.jnlp) debe quedar:

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="6.0+" codebase="." href="miswtapplet.jnlp">
	<information>
		<title>MiSWTApplet</title>
		<vendor>Dan Jared</vendor>
	</information>
	<security>
		<all-permissions/>
	</security>
	<resources>
		<j2se version="1.6+"/>
		<jar href="miswtapplet.jar" main="true"/>
		<jar href="alguna-dependencia.jar"/>
	</resources>
	<resources os="Windows" arch="x86">
		<jar href="swt-win32-windows-x86.jar" />
	</resources>
	<resources os="Windows" arch="x86_64">
		<jar href="swt-win32-windows-x86_64.jar" />
	</resources>
	<resources os="Windows" arch="amd64">
		<jar href="swt-win32-windows-x86_64.jar" />
	</resources>
	<resources os="Linux" arch="ppc">
		<jar href="swt-gtk-linux-ppc.jar" />
	</resources>
	<resources os="Linux" arch="x86_64">
		<jar href="swt-gtk-linux-x86_64.jar" />
	</resources>
	<resources os="Linux" arch="amd64">
		<jar href="swt-gtk-linux-x86_64.jar" />
	</resources>
	<resources os="Linux">
		<jar href="swt-gtk-linux-x86.jar" />
	</resources>
	<resources os="SunOS" arch="sparc">
		<jar href="swt-gtk-solaris-sparc.jar" />
	</resources>
	<resources os="SunOS" arch="x86">
		<jar href="swt-gtk-solaris-x86.jar" />
	</resources>
	<applet-desc
		name="MiSWTApplet"
		main-class="mipaquete.MiSWTApplet"
		width="300"
		height="100">
	</applet-desc>
</jnlp>

Otro punto muy importante es firmar los jar tanto los jars “miswtapplet.jar” así como los jar de SWT. Para esto ya es algo más común así que solo les dejare una liga de como hacer esto:

Este se me hizo muy bueno: Signing jars for java.net Web Start applications

Deja un comentario

Fill in your details below or click an icon to log in:

Logo de WordPress.com

You are commenting using your WordPress.com account. Log Out / Cambiar )

Twitter picture

You are commenting using your Twitter account. Log Out / Cambiar )

Facebook photo

You are commenting using your Facebook account. Log Out / Cambiar )

Connecting to %s