- vừa được xem lúc

Nhật ký phát triển GõKey - Tuần 4

0 0 5

Người đăng: Huy Tran

Theo The Full Snack

<- Quay về trang chủ

Nhật ký phát triển GõKey - Tuần 4

Thật là khó tin khi lần đầu tiên mình có một tuần bận việc ở công ty sấp mặt, không có cả thời gian để làm side project luôn

Số là mình vừa được chuyển từ vị trí Frontend sang Backend, tất nhiên là vẫn ở công ty hiện tại chứ không có nhảy việc, nhưng vừa phải bơi theo task vừa phải học lại đủ thứ để theo kịp chúng đồng nghiệp, sấp mặt. May mắn là vẫn được giữ nguyên title, nên lương không đổi Anyway, lan man quá, chuyện này chắc để dành kể ở một bài viết khác. Hy vọng tuần này có nhiều thời gian hơn để làm GõKey.

Chức năng system tray đã đề cập ở tuần trước thì tạm thời mình gác lại để tập trung vào các chức năng khác (vì làm mấy cái khác dễ hơn )

Ngày 02/07/2023

Chức năng tuỳ chỉnh hotkey (phần 1)

Hiện tại thì GõKey đang được hardcode để nhận diện tổ hợp phím ⌃ ⌘ Space cho việc bật/tắt chế độ gõ tiếng Việt, cho nên hôm nay mình implement vài thứ lặt vặt để chuẩn bị cho việc tuỳ chỉnh hotkey.

Mục tiêu là, về sau, chúng ta có thể define ra một file chứa thông tin cấu hình cho bộ gõ, file này sẽ có dạng như sau:

input_method = "TELEX"
shortcut_key = "super+ctrl+space"

Người dùng có thể thay đổi file này "bằng tay" hoặc thay đổi từ UI của bộ gõ. Để handle việc cấu hình phím tắt (shortcut_key), thì mình làm các bước sau:

  1. Implement một struct mới tên là Hotkey để cấu hình phím tắt.

    Struct này có khả năng nhận diện một string đầu vào dưới dạng "<key> + <key> + ...", ví dụ như "super + ctrl + H", "super + shift + alt + enter", và chuyển nó thành 1 biến kiểu char và 1 biến kiểu KeyModifier.

let hotkey = Hotkey::from("super+ctrl+space");

Khi Event Tap nhận được một event mới, thì chúng ta sẽ kiểm tra cặp giá trị (KeyModifier, Char) từ Event Tap với struct này, nếu các giá trị match với nhau thì tức là hotkey đang được nhấn:

fn event_handler(...) -> bool { match keycode { Some(keycode) => { if hotkey.is_match(modifiers, &keycode) { // hotkey pressed }
...

Ở bên dưới thì KeyModifier chỉ là một giá trị số nguyên dương 32-bit (u32), nên hàm Hotkey::is_match chỉ đơn giản là thực hiện phép so sánh giữa các giá trị cơ bản như u32, char:

pub fn is_match(&self, modifiers: KeyModifier, keycode: &char) -> bool { return self.modifiers == modifiers && self.keycode.eq_ignore_ascii_case(keycode);
}
  1. Render một giá trị kiểu Hotkey trên UI.

    Sau khi tạo một object Hotkey rồi thì mình muốn tận dụng luôn object này để hiên trị dãy phím tắt đã được cấu hình trên UI luôn, để làm được việc này, thì chỉ cần implement trait [std::fmt::Display](https://doc.rust-lang.org/std/fmt/trait.Display.html) cho struct Hotkey, và sau đó chúng ta có thể sử dụng hàm Hotkey::to_string() để hiển thị dãy phím tắt và render lên UI.

impl Display for Hotkey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.modifiers.is_control() { // Phím ⌃ Control } if self.modifiers.is_super() { // Phím ⌘ Cmd trên macÓ } ... write!(f, "{}", keys) }
}

Bằng cách này thì chúng ta còn có thể kiểm soát được thứ tự render của các phím modifiers. Ví dụ input khi khởi tạo Hotkey được viết dưới dạng "super+ctrl+k", nhưng khi hiển thị, phím Control thường được render trước phím ⌘ Cmd.

Đến đây thì còn có một điểm tiện lợi nữa, đó là, trước đây chúng ta chia mã nguồn của từng hệ điều hành thành từng module riêng lẽ, ví dụ src/platform/macos.rs cho macOS, src/platform/windows.rs cho Windows. Nên việc define kí tự hiển thị cho từng phím modifier có thể được đưa vào từng module riêng của từng hệ điều hành, ví dụ phím Super của macOS là phím ⌘ Cmd, nhưng trên Windows thì nó là phím ⊞ Win, việc này có thể được handle bằng cách define kí tự hiển thị cho phím Super riêng ở riêng mỗi hệ điều hành:

// file macos.rs
pub const SYMBOL_SUPER: &str = "⌘"; // file windows.rs
pub const SYMBOL_SUPER: &str = "⊞";

Phần implement chi tiết các bạn có thể xem tại commit a16e2cc trên Github. Sau đây là screenshot tạm:

Với cấu trúc hiện tại, toàn bộ thông tin của bộ gõ (như kiểu gõ, chế độ tiếng Việt,…) được giữ trong INPUT_STATE , nên có thể coi đây là single source of truth, ngoài ra không có bất cứ chỗ nào có thể chứa các thông tin này (để tiện quản lý).

À trừ một trường hợp, đó là UIDataAdapter, có chứa một bản tham chiếu của trạng thái gõ tiếng Việt (INPUT_STATE.is_enabled()) và chế độ gõ hiện tại (INPUT_STATE.typing_method()) để làm nhiệm vụ hiển thị trên UI. Từ phía UI, khi có thay đổi xảy ra, thì các giá trị sẽ được ghi trở lại vào UIDataAdapter và từ đó mình phải catch được những gì đã thay đổi, truyền lại đến INPUT_STATE

Để bảo đảm INPUT_STATE vẫn là single source of truth, thì mình chỉ có thể sử dụng ConfigManager để khởi tạo giá trị ban đầu cho INPUT_STATE khi bộ gõ khởi động, và thực hiện thao tác ghi config vào file từ các hàm setter của INPUT_STATE , và sau một hồi suy nghĩ thì mình nghĩ là sự tồn tại của ConfigManager có vẻ hơi thừa, đáng ra chỉ cần dùng mỗi ConfigStore

Nhưng mà thôi, cứ tạm note lại rồi sang tuần sau làm tiếp. 😔


Có một điều khá vui là dạo gần đây có nhiều bạn đã clone project về và tự compile để sử dụng, và report bug. Mặc dù thấy bug thì khá mệt nhưng mà mình rất vui vì ít ra như thế tức là đã bắt đầu có user rất cảm ơn các bạn đã nhiệt tình ủng hộ dự án, và mình hy vọng sẽ nhận được nhiều bug report hơn nữa.

Như đã đề cập ở đầu bài thì mấy tuần gần đây dự án tiến triển rất chậm, nên devlog ra không đều và không nhiều, rất mong các bạn thông cảm. Sau khi ổn định lại thì mình sẽ chăm viết devlog hơn :D Ngoài ra, devlog này chỉ cover những gì xảy ra trong dự án GõKey, rất nhiều những vấn đề hay ho về gõ tiếng Việt được xử lý từ phía vi-rs, mình nghĩ các bạn nên gây áp lực để tác giả vi-rs chịu ngồi xuống và viết devlog hàng tuần giống như mình :smiling_imp:.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Chuyện cái comment

Chuyện cái comment. Chuyện rằng, có một ông bạn nọ có cái blog ở trên mạng, cũng có dăm.

0 0 19

- vừa được xem lúc

Đừng đánh nhau với borrow checker

Đừng đánh nhau với borrow checker. TL;DR: Đừng bao giờ đánh nhau với borrow checker, nó được sinh ra để bạn phải phục tùng nó .

0 0 19

- vừa được xem lúc

Chuyện biểu diễn ma trận trên máy tính

Chuyện biểu diễn ma trận trên máy tính. Cách đây mấy hôm mình có share cái screenshot trên Facebook, khoe linh tinh vụ mình đang viết lại cái CHIP-8 emulator bằng Rust.

0 0 32

- vừa được xem lúc

Rust và Lập trình Web

Rust và Lập trình Web. Bài viết được dịch lại từ bản gốc "Rust for the Web" đăng tại phiên bản blog tiếng Anh của mình.

0 0 26

- vừa được xem lúc

Viết ứng dụng đọc tin HackerNews bằng Rust

Viết ứng dụng đọc tin HackerNews bằng Rust. Dạo này mình toàn viết bài linh tinh, lâu rồi chưa thấy viết bài kĩ thuật nào mới nên hôm nay mình viết trở lại, mất công các bạn lại bảo mình không biết co

0 0 13

- vừa được xem lúc

Cài đặt Rust trên Arch Linux

Cài đặt Rust trên Arch Linux. Việc cài đặt Rust trên môi trường Arch Linux khá là đơn giản.

0 0 28