美文网首页
[研發日誌] 威注音輸入法的「去 Zonble 化」日記(一)

[研發日誌] 威注音輸入法的「去 Zonble 化」日記(一)

作者: 孫志貴 | 来源:发表于2022-09-07 23:28 被阅读0次

    前言

    一直以來,有相當數量的威注音輸入法的支持者們「希望威注音能盡量減少對上游程式內容的使用」。但最終使得身為主程式的本人下定決心這樣做的,則是出於別的原因。本文主要聊技術,就不多扯這些背後的原因了。

    這是第一篇「去 Zonble 化」日記,主要講威注音 2.3.1 版至 2.4.0 SP1 版當中對上游 Zonble 的特定組件的汰換與重寫。

    P.S.: 本文當中提到的 ctlInputMethod 是威注音輸入法對 IMKInputController 的實作型別、ctlCandidateIMK 是 IMKCandidates 的實作型別。KeyHandler 則是按鍵調度模組、已經被筆者在 2022 年四月用 Swift 重寫。另外,威注音 2.7.5 版開始,ctlInputMethod (ctlIME) 被更名為 SessionCtl,意為「會話控制器」。

    KeyHandlerInput 按鍵訊號載體單位

    牽涉版本:威注音 v2.3.1 與 v2.4.0。

    小麥注音的 KeyHanderInput 相當於把 NSEvent 讀取且翻譯成一個新的按鍵訊號載體單位類型。該組件在威注音當中被更名為 InputSignal,但與 InputSignalProtocol 是兩碼事情:前者遵循後者這個協議所規定的規範,而 KeyHandler 按鍵調度模組當中的函式可以處理任何符合該規範的物件。

    威注音在 v2.3.1 當中淘汰了該模組,轉而直接對 NSEvent 做了符合 InputSignalProtocol 的擴展。主要原因在於「IMK 選字窗只能處理 NSEvent」這個客觀事實。更何況,Zonble 在 KeyHanderInput 當中的很多實現,其實對目前的威注音而言並不經濟。

    對 NSEvent 做了這種擴展處理之後,就明顯減少了 NSEvent 按鍵訊號流從 ctlInputMethod 流往 IMKCandidates 的過程當中的任何不必要的類型轉換。在此之上,威注音 v2.4.0 又引入了「讓 NSEvent 迅速改掉自身的其中任意一處屬性」的全新重構函式(Reconstructor)、進一步減少了 ctlInputMethod 與 ctlCandidateIMK 的篇幅。

    不過呢,威注音從業火五筆輸入法承襲來的「用來判定 Shift 鍵是否有被敲過」的特殊按鍵判定函式要求傳入的 NSEvent 必須是 ctlInputMethod.handle(event:) 吃到的 raw event 且做過 guard-let 處理、然後直接交給特殊按鍵判定函式。不然的話,判定會失效。這與該判定函式本身所使用的方法有關(涉及到對 NX_DEVICELSHIFTKEYMASK 等 NX Mask 的判定處理),且 NSEvent 不是 Struct 而是 Class、在 Swift 當中就容易出現這方面的鬧鬼情況。

    InputState 有限態械系統+NSStringUtils

    牽涉版本:威注音 v2.4.0。

    Zonble 給小麥注音引入的有限態械,很像是參考自 Refactoring.guru Design Patterns 設計模式 網站的 State Pattern 狀態模式、且在 InputMethodController (小麥注音對 IMKInputController 的實作) 當中採用了與 Strategy Pattern 類似的方法來處理不同的狀態。

    但是,這個網站給出的方法(多型別有限態械),真的是唯一解嗎?

    答案顯然是「否」,只是我意識到這一點恐怕有點晚了。

    威注音在 v2.4.0 當中引入了全新的單結構態械系統「IMEState」,整個態械只用到一個 Struct、賦以針對不同 Struct 切換時的各種專有的建構函式(Constructor)。除去「每次切換狀態都要重新初期化一個狀態來取代舊狀態」以外,這樣的設計滿像一個「管家」:手頭的資料與既定事實都是固定的,但管家的行事模式與狀態則是另一回事。

    此外,IMEState 還有一個內部專用的資料型別 StateData,可以理解為管家身上帶著的固定的錢包:裡面有主人要花的錢、主人要用的各種證件,等。但是,管家在不同狀態下,可能會在將具體的物件拿給主人之前,做一些別的事情:錢不夠了,就去銀行取款;在不同的場合,掏出不同的證件。

    這種全新的單結構態械系統,比起之前使用的來自上游的多型別態械系統,更容易維護管理。特別是要在 IMK 的內文組字區的顯示形態繪製運算當中使用的各種 NSAttributedString、以及標記模式下的高亮選取範圍運算,都在新的 IMEState 當中得到了明顯的簡化處理。

    更甚者,這次的組字引擎也做了升級、可以單獨處理要在標記模式下使用的標記游標。這樣一來,KeyHandler 的任務輕鬆了不少,且威注音終於在原理上實現了在就地加詞刪詞時對詞音不等長的情形的正確應對處理。這些都使得 Zonble 的另一個模組「NSStringUtils」不再重要:每次在標記模式當中修改標記範圍時,對新的標記範圍的運算處理都是在組字引擎內產生的、然後被 KeyHandler 翻譯出來、變成更新過的標記狀態當中的資料的一部分。

    單結構態械系統也減輕了 ctlInputMethod 當中的狀態策略管理函式:不再像以前那樣給每種狀態都制定一個專用的處理函式了,而是用一個函式就夠了:這個函式內直接對傳入的狀態做 Switch Case 處理,且對「需要用到同種處理流程的一些狀態」做了合併處理,減少了程式碼的篇幅。

    InputSourceHelper

    牽涉版本:威注音 v2.4.0。

    這個模組無非就是對 CoreFoundation 的某些函式做 Swift 封裝。筆者直接採用了 Mzp 所著的 InputMethodKit 教材當中的方法來重寫,且也做了自己的工具擴充。這樣一來,整個 TISInputSource 體系變得更好用了。

    OVNonModalAlertWindow

    牽涉版本:威注音 v2.4.0。

    這個視窗一開始是 Lukhnos Liu 發明的,用來滿足在 macOS 早期版本下的某些功能需求。Zonble 將其用 Swift 重寫了,所以也算是被淘汰的對象。至少,NSAlert 更好用。但有一點得注意:NSAlert 盡量用 DispatchQueue.main.async 處理一下,否則會在某些型別當中崩潰掉。

    Emacs Key 支援

    牽涉版本:威注音 v2.4.0 SP2。

    Emacs 是某款 GNU 編輯器、與 VIM 一樣可以在終端機內編輯文本。上游本來有對這六種 Emacs 熱鍵的支援「游標向前、游標向後、Home、End、PC Delete、PageDown」。

    威注音一開始立案的時候,以為這是極少數小眾功能,就沒有在意該功能的維護,也一直以來都沒有宣稱說威注音有支援 Emacs Key。然而,最近有工單電郵提報相關功能故障,讓主程式師意識到 macOS 是有對 Emacs Key 提供原生支援的。

    既如此,那就修一下這個功能好了。然而,上游的由 Zonble 完成的既有實現,有如下的顯著問題:

    1. 增加了 KeyHandler 按鍵調度模組的判定工作量。
    2. 無法對 IMK 選字窗生效(因為 IMK 選字窗只吃 NSEvent)。
    3. 威注音輸入法會攔截一些無效的 charCode,防止組字引擎在接收這類資料時出現隨機的問題。這個功能會妨礙 Emacs Key 按鍵指令體系的傳入。

    既然如此,那就狸貓換太子好了:在 ctlInputMethod.handle(event: NSEvent!) 當中插入這種處理。實際實現方式是這樣的:

    1. 所有要直接送給 KeyHandler 的 NSEvent,均在 ctlInputMethod.handle(event: NSEvent!) 的下游函式 ctlInputMethod.commonEventHandler(event: NSEvent) 當中做翻譯轉換處理、且該處理必須放在 Shift 中英文切換判定之後。Shift 中英文切換判定要求傳入的 NSEvent 必須是原始 NSEvent、且不能被 var 處理過(必須由 guard let 傳遞真正存在的原始 NSEvent 事件)。
    2. 所有要送給 ctlCandidateIMK(IMKCandidates)的事件,均在 ctlInputMethod.handle(event: NSEvent!) 當中做翻譯轉換處理。

    上文也有提到威注音的 NSEvent 擁有「可以迅速便捷地自我重新構建、來置換掉指定的屬性資料值」的函式。這個翻譯轉換處理呢,就用到了這個函式。


    以下是威注音 v2.5.0 的已經確定推送的更新內容。

    TooltipController

    牽涉版本:威注音 v2.5.0。

    Zonble 原先的實現只適合橫排輸入時的顯示。一旦縱排輸入,整個文字提示視窗就還是橫排顯示的。威注音這次對該模組做了重寫,引入了 NSAttributedTextView 這個可以縱排顯示文字的 NSView (在 Fuziki 的同款功能的 UIView 的基礎上改來,且得到他本人的准許)。但是呢,因為系統字型的 vert 特性在顯示注音、漢語拼音、英文、阿拉伯數字時的效果非常糟糕,使得筆者決定做出如下處置:

    1. 在使用英文介面時,始終使用橫排顯示的工具提示。
    2. 在使用中文或日文介面時,允許使用者在偏好設定內啟用「始終使用橫排顯示的工具提示」。
    3. 縱排工具提示視窗內的按鍵名稱都使用符號來顯示。
    4. 縱排工具提示視窗內以全形空格來分隔每個漢字的讀音。
    5. 縱排工具提示視窗內僅顯示注音、而不顯示漢語拼音。

    另外,新版工具提示視窗在任何場合都不會遮住使用者正在輸入的文字。

    FSEventStreamHelper

    牽涉版本:威注音 v2.5.0。

    Zonble 這套模組使用了 FSEventStream,但比較麻煩。威注音這次更換了新的 FolderMonitor 監視模組,利用了 DispatchSourceFileSystemObject。這個方法不會偵測由輸入法本體對目錄做出的修改,所以筆者又在 mgrLangModels 當中對「使用者手動加詞」的情況又補上了手動重新載入使用者語彙的動作。這也解決了迄今為止 FSEventStreamHelper 與「威注音的使用者辭典格式自動整理模組」彼此的行為衝突問題。

    VersionUpdateAPI

    牽涉版本:威注音 v2.5.0。

    Zonble 寫的 VersionUpdateAPI 對威注音而言太複雜,於是筆者使用 NSURLSession 重寫了全新的 UpdateSputnik 模組。這樣一來,威注音也實現了「在檢查新版本時,發現沒有新版本」這個情形下的 NSAlert 彈窗提示。

    EOF.

    相关文章

      网友评论

          本文标题:[研發日誌] 威注音輸入法的「去 Zonble 化」日記(一)

          本文链接:https://www.haomeiwen.com/subject/xsxwnrtx.html