Sistem fokus

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 sebagai IFocusableNode.

    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:

  1. Plugin navigasi keyboard:
    • Menerima peristiwa penekanan tombol.
    • Meminta sistem navigasi (bagian dari Blockly inti) untuk memindahkan fokus ke komponen "berikutnya".
  2. Sistem navigasi:
    • Meminta pengelola fokus komponen mana yang memiliki fokus Blockly. Pengelola fokus menampilkan blok sebagai IFocusableNode.
    • Menentukan bahwa IFocusableNode adalah BlockSvg 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.
  3. 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:

  1. Menerima peristiwa DOM focusout pada elemen DOM kolom pertama dan peristiwa focusin pada elemen DOM kolom kedua.
  2. Menentukan bahwa elemen DOM yang menerima fokus sesuai dengan kolom kedua.
  3. Memperbarui statusnya untuk menyetel fokus Blockly pada kolom kedua. (Pengelola fokus tidak perlu menyetel fokus DOM karena browser sudah melakukannya.)
  4. 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 dan IFocusableNode.

  • 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 dan IIcon memperluas IFocusableNode.

  • 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 menerapkan IFocusableNode karena disimpan dalam FlyoutItem dan konstruktor FlyoutItem memerlukan IFocusableNode.

  • Class yang memperluas class yang mengimplementasikan IFocusableNode.

    Misalnya, ToolboxSeparator memperluas ToolboxItem, yang mengimplementasikan IFocusableNode. 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 atau DropDownDiv 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 atau IVariableMap.

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.