Sistem fokus melacak lokasi (fokus) pengguna di editor Blockly. Fungsi ini digunakan oleh Blockly dan oleh kode kustom untuk menentukan komponen (blok, kolom, kategori toolbox, dll.) yang saat ini memiliki fokus dan untuk memindahkan fokus tersebut ke komponen lain.
Penting untuk memahami sistem fokus agar Anda dapat memastikan bahwa kode kustom Anda berfungsi dengan benar.
Arsitektur
Sistem fokus memiliki tiga bagian:
FocusManager
adalah singleton yang mengoordinasikan fokus di semua Blockly. Digunakan oleh Blockly dan oleh kode kustom untuk mengetahui komponen mana yang memiliki fokus Blockly, serta untuk memindahkan fokus Blockly ke komponen lain. Selain itu, komponen ini memproses peristiwa fokus DOM, menyinkronkan fokus Blockly dan fokus DOM, serta mengelola class CSS yang menunjukkan komponen mana yang memiliki fokus.Pengelola fokus terutama digunakan oleh Blockly. Terkadang digunakan oleh kode kustom untuk berinteraksi dengan sistem fokus.
IFocusableTree
adalah area independen editor Blockly, seperti ruang kerja atau toolbox. Terdiri dari node yang dapat difokuskan, seperti blok dan kolom. Pohon juga dapat memiliki subpohon. Misalnya, ruang kerja mutator pada blok di ruang kerja utama adalah subpohon dari ruang kerja utama.IFocusableTree
terutama digunakan oleh pengelola fokus. Kecuali jika Anda menulis toolbox kustom, Anda mungkin tidak perlu menerapkannya.IFocusableNode
adalah komponen Blockly yang dapat memiliki fokus, seperti blok, kolom, atau kategori toolbox. Node yang dapat difokuskan memiliki elemen DOM yang menampilkan node dan yang memiliki fokus DOM saat node memiliki fokus Blockly. Perhatikan bahwa pohon juga merupakan node yang dapat difokuskan. Misalnya, Anda dapat berfokus pada ruang kerja secara keseluruhan.Metode di
IFocusableNode
terutama dipanggil oleh pengelola fokus.IFocusableNode
itu sendiri digunakan untuk merepresentasikan komponen yang memiliki fokus. Misalnya, saat pengguna memilih item pada menu konteks blok, blok diteruskan ke fungsi callback item sebagaiIFocusableNode
.Jika menulis komponen kustom, Anda mungkin perlu menerapkan
IFocusableNode
.
Jenis fokus
Sistem fokus menentukan sejumlah jenis fokus yang berbeda.
Fokus Blockly dan fokus DOM
Dua jenis fokus utama adalah fokus Blockly dan fokus DOM.
Fokus Blockly menentukan komponen Blockly mana (blok, kolom, kategori toolbox, dll.) yang memiliki fokus. Hal ini diperlukan untuk bekerja di tingkat komponen Blockly. Misalnya, plugin navigasi keyboard memungkinkan pengguna menggunakan tombol panah untuk berpindah dari komponen ke komponen, seperti dari blok ke kolom. Demikian pula, sistem menu konteks membuat menu yang sesuai untuk komponen saat ini, yaitu membuat menu yang berbeda untuk ruang kerja, blok, dan komentar ruang kerja.
Fokus DOM menentukan elemen DOM mana yang memiliki fokus. Hal ini diperlukan untuk bekerja di tingkat elemen DOM. Misalnya, pembaca layar menyajikan informasi tentang elemen yang saat ini memiliki fokus DOM dan tab berpindah (mengubah fokus) dari elemen DOM ke elemen DOM.
Pengelola fokus menyinkronkan fokus Blockly dan fokus DOM, sehingga saat node (komponen Blockly) memiliki fokus Blockly, elemen DOM yang mendasarinya memiliki fokus DOM dan sebaliknya.
Fokus aktif dan pasif
Fokus Blockly dibagi lagi menjadi fokus aktif dan fokus pasif. Fokus aktif berarti bahwa node akan menerima input pengguna, seperti penekanan tombol. Fokus pasif berarti bahwa sebelumnya sebuah node memiliki fokus aktif, tetapi kehilangan fokus tersebut saat pengguna berpindah ke node di pohon lain (misalnya, mereka berpindah dari ruang kerja ke toolbox) atau keluar dari editor Blockly sama sekali. Jika pohon mendapatkan kembali fokus, node yang difokuskan secara pasif akan mendapatkan kembali fokus aktif.
Setiap pohon memiliki konteks fokus yang terpisah. Artinya, paling banyak satu node dalam hierarki yang dapat memiliki fokus. Apakah fokus tersebut aktif atau pasif bergantung pada apakah pohon memiliki fokus. Hanya boleh ada maksimal satu node dengan fokus aktif di seluruh halaman.
Pengelola fokus menggunakan sorotan yang berbeda (class CSS) untuk node yang difokuskan secara aktif dan pasif. Hal ini memungkinkan pengguna memahami posisi mereka dan ke mana mereka akan kembali.
Fokus sementara
Ada jenis fokus lain yang disebut fokus sementara. Alur kerja terpisah, seperti dialog atau editor kolom, meminta fokus sementara dari pengelola fokus. Saat pengelola fokus memberikan fokus sementara, pengelola fokus akan menangguhkan sistem fokus. Dari sudut pandang praktis, ini berarti bahwa alur kerja tersebut dapat merekam dan merespons peristiwa fokus DOM tanpa perlu khawatir bahwa sistem fokus juga dapat meresponsnya.
Saat pengelola fokus memberikan fokus sementara, pengelola fokus akan mengubah node yang difokuskan secara aktif menjadi fokus pasif. Tindakan ini memulihkan fokus aktif saat fokus sementara dikembalikan.
Contoh
Contoh berikut mengilustrasikan cara Blockly menggunakan sistem fokus. Hal ini akan membantu Anda memahami cara kode Anda cocok dengan sistem fokus dan cara kode Anda menggunakan sistem fokus.
Memindahkan fokus dengan keyboard
Misalkan blok dengan dua kolom memiliki fokus Blockly, seperti yang ditunjukkan oleh penyorotan (class CSS) pada elemen DOM blok. Sekarang, misalkan pengguna menekan panah kanan:
- Plugin navigasi keyboard:
- Menerima peristiwa penekanan tombol.
- Meminta sistem navigasi (bagian dari Blockly inti) untuk memindahkan fokus ke komponen "berikutnya".
- Sistem navigasi:
- Meminta pengelola fokus komponen mana yang memiliki fokus Blockly. Pengelola
fokus menampilkan blok sebagai
IFocusableNode
. - Menentukan bahwa
IFocusableNode
adalahBlockSvg
dan melihat aturan untuk menavigasi blok, yang menyatakan bahwa fokus Blockly harus dipindahkan dari blok secara keseluruhan ke kolom pertama pada blok. - Memberi tahu pengelola fokus untuk memindahkan fokus Blockly ke kolom pertama.
- Meminta pengelola fokus komponen mana yang memiliki fokus Blockly. Pengelola
fokus menampilkan blok sebagai
- Pengelola fokus:
- Memperbarui statusnya untuk menyetel fokus Blockly pada kolom pertama.
- Menetapkan fokus DOM pada elemen DOM kolom.
- Memindahkan class sorotan dari elemen blok ke elemen kolom.
Memindahkan fokus dengan mouse
Sekarang, misalkan pengguna mengklik kolom kedua di blok. Pengelola fokus:
- Menerima peristiwa DOM
focusout
pada elemen DOM kolom pertama dan peristiwafocusin
pada elemen DOM kolom kedua. - Menentukan bahwa elemen DOM yang menerima fokus sesuai dengan kolom kedua.
- Memperbarui statusnya untuk menyetel fokus Blockly pada kolom kedua. (Pengelola fokus tidak perlu menyetel fokus DOM karena browser sudah melakukannya.)
- Memindahkan class sorotan dari elemen kolom pertama ke elemen kolom kedua.
Contoh lainnya
Berikut adalah beberapa contoh lainnya:
Saat pengguna menarik blok dari toolbox ke ruang kerja, handler peristiwa mouse akan membuat blok baru dan memanggil pengelola fokus untuk menyetel fokus Blockly pada blok tersebut.
Saat blok dihapus, metode
dispose
-nya memanggil pengelola fokus untuk memindahkan fokus ke induk blok.Pintasan keyboard menggunakan
IFocusableNode
untuk mengidentifikasi komponen Blockly yang menerapkan pintasan.Menu konteks menggunakan
IFocusableNode
untuk mengidentifikasi komponen Blockly tempat menu dipanggil.
Penyesuaian dan sistem fokus
Saat menyesuaikan Blockly, Anda harus memastikan bahwa kode Anda berfungsi dengan benar dengan sistem fokus. Anda juga dapat menggunakan sistem fokus untuk mengidentifikasi dan menyetel node yang saat ini difokuskan.
Blok kustom dan isi kotak alat
Cara paling umum untuk menyesuaikan Blockly adalah dengan menentukan blok kustom dan menyesuaikan konten toolbox. Kedua tindakan ini tidak memengaruhi sistem fokus.
Class kustom
Class kustom mungkin perlu mengimplementasikan satu atau kedua antarmuka fokus
(IFocusableTree
dan IFocusableNode
). Tidak selalu jelas kapan hal ini
terjadi.
Beberapa class jelas perlu menerapkan antarmuka fokus. Ini mencakup:
Class yang mengimplementasikan kotak alat kustom. Class ini perlu menerapkan
IFocusableTree
danIFocusableNode
.Class yang membuat komponen yang terlihat (seperti kolom atau ikon) yang dapat dijelajahi pengguna. Class ini harus menerapkan
IFocusableNode
.
Beberapa class perlu menerapkan IFocusableNode
meskipun tidak membuat komponen yang terlihat atau membuat komponen yang terlihat yang tidak dapat dinavigasi pengguna. Ini mencakup:
Class yang mengimplementasikan antarmuka yang memperluas
IFocusableNode
.Misalnya, ikon pindah di plugin navigasi keyboard menampilkan panah empat arah yang menunjukkan bahwa blok dapat dipindahkan dengan tombol panah. Ikon itu sendiri tidak terlihat (panah empat arah adalah balon) dan pengguna tidak dapat membukanya. Namun, ikon harus menerapkan
IFocusableNode
karena ikon menerapkanIIcon
danIIcon
memperluasIFocusableNode
.Class yang digunakan dalam API yang memerlukan
IFocusableNode
.Misalnya, class
FlyoutSeparator
membuat celah antara dua item di flyout. Tidak membuat elemen DOM apa pun, sehingga tidak memiliki komponen yang terlihat dan pengguna tidak dapat menavigasi ke sana. Namun, class ini harus menerapkanIFocusableNode
karena disimpan dalamFlyoutItem
dan konstruktorFlyoutItem
memerlukanIFocusableNode
.Class yang memperluas class yang mengimplementasikan
IFocusableNode
.Misalnya,
ToolboxSeparator
memperluasToolboxItem
, yang mengimplementasikanIFocusableNode
. Meskipun pemisah toolbox memiliki komponen yang terlihat, pengguna tidak dapat menavigasi ke pemisah tersebut karena tidak dapat ditindaklanjuti dan tidak memiliki konten yang berguna.
Class lain membuat komponen yang terlihat dan dapat dijelajahi pengguna, tetapi tidak perlu menerapkan IFocusableNode
. Ini mencakup:
- Class yang membuat komponen yang terlihat yang mengelola fokusnya sendiri, seperti
editor kolom atau dialog. (Perhatikan bahwa class tersebut perlu mengambil
fokus sementara saat dimulai dan mengembalikannya saat
berakhir. Menggunakan
WidgetDiv
atauDropDownDiv
akan menanganinya untuk Anda.)
Terakhir, beberapa class tidak berinteraksi dengan sistem fokus dan tidak perlu
menerapkan IFocusableTree
atau IFocusableNode
. Ini mencakup:
Class yang membuat komponen yang terlihat yang tidak dapat dinavigasi atau ditindaklanjuti pengguna dan yang tidak berisi informasi yang dapat digunakan oleh pembaca layar. Misalnya, latar belakang yang murni dekoratif dalam game.
Class yang sama sekali tidak terkait dengan sistem fokus, seperti class yang mengimplementasikan
IMetricsManager
atauIVariableMap
.
Jika Anda tidak yakin apakah kelas Anda akan berinteraksi dengan sistem fokus, uji dengan plugin navigasi keyboard. Jika gagal, Anda mungkin perlu
menerapkan IFocusableTree
atau IFocusableNode
. Jika berhasil, tetapi Anda masih
tidak yakin, baca kode yang menggunakan class Anda untuk melihat apakah salah satu antarmuka
diperlukan atau apakah ada interaksi lain.
Menerapkan antarmuka fokus
Cara termudah untuk menerapkan IFocusableTree
atau IFocusableNode
adalah dengan memperluas class yang menerapkan antarmuka ini. Misalnya, jika Anda membuat kotak alat kustom, perluas Toolbox
, yang menerapkan IFocusableTree
dan IFocusableNode
. Jika Anda membuat kolom kustom, perluas Field
, yang
mengimplementasikan IFocusableNode
. Pastikan untuk memeriksa bahwa kode Anda tidak mengganggu kode antarmuka fokus di class dasar.
Jika Anda memperluas class yang menerapkan antarmuka fokus, Anda umumnya tidak perlu mengganti metode apa pun. Pengecualian yang paling umum adalah
IFocusableNode.canBeFocused
, yang perlu Anda ganti jika Anda tidak ingin
pengguna membuka komponen Anda.
Lebih jarang diperlukan untuk mengganti metode callback fokus (onTreeFocus
dan onTreeBlur
di IFocusableTree
dan onNodeFocus
serta onNodeBlur
di
IFocusableNode
). Perhatikan bahwa upaya untuk mengubah fokus (panggilan
FocusManager.focusNode
atau FocusManager.focusTree
) dari metode ini akan
menghasilkan pengecualian.
Jika menulis komponen kustom dari awal, Anda harus menerapkan sendiri antarmuka
fokus. Lihat dokumentasi referensi untuk
IFocusableTree
dan
IFocusableNode
untuk mengetahui informasi selengkapnya.
Setelah menerapkan class, uji class tersebut terhadap plugin navigasi keyboard untuk memverifikasi bahwa Anda dapat (atau tidak dapat) membuka komponen.
Menggunakan pengelola fokus
Beberapa class kustom menggunakan pengelola fokus. Alasan paling umum untuk melakukan hal ini adalah untuk mendapatkan node yang saat ini difokuskan dan untuk memfokuskan node yang berbeda. Untuk mendapatkan
pengelola fokus, panggil Blockly.getFocusManager
:
const focusManager = Blockly.getFocusManager();
Untuk mendapatkan node yang saat ini difokuskan, panggil getFocusedNode
:
const focusedNode = focusManager.getFocusedNode();
// Do something with the focused node.
Untuk memindahkan fokus ke node lain, panggil focusNode
:
// Move focus to a different block.
focusManager.focusNode(myOtherBlock);
Untuk memindahkan fokus ke pohon, panggil focusTree
. Tindakan ini juga menetapkan fokus node pada
node root pohon.
// Move focus to the main workspace.
focusManager.focusTree(myMainWorkspace);
Alasan umum lainnya untuk menggunakan pengelola fokus adalah untuk mengambil dan menampilkan fokus sementara. Fungsi takeEphemeralFocus
menampilkan lambda yang harus Anda panggil untuk
menampilkan fokus sementara.
const returnEphemeralFocus = focusManager.takeEphemeralFocus();
// Do something.
returnEphemeralFocus();
Jika Anda menggunakan
WidgetDiv
atau
DropDownDiv
,
keduanya akan menangani fokus sementara untuk Anda.
Tab stops
Sistem fokus menetapkan tab stop (tabindex
dari 0
) pada elemen root semua
pohon (ruang kerja utama, toolbox, dan ruang kerja flyout). Hal ini memungkinkan
pengguna menggunakan tombol tab untuk menjelajahi area utama editor Blockly
dan kemudian (menggunakan plugin navigasi keyboard) menggunakan tombol panah untuk menjelajahi
area tersebut. Jangan mengubah tab stop ini karena akan mengganggu kemampuan pengelola fokus untuk mengelolanya.
Anda umumnya harus menghindari penetapan tab stop pada elemen DOM lain yang digunakan oleh Blockly, karena hal ini mengganggu model Blockly dalam menggunakan tombol tab untuk berpindah antar-area editor dan tombol panah dalam area tersebut. Selain itu, tab stop tersebut mungkin tidak berfungsi sebagaimana mestinya. Hal ini karena setiap node yang dapat difokuskan mendeklarasikan elemen DOM sebagai elemen yang dapat difokuskan. Jika Anda menetapkan tab stop pada turunan elemen yang dapat difokuskan dan pengguna menekan tab ke elemen tersebut, pengelola fokus akan memindahkan fokus DOM ke elemen yang dapat difokuskan yang dideklarasikan.
Anda dapat menyetel tab stop dengan aman pada elemen di aplikasi Anda yang berada di luar editor Blockly. Saat pengguna berpindah dari editor ke elemen tersebut, pengelola fokus mengubah fokus Blockly dari aktif menjadi pasif. Untuk aksesibilitas, Anda harus menetapkan properti tabindex
ke 0
atau -1
, seperti yang direkomendasikan oleh peringatan dalam deskripsi atribut tabindex
di MDN.
Fokus DOM
Untuk alasan aksesibilitas, aplikasi harus menghindari pemanggilan metode focus
pada elemen DOM. Hal ini membingungkan pengguna pembaca layar, karena mereka tiba-tiba dipindahkan ke lokasi yang tidak diketahui dalam aplikasi.
Masalah tambahan adalah pengelola fokus bereaksi terhadap peristiwa fokus dengan
menetapkan fokus DOM pada ancestor-or-self terdekat dari elemen yang difokuskan yang
merupakan elemen yang dapat difokuskan yang dideklarasikan. Hal ini mungkin berbeda dari elemen tempat
focus
dipanggil. (Jika tidak ada ancestor-or-self yang dapat difokuskan terdekat, seperti
saat focus
dipanggil pada elemen di luar editor Blockly, pengelola
fokus hanya mengubah node yang difokuskan secara aktif menjadi fokus pasif.)
Objek yang dapat diposisikan
Dapat diposisikan adalah komponen yang diposisikan di atas ruang kerja dan
menerapkan
IPositionable
.
Contohnya adalah tempat sampah dan ransel di plugin ransel.
Positionable belum diintegrasikan ke dalam sistem fokus.