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

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

Window Management API

تتيح لك واجهة برمجة التطبيقات 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.

رصد الميزات

للتحقّق مما إذا كانت واجهة برمجة التطبيقات 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، ذلك بالضبط: يتم تشغيله عند تعديل مجموعة النجوم على الشاشة. (يُرجى ملاحظة أنّ "الشاشات" هي جمع في اسم الحدث). وهذا يعني أنّه يتم تشغيل الحدث كلما تم توصيل شاشة جديدة أو شاشة حالية (بشكل فعلي أو افتراضي في حالة Sidecar) أو فصلها.

يُرجى العِلم أنّه عليك البحث عن تفاصيل الشاشة الجديدة بشكل غير متزامن، لأنّ حدث 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.

يمكنك تشغيل الإصدار التجريبي المضمّن أدناه، أو الاطّلاع على رمز المصدر على glitch.

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

لقد صمم فريق 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 في تحديد أولويات الميزات ويُظهر لموفّري المتصفّحات الآخرين مدى أهمية توفير هذه الميزات.

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

روابط مفيدة

الشكر والتقدير

تم تعديل مواصفات Window Management API من قِبل Victor Costan و Joshua Bell و Mike Wasserman. نفَّذ واجهة برمجة التطبيقات كلّ من مايك واسرمان و أدريان ووكر. تمت مراجعة هذه المقالة من قِبل جو ميديل وفرنسوا بافوي وكايسي باسكيز. نشكر Laura Torrent Puig على الصور.