מערכת המיקוד עוקבת אחרי המיקום (המוקד) של המשתמש בכלי לעריכת Blockly. הוא משמש את Blockly ואת קוד מותאם אישית כדי לקבוע לאיזה רכיב (בלוק, שדה, קטגוריה בארגז הכלים וכו') יש כרגע מיקוד, ולהעביר את המיקוד הזה לרכיב אחר.
חשוב להבין את מערכת המיקוד כדי לוודא שהקוד המותאם אישית פועל איתה בצורה תקינה.
ארכיטקטורה
מערכת המיקוד מורכבת משלושה חלקים:
FocusManager
הוא סינגלטון שמתאם את המיקוד בכל Blockly. הפונקציה הזו משמשת את Blockly ואת קוד בהתאמה אישית כדי לגלות לאיזה רכיב יש מיקוד ב-Blockly, וגם כדי להעביר את המיקוד ב-Blockly לרכיב אחר. הוא גם מקשיב לאירועי מיקוד ב-DOM, מסנכרן את המיקוד ב-Blockly ואת המיקוד ב-DOM, ומנהל את מחלקות ה-CSS שמציינות לאיזה רכיב יש מיקוד.הכלי לניהול המיקוד משמש בעיקר את Blockly. לפעמים נעשה בו שימוש בקוד מותאם אישית כדי ליצור אינטראקציה עם מערכת המיקוד.
IFocusableTree
הוא אזור עצמאי בכלי לעריכת Blockly, כמו סביבת עבודה או ארגז כלים. הוא מורכב מצמתים שאפשר להתמקד בהם, כמו בלוקים ושדות. לעצים יכולים להיות גם עצי משנה. לדוגמה, סביבת עבודה של מוטטור בבלוק בסביבת העבודה הראשית היא עץ משנה של סביבת העבודה הראשית.האפשרות
IFocusableTree
משמשת בעיקר את מנהל המיקוד. אלא אם אתם כותבים ארגז כלים מותאם אישית, סביר להניח שלא תצטרכו להטמיע אותו.
IFocusableNode
הוא רכיב Blockly שאפשר להעביר אליו את הפוקוס, כמו בלוק, שדה או קטגוריה בארגז הכלים. לצמתים שאפשר להתמקד בהם יש רכיב DOM שמציג את הצומת, ושיש לו מיקוד DOM כשיש לצומת מיקוד Blockly. חשוב לדעת שאפשר להתמקד גם בעצים. לדוגמה, אפשר להתמקד בסביבת העבודה כולה.השיטות ב-
IFocusableNode
מופעלות בעיקר על ידי מנהל המיקוד.הסמל
IFocusableNode
עצמו משמש לייצוג הרכיב שנמצא בפוקוס. לדוגמה, כשמשתמש בוחר פריט בתפריט ההקשר של בלוק, הבלוק מועבר לפונקציית הקריאה החוזרת של הפריט כ-IFocusableNode
.אם אתם כותבים רכיבים בהתאמה אישית, יכול להיות שתצטרכו להטמיע את
IFocusableNode
.
סוגי המיקוד
מערכת המיקוד מגדירה מספר סוגים שונים של מיקוד.
המיקוד ב-Blockly והמיקוד ב-DOM
שני הסוגים העיקריים של פוקוס הם פוקוס של Blockly ופוקוס של DOM.
Blockly focus מציין איזה רכיב של Blockly (בלוק, שדה, קטגוריה של ארגז הכלים וכו') מודגש. היא נדרשת כדי לעבוד ברמה של רכיבי Blockly. לדוגמה, התוסף לניווט במקלדת מאפשר למשתמשים להשתמש במקשי החיצים כדי לעבור מרכיב לרכיב, כמו מבלוק לשדה. באופן דומה, מערכת תפריטי ההקשר יוצרת תפריט שמתאים לרכיב הנוכחי – כלומר, היא יוצרת תפריטים שונים לסביבות עבודה, לבלוקים ולהערות בסביבת העבודה.
DOM focus (מיקוד DOM) מציין לאיזה רכיב DOM יש מיקוד. היא נחוצה כדי לעבוד ברמה של רכיבי DOM. לדוגמה, קוראי מסך מציגים מידע על האלמנט שמוגדר כרגע ב-DOM, ומקשי Tab מעבירים את המיקוד מאלמנט DOM אחד לאלמנט DOM אחר.
מנהל המיקוד שומר על סנכרון בין המיקוד ב-Blockly לבין המיקוד ב-DOM, כך שכאשר לצומת (רכיב Blockly) יש מיקוד ב-Blockly, לרכיב ה-DOM הבסיסי שלו יש מיקוד ב-DOM, ולהפך.
מיקוד פעיל ומיקוד פסיבי
הפוקוס ב-Blockly מחולק לפוקוס פעיל ולפוקוס סביל. מיקוד פעיל פירושו שצומת יקבל קלט מהמשתמש, כמו לחיצה על מקש. מיקוד פסיבי פירושו שצומת מסוים היה בעבר במיקוד פעיל, אבל המיקוד אבד כשהמשתמש עבר לצומת בעץ אחר (לדוגמה, הוא עבר ממרחב העבודה לארגז הכלים) או יצא לגמרי מעורך Blockly. אם המיקוד חוזר לעץ, הצומת שהמיקוד עליו היה פסיבי יחזור להיות פעיל.
לכל עץ יש הקשר מיקוד נפרד. כלומר, לכל היותר צומת אחד בעץ יכול להיות במצב פוקוס. האם המיקוד פעיל או פסיבי תלוי בשאלה אם המיקוד הוא על העץ. יכול להיות לכל היותר צומת אחד עם מיקוד פעיל בכל הדף.
מנהל הפוקוס משתמש בהדגשות שונות (מחלקות CSS) עבור צמתים עם פוקוס פעיל ופוקוס פסיבי. הם מאפשרים למשתמשים להבין איפה הם נמצאים ולאן הם יחזרו.
פוקוס זמני
יש סוג נוסף של פוקוס שנקרא פוקוס זמני. תהליכי עבודה נפרדים, כמו תיבות דו-שיח או עורכי שדות, מבקשים מיקוד זמני ממנהל המיקוד. כשמנהל המיקוד מעניק מיקוד זמני, הוא משעה את מערכת המיקוד. מבחינה מעשית, המשמעות היא שתהליכי עבודה כאלה יכולים לתעד אירועי מיקוד ב-DOM ולפעול לפיהם, בלי לחשוש שמערכת המיקוד תפעל לפיהם גם כן.
כשמנהל המיקוד מעניק מיקוד זמני, הוא משנה את הצומת שמוגדר כמיקוד פעיל למיקוד פסיבי. הוא משחזר את המיקוד הפעיל כשמוחזר מיקוד זמני.
דוגמאות
בדוגמאות הבאות אפשר לראות איך Blockly משתמש במערכת המיקוד. הם אמורים לעזור לכם להבין איך הקוד שלכם משתלב במערכת המיקוד ואיך הקוד שלכם יכול להשתמש במערכת המיקוד.
העברת המיקוד באמצעות המקלדת
נניח שיש בלוק עם שני שדות שמוגדר בו מיקוד ב-Blockly, כפי שמצוין על ידי הדגשה (CSS class) ברכיב ה-DOM של הבלוק. עכשיו נניח שהמשתמש לוחץ על החץ שמאלה:
- תוסף לניווט במקלדת:
- מקבל אירוע של הקשת מקש.
- מבקשת ממערכת הניווט (חלק מליבת Blockly) להעביר את המיקוד לרכיב 'הבא'.
- מערכת הניווט:
- הפונקציה שואלת את מנהל המיקוד איזה רכיב נמצא במיקוד של Blockly. הפונקציה focus
manager מחזירה את הבלוק בתור
IFocusableNode
. - קובע ש-
IFocusableNode
הואBlockSvg
ובוחן את הכללים שלו לניווט בין בלוקים, שקובעים שהוא צריך להעביר את המיקוד של Blockly מהבלוק כולו לשדה הראשון בבלוק. - הפקודה הזו אומרת למנהל המיקוד להעביר את המיקוד ב-Blockly לשדה הראשון.
- הפונקציה שואלת את מנהל המיקוד איזה רכיב נמצא במיקוד של Blockly. הפונקציה focus
manager מחזירה את הבלוק בתור
- מנהל המיקוד:
- מעדכנים את המצב כדי להגדיר את המיקוד של Blockly בשדה הראשון.
- הגדרת המיקוד ב-DOM ברכיב ה-DOM של השדה.
- העברת מחלקת ההדגשה מהרכיב של הבלוק לרכיב של השדה.
העברת המיקוד באמצעות העכבר
נניח שהמשתמש לוחץ על השדה השני בבלוק. המרכז לניהול המיקוד:
- מקבל אירוע DOM
focusout
ברכיב ה-DOM של השדה הראשון ואירועfocusin
ברכיב ה-DOM של השדה השני. - קובע שרכיב ה-DOM שקיבל את המיקוד תואם לשדה השני.
- מעדכנים את המצב שלו כדי להגדיר את המיקוד של Blockly בשדה השני. (אין צורך שמנהל המיקוד יגדיר את המיקוד ב-DOM כי הדפדפן כבר עשה את זה).
- העברת מחלקת ההדגשה מהרכיב של השדה הראשון לרכיב של השדה השני.
דוגמאות נוספות
הנה עוד כמה דוגמאות:
כשמשתמש גורר בלוק מארגז הכלים אל סביבת העבודה, הפונקציה לטיפול באירועי העכבר יוצרת בלוק חדש וקוראת למנהל המיקוד כדי להגדיר את המיקוד של Blockly על הבלוק הזה.
כשמוחקים בלוק, השיטה
dispose
שלו קוראת למנהל הפוקוס כדי להעביר את הפוקוס לבלוק ההורה.במקשי קיצור משתמשים ב-
IFocusableNode
כדי לזהות את רכיב Blockly שאליו מתייחס קיצור הדרך.תפריטי הקשר משתמשים ב-
IFocusableNode
כדי לזהות את רכיב Blockly שהתפריט הופעל בו.
התאמות אישיות ומערכת המיקוד
כשמבצעים התאמה אישית של Blockly, צריך לוודא שהקוד פועל בצורה תקינה עם מערכת המיקוד. אפשר גם להשתמש במערכת המיקוד כדי לזהות את הצומת שמוגדר כרגע כצומת הממוקד ולהגדיר אותו.
בלוקים בהתאמה אישית ותוכן ארגז הכלים
הדרך הכי נפוצה להתאים אישית את Blockly היא להגדיר בלוקים בהתאמה אישית ולהתאים אישית את התוכן של ארגז הכלים. אף אחת מהפעולות האלה לא משפיעה על מערכת המיקוד.
שיעורים בהתאמה אישית
יכול להיות שבכיתות מותאמות אישית צריך להטמיע אחד או את שני ממשקי המיקוד (IFocusableTree
ו-IFocusableNode
). לא תמיד ברור מתי זה המצב.
ברור שבחלק מהמחלקות צריך להטמיע ממשקי מיקוד. למשל:
מחלקה שמטמיעה ארגז כלים בהתאמה אישית. במחלקה הזו צריך להטמיע את הפונקציות
IFocusableTree
ו-IFocusableNode
.מחלקות שיוצרות רכיב גלוי (כמו שדה או סמל) שהמשתמשים יכולים לנווט אליו. המחלקה הזו צריכה להטמיע את
IFocusableNode
.
יש מחלקות שצריך להטמיע בהן את IFocusableNode
גם אם הן לא יוצרות רכיב גלוי או שהן יוצרות רכיב גלוי שהמשתמשים לא יכולים לנווט אליו. למשל:
מחלקה שמטמיעה ממשק שמרחיב את
IFocusableNode
.לדוגמה, סמל ההזזה בתוסף לניווט באמצעות מקלדת מציג חץ עם ארבעה כיוונים שמציין שאפשר להזיז את הבלוק באמצעות מקשי החיצים. הסמל עצמו לא מוצג (החץ עם ארבעת הכיוונים הוא בתוך בועה) והמשתמשים לא יכולים לנווט אליו. עם זאת, הסמל חייב להטמיע את
IFocusableNode
כי הסמלים מטמיעים אתIIcon
ו-IIcon
מרחיב אתIFocusableNode
.מחלקות שמשמשות ב-API שנדרש בו
IFocusableNode
.לדוגמה, המחלקה
FlyoutSeparator
יוצרת רווח בין שני פריטים בתפריט נפתח. הוא לא יוצר רכיבי 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
, שצריך לבטל אם לא רוצים שהמשתמשים יוכלו לנווט לרכיב.
פחות נפוץ הצורך לבטל את השיטות של קריאה חוזרת (callback) למיקוד (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
מערכת ההתמקדות מגדירה עצירת טאב (tabindex
מתוך 0
) ברכיב הבסיס של כל העצים (סביבת העבודה הראשית, ארגז הכלים וסביבות העבודה הנפתחות). כך המשתמשים יכולים להשתמש במקש Tab כדי לנווט בין האזורים הראשיים של כלי Blockly, ואז (באמצעות התוסף לניווט במקלדת) להשתמש במקשי החיצים כדי לנווט בתוך האזורים האלה. אל תשנו את מיקומי הטאבים האלה, כי זה יפריע ליכולת של מנהל המיקוד לנהל אותם.
בדרך כלל כדאי להימנע מהגדרת עצירות טאב ברכיבי DOM אחרים שמשמשים את Blockly, כי זה מפריע למודל של Blockly שבו משתמשים במקש Tab כדי לנווט בין אזורים של כלי העריכה ובמקשי החיצים בתוך האזורים האלה. בנוסף, יכול להיות שטאבים כאלה לא יפעלו כמו שרוצים. הסיבה לכך היא שכל צומת שניתן למיקוד מגדיר רכיב DOM כרכיב שניתן למיקוד. אם מגדירים עצירת טאב בצאצא של אלמנט שאפשר להתמקד בו, והמשתמש עובר באמצעות מקש הטאב לאלמנט הזה, מנהל המיקוד יעביר את המיקוד ב-DOM לאלמנט שאפשר להתמקד בו.
אפשר להגדיר עצירות טאב ברכיבים באפליקציה שלא נמצאים מחוץ לעורך Blockly. כשהמשתמש עובר באמצעות מקש Tab מהכלי לעריכה לרכיב כזה, מנהל המיקוד משנה את המיקוד ב-Blockly מפעיל לפסיבי. לצורך נגישות, מומלץ להגדיר את המאפיין tabindex
לערך 0
או -1
, כמו שמומלץ באזהרה בתיאור של המאפיין tabindex
ב-MDN.
מיקוד DOM
מסיבות שקשורות לנגישות, באפליקציות לא מומלץ להפעיל את השיטה focus
על רכיבי DOM. הדבר גורם לבלבול בקרב משתמשים בקוראי מסך, כי הם מועברים פתאום למיקום לא ידוע באפליקציה.
בעיה נוספת היא שמנהל המיקוד מגיב לאירועי מיקוד על ידי הגדרת מיקוד DOM באלמנט הקרוב ביותר ברמה העליונה או באלמנט עצמו של האלמנט הממוקד, שהוא אלמנט ניתן למיקוד מוצהר. יכול להיות שהיא תהיה שונה מהרכיב שבו בוצעה הקריאה ל-focus
. (אם אין צומת אב או צומת עצמי שניתן להתמקד בו, למשל אם קוראים ל-focus
ברכיב מחוץ לכלי לעריכת Blockly, מנהל המיקוד פשוט משנה את הצומת שמתמקדים בו באופן פעיל למיקוד פסיבי).
Positionables
רכיבים שניתן למקם הם רכיבים שממוקמים מעל סביבת העבודה ומיישמים את IPositionable
.
דוגמאות: פח האשפה והתרמיל בתוסף backpack.
עדיין לא שילבנו את הרכיבים שניתן למקם במערכת המיקוד.