Site icon Tosolini.info

Electron API: Menu e MenuItem

Nel processo di costruzione di un applicativo con Electron prima o poi arriva il momento in cui bisogna decidere il menu applicativo che interagisce con la nostra App. Ad esempio i canonici Copia e Incolla, ma possiamo anche creare delle voci totalmente proprietarie che vanno a comandare l’interfaccia. Ad esempio un programma di note potremo avere “aggiungi Nota” oppure/anche “Aggiungi immagine alla Nota”.

La parte estetica del Menu chiaramente è contestuale al sistema operativo, ad esempio macOS e molte applicazioni Linux mostrano le voci non sotto il titolo della finestra attiva, ma direttamente nella barra dei Menu (quella in alto sullo schermo che contiene le icone Tray). Inoltre macOS ha delle voci aggiuntive che gli altri non hanno, e sono definite in ruoli speciali, ovvero delle definizioni di default, come appunto il già citato copia, incolla eccetera.

Tecnicamente le istruzioni del Menu vanno inserite nel main.js (o comunque il file principale) perché chiaramente sono esterne al render della pagina, tuttavia spesso l’articolazione dei menu e il fatto che una volta scelto non si andrà a modificarlo diventa una scocciatura vedere il codice a schermo, quindi si opta per inserire il codice in un file dedicato da caricare attraverso un gancio al main.js. Per questo ci viene in aiuto il metodo Menu.buildFromTemplate. Vediamo un esempio:

//carichiamo i moduli tra cui Menu e MenuItem
const {app, BrowserWindow, Menu, Menuitem} = electron;

//impostiamo il gancio per il file esterno
let menuPrincipale = Menu.buildFromTemplae( require.('./menuPrincipale.js') );

Come vediamo dal codice a questo punto potremo creare un file che si chiamerà menuPrincipale.js e che metteremo nella root della cartella, anche se molto probabilmente per “eleganza” lo metteremo in sottocartelle apposite. Ad ogni modo vediamo come strutturare il menu.

In questo caso avendo esportato in un file esterno al main.js dovremo utilizzare la funzione module.exports che ci permette di fruire il contenuto esportato nel file principale. Il file chiaramente deve essere un array con le voci specifiche di Menu e MenuItem. Sostanzialmente è necessaria una Label, ovvero la voce che appare nel menu, una submenu nel caso si voglia strutturare un menu a tendina sottostante, e la Label di cui sopra sia solo la voce per espandere la tendina, ed infine il role, nel caso di azioni predefinite, oppure click nel caso di azioni proprietarie. Esiste anche la voce separator che non è altro che una linea di separazione tra le voci dei menu, che serve per dare un raggruppamento visivo agli stessi.

Vediamo un esempio:

module.exports = [
 {
  label: 'miaApplicazione',
  submenu: [
   {role: 'copy'},
   {role: 'paste'},
   {type: 'separator'},
   {role: 'quit'}
  ]
 },
 {
  label: 'azioni dedicate',
  submenu: [
   {
     label: 'apri sito di Electron',
     click () { require('electron').shell.openExternal('https://electronjs.org') }
   }
  ]
 }
]

La struttura è piuttosto comprensibile. Come per tutti gli array in Javascript, occorre fare attenzione all’utilizzo delle virgole di concatenazione tra le voci, lasciando l’ultima sprovvista per dichiararne la fine.

Tuttavia la prima label può porre delle incomprensioni su macOS perché generalmente la prima voce riporta il nome dell’applicativo. Se il mio programma si chiama pippo e la label pluto, potrebbe apparire sempre pippo (ovvero electron nella pratica), questo generalmente fino alla creazione dei file compilati dove poi il comportamento ritorna ad essere quello atteso. Per ovviare al problema, ma anche per poter usufruire delle role specifiche citate in precedenza solitamente si opta per l’inserimento di una porzione di codice dedicato a macOS tramite il metodo process.platform:

if (process.platform === 'darwin') {
    template.unshift({
      label: app.getName(),
      submenu: [
        {role: 'about'},
        {type: 'separator'},
        {role: 'services', submenu: []},
        {type: 'separator'},
        {role: 'hide'},
        {role: 'hideothers'},
        {role: 'unhide'},
        {type: 'separator'},
        {role: 'quit'}
      ]
    })
addUpdateMenuItems(template[0].submenu, 1)
}

Si tratta di un semplice ciclo if, da notare invece la prima label che fa richiesta di app.getName che per l’appunto restituisce il nome dell’app dichiarato nel file package.json.

Siccome si tratta di un array è possibile anche “iniettare” delle parti di Template Menu a posteriori, o come sopra in un ciclo condizionale, oppure sostituire in blocco un intero template. Vediamo un esempio in cui aggiungiamo delle voci al template ‘azioni dedicate’:

    // Aggiungo delle voci di menu
    template[1].submenu.push(
      {type: 'separator'},
      {role: 'redo'},
      {role: 'undo'}
    )

in questo caso ci viene in aiuto il metodo submenu.push. Da notare che il template[1] si riferisce alla seconda voce, visto che il conteggio in Javascript parte sempre da zero come primo numero.

Nel caso di una sostituzione in blocco il codice è leggermente differente:

    // Sostituisco il menu
    template[1].submenu = [
      {role: 'close'},
      {role: 'minimize'}
    ]
  }

Conclusioni.

Esistono molti altri metodi, tra cui mostrare delle voci non cliccabili, nascondere il menu in talune condizioni. Prima ho parlato circa la posizione del menu in macOS sulla MenuBar, ebbene con il comando setApplicationMenu è possibile forzarne la visualizzazione come in Windows, sotto la barra del titolo. Insomma i metodi coprono moltissime aree di funzionamento logico e difficilmente vi ritroverete con una situazione non prevista.

Documentazione di Menu e MenuItem su Electron.

Exit mobile version