Gestione di diversi schermi con l'API Window Management

Consente di ottenere informazioni sui display collegati e di posizionare le finestre rispetto a questi schermi.

API Window Management

L'API Window Management ti consente di enumerare i display connessi alla tua macchina e di posizionare le finestre su schermate specifiche.

Casi d'uso suggeriti

Esempi di siti che possono utilizzare questa API includono:

  • Gli editor di grafica multi-finestra di Gimp possono posizionare vari strumenti di editing in finestre posizionate con precisione.
  • I trading desk virtuali possono mostrare le tendenze del mercato in più finestre, ciascuna delle quali può essere visualizzata in modalità a schermo intero.
  • Le app di slide possono mostrare le note del relatore sullo schermo principale interno e la presentazione su un proiettore esterno.

Come utilizzare l'API Window Management

Il problema

L'approccio collaudato al controllo delle finestre, Window.open(), purtroppo, non è a conoscenza di schermate aggiuntive. Sebbene alcuni aspetti di questa API sembrino un po' obsoleti, come il parametro windowFeatures DOMString, ci è stato comunque utile nel corso degli anni. Per specificare la posizione di una finestra, puoi passare le coordinate come left e top (o screenX e screenY rispettivamente) e passare la dimensione desiderata come width e height (o rispettivamente innerWidth e innerHeight). Ad esempio, per aprire una finestra 400 × 300 a 50 pixel da sinistra e 50 pixel dall'alto, puoi utilizzare questo codice:

const popup = window.open(
  'https://example.com/',
  'My Popup',
  'left=50,top=50,width=400,height=300',
);

Puoi ottenere informazioni sulla schermata corrente esaminando la proprietà window.screen, che restituisce un oggetto Screen. Questo è l'output sul mio MacBook Pro 13":

window.screen;
/* Output from my MacBook Pro 13″:
  availHeight: 969
  availLeft: 0
  availTop: 25
  availWidth: 1680
  colorDepth: 30
  height: 1050
  isExtended: true
  onchange: null
  orientation: ScreenOrientation {angle: 0, type: "landscape-primary", onchange: null}
  pixelDepth: 30
  width: 1680
*/

Come molte persone che lavorano nel settore tecnologico, ho dovuto adattarmi alla nuova realtà lavorativa e allestire il mio ufficio domestico personale. Il mio è simile alla foto qui sotto (se ti interessa, puoi leggere i dettagli completi sulla mia configurazione). L'iPad accanto al mio MacBook è collegato al laptop tramite Sidecar, quindi ogni volta che ne ho bisogno posso trasformare rapidamente l'iPad in un secondo schermo.

Panca della scuola su due sedie. In cima alla panca della scuola ci sono scatole di scarpe che supportano un laptop e due iPad intorno.
Una configurazione multischermo.

Per sfruttare lo schermo più grande, posso inserire sul secondo schermo il popup dell'esempio di codice riportato sopra. Procedi nel seguente modo:

popup.moveTo(2500, 50);

Questa è una supposizione approssimativa, dato che non c'è modo di conoscere le dimensioni del secondo schermo. Le informazioni di window.screen coprono solo lo schermo integrato, ma non quello dell'iPad. Il valore width dello schermo integrato indicato era 1680 pixel, quindi passando a 2500 pixel potrebbe essere necessario spostare la finestra sull'iPad, dato che I so che si trova sulla destra del mio MacBook. Come posso farlo nel caso generale? Ho scoperto che c'è un modo migliore che provare a indovinare. In questo modo è l'API Window Management.

Rilevamento delle funzionalità

Per verificare se l'API Window Management è supportata, utilizza:

if ('getScreenDetails' in window) {
  // The Window Management API is supported.
}

L'autorizzazione window-management

Prima di poter utilizzare l'API Window Management, devo chiedere all'utente l'autorizzazione in tal senso. È possibile eseguire query sull'autorizzazione window-management con l' API Permissions, ad esempio:

let granted = false;
try {
  const { state } = await navigator.permissions.query({ name: 'window-management' });
  granted = state === 'granted';
} catch {
  // Nothing.
}

Mentre sono in uso i browser con il nome precedente e quello nuovo, assicurati di utilizzare codice difensivo quando richiedi l'autorizzazione, come nell'esempio riportato di seguito.

async function getWindowManagementPermissionState() {
  let state;
  // The new permission name.
  try {
    ({ state } = await navigator.permissions.query({
      name: "window-management",
    }));
  } catch (err) {
    return `${err.name}: ${err.message}`;
  }
  return state;
}

document.querySelector("button").addEventListener("click", async () => {
  const state = await getWindowManagementPermissionState();
  document.querySelector("pre").textContent = state;
});

Il browser può scegliere di mostrare la richiesta di autorizzazione in modo dinamico al primo tentativo di utilizzare uno dei metodi della nuova API. Continua a leggere per saperne di più.

La proprietà window.screen.isExtended

Per scoprire se più schermi sono collegati al mio dispositivo, accedo alla proprietà window.screen.isExtended. Restituisce true o false. Per la mia configurazione, restituisce true.

window.screen.isExtended;
// Returns `true` or `false`.

Il metodo getScreenDetails()

Ora che so che la configurazione attuale è multischermo, posso ottenere ulteriori informazioni sul secondo schermo utilizzando Window.getScreenDetails(). Chiamando questa funzione verrà visualizzato un messaggio di autorizzazione che mi chiede se il sito può aprire e posizionare finestre sullo schermo. La funzione restituisce una promessa che si risolve con un oggetto ScreenDetailed. Sul mio MacBook Pro 13 con un iPad connesso, è incluso un campo screens con due oggetti ScreenDetailed:

await window.getScreenDetails();
/* Output from my MacBook Pro 13″ with the iPad attached:
{
  currentScreen: ScreenDetailed {left: 0, top: 0, isPrimary: true, isInternal: true, devicePixelRatio: 2, …}
  oncurrentscreenchange: null
  onscreenschange: null
  screens: [{
    // The MacBook Pro
    availHeight: 969
    availLeft: 0
    availTop: 25
    availWidth: 1680
    colorDepth: 30
    devicePixelRatio: 2
    height: 1050
    isExtended: true
    isInternal: true
    isPrimary: true
    label: "Built-in Retina Display"
    left: 0
    onchange: null
    orientation: ScreenOrientation {angle: 0, type: "landscape-primary", onchange: null}
    pixelDepth: 30
    top: 0
    width: 1680
  },
  {
    // The iPad
    availHeight: 999
    availLeft: 1680
    availTop: 25
    availWidth: 1366
    colorDepth: 24
    devicePixelRatio: 2
    height: 1024
    isExtended: true
    isInternal: false
    isPrimary: false
    label: "Sidecar Display (AirPlay)"
    left: 1680
    onchange: null
    orientation: ScreenOrientation {angle: 0, type: "landscape-primary", onchange: null}
    pixelDepth: 24
    top: 0
    width: 1366
  }]
}
*/

Le informazioni sugli schermi connessi sono disponibili nell'array screens. Nota come il valore di left per l'iPad parte da 1680, che corrisponde esattamente al valore width del display integrato. Questo mi consente di determinare esattamente in che modo gli schermi sono disposti in modo logico (uno accanto all'altro, uno sopra l'altro e così via). Ora sono disponibili anche dei dati per ogni schermata, che mostrano se è di isInternal e se è di isPrimary. Tieni presente che lo schermo integrato non è necessariamente la schermata principale.

Il campo currentScreen è un oggetto attivo corrispondente all'attuale window.screen. L'oggetto viene aggiornato in caso di posizionamenti di finestre su più schermi o modifiche ai dispositivi.

L'evento screenschange

L'unica cosa che manca ora è un modo per rilevare quando la configurazione dello schermo cambia. Un nuovo evento, screenschange, fa esattamente questo: si attiva ogni volta che la costellazione dello schermo viene modificata. (Nota che la parola "pages" è plurale nel nome dell'evento.) Ciò significa che l'evento si attiva ogni volta che uno schermo nuovo o esistente viene (fisicamente o virtualmente nel caso di Sidecar) collegato o scollegato.

Tieni presente che devi cercare i dettagli della nuova schermata in modo asincrono, l'evento screenschange stesso non fornisce questi dati. Per cercare i dettagli sullo schermo, utilizza l'oggetto attivo di un'interfaccia Screens memorizzata nella cache.

const screenDetails = await window.getScreenDetails();
let cachedScreensLength = screenDetails.screens.length;
screenDetails.addEventListener('screenschange', (event) => {
  if (screenDetails.screens.length !== cachedScreensLength) {
    console.log(
      `The screen count changed from ${cachedScreensLength} to ${screenDetails.screens.length}`,
    );
    cachedScreensLength = screenDetails.screens.length;
  }
});

L'evento currentscreenchange

Se mi interessano solo le modifiche alla schermata corrente (ovvero il valore dell'oggetto attivo currentScreen), posso ascoltare l'evento currentscreenchange.

const screenDetails = await window.getScreenDetails();
screenDetails.addEventListener('currentscreenchange', async (event) => {
  const details = screenDetails.currentScreen;
  console.log('The current screen has changed.', event, details);
});

L'evento change

Infine, se mi interessa solo apportare modifiche a una schermata concreta, posso ascoltare l'evento change di quella schermata.

const firstScreen = (await window.getScreenDetails())[0];
firstScreen.addEventListener('change', async (event) => {
  console.log('The first screen has changed.', event, firstScreen);
});

Nuove opzioni a schermo intero

Finora, potevi richiedere che gli elementi vengano visualizzati in modalità a schermo intero tramite il metodo requestFullScreen() con il nome appropriato. Il metodo richiede un parametro options che ti consente di trasmettere FullscreenOptions. Finora, la sua unica proprietà è stata navigationUI. L'API Window Management aggiunge una nuova proprietà screen che ti consente di stabilire su quale schermata avviare la visualizzazione a schermo intero. Ad esempio, se vuoi visualizzare la schermata principale a schermo intero:

try {
  const primaryScreen = (await getScreenDetails()).screens.filter((screen) => screen.isPrimary)[0];
  await document.body.requestFullscreen({ screen: primaryScreen });
} catch (err) {
  console.error(err.name, err.message);
}

Polyfill

Non è possibile eseguire il polyfill dell'API Window Management, ma puoi ridurne la forma in modo da programmare esclusivamente sulla base della nuova API:

if (!('getScreenDetails' in window)) {
  // Returning a one-element array with the current screen,
  // noting that there might be more.
  window.getScreenDetails = async () => [window.screen];
  // Set to `false`, noting that this might be a lie.
  window.screen.isExtended = false;
}

Gli altri aspetti dell'API, ovvero i vari eventi di modifica dello schermo e la proprietà screen di FullscreenOptions, non verranno mai attivati o ignorati, rispettivamente, dai browser che non supportano.

Demo

Se anche tu come me, tieni d'occhio lo sviluppo delle varie criptovalute. (In realtà non lo faccio perché amo questo pianeta ma, per amore di questo articolo, presumo di averlo fatto.) Per tenere traccia delle criptovalute che possiedo, ho sviluppato un'app web che mi permette di guardare i mercati in tutte le situazioni della vita, ad esempio stando comodamente a letto, dove posso avere una discreta configurazione con un solo schermo.

Enorme schermo televisivo in fondo a un letto con le gambe dell'autore parzialmente visibili. Sullo schermo un banco di compravendita di criptovalute falsi.
Relax e guardare i mercati.

Trattandosi di criptovalute, i mercati possono diventare frenetici in qualsiasi momento. In questo caso, posso passare rapidamente alla mia scrivania, dove ho configurato il multischermo. Posso fare clic sulla finestra di qualsiasi valuta e vedere tutti i dettagli in visualizzazione a schermo intero sullo schermo opposto. Ecco una foto recente di me scattata durante l'ultimo bagno di sangue di YCY. Mi ha colto completamente e mi ha lasciato con le mani sul viso.

L'autore con le mani sul viso in preda al panico fissa il finto trading desk di criptovalute.
Panicky, ad assistere al bagno di sangue di YCY.

Puoi giocare con la demo incorporata di seguito, oppure controllare il suo codice sorgente su glitch.

Sicurezza e autorizzazioni

Il team di Chrome ha progettato e implementato l'API Window Management utilizzando i principi fondamentali definiti nella sezione Controllare l'accesso alle funzionalità avanzate della piattaforma web, tra cui controllo degli utenti, trasparenza ed ergonomia. L'API Window Management mostra nuove informazioni sugli schermi connessi a un dispositivo, aumentando la superficie di fingerprinting degli utenti, in particolare quelli con più schermi collegati in modo coerente ai propri dispositivi. Per limitare questo problema di privacy, le proprietà delle schermate esposte sono limitate al minimo necessario per i casi d'uso di posizionamento più comuni. È necessaria l'autorizzazione dell'utente per consentire ai siti di ricevere informazioni multischermo e posizionare finestre su altri schermi. Anche se Chromium restituisce etichette dettagliate, i browser sono liberi di restituire etichette meno descrittive (o anche vuote).

Controllo utente

L'utente ha il pieno controllo dell'esposizione della propria configurazione. Possono accettare o rifiutare la richiesta di autorizzazione e revocare un'autorizzazione concessa in precedenza tramite la funzionalità Informazioni sul sito nel browser.

Controllo aziendale

Gli utenti di Chrome Enterprise possono controllare diversi aspetti dell'API Window Management come descritto nella sezione pertinente delle impostazioni dei gruppi di criteri atomici.

Trasparenza

Il fatto che sia stata concessa l'autorizzazione per l'utilizzo dell'API Window Management viene mostrato nelle informazioni del sito del browser ed è interrogabile anche tramite l'API Permissions.

Persistenza delle autorizzazioni

Il browser conserva le concessioni. L'autorizzazione può essere revocata tramite le informazioni sul sito del browser.

Feedback

Il team di Chrome vuole conoscere la tua esperienza con l'API Window Management.

Parlaci della progettazione dell'API

C'è qualcosa nell'API che non funziona come previsto? Oppure mancano metodi o proprietà di cui hai bisogno per implementare la tua idea? Hai domande o commenti sul modello di sicurezza?

  • Segnala un problema relativo alle specifiche nel repository GitHub corrispondente o aggiungi le tue opinioni su un problema esistente.

Segnala un problema con l'implementazione

Hai trovato un bug nell'implementazione di Chrome? Oppure l'implementazione è diversa dalle specifiche?

  • Segnala un bug all'indirizzo new.crbug.com. Assicurati di includere il maggior numero di dettagli possibile, semplici istruzioni per la riproduzione e inserisci Blink>Screen>MultiScreen nella casella Componenti. Glitch funziona benissimo per condividere riproduzioni rapide e semplici.

Mostra il supporto dell'API

Intendi utilizzare l'API Window Management? Il supporto pubblico aiuta il team di Chrome a dare la priorità alle funzionalità e mostra ad altri fornitori di browser quanto è fondamentale supportarle.

Link utili

Ringraziamenti

La specifica dell'API Window Management è stata modificata da Victor Costan, Joshua Bell e Mike Wasserman. L'API è stata implementata da Mike Wasserman e Adrienne Walker. Questo articolo è stato recensito da Joe Medley, François Beaufort e Kayce Basques. Grazie a Laura Torrent Puig per le foto.