Administración de ventanas

Una de las tareas encomendadas al Proceso Principal es la creación de instancias de Procesos de Randerizado o de ventanas. Ampliaríamos esta funcionalidad a la gestión de estos procesos a lo largo de su ciclo de vida. En este apartado veremos los distintos métodos que facilita la Api de ElectronJs al Proceso principal para gestionar los Procesos de Randerizado que crea



Creación de Ventanas

El módulo BrowserWindow permite gestionar ventanas dentro de una aplicación Electron. Aquí, es importante recordar, que tanto los Procesos de Randerizado como el propio Proceso Principal tienen acceso a las APIs de Node.js.

Para poder usarlo hay que integrarlo en aquel proceso en el que lo estemos usando



          const {remote} = require('electron')

          const {BrowserWindow} = remote

          /* otra alternativa para invocarlo desde un Proceso de Renderizado
          const { BrowserWindow } = require('electron').remote
          */
                  


Para usarlo en el proceso principal



          const electron=require ('electron')

          //Cargamos los módulos necesarios de la Api de electron, app, BrowserWindow y Menu
          /*
          const app=electron.app
          const BrowserWindow=electron.BrowserWindow
          const Menu=electron.Menu
          const shell =require('electron').shell

          */

          const { app, BrowserWindow, Menu ,menuItem, shell, dialog, ipcMain} = electron
                    
let win = new BrowserWindow({ propiedades })

Tanto las propiedades como los métodos de BrowserWindow están recogidas en la documentación de electron. En las propiedades encontramos características de las Ventanas como el tamaño, el color del fondo, si es transparente, el nivel de opacidad, si se puede cambiar su tamaño o moverse, ... Propiedades BrowserWindow

Una de las propiedades que no debemos incluir es nodeIntegration: true que habilita la integración de NodeJs en nuestra aplicación.

Después de definidas las propiedades de la nueva instancia de ventana, debemos indicarle qué página web es la que cargaremos en ella, win.loadFile('index.html'), y este html, junto con su Css y JavaScript,

Esta instancia de ventana, let win, la incluimos en una función function createWindow() para llamarla cuando la aplicación ElectronJs ha terminado de iniciarse. definirán nuestro primer Proceso de Randerizado. Más adelante veremos cómo se comunica con el Proceso Principal.

Después de crear la instancia de BrowserWindow, debemos indicarle a nuestra aplicación que la cargue una vez esté lista. Para ello, debemos haber cargado el módulo app y usar su método app.on('ready', createWindow). Más información sobre este módulo la encontramos en Módulo app



        //cargamos la Api de electron.js
        const electron=require ('electron')
        //invoca la api de electron y carga el módulo app y BrowserWindow
        const { app, BrowserWindow } = require('electron')

        // Crea la ventana del navegador.
        function createWindow () {
        let win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
        nodeIntegration: true
        }
        })

        // y carga el  index.html de la aplicación.
        win.loadFile('index.html')
        }

        //abre la venta correspondiente al proceso de Randerizado una vez está lista la app
        app.on('ready', createWindow)

              

Podemos habilitar el modo de desarrollador para nuestra aplicación añadiendo a nuestra instancia BrowserWindow win.webContents.openDevTools() Es importante comentar este línea de código cuando vayamos a distribuir nuestra aplicación.



El sistema operativo MacOs requiere ciertas configuraciones especiales para su correcto funcionamiento.Es común volver a crear una ventana en la aplicación cuando el icono del dock es clicado y no hay otras ventanas abiertas. Para ello añadimos el siguiente código después de app.on('ready', createWindow)


        app.on('activate', () => {

          if (win === null) {
            createWindow()
          }
        })
              


Consejos: Utiliza una ventana invisible para ejecutar tareas de fondo. Esto se realiza con la propiedad show:false.



Cierre de Ventanas

Es fundamental admninistrar correctamente el cierre de las ventanas, tanto de la que se crea como el cierre global de la aplicación. Para controlar qué hacer cuando se cierra una ventana usamos el evento closed de la instancia creada BrowserWindow e incluimos el siguiente código en la función createWindow() de nuestro Proceso de Randerizado. Con ello buscamos eliminar cualquier referencia al objeto.


        win.on('closed', () => {

          win = null

        })
            

Para que la aplicación salga, después de cerradas todas las ventanas es preciso usar el evento window-all-closed de la instancia de la aplicación creada con el método app Si no se subscribe a este evento y todas las ventanas están cerradas, el comportamiento por defecto es salir de la aplicación. Sin embargo, en MacOS es común para las aplicaciones y sus barras de menú que estén activas hasta que el usuario salga explicitamente con Cmd + Q. Para ello incluimos este evento con el siguiente código:



        app.on('window-all-closed', () => {

          if (process.platform !== 'darwin') {
            app.quit()
          }
        })
          


Después de añadidas las distintos eventos que nos permiten un manejo básico de las ventanas, nuestro código inicial para el Proceso Principal contenido en el archivo main.js quedaría de la siguiente forma


        //cargamos la Api de electron.js
        const electron=require ('electron')
        //Cargamos los módulos necesarios de la Api de electron, app, BrowserWindow y Menu
        const { app, BrowserWindow, Menu } = electron

        // Mantén una referencia global del objeto window, si no lo haces, la ventana
        // se cerrará automáticamente cuando el objeto JavaScript sea eliminado por el recolector de basura.
        let win

        function createWindow () {
          // Crea la ventana del navegador.
          win = new BrowserWindow({
            width: 1200,
            height: 800,
            webPreferences: {
              nodeIntegration: true
            }
          })

          // y carga el index.html de la aplicación.
        win.loadFile('./src/index2.html')
          /*
          Se ha encontrado esta forma de cargar los procesos de randerizado pero a nosotros no nos ha funcionado
          const path =require('path')
          const url =require('url')
          win.loadUrl(url.format({
            pathname:path.join(__dirname,'index.html'),
            protocol:'file',
            slashes:true
          }))
          */
          // Abre las herramientas de desarrollo (DevTools).
          win.webContents.openDevTools()

          // Emitido cuando la ventana es cerrada.
          win.on('closed', () => {
            // Elimina la referencia al objeto window, normalmente  guardarías las ventanas
            // en un vector si tu aplicación soporta múltiples ventanas, este es el momento
            // en el que deberías borrar el elemento correspondiente.
            win = null
          })

        // Este método será llamado cuando Electron haya terminado
        // la inicialización y esté listo para crear ventanas del navegador.
        // Algunas APIs pueden usarse sólo después de que este evento ocurra.
        app.on('ready', createWindow)

        app.on('activate', () => {
          // En macOS es común volver a crear una ventana en la aplicación cuando el
          // icono del dock es clicado y no hay otras ventanas abiertas.
          if (win === null) {
            createWindow()
          }
        })

        // Sal cuando todas las ventanas hayan sido cerradas.
        app.on('window-all-closed', () => {
          // En macOS es común para las aplicaciones y sus barras de menú
          // que estén activas hasta que el usuario salga explicitamente con Cmd + Q
          if (process.platform !== 'darwin') {
            app.quit()
          }
        })

          



Ejercicio

Os proponemos el siguiente ejercicio para ver si sois capaces de crear vuestra propia aplicación de Escritorio, saludar al mundo y añadirle alguna funcionalidad más de la Api de ElectronJs. Necesitamos mostrar si la web que estamos mostrando es un Proceso de Randerizado como debería de ser, así como el sistema operativo en el que está corriendo la aplicación.

Nota: debes usar el método process, válido para el Proceso Principal y los Procesos de Randerizado


La solución en nuestro en Github Tutorial Electron