Window Management API ile birkaç ekranı yönetme

Bağlı ekranlar hakkında bilgi edinme ve pencereleri bu ekranlara göre konumlandırma

Window Management API

Pencere Yönetimi API'si, makinenize bağlı ekranları listelemenize ve pencereleri belirli ekranlara yerleştirmenize olanak tanır.

Önerilen kullanım alanları

Bu API'yi kullanabilecek sitelere örnek olarak şunlar verilebilir:

  • Gimp gibi çok pencereli grafik düzenleyiciler, çeşitli düzenleme araçlarını doğru konumlandırılmış pencerelere yerleştirebilir.
  • Sanal ticaret masaları, piyasa trendlerini birden fazla pencerede gösterebilir. Bu pencerelerin herhangi biri tam ekran modunda görüntülenebilir.
  • Slayt gösterisi uygulamaları, konuşmacı notlarını dahili birincil ekranda, sunuyu ise harici bir projektörde gösterebilir.

Window Management API'yi kullanma

Sorun

Pencereleri kontrol etmek için denenmiş ve test edilmiş yaklaşım Window.open() maalesef ek ekranlardan haberdar değildir. Bu API'nin bazı yönleri (ör. windowFeatures DOMString parametresi) biraz eski gibi görünse de yıllar içinde bize iyi hizmet etti. Bir pencerenin konumunu belirtmek için koordinatları left ve top (veya sırasıyla screenX ve screenY) olarak, istenen boyutu ise width ve height (veya sırasıyla innerWidth ve innerHeight) olarak iletebilirsiniz. Örneğin, soldan 50 piksel ve üstten 50 piksel uzaklıkta 400×300 boyutlarında bir pencere açmak için kullanabileceğiniz kod şudur:

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

window.screen özelliğine bakarak geçerli ekran hakkında bilgi edinebilirsiniz. Bu özellik, Screen nesnesi döndürür. Bu, 13 inç MacBook Pro'mdaki çıkıştır:

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
*/

Teknoloji sektöründe çalışan çoğu kişi gibi ben de kendimi yeni çalışma şekline adapte etmek ve kişisel ev ofisimi kurmak zorunda kaldım. Benimki aşağıdaki fotoğraftaki gibi görünüyor (İlgileniyorsanız kurulumumla ilgili tüm ayrıntıları okuyabilirsiniz). MacBook'umun yanındaki iPad, Sidecar ile dizüstü bilgisayara bağlı. Bu sayede, gerektiğinde iPad'i hızlıca ikinci bir ekrana dönüştürebiliyorum.

İki sandalyenin üzerinde okul bankı. Okul sırasının üzerinde, bir dizüstü bilgisayarı destekleyen ayakkabı kutuları ve etrafında iki iPad var.
Çok ekranlı kurulum.

Daha büyük ekrandan yararlanmak istiyorsam yukarıdaki kod örneğindeki pop-up'ı ikinci ekrana yerleştirebilirim. Ben şu şekilde yapıyorum:

popup.moveTo(2500, 50);

İkinci ekranın boyutlarını bilmenin bir yolu olmadığından bu, kabaca bir tahmindir. window.screen cihazındaki bilgiler yalnızca yerleşik ekranı kapsar, iPad ekranını kapsamaz. Yerleşik ekranın bildirilen width değeri 1680 pikseldi. Bu nedenle, pencereyi iPad'e taşımak için 2500 piksele geçmek işe yarayabilir. Çünkü ben, iPad'in MacBook'umun sağ tarafında olduğunu biliyorum. Bunu genel olarak nasıl yapabilirim? Tahmin etmek yerine daha iyi bir yöntem var. Bu yöntem, Pencere Yönetimi API'sidir.

Özellik algılama

Window Management API'nin desteklenip desteklenmediğini kontrol etmek için şunu kullanın:

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

window-management izni

Pencere Yönetimi API'sini kullanmadan önce kullanıcıdan izin istemem gerekiyor. window-management izni, Permissions API ile şu şekilde sorgulanabilir:

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

Eski ve yeni izin adını içeren tarayıcılar kullanılırken izin isterken aşağıdaki örnekte olduğu gibi savunma amaçlı kod kullandığınızdan emin olun.

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;
});

Tarayıcı, yeni API'nin yöntemlerinden herhangi birini kullanma girişiminde izin istemini dinamik olarak göstermeyi seçebilir. Daha fazla bilgi edinmek için okumaya devam edin.

window.screen.isExtended özelliği

Cihazıma birden fazla ekranın bağlanıp bağlanmadığını öğrenmek için window.screen.isExtended özelliğine erişiyorum. true veya false döndürür. Kurulumum için true değerini döndürüyor.

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

getScreenDetails() yöntemi

Mevcut kurulumun çoklu ekran olduğunu öğrendiğime göre, Window.getScreenDetails() kullanarak ikinci ekran hakkında daha fazla bilgi edinebilirim. Bu işlevi çağırdığımda, site ekranımda pencere açıp yerleştirebilir mi diye soran bir izin istemi gösteriliyor. İşlev, ScreenDetailed nesnesiyle çözümlenen bir söz döndürür. Bağlı bir iPad'in bulunduğu MacBook Pro 13'ümde, bu, iki ScreenDetailed nesnesi içeren bir screens alanı içerir:

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
  }]
}
*/

Bağlı ekranlarla ilgili bilgiler screens dizisinde yer alır. iPad için left değerinin, yerleşik ekranın tam width'si olan 1680 ile başladığını unutmayın. Bu sayede, ekranların mantıksal olarak nasıl düzenlendiğini (yan yana, üst üste vb.) tam olarak belirleyebilirim. Ayrıca, her ekranın isInternal veya isPrimary olup olmadığını gösteren veriler de mevcuttur. Yerleşik ekranın birincil ekran olması gerekmediğini unutmayın.

currentScreen alanı, mevcut window.screen ile eşleşen canlı bir nesnedir. Nesne, ekranlar arası pencere yerleşimlerinde veya cihaz değişikliklerinde güncellenir.

screenschange etkinliği

Şu anda eksik olan tek şey, ekran kurulumumun değiştiğini algılamanın bir yoludur. Yeni bir etkinlik olan screenschange tam olarak bunu yapar: Ekran takımyıldızı her değiştirildiğinde tetiklenir. ("Ekranlar"ın etkinlik adında çoğul olduğuna dikkat edin.) Bu, yeni bir ekran veya mevcut bir ekran (Sidecar durumunda fiziksel ya da sanal olarak) takıldığında veya çıkarıldığında etkinliğin tetiklendiği anlamına gelir.

Yeni ekran ayrıntılarını eşzamansız olarak aramanız gerektiğini unutmayın. screenschange etkinliğinin kendisi bu verileri sağlamaz. Ekran ayrıntılarını aramak için önbelleğe alınmış bir Screens arayüzünden canlı nesneyi kullanın.

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;
  }
});

currentscreenchange etkinliği

Yalnızca mevcut ekrandaki değişikliklerle (yani canlı nesnenin değeriyle currentScreen) ilgileniyorsam currentscreenchange etkinliğini dinleyebilirim.

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

change etkinliği

Son olarak, yalnızca belirli bir ekrandaki değişikliklerle ilgileniyorsam o ekranın change etkinliğini dinleyebilirim.

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

Yeni tam ekran seçenekleri

Şimdiye kadar, requestFullScreen() adlı yöntemle öğelerin tam ekran modunda gösterilmesini isteyebiliyordunuz. Yöntem, FullscreenOptions değerini iletebileceğiniz bir options parametresi alır. Şu ana kadar tek özelliği navigationUI oldu. Window Management API, tam ekran görünümünün hangi ekranda başlatılacağını belirlemenize olanak tanıyan yeni bir screen özelliği ekler. Örneğin, birincil ekranı tam ekran yapmak istiyorsanız:

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

Window Management API'nin polyfill'i mümkün değildir ancak şeklini doldurarak yalnızca yeni API'ye karşı kod yazabilirsiniz:

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;
}

API'nin diğer yönleri (yani çeşitli ekran değişikliği etkinlikleri ve screen özelliğinin FullscreenOptions) desteklenmeyen tarayıcılar tarafından hiçbir zaman tetiklenmez veya sessizce yoksayılır.

Demo

Benim gibiyseniz çeşitli kripto para birimlerinin gelişimini yakından takip ediyorsunuzdur. (Aslında bu gezegeni çok sevdiğim için hiç de öyle düşünmüyorum ancak bu makale için öyle düşündüğümü varsayalım.) Sahip olduğum kripto para birimlerini takip etmek için bir web uygulaması geliştirdim. Bu uygulama, tek ekranlı ve rahat bir yatakta olduğum gibi tüm yaşam koşullarında piyasaları izlememe olanak tanıyor.

Yatağın ucunda, yazarın bacaklarının kısmen göründüğü büyük bir TV ekranı. Ekranda sahte bir kripto para birimi işlem masası gösteriliyor.
Rahatlayıp piyasaları izliyorum.

Kripto para piyasalarıyla ilgili bu durum, piyasaların herhangi bir zamanda hareketlenebileceği anlamına gelir. Böyle bir durumda, çok ekranlı kurulumun bulunduğu masama hızlıca geçebilirim. Herhangi bir para biriminin penceresini tıklayarak tam ekran görünümünde tüm ayrıntıları hızlıca görebiliyorum. Aşağıda, son YCY katliamı sırasında çekilmiş yeni bir fotoğrafım var. Bu durum beni tamamen hazırlıksız yakaladı ve ellerimle yüzümü kapatmama neden oldu.

Panikleyen yüzünü elleriyle kapatan yazar, sahte kripto para birimi alım satım masasına bakıyor.
YCY'deki kan banyosunu görünce panikledi.

Aşağıda yerleştirilmiş demoyu deneyebilir veya GitHub'daki kaynak kodunu inceleyebilirsiniz.

Güvenlik ve izinler

Chrome Ekibi, Window Management API'yi Güçlü Web Platformu Özelliklerine Erişimi Kontrol Etme'de tanımlanan temel ilkeleri (kullanıcı kontrolü, şeffaflık ve ergonomi dahil) kullanarak tasarladı ve uyguladı. Pencere Yönetimi API'si, bir cihaza bağlı ekranlarla ilgili yeni bilgiler sunarak kullanıcıların parmak izi yüzeyini artırır. Bu durum, özellikle cihazlarına sürekli olarak birden fazla ekran bağlayan kullanıcılar için geçerlidir. Bu gizlilik endişesini gidermek için, gösterilen ekran özellikleri yaygın yerleşim kullanım alanları için gereken minimum düzeyle sınırlıdır. Sitelerin çoklu ekran bilgisi alması ve pencereleri diğer ekranlara yerleştirmesi için kullanıcı izni gerekir. Chromium, ayrıntılı ekran etiketleri döndürürken tarayıcılar daha az açıklayıcı (hatta boş etiketler) döndürebilir.

Kullanıcı denetimi

Kullanıcı, kurulumunun görünürlüğü üzerinde tam denetime sahiptir. Kullanıcılar izin istemini kabul edebilir veya reddedebilir ve daha önce verilen bir izni tarayıcıdaki site bilgileri özelliği aracılığıyla iptal edebilir.

Kurumsal kontrol

Chrome Enterprise kullanıcıları, Atomic Policy Groups ayarlarının ilgili bölümünde belirtildiği gibi Pencere Yönetimi API'sinin çeşitli yönlerini kontrol edebilir.

Şeffaflık

Pencere Yönetimi API'sini kullanma izninin verilip verilmediği, tarayıcının site bilgilerinde gösterilir ve Permissions API aracılığıyla da sorgulanabilir.

İzin kalıcılığı

Tarayıcı, izinleri kalıcı olarak saklar. İzin, tarayıcının site bilgileri bölümünden iptal edilebilir.

Geri bildirim

Chrome Ekibi, Pencere Yönetimi API'si ile ilgili deneyimlerinizi öğrenmek istiyor.

API tasarımı hakkında bilgi verin.

API'nin beklentilerinizi karşılamayan bir yönü var mı? Yoksa fikrinizi uygulamak için eksik yöntemler veya özellikler mi var? Güvenlik modeliyle ilgili sorunuz veya yorumunuz mu var?

  • İlgili GitHub deposunda bir spesifikasyon sorunu bildirin veya düşüncelerinizi mevcut bir soruna ekleyin.

Uygulamayla ilgili sorun bildirme

Chrome'un uygulamasında bir hata mı buldunuz? Yoksa uygulama, spesifikasyondan farklı mı?

  • new.crbug.com adresinden hata bildirin. Mümkün olduğunca ayrıntılı bilgi vermeye, yeniden oluşturma için basit talimatlar eklemeye ve Bileşenler kutusuna Blink>Screen>MultiScreen girmeye dikkat edin.

API'ye desteğinizi gösterme

Pencere Yönetimi API'sini kullanmayı planlıyor musunuz? Herkese açık desteğiniz, Chrome ekibinin özelliklere öncelik vermesine yardımcı olur ve diğer tarayıcı satıcılarına bu özelliklerin desteklenmesinin ne kadar önemli olduğunu gösterir.

Faydalı bağlantılar

Teşekkür

Window Management API spesifikasyonu Victor Costan, Joshua Bell ve Mike Wasserman tarafından düzenlendi. API, Mike Wasserman ve Adrienne Walker tarafından uygulanmıştır. Bu makale Joe Medley, François Beaufort ve Kayce Basques tarafından incelenmiştir. Fotoğraflar için Laura Torrent Puig'e teşekkür ederiz.