Hệ thống tiêu điểm theo dõi vị trí (tiêu điểm) của người dùng trong trình chỉnh sửa Blockly. Thành phần này được Blockly và mã tuỳ chỉnh dùng để xác định thành phần nào (khối, trường, danh mục hộp công cụ, v.v.) hiện có tiêu điểm và để di chuyển tiêu điểm đó sang một thành phần khác.
Điều quan trọng là bạn phải hiểu hệ thống lấy nét để có thể đảm bảo mã tuỳ chỉnh của bạn hoạt động chính xác với hệ thống này.
Kiến trúc
Hệ thống lấy nét có 3 phần:
FocusManager
là một singleton điều phối tiêu điểm trên tất cả Blockly. Thành phần này được Blockly và mã tuỳ chỉnh dùng để tìm hiểu thành phần nào có tiêu điểm Blockly, cũng như để di chuyển tiêu điểm Blockly sang một thành phần khác. Thành phần này cũng theo dõi các sự kiện tiêu điểm DOM, đồng bộ hoá tiêu điểm Blockly và tiêu điểm DOM, đồng thời quản lý các lớp CSS cho biết thành phần nào có tiêu điểm.Trình quản lý tiêu điểm chủ yếu được Blockly sử dụng. Đôi khi, mã tuỳ chỉnh dùng thành phần này để tương tác với hệ thống tiêu điểm.
IFocusableTree
là một khu vực độc lập của trình chỉnh sửa Blockly, chẳng hạn như không gian làm việc hoặc hộp công cụ. Thành phần này bao gồm các nút có thể lấy tiêu điểm, chẳng hạn như các khối và trường. Cây cũng có thể có cây con. Ví dụ: không gian làm việc của đối tượng sửa đổi trên một khối trong không gian làm việc chính là một cây con của không gian làm việc chính.IFocusableTree
chủ yếu được trình quản lý tiêu điểm sử dụng. Trừ phi viết một hộp công cụ tuỳ chỉnh, nếu không có lẽ bạn sẽ không cần triển khai nó.IFocusableNode
là một thành phần Blockly có thể lấy tiêu điểm, chẳng hạn như khối, trường hoặc danh mục hộp công cụ. Các nút có thể lấy tiêu điểm có một phần tử DOM hiển thị nút và có tiêu điểm DOM khi nút có tiêu điểm Blockly. Xin lưu ý rằng cây cũng là các nút có thể lấy tiêu điểm. Ví dụ: bạn có thể tập trung vào không gian làm việc nói chung.Các phương thức trong
IFocusableNode
chủ yếu được trình quản lý tiêu điểm gọi.Bản thân
IFocusableNode
được dùng để biểu thị thành phần có tiêu điểm. Ví dụ: khi người dùng chọn một mục trên trình đơn ngữ cảnh của một khối, khối đó sẽ được truyền đến hàm gọi lại của mục dưới dạng mộtIFocusableNode
.Nếu viết các thành phần tuỳ chỉnh, bạn có thể cần triển khai
IFocusableNode
.
Các loại tiêu điểm
Hệ thống lấy nét xác định một số loại tiêu điểm khác nhau.
Tiêu điểm Blockly và tiêu điểm DOM
Hai loại tâm điểm chính là tâm điểm Blockly và tâm điểm DOM.
Tiêu điểm Blockly chỉ định thành phần Blockly nào (khối, trường, danh mục hộp công cụ, v.v.) có tiêu điểm. Bạn cần phải làm việc ở cấp độ của các thành phần Blockly. Ví dụ: trình bổ trợ điều hướng bằng bàn phím cho phép người dùng sử dụng các phím mũi tên để di chuyển từ thành phần này sang thành phần khác, chẳng hạn như từ một khối sang một trường. Tương tự, hệ thống trình đơn ngữ cảnh sẽ tạo một trình đơn phù hợp với thành phần hiện tại – tức là hệ thống này sẽ tạo các trình đơn khác nhau cho không gian làm việc, khối và bình luận trong không gian làm việc.
Tiêu điểm DOM chỉ định phần tử DOM nào có tiêu điểm. Điều này là cần thiết để hoạt động ở cấp độ các phần tử DOM. Ví dụ: trình đọc màn hình trình bày thông tin về phần tử hiện có tiêu điểm DOM và các thẻ di chuyển (thay đổi tiêu điểm) từ phần tử DOM sang phần tử DOM.
Trình quản lý tiêu điểm duy trì trạng thái đồng bộ giữa tiêu điểm Blockly và tiêu điểm DOM, vì vậy, khi một nút (thành phần Blockly) có tiêu điểm Blockly, phần tử DOM cơ bản của nút đó sẽ có tiêu điểm DOM và ngược lại.
Tiêu điểm chủ động và tiêu điểm thụ động
Tiêu điểm Blockly được chia thành tiêu điểm đang hoạt động và tiêu điểm thụ động. Tiêu điểm hoạt động có nghĩa là một nút sẽ nhận được hoạt động đầu vào của người dùng, chẳng hạn như một lượt nhấn phím. Tiêu điểm thụ động có nghĩa là một nút trước đây có tiêu điểm hoạt động, nhưng đã mất tiêu điểm đó khi người dùng chuyển đến một nút trong cây khác (ví dụ: họ chuyển từ không gian làm việc sang hộp công cụ) hoặc rời khỏi trình chỉnh sửa Blockly hoàn toàn. Nếu cây lấy lại tiêu điểm, thì nút được lấy tiêu điểm thụ động sẽ lấy lại tiêu điểm hoạt động.
Mỗi cây có một bối cảnh tiêu điểm riêng. Tức là tối đa một nút trong cây có thể có tiêu điểm. Tiêu điểm đó ở trạng thái chủ động hay thụ động phụ thuộc vào việc cây có tiêu điểm hay không. Trên toàn bộ trang, có thể có tối đa một nút có tiêu điểm đang hoạt động.
Trình quản lý tiêu điểm sử dụng các điểm nổi bật (lớp CSS) khác nhau cho các nút được lấy tiêu điểm chủ động và thụ động. Các nút này giúp người dùng biết họ đang ở đâu và sẽ quay lại đâu.
Tiêu điểm tạm thời
Ngoài ra còn có một loại tiêu điểm khác gọi là tiêu điểm tạm thời. Các quy trình riêng biệt, chẳng hạn như hộp thoại hoặc trình chỉnh sửa trường, yêu cầu tiêu điểm tạm thời từ trình quản lý tiêu điểm. Khi trình quản lý tiêu điểm cấp tiêu điểm tạm thời, trình quản lý này sẽ tạm ngưng hệ thống tiêu điểm. Trên thực tế, điều này có nghĩa là những quy trình như vậy có thể ghi lại và hành động dựa trên các sự kiện tiêu điểm DOM mà không phải lo lắng rằng hệ thống tiêu điểm cũng có thể hành động dựa trên các sự kiện đó.
Khi trình quản lý tiêu điểm cấp tiêu điểm tạm thời, trình quản lý này sẽ thay đổi nút đang được lấy tiêu điểm thành tiêu điểm thụ động. Thao tác này khôi phục tiêu điểm hoạt động khi tiêu điểm tạm thời được trả về.
Ví dụ
Các ví dụ sau đây minh hoạ cách Blockly sử dụng hệ thống tiêu điểm. Các ví dụ này sẽ giúp bạn hiểu cách mã của bạn phù hợp với hệ thống lấy tiêu điểm và cách mã của bạn có thể sử dụng hệ thống lấy tiêu điểm.
Di chuyển tiêu điểm bằng bàn phím
Giả sử một khối có 2 trường có tiêu điểm Blockly, như được biểu thị bằng một điểm đánh dấu (lớp CSS) trên phần tử DOM của khối. Bây giờ, giả sử người dùng nhấn vào mũi tên bên phải:
- Trình bổ trợ điều hướng bằng bàn phím:
- Nhận một sự kiện nhấn phím.
- Yêu cầu hệ thống điều hướng (một phần của Blockly cốt lõi) di chuyển tiêu điểm đến thành phần "tiếp theo".
- Hệ thống định vị:
- Hỏi trình quản lý tiêu điểm thành phần nào có tiêu điểm Blockly. Trình quản lý tiêu điểm trả về khối dưới dạng
IFocusableNode
. - Xác định rằng
IFocusableNode
là mộtBlockSvg
và xem xét các quy tắc của nó để điều hướng các khối, trong đó nêu rõ rằng nó sẽ di chuyển tiêu điểm Blockly từ khối nói chung đến trường đầu tiên trên khối. - Yêu cầu trình quản lý tiêu điểm di chuyển tiêu điểm Blockly đến trường đầu tiên.
- Hỏi trình quản lý tiêu điểm thành phần nào có tiêu điểm Blockly. Trình quản lý tiêu điểm trả về khối dưới dạng
- Trình quản lý tiêu điểm:
- Cập nhật trạng thái để đặt tiêu điểm Blockly vào trường đầu tiên.
- Đặt tiêu điểm DOM trên phần tử DOM của trường.
- Di chuyển lớp làm nổi bật từ phần tử của khối sang phần tử của trường.
Di chuyển tiêu điểm bằng chuột
Giả sử người dùng nhấp vào trường thứ hai trên khối. Trình quản lý tiêu điểm:
- Nhận một sự kiện
focusout
DOM trên phần tử DOM của trường đầu tiên và một sự kiệnfocusin
trên phần tử DOM của trường thứ hai. - Xác định rằng phần tử DOM nhận được tiêu điểm tương ứng với trường thứ hai.
- Cập nhật trạng thái để đặt tiêu điểm Blockly vào trường thứ hai. (Trình quản lý tiêu điểm không cần đặt tiêu điểm DOM vì trình duyệt đã thực hiện việc này.)
- Di chuyển lớp làm nổi bật từ phần tử của trường đầu tiên sang phần tử của trường thứ hai.
Ví dụ khác
Sau đây là một số ví dụ khác:
Khi người dùng kéo một khối từ hộp công cụ vào không gian làm việc, trình xử lý sự kiện chuột sẽ tạo một khối mới và gọi trình quản lý tiêu điểm để đặt tiêu điểm Blockly trên khối đó.
Khi một khối bị xoá, phương thức
dispose
của khối đó sẽ gọi trình quản lý tiêu điểm để di chuyển tiêu điểm đến phần tử mẹ của khối.Phím tắt sử dụng
IFocusableNode
để xác định thành phần Blockly mà phím tắt áp dụng.Trình đơn theo bối cảnh sử dụng
IFocusableNode
để xác định thành phần Blockly mà trình đơn được gọi.
Chế độ tuỳ chỉnh và hệ thống lấy tiêu điểm
Khi tuỳ chỉnh Blockly, bạn cần đảm bảo rằng mã của mình hoạt động chính xác với hệ thống tiêu điểm. Bạn cũng có thể sử dụng hệ thống tiêu điểm để xác định và đặt nút hiện đang được lấy tiêu điểm.
Khối tuỳ chỉnh và nội dung hộp công cụ
Cách phổ biến nhất để tuỳ chỉnh Blockly là xác định các khối tuỳ chỉnh và tuỳ chỉnh nội dung của hộp công cụ. Không có hành động nào trong số này ảnh hưởng đến hệ thống tiêu điểm.
Lớp học tuỳ chỉnh
Các lớp tuỳ chỉnh có thể cần triển khai một hoặc cả hai giao diện tiêu điểm (IFocusableTree
và IFocusableNode
). Không phải lúc nào cũng rõ ràng khi nào thì cần làm như vậy.
Một số lớp rõ ràng cần triển khai các giao diện tiêu điểm. bao gồm:
Một lớp triển khai hộp công cụ tuỳ chỉnh. Lớp này cần triển khai
IFocusableTree
vàIFocusableNode
.Các lớp tạo một thành phần hiển thị (chẳng hạn như trường hoặc biểu tượng) mà người dùng có thể chuyển đến. Các lớp này cần triển khai
IFocusableNode
.
Một số lớp cần triển khai IFocusableNode
mặc dù chúng không tạo thành phần hiển thị hoặc chúng tạo thành phần hiển thị mà người dùng không thể chuyển đến. bao gồm:
Các lớp triển khai một giao diện mở rộng
IFocusableNode
.Ví dụ: biểu tượng di chuyển trong trình bổ trợ điều hướng bằng bàn phím sẽ hiển thị một mũi tên bốn chiều cho biết rằng bạn có thể di chuyển khối bằng các phím mũi tên. Bản thân biểu tượng này không xuất hiện (mũi tên bốn chiều là một bong bóng) và người dùng không thể chuyển đến biểu tượng này. Tuy nhiên, biểu tượng phải triển khai
IFocusableNode
vì các biểu tượng triển khaiIIcon
vàIIcon
mở rộngIFocusableNode
.Các lớp được dùng trong một API yêu cầu phải có
IFocusableNode
.Ví dụ: lớp
FlyoutSeparator
tạo khoảng trống giữa 2 mục trong một bảng chọn. Thành phần này không tạo ra bất kỳ phần tử DOM nào, vì vậy, thành phần này không có thành phần hiển thị và người dùng không thể chuyển đến thành phần này. Tuy nhiên, mã này phải triển khaiIFocusableNode
vì được lưu trữ trongFlyoutItem
và hàm khởi tạoFlyoutItem
yêu cầuIFocusableNode
.Các lớp mở rộng một lớp triển khai
IFocusableNode
.Ví dụ:
ToolboxSeparator
mở rộngToolboxItem
, triển khaiIFocusableNode
. Mặc dù các đường phân cách trong hộp công cụ có một thành phần hiển thị, nhưng người dùng không thể chuyển đến các đường phân cách này vì họ không thể thực hiện hành động và không có nội dung hữu ích.
Các lớp khác tạo ra những thành phần mà người dùng có thể chuyển đến nhưng không cần triển khai IFocusableNode
. bao gồm:
- Các lớp tạo một thành phần hiển thị quản lý tiêu điểm của riêng thành phần đó, chẳng hạn như trình chỉnh sửa trường hoặc hộp thoại. (Xin lưu ý rằng các lớp như vậy cần lấy tiêu điểm tạm thời khi bắt đầu và trả lại tiêu điểm đó khi kết thúc. Việc sử dụng
WidgetDiv
hoặcDropDownDiv
sẽ xử lý việc này cho bạn.)
Cuối cùng, một số lớp không tương tác với hệ thống lấy tiêu điểm và không cần triển khai IFocusableTree
hoặc IFocusableNode
. bao gồm:
Các lớp tạo một thành phần hiển thị mà người dùng không thể chuyển đến hoặc thực hiện hành động và không chứa thông tin mà trình đọc màn hình có thể sử dụng. Ví dụ: một phông nền chỉ mang tính trang trí trong trò chơi.
Các lớp hoàn toàn không liên quan đến hệ thống tiêu điểm, chẳng hạn như các lớp triển khai
IMetricsManager
hoặcIVariableMap
.
Nếu bạn không chắc chắn liệu lớp học của mình có tương tác với hệ thống tiêu điểm hay không, hãy kiểm thử lớp học đó bằng trình bổ trợ điều hướng bằng bàn phím. Nếu không được, bạn có thể cần triển khai IFocusableTree
hoặc IFocusableNode
. Nếu thành công nhưng bạn vẫn không chắc chắn, hãy đọc mã sử dụng lớp của bạn để xem có cần giao diện nào không hoặc có tương tác nào khác không.
Triển khai các giao diện tiêu điểm
Cách dễ nhất để triển khai IFocusableTree
hoặc IFocusableNode
là mở rộng một lớp triển khai các giao diện này. Ví dụ: nếu bạn đang tạo một hộp công cụ tuỳ chỉnh, hãy mở rộng Toolbox
, triển khai IFocusableTree
và IFocusableNode
. Nếu bạn đang tạo một trường tuỳ chỉnh, hãy mở rộng Field
, triển khai IFocusableNode
. Hãy nhớ kiểm tra để đảm bảo mã của bạn không can thiệp vào mã giao diện tiêu điểm trong lớp cơ sở.
Nếu mở rộng một lớp triển khai giao diện tiêu điểm, bạn thường không cần ghi đè bất kỳ phương thức nào. Ngoại lệ phổ biến nhất là IFocusableNode.canBeFocused
. Bạn cần ghi đè ngoại lệ này nếu không muốn người dùng chuyển đến thành phần của bạn.
Ít phổ biến hơn là nhu cầu ghi đè các phương thức gọi lại tiêu điểm (onTreeFocus
và onTreeBlur
trong IFocusableTree
và onNodeFocus
và onNodeBlur
trong IFocusableNode
). Lưu ý rằng việc cố gắng thay đổi tiêu điểm (gọi FocusManager.focusNode
hoặc FocusManager.focusTree
) từ các phương thức này sẽ dẫn đến một ngoại lệ.
Nếu tự viết một thành phần tuỳ chỉnh, bạn sẽ cần tự triển khai các giao diện tiêu điểm. Hãy xem tài liệu tham khảo về IFocusableTree
và IFocusableNode
để biết thêm thông tin.
Sau khi triển khai lớp, hãy kiểm thử lớp đó với trình bổ trợ điều hướng bằng bàn phím để xác minh rằng bạn có thể (hoặc không thể) điều hướng đến thành phần của mình.
Sử dụng trình quản lý tiêu điểm
Một số lớp tuỳ chỉnh sử dụng trình quản lý tiêu điểm. Lý do phổ biến nhất để làm việc này là lấy nút hiện đang được lấy tiêu điểm và lấy tiêu điểm vào một nút khác. Để lấy trình quản lý tiêu điểm, hãy gọi Blockly.getFocusManager
:
const focusManager = Blockly.getFocusManager();
Để lấy nút hiện đang được đặt tiêu điểm, hãy gọi getFocusedNode
:
const focusedNode = focusManager.getFocusedNode();
// Do something with the focused node.
Để di chuyển tiêu điểm đến một nút khác, hãy gọi focusNode
:
// Move focus to a different block.
focusManager.focusNode(myOtherBlock);
Để di chuyển tiêu điểm đến một cây, hãy gọi focusTree
. Thao tác này cũng đặt tiêu điểm nút vào nút gốc của cây.
// Move focus to the main workspace.
focusManager.focusTree(myMainWorkspace);
Một lý do phổ biến khác để sử dụng trình quản lý tiêu điểm là lấy và trả lại tiêu điểm tạm thời. Hàm takeEphemeralFocus
trả về một lambda mà bạn phải gọi để trả về tiêu điểm tạm thời.
const returnEphemeralFocus = focusManager.takeEphemeralFocus();
// Do something.
returnEphemeralFocus();
Nếu bạn sử dụng WidgetDiv
hoặc DropDownDiv
, các thành phần này sẽ xử lý tiêu điểm tạm thời cho bạn.
Vị trí đánh dấu tab
Hệ thống tiêu điểm đặt một điểm dừng phím tab (tabindex
của 0
) trên phần tử gốc của tất cả các cây (không gian làm việc chính, hộp công cụ và không gian làm việc bật lên). Điều này cho phép người dùng sử dụng phím tab để di chuyển xung quanh các vùng chính của trình chỉnh sửa Blockly, sau đó (bằng cách sử dụng trình bổ trợ điều hướng bằng bàn phím) sử dụng các phím mũi tên để di chuyển trong các vùng đó. Đừng thay đổi các điểm dừng phím tab này, vì điều này sẽ ảnh hưởng đến khả năng quản lý các điểm dừng phím tab của trình quản lý tiêu điểm.
Nhìn chung, bạn nên tránh đặt các điểm dừng phím tab trên các phần tử DOM khác mà Blockly sử dụng, vì điều này sẽ ảnh hưởng đến mô hình sử dụng phím tab của Blockly để di chuyển giữa các vùng của trình chỉnh sửa và các phím mũi tên trong các vùng đó. Ngoài ra, các dấu tab như vậy có thể không hoạt động như dự kiến. Điều này là do mỗi nút có thể lấy tiêu điểm đều khai báo một phần tử DOM làm phần tử có thể lấy tiêu điểm. Nếu bạn đặt một điểm dừng phím tab trên một phần tử con của phần tử có thể lấy tiêu điểm và người dùng nhấn phím tab vào phần tử đó, trình quản lý tiêu điểm sẽ di chuyển tiêu điểm DOM đến phần tử có thể lấy tiêu điểm đã khai báo.
Bạn có thể đặt các điểm dừng phím Tab trên những phần tử trong ứng dụng nằm ngoài trình chỉnh sửa Blockly. Khi người dùng nhấn phím Tab từ trình chỉnh sửa đến một phần tử như vậy, trình quản lý tiêu điểm sẽ thay đổi tiêu điểm Blockly từ hoạt động sang thụ động. Để hỗ trợ tiếp cận, bạn nên đặt thuộc tính tabindex
thành 0
hoặc -1
, theo khuyến nghị của cảnh báo trong nội dung mô tả của MDN về thuộc tính tabindex
.
Tiêu điểm DOM
Vì lý do hỗ trợ tiếp cận, các ứng dụng nên tránh gọi phương thức focus
trên các phần tử DOM. Điều này khiến người dùng trình đọc màn hình bị mất phương hướng vì họ đột ngột được chuyển đến một vị trí không xác định trong ứng dụng.
Một vấn đề khác là trình quản lý tiêu điểm phản ứng với các sự kiện tiêu điểm bằng cách đặt tiêu điểm DOM trên tổ tiên gần nhất hoặc chính phần tử được lấy tiêu điểm là một phần tử có thể lấy tiêu điểm đã khai báo. Điều này có thể khác với phần tử mà focus
được gọi. (Nếu không có tổ tiên hoặc chính nó có thể lấy tiêu điểm gần nhất, chẳng hạn như khi focus
được gọi trên một phần tử bên ngoài trình chỉnh sửa Blockly, trình quản lý tiêu điểm chỉ cần thay đổi nút đang được lấy tiêu điểm thành tiêu điểm thụ động.)
Positionables
Positionable là những thành phần nằm trên cùng của không gian làm việc và triển khai IPositionable
.
Ví dụ: thùng rác và ba lô trong trình bổ trợ ba lô.
Positionable chưa được tích hợp vào hệ thống tiêu điểm.