إدارة عدة شاشات باستخدام Window Management API

يمكنك الحصول على معلومات عن الشاشات المتصلة وتحديد موضع النوافذ بالنسبة إلى تلك الشاشات.

واجهة برمجة التطبيقات Window Management

تُتيح لك واجهة برمجة التطبيقات Window Management API تعداد الشاشات المتصلة بجهازك ووضع النوافذ على شاشات محدّدة.

حالات الاستخدام المقترَحة

في ما يلي أمثلة على المواقع الإلكترونية التي قد تستخدم واجهة برمجة التطبيقات هذه:

  • بإمكان أدوات تحرير الرسومات ذات النوافذ المتعددة في Gimp وضع أدوات تعديل متنوعة في نوافذ دقيقة في مواضعها.
  • يمكن لمكاتب التداول الافتراضية عرض مؤشرات السوق في نوافذ متعددة يمكن عرض أي منها في وضع ملء الشاشة.
  • يمكن لتطبيقات عرض الشرائح عرض ملاحظات المحاضر على الشاشة الأساسية الداخلية والعرض التقديمي على جهاز عرض خارجي.

كيفية استخدام واجهة برمجة التطبيقات Window Management API

المشكلة

النظام الذي تم اختباره على مدار الوقت للتحكم في النوافذ، Window.open()، لا يعرف للأسف الشاشات الإضافية. مع أنّ بعض جوانب واجهة برمجة التطبيقات هذه تبدو قديمة بعض الشيء، مثل المَعلمة windowFeatures DOMString، إلا أنّها استفدت منها بشكل جيد على مرّ السنين. لتحديد موضع النافذة، يمكنك ضبط الإحداثيات على left وtop (أو screenX وscreenY على التوالي) وضبط الحجم المطلوب على شكل width وheight (أو innerWidth وinnerHeight على التوالي). على سبيل المثال، لفتح نافذة بحجم 400×300 على 50 بكسل من اليسار و50 بكسل من الأعلى، إليك التعليمة البرمجية التي يمكنك استخدامها:

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

يمكنك الحصول على معلومات حول الشاشة الحالية من خلال الاطّلاع على سمة window.screen التي تعرض كائن Screen. هذا هو الناتج على جهاز 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
*/

مثل معظم الأشخاص الذين يعملون في مجال التكنولوجيا، كان علي أن أتكيّف مع واقع العمل الجديد وإعداد مكتب منزلي شخصي. يظهر عنصري كما في الصورة أدناه (إذا كنت مهتمًا، يمكنك قراءة التفاصيل الكاملة حول الإعداد الخاص بي). جهاز iPad المجاور لجهاز MacBook متصلاً بالكمبيوتر المحمول عبر Sidecar، لذا يمكنني تحويل جهاز iPad بسرعة إلى شاشة ثانية كلما احتجت إلى ذلك.

مقعد مدرسي على كرسيين. فوق مقعد المدرسة توجد صناديق أحذية تدعم جهاز كمبيوتر محمول وجهازَي iPad يحيطان به.
إعداد عدة شاشات:

إذا أردت الاستفادة من الشاشة الأكبر، يمكنني وضع النافذة المنبثقة من نموذج الرمز البرمجي أعلاه على الشاشة الثانية. أنا أفعل ذلك هكذا:

popup.moveTo(2500, 50);

هذا تخمين تقريبي، لأنه لا توجد طريقة لمعرفة أبعاد الشاشة الثانية. تشمل المعلومات الواردة من "window.screen" الشاشة المدمجة فقط، ولا تشمل شاشة iPad. كانت نسبة width من الشاشة المدمجة التي تم الإبلاغ عنها 1680 بكسل، لذلك قد يساعد الانتقال إلى 2500 بكسل على نقل النافذة إلى جهاز iPad، لأنّني أعرف أنّ هذه الشاشة على يمين جهاز MacBook. كيف يمكنني القيام بذلك في الحالة العامة؟ اتضح أن هناك طريقة أفضل من التخمين. بهذه الطريقة هي واجهة برمجة التطبيقات لإدارة النوافذ.

رصد الميزات

للتحقّق من توفُّر واجهة برمجة التطبيقات Window Management API، استخدِم ما يلي:

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

إذن "window-management"

يجب أن أطلب من المستخدم الإذن لتنفيذ ذلك، قبل أن أتمكّن من استخدام واجهة برمجة التطبيقات Window Management API. يمكن طلب إذن window-management باستخدام Permissions API على النحو التالي:

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

في حين أنّ المتصفِّحات التي تحمل اسم الإذن القديم والجديد قيد الاستخدام، احرص على استخدام رمز دفاعي عند طلب الإذن، كما في المثال أدناه.

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

يمكن للمتصفِّح اختيار عرض الطلب بالأذونات بشكل ديناميكي عند المحاولة الأولى لاستخدام أي من طرق واجهة برمجة التطبيقات الجديدة. تابع القراءة لمعرفة المزيد.

السمة window.screen.isExtended

لمعرفة ما إذا كان هناك أكثر من شاشة متصلة بجهازي، أستخدم السمة window.screen.isExtended. وتعرض هذه القيمة true أو false. بالنسبة إلى الإعداد الذي أجريته، يعرض الدالة true.

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

طريقة getScreenDetails()

بعد أن عرفت أنّ الإعداد الحالي هو "الشاشة المتعددة"، يمكنني الحصول على مزيد من المعلومات حول الشاشة الثانية باستخدام Window.getScreenDetails(). سيؤدي استدعاء هذه الدالة إلى إظهار مطالبة الإذن التي يسألني عما إذا كان الموقع يمكنه فتح النوافذ ووضعها على شاشتي. تعرض الدالة وعدًا يتم حلّه باستخدام كائن ScreenDetailed. على جهاز MacBook Pro 13 المزوّد بجهاز iPad متّصل، يتضمّن ذلك حقل screens مع عنصرَي 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
  }]
}
*/

تتوفر معلومات حول الشاشات المتصلة في مصفوفة screens. يُرجى ملاحظة كيف تبدأ قيمة left لجهاز iPad عند 1680، وهو بالضبط width للشاشة المُدمَجة. يسمح لي هذا بتحديد كيفية ترتيب الشاشات بالضبط بشكل منطقي (بجانب بعضها البعض، فوق بعضها البعض، إلخ.). تتوفّر الآن أيضًا بيانات لكل شاشة لتوضيح ما إذا كانت شاشة isInternal وما إذا كانت شاشة isPrimary. يُرجى ملاحظة أن الشاشة المضمّنة ليست بالضرورة الشاشة الأساسية.

الحقل currentScreen هو كائن مباشر يتوافق مع window.screen الحالي. ويتم تحديث الكائن في مواضع النوافذ عبر الشاشات أو عند تغيير الأجهزة.

حدث "screenschange"

الشيء الوحيد المفقود الآن هو طريقة اكتشاف متى تغير إعداد الشاشة. ويؤدّي الحدث الجديد، "screenschange"، هذا الإجراء تمامًا، إذ يتم تنشيطه كلما تم تعديل مجموعة الصور البانورامية للشاشة. (لاحظ أن "الشاشات" هي صيغة جمع في اسم الحدث). هذا يعني أنّ الحدث يبدأ كلّما تم توصيل شاشة جديدة أو شاشة حالية (فعليًا أو افتراضيًا في حال استخدام Si Decar) أو فصله عن مصدر الطاقة.

ملاحظة: تحتاج إلى البحث عن تفاصيل الشاشة الجديدة بشكل غير متزامن، ولا يوفّر حدث screenschange نفسه هذه البيانات. للبحث عن تفاصيل الشاشة، يمكنك استخدام الكائن المباشر من واجهة Screens المخزَّنة مؤقتًا.

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"

إذا كنت مهتمًا فقط بالتغييرات على الشاشة الحالية (أي قيمة العنصر المباشر currentScreen)، يمكنني الاطّلاع على حدث currentscreenchange.

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

حدث "change"

وأخيرًا، إذا كنت مهتمًا فقط بإجراء تغييرات على شاشة ملموسة، يمكنني الاستماع إلى حدث change على تلك الشاشة.

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

خيارات جديدة بملء الشاشة

وحتى الآن، يمكنك طلب عرض العناصر في وضع ملء الشاشة باستخدام الطريقة المسماة بشكل مناسب requestFullScreen(). تستخدم الطريقة معلَمة options حيث يمكنك تمرير FullscreenOptions. حتى الآن، خاصية واحدة فقط هي navigationUI. تُضيف واجهة برمجة التطبيقات Window Management API سمة screen جديدة تتيح لك تحديد الشاشة التي تريد بدء العرض بملء الشاشة عليها. على سبيل المثال، إذا كنت تريد جعل الشاشة الأساسية بملء الشاشة:

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

الملء التلقائي

ليس من الممكن تعويض واجهة برمجة التطبيقات Window Management 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;
}

أمّا الجوانب الأخرى لواجهة برمجة التطبيقات، أي أحداث تغيير الشاشة المختلفة والسمة screen في FullscreenOptions، فلن يتم تنشيطها مطلقًا أو يتم تجاهلها بدون تجاهل على التوالي من خلال المتصفّحات غير المتوافقة.

عرض توضيحي

إذا كنت مثلي، تراقب عن كثب تطوّر العملات المشفّرة المختلفة. (في الواقع، لا أحب هذا الكوكب كثيرًا، ولكن أفترض أنني أحببت هذه الكوكب في هذه المقالة.) لتتبع العملات المشفّرة التي أملكها، طوّرت تطبيق ويب يتيح لي مشاهدة الأسواق في جميع مواقف الحياة، مثل الاسترخاء في سريري، حيث أعمل على إعداد شاشة واحدة مناسبة.

شاشة تلفزيون ضخمة في نهاية السرير تظهر جزئيًا ساقي المؤلف تظهر على الشاشة مكتب مزيّف لتداول العملات المشفّرة.
الاسترخاء ومشاهدة الأسواق المالية

بالنسبة إلى العملات المشفّرة، يمكن أن تكون الأسواق مشغولة في أي وقت. إذا حدث ذلك، يمكنني الانتقال بسرعة إلى مكتبي حيث لدي إعداد متعدد الشاشات. يمكنني النقر فوق نافذة أي عملة ومشاهدة التفاصيل الكاملة بسرعة في عرض ملء الشاشة على الشاشة المقابلة. في ما يلي صورة حديثة لي تم التقاطها خلال آخر مضخ دماء YCY. لقد فاجأتني تمامًا وأصبحت واضعًا يدي على وجهي.

المؤلف يديه على وجهه المذعور وينظران إلى مكتب تداول العملات المشفّرة الزائفة.
هل أنت مذعور ومشهد مذبحة YCY

يمكنك تشغيل العرض التوضيحي المضمّن أدناه، أو الاطّلاع على رمز المصدر الخاص به بسبب حدوث خلل.

الأمان والأذونات

لقد صمّم فريق Chrome واجهة برمجة التطبيقات Window Management API وطبّقها باستخدام المبادئ الأساسية المحدّدة في التحكّم في الوصول إلى الميزات الفعّالة لمنصة الويب، بما في ذلك التحكّم في المستخدم والشفافية وهندسة العمل. تعرض واجهة برمجة التطبيقات Window Management API معلومات جديدة حول الشاشات المتصلة بأحد الأجهزة، ما يزيد من سطح البصمات الرقمية للمستخدمين، خاصةً أولئك الذين تتضمن شاشات متعددة متصلة بأجهزتهم باستمرار. في سبيل الحد من هذه المشكلة المتعلقة بالخصوصية، تقتصر خصائص الشاشة المكشوفة على الحد الأدنى المطلوب لحالات الاستخدام الشائعة لمواضع الإعلانات. يجب الحصول على إذن المستخدم حتى تتمكن المواقع الإلكترونية من الحصول على معلومات الشاشات المتعددة ووضع النوافذ على شاشات أخرى. بينما يعرض Chromium تصنيفات الشاشة التفصيلية، تُعرض المتصفحات حرية عرض تصنيفات أقل وصفية (أو حتى تصنيفات فارغة).

تحكُّم المستخدم

ويتحكّم المستخدم بشكل كامل في مدى تعرّض المستخدم للتهديد في الإعداد. ويمكنهم قبول طلب الإذن أو رفضه أو إبطال الإذن الذي تم منحه مسبقًا عبر ميزة معلومات الموقع الإلكتروني في المتصفّح.

التحكُّم في المؤسسة

يمكن لمستخدمي Chrome Enterprise التحكّم في جوانب متعدّدة من واجهة برمجة التطبيقات Window Management API كما هو موضّح في القسم ذي الصلة من إعدادات مجموعات السياسات الصغيرة.

الشفافية

فحقيقة ما إذا كان إذن استخدام واجهة برمجة التطبيقات Window Management API قد تم منحها، في معلومات الموقع الإلكتروني للمتصفّح، ويمكن أيضًا طلبها من خلال Permissions API.

استمرارية الإذن

يواصل المتصفّح منح الأذونات. ويمكن إبطال الإذن من خلال معلومات موقع المتصفح.

إضافة ملاحظات

يريد فريق Chrome معرفة معلومات عن تجاربك في استخدام واجهة برمجة التطبيقات Window Management API.

أخبرنا عن تصميم واجهة برمجة التطبيقات

هل هناك مشكلة في واجهة برمجة التطبيقات لا تعمل كما توقعت؟ أو هل هناك طرق أو خصائص مفقودة تحتاج إلى تنفيذ فكرتك؟ هل لديك سؤال أو تعليق على نموذج الأمان؟

  • عليك الإبلاغ عن مشكلة في المواصفات على مستودع GitHub المقابل، أو إضافة أفكارك إلى مشكلة حالية.

الإبلاغ عن مشكلة في التنفيذ

هل واجهت خطأً في تنفيذ Chrome؟ أم أنّ التنفيذ مختلف عن المواصفات؟

  • أبلِغ عن الخطأ على new.crbug.com. واحرص على تضمين أكبر قدر ممكن من التفاصيل وتعليمات بسيطة لإعادة الإنتاج، وأدخِل Blink>Screen>MultiScreen في مربّع المكونات. تعمل ميزة Glitch بشكل رائع لمشاركة عمليات إعادة الإنشاء بسرعة وسهولة.

إظهار الدعم لواجهة برمجة التطبيقات

هل تخطّط لاستخدام واجهة برمجة التطبيقات Window Management API؟ يساعد الدعم المتاح للجميع فريق Chrome في تحديد أولويات الميزات ويُظهر لمورّدي المتصفِّح الآخرين مدى أهمية دعمهم لها.

  • شارِك كيف تنوي استخدامها في سلسلة محادثات WICG Discourse.
  • يمكنك إرسال تغريدة إلى @ChromiumDev باستخدام الهاشتاغ #WindowManagement وإعلامنا بمكان استخدامك لها وطريقة استخدامك لها.
  • طلب تنفيذ واجهة برمجة التطبيقات من مورِّدي المتصفِّح الآخرين

روابط مفيدة

شكر وتقدير

تم تعديل مواصفات واجهة برمجة التطبيقات Window Management API من قِبل Victor Costan وJoshua Bell وMike Wasserman. تم تنفيذ واجهة برمجة التطبيقات من قِبل Mike Wasserman وAdrienne Walker. راجعت هذه المقالة جو ميدلي وفرانسوا بوفورت وكايس باسك. شكرًا لورا تورنت بويغ على الصور.