> Manuales > Manual de Electron

Configuración de aplicaciones desktop en Electron mediante la creación de manejadores de eventos del ciclo de vida y la ejecución de código personalizado dependiendo del sistema operativo del usuario.

Configuración básica del comportamiento de aplicaciones Electron

En el artículo anterior hemos conseguido crear y arrancar nuestra primera aplicación con Electron y ahora vamos a personalizar un poco más su funcionamiento, profundizando en el código NodeJS que teníamos en el archivo main.js.

Qué es app y qué es BrowserWindow

En el comienzo de este archivo main.js habíamos hecho el require de un par de artefactos:

Puedes apreciar que app es un objeto y BrowserWindow una clase por sus nombres y las convenciones que sigue la comunidad de Javascript y el propio Electron, de nombrar las clases con la primera en mayúscula.

Qué significa que la aplicación está "ready"

Una de las cosas que podemos hacer con el objeto app es enganchar código a ejecutar cuando la aplicación está lista para recibir acciones. Ese estado "ready" es muy importante porque si no esperamos a que la aplicación esté lista no seremos capaces de crear ventanas.

Muchos de los objetos que te ofrece Electron para desarrollar tus aplicaciones trabajan con la arquitectura dirigida por eventos de Node. Esta es una de las características de desarrollo de Javascript que explota la asincronía del lenguaje. El objeto app es uno de esos artefactos que trabaja con eventos.

Existen diversos métodos que te devuelven promesas a las que puedes enganchar código cuando éstas se resuelven. Este es el código para hacer cosas cuando la aplicación está lista (ready).

app.whenReady().then(() => {
  // Hacer cosas cuando la aplicación está lista
});

Típicamente en Node a los eventos se les colocan funciones callback o manejadores con el método on(), para ejecutar código cuando se producen, por lo que también podría funcionar un código como el siguiente:

app.on('ready', () => {
  // Hacer cosas...
});

Electron no obstante nos ofrece el método whenReady() para ser más expresivos y para que no corra el riesgo de equivocarnos al escribir el nombre del evento sobre el que queremos enganchar el código. Además whenReady() también nos permite escribir el código con el estilo de promesas, con el then(), que es más claro.

Más métodos del ciclo de vida básico de la aplicación

Vamos a avanzar un poco más en nuestros primeros pasos con el framework abordando nuevos métodos del app, el objeto con el que controlamos el ciclo de vida de la aplicación.

Primero debemos saber que, aunque Electron se encargue de manejar las diferentes plataformas por nosotros (Windows, Linux o Mac), sí que hay algunos eventos específicos que atañen a uno u otro sistema operativo en concreto.

Evento activate

El que vamos a conocer a continuación es exclusivo de MacOS y se llama "activate". Este evento permite hacer cosas en el momento en el que una aplicación se activa, lo que puede ocurrir ante diversas situaciones:

Podemos ejecutar código cuando se produce la activación de la ventana de esta manera:

app.on('activate', () => {
  console.log('activada!!');
});

Gracias a este mensaje en la consola cuando se produce la activación puedes reconocer las situaciones que pueden desencadenar este método "activate".

Recuerda que si lanzas un mensaje a la consola éste aparecerá en el terminal desde el que arrancaste la aplicación con "npm run start".

Evento window-all-closed

Ahora vamos a conocer otro método interesante, en este caso que atañe a todas las plataformas y no solo a MacOS. Se trata del evento 'window-all-closed' que ocurre cuando todas las ventanas de una aplicación han sido cerradas.

Puedes ejecutar código frente a este evento de la siguiente manera:

app.on('window-all-closed', () => {
  console.log('Todas las ventanas han sido cerradas');
});

Ahora, al cerrar la ventana de la aplicación recibirás un mensaje en la consola.

Gestionar comportamientos especiales para cada plataforma

Al dejar la aplicación escuchando eventos como los anteriores podrás observar que el comportamiento de la aplicación ha variado un poco.

En concreto, al cerrar las ventanas de la aplicación no se libera el proceso dentro de nuestro terminal, por lo que la aplicación en realidad permanece abierta.

Este comportamiento no es tan extraño en un sistema MacOS, ya que a menudo las aplicaciones funcionan justamente así. Sin embargo, en Windows o Linux no es un comportamiento esperado, por lo que lo correcto de cara al usuario sería terminar el proceso de la aplicación que se está ejecutando al cerrar todas las ventanas.

Si queremos que nuestra aplicación funcione de maneras distintas en cada plataforma, de modo que en Windows y Linux se detenga completamente y en Mac se quede en el Dock, podemos usar una propiedad de NodeJS llamada process.platform.

En Node process nos ofrece información sobre el proceso que está ejecutando el código y su propiedad platform puede tener tres valores distintos.

De modo que, si deseamos que al cerrar la aplicación se cierre completamente el proceso pero solamente para sistemas que no sean MacOS, podríamos usar este código.

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  } 
});

Simplemente nos quedamos escuchando el evento window-all-closed y cuando se detecta realizamos una comprobación para saber si no estamos en Mac. En caso de que sea así, ejecutamos el método app.quit() y la aplicación se cerrará.

Ahora en Windows y Linux el sistema funcionará tal como espera el usuario, que la aplicación se detenga completamente al cerrar la ventana. Sin embargo, en MacOS ahora se queda abierta, por ello veremos que en el Dock permanece el icono de la aplicación como si estuviera ejecutándose todavía.

El problema que nos encontramos ahora en MacOS es que, al hacer clic sobre el icono del Dock no ocurre nada. Esto es también normal, pues la ventana la había cerrado el usuario. Pero lo que el usuario también podría esperar es que la aplicación se recupere al pulsar de nuevo el icono y eso lo tenemos que programar.

Para ello creamos un manejador de evento para activate, en el que preguntamos si todas las ventanas de la aplicación están cerradas. Si fuera el caso, abrimos una nueva ventana.

app.on('activate', () => { 
  if (BrowserWindow.getAllWindows().length === 0) { 
    createWindow(); 
  }
});

No tenemos que hacer en este caso comprobaciones de plataforma porque el evento activate solamente aplica a MacOS.

Código completo de personalización de la aplicación

Veamos el código completo de nuestro ejemplo de aplicación, en el archivo main.js, para ver cómo se ha conseguido este comportamiento:

const { app, BrowserWindow } = require('electron')

const createWindow = () => {
  const win = new BrowserWindow({
    width: 400,
    height: 300,
  })
  win.loadFile('index.html')
}

app.whenReady().then(createWindow);

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  } 
});

Los cambios efectuados son los siguientes:

Eso es todo de momento en esta primera aplicación con Electron. De momento sigue siendo muy sencilla, pero hemos podido aprender nuevas utilidades sobre la aplicación y la gestión del flujo de su ciclo de vida.

Miguel Angel Alvarez

Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...

Manual