سیستم فوکوس، سیستم فوکوس، سیستم فوکوس

سیستم فوکوس موقعیت کاربر (فوکوس) را در ویرایشگر Blockly پیگیری می کند. توسط Blockly و کد سفارشی برای تعیین اینکه کدام مؤلفه (بلاک، فیلد، دسته جعبه ابزار و غیره) در حال حاضر فوکوس دارد و برای انتقال آن فوکوس به مؤلفه دیگر استفاده می شود.

درک سیستم فوکوس بسیار مهم است تا بتوانید اطمینان حاصل کنید که کد سفارشی شما به درستی با آن کار می کند.

معماری

سیستم فوکوس دارای سه بخش است:

  • FocusManager یک تکتنه است که فوکوس را در تمام Blockly هماهنگ می کند. توسط Blockly و کد سفارشی برای یافتن اینکه کدام مؤلفه فوکوس Blockly دارد و همچنین برای انتقال فوکوس Blockly به مؤلفه دیگری استفاده می شود. همچنین به رویدادهای فوکوس DOM گوش می‌دهد، فوکوس Blockly و فوکوس DOM را همگام‌سازی می‌کند، و کلاس‌های CSS را که نشان می‌دهند کدام مؤلفه فوکوس دارد، مدیریت می‌کند.

    مدیر تمرکز در درجه اول توسط Blockly استفاده می شود. گاهی اوقات توسط کد سفارشی برای تعامل با سیستم فوکوس استفاده می شود.

  • IFocusableTree یک ناحیه مستقل از یک ویرایشگر Blockly است، مانند یک فضای کاری یا جعبه ابزار. از گره های قابل تمرکز، مانند بلوک ها و فیلدها تشکیل شده است. درختان می توانند زیردرخت نیز داشته باشند. به عنوان مثال، یک فضای کاری mutator در یک بلوک در فضای کاری اصلی، زیردرختی از فضای کاری اصلی است.

    IFocusableTree در درجه اول توسط مدیر تمرکز استفاده می شود. مگر اینکه یک جعبه ابزار سفارشی بنویسید، احتمالاً نیازی به پیاده سازی آن نخواهید داشت.

  • IFocusableNode یک مؤلفه Blockly است که می‌تواند فوکوس داشته باشد، مانند دسته بلوک، فیلد یا جعبه ابزار. گره های قابل فوکوس دارای یک عنصر DOM هستند که گره را نمایش می دهد و زمانی که گره فوکوس بلوکی دارد، دارای فوکوس DOM است. توجه داشته باشید که درختان نیز گره های قابل تمرکز هستند. به عنوان مثال، می توانید روی فضای کاری به طور کلی تمرکز کنید.

    متدها در IFocusableNode در درجه اول توسط مدیر تمرکز فراخوانی می شوند.

    خود IFocusableNode برای نشان دادن مؤلفه ای که فوکوس دارد استفاده می شود. به عنوان مثال، هنگامی که یک کاربر یک مورد را در منوی زمینه بلوک انتخاب می کند، بلوک به عنوان یک IFocusableNode به تابع callback مورد ارسال می شود.

    اگر کامپوننت های سفارشی می نویسید، ممکن است نیاز به پیاده سازی IFocusableNode داشته باشید.

انواع تمرکز

سیستم فوکوس انواع مختلفی از فوکوس را تعریف می کند.

فوکوس بلوکی و فوکوس DOM

دو نوع اصلی فوکوس عبارتند از فوکوس بلوکی و فوکوس DOM.

  • Blockly Focus مشخص می‌کند که کدام جزء Blockly (بلوک، فیلد، دسته جعبه ابزار و غیره) دارای فوکوس باشد. برای کار در سطح اجزای Blockly ضروری است. به عنوان مثال، پلاگین پیمایش صفحه کلید به کاربران اجازه می دهد تا از کلیدهای جهت دار برای جابجایی از یک جزء به جزء دیگر مانند از یک بلوک به یک فیلد استفاده کنند. به طور مشابه، سیستم منوی زمینه، منویی را می‌سازد که برای مؤلفه فعلی مناسب است -- یعنی منوهای مختلفی را برای فضاهای کاری، بلوک‌ها و نظرات فضای کاری می‌سازد.

  • فوکوس DOM مشخص می کند که کدام عنصر DOM فوکوس داشته باشد. برای کار در سطح عناصر DOM ضروری است. برای مثال، صفحه‌خوان‌ها اطلاعاتی را درباره عنصری که در حال حاضر دارای فوکوس DOM است ارائه می‌کنند و برگه‌ها از عنصر DOM به عنصر DOM حرکت می‌کنند (تغییر فوکوس).

مدیر فوکوس فوکوس Blockly و فوکوس DOM را همگام نگه می دارد، بنابراین وقتی یک گره (مولفه Blockly) فوکوس Blockly دارد، عنصر DOM زیربنایی آن دارای فوکوس DOM است و بالعکس.

تمرکز فعال و غیرفعال

فوکوس بلوکی بیشتر به فوکوس فعال و فوکوس غیرفعال تقسیم می شود. تمرکز فعال به این معنی است که یک گره ورودی کاربر را دریافت می کند، مانند فشار دادن کلید. فوکوس غیرفعال به این معنی است که یک گره قبلاً دارای فوکوس فعال بوده است، اما زمانی که کاربر به یک گره در درخت دیگری منتقل می‌شود (مثلاً از فضای کاری به جعبه ابزار منتقل می‌شود) یا کلاً از ویرایشگر Blockly دور می‌شود، آن را از دست می‌دهد. اگر درخت فوکوس را دوباره به دست آورد، گره متمرکز غیرفعال فوکوس فعال را به دست خواهد آورد.

هر درخت یک زمینه تمرکز جداگانه دارد. یعنی حداکثر یک گره در درخت می تواند فوکوس داشته باشد. فعال یا غیرفعال بودن آن کانون به این بستگی دارد که درخت فوکوس داشته باشد. حداکثر می توان یک گره با تمرکز فعال در کل صفحه وجود داشته باشد.

مدیر فوکوس از نقاط برجسته مختلف (کلاس های CSS) برای گره های متمرکز فعال و غیرفعال استفاده می کند. اینها به کاربران امکان می دهد بفهمند کجا هستند و به کجا باز خواهند گشت.

فوکوس زودگذر

نوع دیگری از فوکوس به نام فوکوس زودگذر وجود دارد. گردش‌های کاری جداگانه، مانند گفتگوها یا ویرایشگرهای فیلد، فوکوس زودگذر را از مدیر تمرکز درخواست می‌کنند. هنگامی که مدیر تمرکز فوکوس زودگذر را اعطا می کند، سیستم فوکوس را به حالت تعلیق در می آورد. از نقطه نظر عملی، این بدان معناست که چنین گردش‌کاری‌ها می‌توانند رویدادهای تمرکز DOM را ضبط کرده و روی آن‌ها عمل کنند، بدون اینکه نگران باشند که سیستم فوکوس ممکن است روی آنها نیز عمل کند.

وقتی مدیر فوکوس فوکوس زودگذر را اعطا می کند، گره متمرکز فعال را به فوکوس غیرفعال تغییر می دهد. هنگامی که فوکوس زودگذر برمی‌گردد، فوکوس فعال را بازیابی می‌کند.

نمونه ها

مثال‌های زیر نحوه استفاده Blockly از سیستم فوکوس را نشان می‌دهند. آنها باید به شما کمک کنند تا بفهمید کد شما چگونه در سیستم فوکوس قرار می گیرد و چگونه ممکن است کد شما از سیستم فوکوس استفاده کند.

حرکت فوکوس با صفحه کلید

فرض کنید یک بلوک با دو فیلد دارای فوکوس Blockly است، همانطور که با برجسته (کلاس CSS) در عنصر DOM بلوک نشان داده شده است. حال فرض کنید کاربر فلش سمت راست را فشار داده است:

  1. پلاگین ناوبری صفحه کلید :
    • یک رویداد فشار کلید را دریافت می کند.
    • از سیستم ناوبری (بخشی از هسته Blockly) می خواهد تا فوکوس را به مؤلفه «بعدی» منتقل کند.
  2. سیستم ناوبری:
    • از مدیر تمرکز می پرسد که کدام مؤلفه دارای فوکوس Blockly است. مدیر تمرکز بلوک را به عنوان IFocusableNode برمی گرداند.
    • تعیین می کند که IFocusableNode یک BlockSvg است و به قوانین آن برای مسیریابی بلوک ها نگاه می کند، که بیان می کند باید فوکوس Blockly را از کل بلوک به اولین فیلد روی بلوک منتقل کند.
    • به مدیر فوکوس می‌گوید فوکوس Blockly را به فیلد اول منتقل کند.
  3. مدیر تمرکز:
    • وضعیت خود را به‌روزرسانی می‌کند تا فوکوس Blockly را در فیلد اول تنظیم کند.
    • تمرکز DOM را روی عنصر DOM فیلد تنظیم می کند.
    • کلاس برجسته را از عنصر بلوک به عنصر فیلد منتقل می کند.

حرکت فوکوس با ماوس

حال فرض کنید کاربر روی فیلد دوم بلوک کلیک کند. مدیر تمرکز:

  1. یک رویداد focusout DOM روی عنصر DOM فیلد اول و یک رویداد focusin روی عنصر DOM فیلد دوم را دریافت می کند.
  2. تعیین می کند که عنصر DOM که فوکوس دریافت کرده است با فیلد دوم مطابقت دارد.
  3. وضعیت خود را به‌روزرسانی می‌کند تا فوکوس Blockly را در فیلد دوم تنظیم کند. (مدیر تمرکز نیازی به تنظیم فوکوس DOM ندارد زیرا مرورگر قبلاً این کار را انجام داده است.)
  4. کلاس برجسته را از عنصر فیلد اول به عنصر فیلد دوم منتقل می کند.

نمونه های دیگر

در اینجا چند نمونه دیگر وجود دارد:

  • هنگامی که یک کاربر بلوکی را از جعبه ابزار به فضای کاری می کشد، کنترل کننده رویداد ماوس یک بلوک جدید ایجاد می کند و مدیر تمرکز را فرا می خواند تا فوکوس Blockly را روی آن بلوک تنظیم کند.

  • هنگامی که یک بلوک حذف می شود، روش dispose آن، مدیر فوکوس را فرا می خواند تا فوکوس را به والد بلوک منتقل کند.

  • میانبرهای صفحه کلید از IFocusableNode برای شناسایی مؤلفه Blockly که میانبر برای آن اعمال می شود، استفاده می کند.

  • منوهای زمینه از IFocusableNode برای شناسایی مؤلفه Blockly که منو در آن فراخوانی شده است استفاده می کنند.

سفارشی سازی ها و سیستم تمرکز

هنگامی که Blockly را سفارشی می کنید، باید مطمئن شوید که کد شما به درستی با سیستم فوکوس کار می کند. همچنین می توانید از سیستم فوکوس برای شناسایی و تنظیم گره متمرکز فعلی استفاده کنید.

بلوک های سفارشی و محتویات جعبه ابزار

رایج ترین راه برای سفارشی کردن Blockly، تعریف بلوک های سفارشی و سفارشی کردن محتویات جعبه ابزار است. هیچ یک از این اقدامات هیچ تأثیری بر سیستم فوکوس ندارد.

کلاس های سفارشی

کلاس های سفارشی ممکن است نیاز به پیاده سازی یک یا هر دو رابط فوکوس داشته باشند ( IFocusableTree و IFocusableNode ). زمانی که چنین است همیشه مشخص نیست.

برخی از کلاس ها به وضوح نیاز به پیاده سازی رابط های تمرکز دارند. این موارد عبارتند از:

  • کلاسی که جعبه ابزار سفارشی را پیاده سازی می کند. این کلاس نیاز به پیاده سازی IFocusableTree و IFocusableNode دارد.

  • کلاس هایی که یک مؤلفه قابل مشاهده (مانند یک فیلد یا نماد) ایجاد می کنند که کاربران می توانند به آن پیمایش کنند. این کلاس ها نیاز به پیاده سازی IFocusableNode دارند.

برخی از کلاس ها نیاز به پیاده سازی IFocusableNode دارند حتی اگر یک جزء قابل مشاهده ایجاد نمی کنند یا یک جزء قابل مشاهده ایجاد می کنند که کاربران نمی توانند به آن پیمایش کنند. این موارد عبارتند از:

  • کلاس هایی که رابطی را پیاده سازی می کنند که IFocusableNode گسترش می دهد.

    به عنوان مثال، نماد حرکت در افزونه پیمایش صفحه کلید یک فلش چهار طرفه را نشان می دهد که نشان می دهد می توان بلوک را با کلیدهای جهت دار جابجا کرد. خود نماد قابل مشاهده نیست (فلش چهار طرفه یک حباب است) و کاربران نمی توانند به سمت آن حرکت کنند. با این حال، آیکون باید IFocusableNode را پیاده‌سازی کند زیرا آیکون‌ها IIcon را پیاده‌سازی می‌کنند و IIcon IFocusableNode را گسترش می‌دهد.

  • کلاس های مورد استفاده در یک API که به IFocusableNode نیاز دارد.

    به عنوان مثال، کلاس FlyoutSeparator یک شکاف بین دو آیتم در flyout ایجاد می کند. هیچ عنصر DOM ایجاد نمی کند، بنابراین یک جزء قابل مشاهده ندارد و کاربران نمی توانند به آن پیمایش کنند. با این حال، باید IFocusableNode پیاده سازی کند زیرا در یک FlyoutItem ذخیره شده است و سازنده FlyoutItem به یک IFocusableNode نیاز دارد.

  • کلاس هایی که کلاسی را گسترش می دهند که IFocusableNode را پیاده سازی می کند.

    به عنوان مثال، ToolboxSeparator ToolboxItem گسترش می دهد که IFocusableNode پیاده سازی می کند. اگرچه جداکننده‌های جعبه ابزار دارای یک جزء قابل مشاهده هستند، کاربران نمی‌توانند به آن‌ها پیمایش کنند، زیرا نمی‌توان روی آنها عمل کرد و محتوای مفیدی ندارند.

کلاس‌های دیگر اجزای قابل مشاهده را ایجاد می‌کنند که کاربر می‌تواند به آنها پیمایش کند، اما نیازی به پیاده‌سازی IFocusableNode ندارند. این موارد عبارتند از:

  • کلاس هایی که یک مؤلفه قابل مشاهده ایجاد می کنند که تمرکز خود را مدیریت می کند، مانند یک ویرایشگر فیلد یا یک گفتگو. (توجه داشته باشید که چنین کلاس هایی باید فوکوس زودگذر را هنگام شروع و پس از پایان برگردانند. استفاده از WidgetDiv یا DropDownDiv این کار را برای شما انجام می دهد.)

در نهایت، برخی از کلاس‌ها با سیستم تمرکز تعامل ندارند و نیازی به پیاده‌سازی IFocusableTree یا IFocusableNode ندارند. این موارد عبارتند از:

  • کلاس هایی که یک مؤلفه قابل مشاهده ایجاد می کنند که کاربران نمی توانند به آن پیمایش کنند یا روی آن عمل کنند و حاوی اطلاعاتی نیست که ممکن است یک صفحه خوان از آن استفاده کند. به عنوان مثال، یک پس زمینه صرفا تزئینی در یک بازی.

  • کلاس‌هایی که کاملاً با سیستم تمرکز نامرتبط هستند، مانند کلاس‌هایی که IMetricsManager یا IVariableMap را پیاده‌سازی می‌کنند.

اگر مطمئن نیستید که کلاس شما با سیستم فوکوس تعامل خواهد داشت، آن را با افزونه پیمایش صفحه کلید تست کنید. اگر این کار انجام نشد، ممکن است لازم باشد IFocusableTree یا IFocusableNode را پیاده سازی کنید. اگر موفق شد اما هنوز مطمئن نیستید، کدی که از کلاس شما استفاده می‌کند را بخوانید تا ببینید آیا هر یک از اینترفیس‌ها مورد نیاز است یا تعاملات دیگری وجود دارد.

پیاده سازی رابط های تمرکز

ساده ترین راه برای پیاده سازی IFocusableTree یا IFocusableNode گسترش کلاسی است که این رابط ها را پیاده سازی می کند. برای مثال، اگر در حال ایجاد یک جعبه ابزار سفارشی هستید، Toolbox گسترش دهید که IFocusableTree و IFocusableNode را پیاده سازی می کند. اگر در حال ایجاد یک فیلد سفارشی هستید، Field گسترش دهید که IFocusableNode را پیاده سازی می کند. مطمئن شوید که کد شما با کد رابط فوکوس در کلاس پایه تداخلی نداشته باشد.

اگر کلاسی را گسترش دهید که رابط فوکوس را پیاده سازی می کند، معمولاً نیازی به لغو هیچ روشی نخواهید داشت. رایج ترین استثنا IFocusableNode.canBeFocused است که اگر نمی خواهید کاربران به مؤلفه شما حرکت کنند، باید آن را لغو کنید.

نیاز به نادیده گرفتن روش‌های بازگشت به تماس تمرکز ( onTreeFocus و onTreeBlur در IFocusableTree و onNodeFocus و onNodeBlur در IFocusableNode ) کمتر رایج است. توجه داشته باشید که تلاش برای تغییر فوکوس (فراخوانی FocusManager.focusNode یا FocusManager.focusTree ) از این روش ها منجر به یک استثنا می شود.

اگر یک کامپوننت سفارشی را از ابتدا بنویسید، باید خودتان رابط های فوکوس را پیاده سازی کنید. به مستندات مرجع برای اطلاعات بیشتر IFocusableTree و IFocusableNode مراجعه کنید.

بعد از اینکه کلاس خود را پیاده سازی کردید، آن را با افزونه پیمایش صفحه کلید آزمایش کنید تا مطمئن شوید که می توانید (یا نمی توانید) به مؤلفه خود بروید.

از مدیر تمرکز استفاده کنید

برخی از کلاس های سفارشی از مدیر تمرکز استفاده می کنند. رایج ترین دلایل برای انجام این کار، دریافت گره متمرکز فعلی و تمرکز بر روی یک گره متفاوت است. برای دریافت مدیر تمرکز، Blockly.getFocusManager تماس بگیرید:

const focusManager = Blockly.getFocusManager();

برای دریافت گره متمرکز فعلی، getFocusedNode فراخوانی کنید:

const focusedNode = focusManager.getFocusedNode();
// Do something with the focused node.

برای انتقال فوکوس به یک گره دیگر، focusNode را فراخوانی کنید:

// Move focus to a different block.
focusManager.focusNode(myOtherBlock);

برای انتقال فوکوس به درخت، focusTree فراخوانی کنید. این همچنین تمرکز گره را روی گره ریشه درخت تنظیم می کند.

// Move focus to the main workspace.
focusManager.focusTree(myMainWorkspace);

دلیل متداول دیگر برای استفاده از مدیریت تمرکز، گرفتن و برگرداندن فوکوس زودگذر است. تابع takeEphemeralFocus یک لامبدا را برمی گرداند که برای بازگشت فوکوس زودگذر باید آن را فراخوانی کنید.

const returnEphemeralFocus = focusManager.takeEphemeralFocus();
// Do something.
returnEphemeralFocus();

اگر از WidgetDiv یا DropDownDiv استفاده می کنید، آنها فوکوس زودگذر را برای شما انجام می دهند.

تب متوقف می شود

سیستم فوکوس یک tab stop ( tabindex 0 ) روی عنصر ریشه همه درختان (فضای کار اصلی، جعبه ابزار و فضاهای کاری پرواز) تنظیم می کند. این به کاربران اجازه می دهد تا از کلید تب برای پیمایش در مناطق اصلی ویرایشگر Blockly استفاده کنند و سپس (با استفاده از افزونه پیمایش صفحه کلید) از کلیدهای جهت دار برای حرکت در آن مناطق استفاده کنند. این tab stop ها را تغییر ندهید، زیرا در توانایی مدیر تمرکز برای مدیریت آنها اختلال ایجاد می کند.

به طور کلی باید از تنظیم tab stop در سایر عناصر DOM استفاده شده توسط Blockly اجتناب کنید، زیرا این کار با مدل Blockly در استفاده از کلید tab برای پیمایش بین نواحی ویرایشگر و کلیدهای پیکان در آن مناطق تداخل دارد. علاوه بر این، چنین توقف‌های زبانه ممکن است آنطور که در نظر گرفته شده کار نکنند. این به این دلیل است که هر گره قابل تمرکز یک عنصر DOM را به عنوان عنصر قابل تمرکز خود اعلام می کند. اگر یک tab stop را روی یکی از نوادگان عنصر قابل تمرکز و برگه‌های کاربر روی آن عنصر تنظیم کنید، مدیر تمرکز فوکوس DOM را به عنصر قابل تمرکز اعلام شده منتقل می‌کند.

تنظیم توقف های تب روی عناصری در برنامه خود که خارج از ویرایشگر Blockly هستند، بی خطر است. هنگامی که کاربر از ویرایشگر به چنین عنصری تب می کند، مدیر فوکوس فوکوس Blockly را از فعال به غیرفعال تغییر می دهد. برای دسترسی، باید ویژگی tabindex را بر روی 0 یا -1 تنظیم کنید، همانطور که در اخطار در توضیحات MDN از ویژگی tabindex توصیه شده است.

تمرکز DOM

به دلایل دسترسی، برنامه ها باید از فراخوانی روش focus بر روی عناصر DOM اجتناب کنند. این برای کاربران صفحه‌خوان‌ها سرگردان است، زیرا آنها به طور ناگهانی به مکان نامعلومی در برنامه منتقل می‌شوند.

یک مشکل دیگر این است که مدیر تمرکز با تنظیم فوکوس DOM روی نزدیک‌ترین اجداد عنصر متمرکز که یک عنصر قابل تمرکز اعلام شده است، به رویدادهای متمرکز واکنش نشان می‌دهد. این ممکن است با عنصری که focus روی آن فراخوانی شده است متفاوت باشد. (اگر نزدیکترین اجداد قابل تمرکز وجود نداشته باشد، مانند زمانی که focus روی عنصری خارج از ویرایشگر Blockly فراخوانی می شود، مدیر فوکوس فقط گره متمرکز شده فعال را به فوکوس غیرفعال تغییر می دهد.)

موقعیت پذیرها

Positionable ها اجزایی هستند که در بالای فضای کاری قرار می گیرند و IPositionable را پیاده سازی می کنند. به عنوان مثال می توان به سطل زباله و کوله پشتی موجود در افزونه کوله پشتی اشاره کرد. موقعیت پذیرها هنوز در سیستم فوکوس ادغام نشده اند.